"""Base class and shared data structures for all recommendation sub-agents.""" from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from typing import ClassVar @dataclass class AgentInput: """Everything an agent may need to produce its prompt snippet.""" user_id: str tasks: list[dict] # task signal dicts (content, priority, is_overdue, …) profile: dict[str, float | None] # profile feature values keyed by feature name feedback_history: list[dict] = field(default_factory=list) # [{action, dwell_ms, created_at}, …] now: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) @dataclass class AgentOutput: """Result produced by an agent; persisted to agent_outputs table.""" user_id: str agent_id: str prompt_text: str # snippet passed to the orchestrator signals_snapshot: dict # inputs consumed (for explainability / debugging) computed_at: str # ISO 8601 expires_at: str # ISO 8601 agent_version: str class BaseAgent(ABC): agent_id: ClassVar[str] ttl_seconds: ClassVar[int] version: ClassVar[str] @abstractmethod def compute(self, inp: AgentInput) -> AgentOutput: """Analyse inp and return a prompt snippet describing what was found.""" ... def _make_output(self, inp: AgentInput, prompt_text: str, snapshot: dict) -> AgentOutput: computed_at = inp.now.astimezone(timezone.utc).isoformat() expires_at = (inp.now.astimezone(timezone.utc) + timedelta(seconds=self.ttl_seconds)).isoformat() return AgentOutput( user_id=inp.user_id, agent_id=self.agent_id, prompt_text=prompt_text, signals_snapshot=snapshot, computed_at=computed_at, expires_at=expires_at, agent_version=self.version, )