"""Shared contracts and job-local accounting for Agent-mode workers.""" import threading from dataclasses import dataclass, field from typing import Any, Dict, List, Optional, Protocol @dataclass(frozen=True) class AgentScope: """A bounded work package passed to exactly one specialist agent.""" scope_id: str payload: Dict[str, Any] = field(default_factory=dict) @dataclass class AgentResult: """Artifacts returned by a specialist for collection by the orchestrator.""" scope_id: str artifacts: List[Dict[str, Any]] = field(default_factory=list) error: str = "" @dataclass class AgentUsage: """Thread-safe usage accounting owned by one Agent pipeline run.""" usd: float = 0.0 calls: int = 0 cached: int = 0 by_stage: Dict[str, Dict[str, Any]] = field(default_factory=dict) models: Dict[str, set] = field(default_factory=lambda: { "vision": set(), "text_cloud": set(), }) _lock: threading.Lock = field(default_factory=threading.Lock, repr=False) def record( self, stage: str, model: str, usd: float = 0.0, cached: bool = False, has_images: bool = False, ) -> None: with self._lock: bucket = self.by_stage.setdefault( stage, {"usd": 0.0, "calls": 0, "cached": 0} ) if cached: self.cached += 1 bucket["cached"] += 1 else: self.calls += 1 self.usd += usd bucket["calls"] += 1 bucket["usd"] += usd family = "vision" if has_images else "text_cloud" self.models[family].add(model) def snapshot(self) -> Dict[str, Any]: with self._lock: return { "usd": self.usd, "calls": self.calls, "cached": self.cached, "by_stage": {k: dict(v) for k, v in self.by_stage.items()}, "models": { "vision": sorted(self.models["vision"]), "text_local": [], "text_cloud": sorted(self.models["text_cloud"]), "fallback_count": 0, }, } class ScopedAgent(Protocol): """Protocol implemented by each future specialist agent.""" name: str def run(self, scope: AgentScope) -> AgentResult: ... def failure(scope: AgentScope, error: Exception) -> AgentResult: """Convert a worker exception into a non-fatal scoped result.""" return AgentResult(scope_id=scope.scope_id, error=str(error))