""" 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, ProjectType, 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} # ─── Deploy Server Schemas ───────────────────────────────────────────────────── class DeployServerCreate(BaseModel): name: str = Field(..., min_length=1, max_length=200) host: str = Field(..., min_length=1) user: str = "deploy" password: Optional[str] = None ssh_port: int = 22 description: str = "" class DeployServerResponse(BaseModel): id: int name: str host: str user: str ssh_port: int description: str created_at: datetime model_config = {"from_attributes": True} # ─── Git Server Schemas ──────────────────────────────────────────────────────── class GitServerCreate(BaseModel): name: str = Field(..., min_length=1, max_length=200) url: str = Field(..., min_length=1) token: Optional[str] = None org: str = "openclaw" description: str = "" class GitServerResponse(BaseModel): id: int name: str url: str org: str description: str created_at: datetime model_config = {"from_attributes": True} # ─── Project Type Info ───────────────────────────────────────────────────────── class ProjectTypeInfo(BaseModel): value: str emoji: str label: str description: str # ─── Project Schemas ─────────────────────────────────────────────────────────── class ProjectCreate(BaseModel): name: str = Field(..., min_length=1, max_length=200) description: str = "" workflow_type: WorkflowType = WorkflowType.SOFTWARE_DESIGN project_type: ProjectType = ProjectType.DOCKER_WEBAPP test_mode: bool = False install_path: Optional[str] = None deploy_server_id: Optional[int] = None git_server_id: Optional[int] = None class ProjectUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None workflow_type: Optional[WorkflowType] = None project_type: Optional[ProjectType] = None install_path: Optional[str] = None deploy_server_id: Optional[int] = None git_server_id: Optional[int] = None class ProjectSummary(BaseModel): id: int name: str slug: str status: ProjectStatus workflow_type: WorkflowType project_type: ProjectType test_mode: bool install_path: Optional[str] task_count: int = 0 tasks_done: int = 0 deploy_server_name: Optional[str] = None git_server_name: Optional[str] = None 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 project_type: ProjectType test_mode: bool install_path: Optional[str] gitea_repo: Optional[str] deployment_target: Optional[str] deploy_server_id: Optional[int] git_server_id: Optional[int] deploy_server_name: Optional[str] = None git_server_name: Optional[str] = None 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