feat(clustering): 1h TTL + skip recompute when tasks unchanged

focus-area now recomputes at most once per hour, and only if the task list
actually changed since the last compute.

- focus-area TTL: 43200s → 3600s; version bumped to 2.1.0
- computeAndStore hashes sorted task contents (MD5) and checks the stored
  _task_hash in the existing snapshot; skips the ml-serving call when the
  hash matches and the output isn't expired
- ml-serving injects _task_hash into the snapshot so the next cycle can compare

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 14:45:15 +00:00
parent 9ddeea6cac
commit d12f11d29d
4 changed files with 38 additions and 5 deletions

View File

@@ -199,6 +199,9 @@ class AgentComputeRequest(BaseModel):
# Pre-fetched enrichment cache: {content_hash -> description}. Avoids re-calling
# LiteLLM for task titles already expanded in a prior compute cycle.
enrichment_cache: dict[str, str] = {}
# MD5 of sorted task contents; stored in snapshot so the next cycle can skip
# recompute when the task list hasn't changed.
task_hash: Optional[str] = None
class AgentComputeResponse(BaseModel):
@@ -327,6 +330,8 @@ async def compute_agent(agent_id: str, req: AgentComputeRequest) -> AgentCompute
log.error("agent_compute_failed", agent_id=agent_id, user_id=req.user_id, error=str(exc))
raise HTTPException(status_code=500, detail=f"Agent compute failed: {exc}")
if req.task_hash:
output.signals_snapshot["_task_hash"] = req.task_hash
new_enrichments: dict[str, str] = output.signals_snapshot.pop("_new_enrichments", {})
log.info("agent_computed", agent_id=agent_id, user_id=req.user_id, expires_at=output.expires_at)