71 lines
2.6 KiB
Python
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)
|