Step 4 — /api/profile read-through API:
GET /api/profile → { user, prefs, consents, contexts }
PATCH /api/profile/prefs/:scope upsert user_preferences (source='user')
PATCH /api/profile/consents grant / revoke consent keys
PATCH /api/profile/contexts create / activate / deactivate contexts
Legacy consentGiven bit folded in as data:core fallback.
Step 5 — registry-driven eligibility filter:
fetchRegistry() exported from agent-registry.ts.
profile/eligibility.ts: getEligibleAgentIds(userId) — filters by required
consents, silenced_in_contexts, and user_preferences[enabled=false].
fetchOrchestratorTip filters agent_outputs to eligible set before calling
ml/serving /recommend. Fail-closed: registry unavailable → empty set.
Step 6 — shared context-inference framework (#111) + time-of-day proof (#112):
ml/agents/inference/: UserHistory, FeedbackEvent, run_inference().
Framework: cold-start, min_history gating, error fallback, structured logs.
TimeOfDayAgent v1.1.0: inferred_params=[preferred_hour]; also reads
quiet_start/quiet_end from agent_prefs. agent_prefs injected by TS caller.
AgentInput gains agent_prefs field.
ml/serving: POST /agents/{agent_id}/infer endpoint.
agent-outputs.ts computeAndStore: loads prefs before compute, calls /infer
after, persists results (source='inferred'); user overrides never touched.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
30 lines
848 B
Python
30 lines
848 B
Python
"""UserHistory — normalised view of a user's feedback events for inference."""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
@dataclass
|
|
class FeedbackEvent:
|
|
action: str # 'done' | 'dismiss' | 'snooze' | 'helpful' | 'not_helpful'
|
|
dwell_ms: int | None
|
|
created_at: str # ISO 8601
|
|
|
|
@property
|
|
def hour(self) -> int:
|
|
"""Hour of day (0-23) when the feedback was recorded."""
|
|
try:
|
|
dt = datetime.fromisoformat(self.created_at.replace("Z", "+00:00"))
|
|
except ValueError:
|
|
return 12
|
|
if dt.tzinfo is None:
|
|
dt = dt.replace(tzinfo=timezone.utc)
|
|
return dt.hour
|
|
|
|
|
|
@dataclass
|
|
class UserHistory:
|
|
user_id: str
|
|
events: list[FeedbackEvent] = field(default_factory=list)
|