""" Pydantic schemas for API request/response serialization. """ from datetime import datetime from typing import Optional, List, Any from pydantic import BaseModel, Field from app.models import ( ProjectStatus, WorkflowType, TaskStatus, TaskType, TaskPriority, AgentExecutionStatus, ) # ─── Task Schemas ────────────────────────────────────────────────────────────── class TaskCreate(BaseModel): task_id: str = Field(..., examples=["TASK-001"]) type: TaskType = TaskType.BACKEND title: str priority: TaskPriority = TaskPriority.P3 assigned_to: Optional[str] = None dependencies: List[str] = [] acceptance_criteria: Optional[str] = None class TaskResponse(BaseModel): id: int project_id: int task_id: str type: TaskType title: str priority: TaskPriority assigned_to: Optional[str] status: TaskStatus dependencies: Any acceptance_criteria: Optional[str] agent_payloads: Any created_at: datetime updated_at: datetime model_config = {"from_attributes": True} # ─── AuditLog Schemas ───────────────────────────────────────────────────────── class AuditLogResponse(BaseModel): id: int project_id: int timestamp: datetime agent: str action: str target: Optional[str] message: Optional[str] source: str model_config = {"from_attributes": True} # ─── AgentExecution Schemas ──────────────────────────────────────────────────── class AgentExecutionResponse(BaseModel): id: int project_id: int agent_name: str status: AgentExecutionStatus pid: Optional[int] started_at: datetime finished_at: Optional[datetime] exit_code: Optional[int] error_output: Optional[str] model_config = {"from_attributes": True} # ─── Project Schemas ─────────────────────────────────────────────────────────── class ProjectCreate(BaseModel): name: str = Field(..., min_length=1, max_length=200) description: str = "" workflow_type: WorkflowType = WorkflowType.SOFTWARE_DESIGN test_mode: bool = False class ProjectUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None workflow_type: Optional[WorkflowType] = None class ProjectSummary(BaseModel): id: int name: str slug: str status: ProjectStatus workflow_type: WorkflowType test_mode: bool task_count: int = 0 tasks_done: int = 0 created_at: datetime updated_at: datetime model_config = {"from_attributes": True} class ProjectDetail(BaseModel): id: int name: str slug: str description: str status: ProjectStatus workflow_type: WorkflowType test_mode: bool gitea_repo: Optional[str] deployment_target: Optional[str] created_at: datetime updated_at: datetime tasks: List[TaskResponse] = [] audit_logs: List[AuditLogResponse] = [] agent_executions: List[AgentExecutionResponse] = [] model_config = {"from_attributes": True} # ─── Agent Schemas ───────────────────────────────────────────────────────────── class AgentStatus(BaseModel): name: str display_name: str model: str current_status: str = "idle" # idle | running | failed current_project: Optional[str] = None total_executions: int = 0 success_count: int = 0 failure_count: int = 0 # ─── Config Schemas ──────────────────────────────────────────────────────────── class ConfigResponse(BaseModel): OPENCLAW_WORKSPACE: str GITEA_SERVER: str DEPLOYMENT_SERVER: str DEPLOYMENT_USER: str TELEGRAM_CHAT_ID: str LOG_LEVEL: str # Secrets are masked GITEA_OPENCLAW_TOKEN: str = "***" DEPLOYMENT_PWD: str = "***" TELEGRAM_BOT_TOKEN: str = "***" class ConfigUpdate(BaseModel): OPENCLAW_WORKSPACE: Optional[str] = None GITEA_SERVER: Optional[str] = None DEPLOYMENT_SERVER: Optional[str] = None DEPLOYMENT_USER: Optional[str] = None LOG_LEVEL: Optional[str] = None # ─── WebSocket Messages ─────────────────────────────────────────────────────── class WSMessage(BaseModel): type: str # "agent_status" | "log" | "project_update" data: Any