Files
oO/ml/agents/focus_area.py
2026-05-12 15:00:06 +00:00

71 lines
2.6 KiB
Python

from __future__ import annotations
from typing import ClassVar
from .base import BaseAgent, AgentInput, AgentOutput
from .clustering import cluster_tasks
from .manifest import AgentManifest
MANIFEST = AgentManifest(
id="focus-area",
version="3.0.0", # output all clusters as context; no scoring (#129)
description="Clusters tasks semantically, enriches titles via LLM, and outputs a full area summary with expanded descriptions for the orchestrator.",
pref_schema={"type": "object", "additionalProperties": False, "properties": {}},
context_schema=["todoist.tasks"],
required_consents=["data:core", "data:todoist"],
output_contract={"type": "snippet", "format": "free_text"},
ttl_sec=86_400,
inferred_params=[],
)
class FocusAreaAgent(BaseAgent):
"""Clusters tasks and outputs a full area summary for the orchestrator."""
agent_id: ClassVar[str] = MANIFEST.id
ttl_seconds: ClassVar[int] = MANIFEST.ttl_sec
version: ClassVar[str] = MANIFEST.version # 3.0.0
def compute(self, inp: AgentInput) -> AgentOutput:
if not inp.tasks:
return self._make_output(
inp,
"No tasks available to identify focus areas.",
{"cluster_count": 0},
)
clusters, new_enrichments = cluster_tasks(inp.tasks, enrichment_cache=inp.enrichment_cache)
if not clusters:
return self._make_output(
inp,
"No tasks available to identify focus areas.",
{"cluster_count": 0},
)
lines = [f"The user's tasks are grouped into {len(clusters)} area(s):"]
for i, cluster in enumerate(clusters, 1):
descs = [
t.get("enriched_description") or t.get("content", "")
for t in cluster.tasks
if t.get("content")
]
descs = [d.strip() for d in descs if d.strip()]
descs_str = "; ".join(f'"{d}"' for d in descs[:8])
if len(descs) > 8:
descs_str += f" (and {len(descs) - 8} more)"
lines.append(f"{i}. {cluster.label}{cluster.task_count} task(s): {descs_str}")
lines.append("(Task titles may be in any language — always write the tip in English.)")
snapshot = {
"cluster_count": len(clusters),
"clusters": [
{"label": c.label, "task_count": c.task_count,
"tasks": [t.get("content", "") for t in c.tasks]}
for c in clusters
],
"_new_enrichments": new_enrichments,
}
return self._make_output(inp, "\n".join(lines), snapshot)