from __future__ import annotations from datetime import datetime from typing import Any, Literal from pydantic import BaseModel, Field, model_validator class WorkAssignmentPayload(BaseModel): kind: Literal[ "account", "group", "role", "function", "function_assignment", "anyone", ] id: str = Field(min_length=1, max_length=255) label: str | None = Field(default=None, max_length=500) class WorkSourcePayload(BaseModel): module_id: str = Field(min_length=1, max_length=100) resource_type: str = Field(min_length=1, max_length=100) resource_id: str = Field(min_length=1, max_length=255) revision: str | None = Field(default=None, max_length=255) url: str | None = Field(default=None, max_length=1500) label: str | None = Field(default=None, max_length=500) class WorkItemResponse(BaseModel): id: str provider_id: str owner_module: str tenant_id: str title: str status: str priority: str summary: str | None = None required_action: str | None = None action_url: str | None = None due_at: datetime | None = None deferred_until: datetime | None = None assignments: list[WorkAssignmentPayload] = Field(default_factory=list) sources: list[WorkSourcePayload] = Field(default_factory=list) provenance: dict[str, Any] = Field(default_factory=dict) metadata: dict[str, Any] = Field(default_factory=dict) revision: str etag: str | None = None created_at: datetime | None = None updated_at: datetime | None = None class WorkProviderDiagnostic(BaseModel): provider_id: str owner_module: str code: str message: str class WorkItemListResponse(BaseModel): items: list[WorkItemResponse] total: int truncated: bool = False diagnostics: list[WorkProviderDiagnostic] = Field(default_factory=list) class WorkSummaryResponse(BaseModel): total: int open: int in_progress: int deferred: int blocked: int overdue: int urgent: int truncated: bool = False diagnostics: list[WorkProviderDiagnostic] = Field(default_factory=list) class TaskCreatePayload(BaseModel): title: str = Field(min_length=1, max_length=500) summary: str | None = Field(default=None, max_length=4000) priority: Literal["low", "normal", "high", "urgent"] = "normal" due_at: datetime | None = None required_action: str | None = Field(default=None, max_length=500) action_url: str | None = Field(default=None, max_length=1500) assignments: list[WorkAssignmentPayload] = Field(min_length=1, max_length=100) sources: list[WorkSourcePayload] = Field(default_factory=list, max_length=100) provenance: dict[str, Any] = Field(default_factory=dict) metadata: dict[str, Any] = Field(default_factory=dict) idempotency_key: str = Field(min_length=1, max_length=255) class TaskActionPayload(BaseModel): action: Literal["start", "complete", "defer", "reopen", "cancel"] expected_revision: int = Field(ge=1) deferred_until: datetime | None = None comment: str | None = Field(default=None, max_length=4000) @model_validator(mode="after") def validate_action(self) -> "TaskActionPayload": if self.action == "defer" and self.deferred_until is None: raise ValueError("Deferring a task requires a date and time.") if self.action != "defer" and self.deferred_until is not None: raise ValueError("Only a defer action accepts deferred_until.") return self __all__ = [ "TaskActionPayload", "TaskCreatePayload", "WorkAssignmentPayload", "WorkItemListResponse", "WorkItemResponse", "WorkProviderDiagnostic", "WorkSourcePayload", "WorkSummaryResponse", ]