wire.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. """Stable wire models shared by the V2 HTTP API and Python client."""
  2. from __future__ import annotations
  3. from dataclasses import asdict
  4. from typing import Annotated, Any, Dict, List, Literal, Optional, Union
  5. from pydantic import BaseModel, ConfigDict, Field
  6. from agent.failures import FailureDetail
  7. from .models import (
  8. ArtifactSnapshot,
  9. BackgroundOperation,
  10. AttemptStatus,
  11. DecisionAction,
  12. FailureCode,
  13. OperationKind,
  14. OperationStatus,
  15. OrchestrationEvent,
  16. PlannerDecision,
  17. TaskAttempt,
  18. TaskRecord,
  19. TaskStatus,
  20. ValidationReport,
  21. ValidationMode,
  22. ValidationRunStatus,
  23. ValidationVerdict,
  24. json_values,
  25. )
  26. class WireModel(BaseModel):
  27. model_config = ConfigDict(extra="forbid")
  28. class ResourceClaimView(WireModel):
  29. uri: str
  30. exclusive: bool = True
  31. class DispatchOperationRequest(WireModel):
  32. kind: Literal["dispatch"] = "dispatch"
  33. task_ids: List[Annotated[str, Field(min_length=1)]] = Field(min_length=1)
  34. worker_presets: Optional[List[str]] = None
  35. resource_claims: List["ResourceClaimView"] = Field(default_factory=list)
  36. deadline_at: Optional[str] = None
  37. class RevalidateOperationRequest(WireModel):
  38. kind: Literal["revalidate"] = "revalidate"
  39. task_id: str = Field(min_length=1)
  40. attempt_id: str = Field(min_length=1)
  41. deadline_at: Optional[str] = None
  42. OperationRequest = Annotated[
  43. Union[DispatchOperationRequest, RevalidateOperationRequest],
  44. Field(discriminator="kind"),
  45. ]
  46. class OperationView(WireModel):
  47. operation_id: str
  48. root_trace_id: str
  49. kind: OperationKind
  50. status: OperationStatus
  51. task_ids: List[str]
  52. attempt_ids: List[str]
  53. validation_ids: List[str]
  54. resource_claims: List[ResourceClaimView] = Field(default_factory=list)
  55. result_ref: Optional[Dict[str, Any]] = None
  56. execution_epoch: int
  57. deadline_at: Optional[str] = None
  58. error: Optional[str] = None
  59. started_at: Optional[str] = None
  60. completed_at: Optional[str] = None
  61. created_at: str
  62. updated_at: str
  63. @classmethod
  64. def from_domain(cls, value: BackgroundOperation) -> "OperationView":
  65. data = json_values(asdict(value))
  66. data.pop("request", None)
  67. data.pop("request_fingerprint", None)
  68. return cls.model_validate(data)
  69. class AcceptanceCriterionView(WireModel):
  70. criterion_id: str
  71. description: str
  72. hard: bool
  73. class TaskSpecView(WireModel):
  74. version: int
  75. objective: str
  76. acceptance_criteria: List[AcceptanceCriterionView]
  77. context_refs: List[str]
  78. created_at: str
  79. class ArtifactRefView(WireModel):
  80. uri: str
  81. kind: str
  82. version: Optional[str] = None
  83. digest: Optional[str] = None
  84. summary: Optional[str] = None
  85. metadata: Dict[str, Any]
  86. class AttemptSubmissionView(WireModel):
  87. summary: str
  88. artifact_refs: List[ArtifactRefView]
  89. evidence_refs: List[ArtifactRefView]
  90. class CriterionResultView(WireModel):
  91. criterion_id: str
  92. verdict: ValidationVerdict
  93. reason: str
  94. evidence_refs: List[ArtifactRefView]
  95. class ValidationPlanView(WireModel):
  96. mode: ValidationMode
  97. validator_preset: Optional[str] = None
  98. rule_ids: List[str]
  99. preflight_rule_ids: List[str]
  100. max_evidence_queries: int
  101. max_evidence_items_per_query: int
  102. evidence_timeout_seconds: float
  103. class ValidationEvidenceGrantView(WireModel):
  104. artifact_ref: ArtifactRefView
  105. handle: str
  106. source: str
  107. granted_at: str
  108. class ValidationEvidenceSnapshotView(WireModel):
  109. revision: int
  110. grants: List[ValidationEvidenceGrantView]
  111. updated_at: str
  112. class ValidationReadSessionView(WireModel):
  113. evidence_revision: int
  114. required_handles: List[str]
  115. authorized_handles: List[str]
  116. completed_handles: List[str]
  117. next_cursors: Dict[str, Optional[str]]
  118. active_reads: Dict[str, Optional[str]]
  119. read_results: Dict[str, Dict[str, Any]]
  120. class ExecutionStatsView(WireModel):
  121. primary_model: Optional[str] = None
  122. total_tokens: Optional[int] = None
  123. total_cost: Optional[float] = None
  124. failure_code: Optional[FailureCode] = None
  125. class FailureDetailView(WireModel):
  126. code: str
  127. message: str
  128. disposition: str
  129. source_tool: Optional[str] = None
  130. details: Dict[str, Any]
  131. @classmethod
  132. def from_domain(cls, value: FailureDetail) -> "FailureDetailView":
  133. return cls.model_validate(value.to_dict())
  134. class TaskView(WireModel):
  135. task_id: str
  136. # Deprecated compatibility field. Explicit ledgers no longer persist or
  137. # project GoalTree identities, so new responses always expose ``None``.
  138. goal_id: Optional[str] = None
  139. parent_task_id: Optional[str] = None
  140. display_path: str
  141. specs: List[TaskSpecView]
  142. current_spec_version: int
  143. status: TaskStatus
  144. child_task_ids: List[str]
  145. attempt_ids: List[str]
  146. validation_ids: List[str]
  147. decision_ids: List[str]
  148. repair_count_by_version: Dict[str, int]
  149. blocked_reason: Optional[str] = None
  150. superseded_by: Optional[str] = None
  151. created_at: str
  152. updated_at: str
  153. @classmethod
  154. def from_domain(cls, value: TaskRecord) -> "TaskView":
  155. return cls.model_validate(json_values(asdict(value)))
  156. class AttemptView(WireModel):
  157. attempt_id: str
  158. task_id: str
  159. spec_version: int
  160. worker_trace_id: str
  161. worker_preset: str
  162. execution_mode: str
  163. accepted_child_decision_ids: Optional[List[str]] = None
  164. status: AttemptStatus
  165. operation_id: Optional[str] = None
  166. execution_epoch: int
  167. continue_from_trace_id: Optional[str] = None
  168. snapshot_id: Optional[str] = None
  169. submission: Optional[AttemptSubmissionView] = None
  170. execution_stats: Optional[ExecutionStatsView] = None
  171. error: Optional[str] = None
  172. failure: Optional[FailureDetailView] = None
  173. worker_summary: str = ""
  174. started_at: Optional[str] = None
  175. completed_at: Optional[str] = None
  176. duration_ms: Optional[int] = None
  177. created_at: str
  178. updated_at: str
  179. @classmethod
  180. def from_domain(cls, value: TaskAttempt) -> "AttemptView":
  181. data = json_values(asdict(value))
  182. data["duration_ms"] = value.duration_ms
  183. return cls.model_validate(data)
  184. class ValidationView(WireModel):
  185. validation_id: str
  186. task_id: str
  187. attempt_id: str
  188. spec_version: int
  189. snapshot_id: str
  190. validator_trace_id: str
  191. validator_preset: str
  192. validation_plan: ValidationPlanView
  193. evidence_queries_used: int
  194. evidence_snapshot: Optional[ValidationEvidenceSnapshotView] = None
  195. read_session: Optional[ValidationReadSessionView] = None
  196. status: ValidationRunStatus
  197. operation_id: Optional[str] = None
  198. execution_epoch: int
  199. verdict: Optional[ValidationVerdict] = None
  200. criterion_results: List[CriterionResultView]
  201. summary: str
  202. evidence_refs: List[ArtifactRefView]
  203. unverified_claims: List[str]
  204. risks: List[str]
  205. recommendation: str
  206. execution_stats: Optional[ExecutionStatsView] = None
  207. error: Optional[str] = None
  208. failure: Optional[FailureDetailView] = None
  209. started_at: Optional[str] = None
  210. completed_at: Optional[str] = None
  211. duration_ms: Optional[int] = None
  212. created_at: str
  213. updated_at: str
  214. @classmethod
  215. def from_domain(cls, value: ValidationReport) -> "ValidationView":
  216. data = json_values(asdict(value))
  217. data.pop("evidence_query_results", None)
  218. data["duration_ms"] = value.duration_ms
  219. return cls.model_validate(data)
  220. class PlannerDecisionView(WireModel):
  221. decision_id: str
  222. task_id: str
  223. action: DecisionAction
  224. reason: str
  225. from_status: TaskStatus
  226. to_status: TaskStatus
  227. attempt_id: Optional[str] = None
  228. validation_id: Optional[str] = None
  229. payload: Dict[str, Any]
  230. created_at: str
  231. @classmethod
  232. def from_domain(cls, value: PlannerDecision) -> "PlannerDecisionView":
  233. return cls.model_validate(json_values(asdict(value)))
  234. class MissionSnapshotView(WireModel):
  235. schema_version: Literal[1] = 1
  236. revision: int
  237. root_trace_id: str
  238. root_task_id: Optional[str] = None
  239. root_objective: str
  240. focused_task_id: Optional[str] = None
  241. tasks: List[TaskView]
  242. attempts: List[AttemptView]
  243. validations: List[ValidationView]
  244. decisions: List[PlannerDecisionView]
  245. operations: List[OperationView]
  246. class RootCompletionView(WireModel):
  247. root_task_id: str
  248. root_objective: str
  249. status: TaskStatus
  250. blocked_reason: Optional[str] = None
  251. result_summary: Optional[str] = None
  252. class ArtifactSnapshotView(WireModel):
  253. snapshot_id: str
  254. attempt_id: str
  255. sha256: str
  256. artifact_refs: List[ArtifactRefView]
  257. evidence_refs: List[ArtifactRefView]
  258. created_at: str
  259. @classmethod
  260. def from_domain(cls, value: ArtifactSnapshot) -> "ArtifactSnapshotView":
  261. return cls(
  262. snapshot_id=value.snapshot_id,
  263. attempt_id=value.attempt_id,
  264. sha256=value.sha256,
  265. artifact_refs=json_values([asdict(item) for item in value.artifact_refs]),
  266. evidence_refs=json_values([asdict(item) for item in value.evidence_refs]),
  267. created_at=value.created_at,
  268. )
  269. class TaskList(WireModel):
  270. revision: int
  271. root_task_id: Optional[str] = None
  272. items: List[TaskView]
  273. class AttemptList(WireModel):
  274. revision: int
  275. items: List[AttemptView]
  276. class ValidationList(WireModel):
  277. revision: int
  278. items: List[ValidationView]
  279. class EventView(WireModel):
  280. schema_version: int
  281. event_id: str
  282. sequence: int
  283. root_trace_id: str
  284. ledger_revision: int
  285. event_type: str
  286. occurred_at: str
  287. payload: Dict[str, Any]
  288. command_id: Optional[str] = None
  289. operation_id: Optional[str] = None
  290. @classmethod
  291. def from_domain(cls, value: OrchestrationEvent) -> "EventView":
  292. return cls.model_validate(json_values(asdict(value)))
  293. class EventPageView(WireModel):
  294. events: List[EventView]
  295. next_cursor: Optional[str] = None
  296. has_more: bool = False
  297. class CapabilitiesView(WireModel):
  298. api_version: Literal["v2"] = "v2"
  299. mission_snapshot_schema_version: Literal[1] = 1
  300. event_schema_version: Literal[2] = 2
  301. capabilities: List[str]
  302. class ProblemDetails(WireModel):
  303. type: str = "about:blank"
  304. title: str
  305. status: int
  306. detail: str
  307. code: str
  308. instance: Optional[str] = None
  309. __all__ = [
  310. "ArtifactSnapshotView",
  311. "AttemptList",
  312. "AttemptView",
  313. "CapabilitiesView",
  314. "DispatchOperationRequest",
  315. "EventPageView",
  316. "EventView",
  317. "ExecutionStatsView",
  318. "MissionSnapshotView",
  319. "OperationRequest",
  320. "OperationView",
  321. "PlannerDecisionView",
  322. "ProblemDetails",
  323. "RevalidateOperationRequest",
  324. "RootCompletionView",
  325. "TaskList",
  326. "TaskView",
  327. "ValidationList",
  328. "ValidationView",
  329. ]