feat(features): per-feature freshness spec — JIT vs batched (#61)

Each ml/features/*.py now declares freshness, source, and fallback per
feature. ProfileFeature gains ttl_sec (mirrored from registry.ts),
freshness="batched", source, and fallback. context.py adds
ContextFeatureSpec + CONTEXT_FEATURES for the three JIT features
(hour_of_day, day_of_week, tasks). CI test parses ttlSec from registry.ts
to catch drift. ml/README updated with split JIT/batched feature contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 17:02:55 +00:00
parent bd3ea1b8b1
commit 45416000f9
6 changed files with 218 additions and 21 deletions

View File

@@ -2,12 +2,56 @@
Context assembler — converts raw user signals into a PromptContext for LLM tip generation.
Usage:
from ml.features.context import build_context
from ml.features.context import build_context, CONTEXT_FEATURES
ctx = build_context(tasks, hour_of_day=9, day_of_week=2)
Feature-spec (issue #61):
All context features are JIT — they are assembled at request time from live
sources (system clock, caller-supplied task list) rather than read from a
cached profile store. They carry no TTL because they are never persisted.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Literal
@dataclass(frozen=True)
class ContextFeatureSpec:
name: str
dtype: Literal["numeric", "categorical", "list"]
freshness: Literal["jit", "batched"]
source: str
fallback: str
description: str
CONTEXT_FEATURES: tuple[ContextFeatureSpec, ...] = (
ContextFeatureSpec(
name="hour_of_day",
dtype="numeric",
freshness="jit",
source="request",
fallback="12",
description="Current hour (023), supplied by the caller at score time.",
),
ContextFeatureSpec(
name="day_of_week",
dtype="numeric",
freshness="jit",
source="request",
fallback="0",
description="ISO weekday (0=Monday … 6=Sunday), supplied by the caller at score time.",
),
ContextFeatureSpec(
name="tasks",
dtype="list",
freshness="jit",
source="todoist-integration",
fallback="[]",
description="User's open tasks fetched live from the Todoist integration at request time.",
),
)
@dataclass