refactor: update module docstrings for clarity and consistency
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
"""Public schema exports shared across API route modules."""
|
||||
|
||||
from app.schemas.activity_events import ActivityEventRead
|
||||
from app.schemas.agents import AgentCreate, AgentRead, AgentUpdate
|
||||
from app.schemas.approvals import ApprovalCreate, ApprovalRead, ApprovalUpdate
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
"""Response schemas for activity events and task-comment feed items."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class ActivityEventRead(SQLModel):
|
||||
"""Serialized activity event payload returned by activity endpoints."""
|
||||
|
||||
id: UUID
|
||||
event_type: str
|
||||
message: str | None
|
||||
@@ -16,6 +20,8 @@ class ActivityEventRead(SQLModel):
|
||||
|
||||
|
||||
class ActivityTaskCommentFeedItemRead(SQLModel):
|
||||
"""Denormalized task-comment feed item enriched with task and board fields."""
|
||||
|
||||
id: UUID
|
||||
created_at: datetime
|
||||
message: str | None
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
"""Schemas for approval create/update/read API payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from typing import Literal, Self
|
||||
from uuid import UUID
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from pydantic import model_validator
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
ApprovalStatus = Literal["pending", "approved", "rejected"]
|
||||
STATUS_REQUIRED_ERROR = "status is required"
|
||||
|
||||
|
||||
class ApprovalBase(SQLModel):
|
||||
"""Shared approval fields used across create/read payloads."""
|
||||
|
||||
action_type: str
|
||||
task_id: UUID | None = None
|
||||
payload: dict[str, object] | None = None
|
||||
@@ -20,20 +25,27 @@ class ApprovalBase(SQLModel):
|
||||
|
||||
|
||||
class ApprovalCreate(ApprovalBase):
|
||||
"""Payload for creating a new approval request."""
|
||||
|
||||
agent_id: UUID | None = None
|
||||
|
||||
|
||||
class ApprovalUpdate(SQLModel):
|
||||
"""Payload for mutating approval status."""
|
||||
|
||||
status: ApprovalStatus | None = None
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_status(self) -> Self:
|
||||
"""Ensure explicitly provided `status` is not null."""
|
||||
if "status" in self.model_fields_set and self.status is None:
|
||||
raise ValueError("status is required")
|
||||
raise ValueError(STATUS_REQUIRED_ERROR)
|
||||
return self
|
||||
|
||||
|
||||
class ApprovalRead(ApprovalBase):
|
||||
"""Approval payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
board_id: UUID
|
||||
agent_id: UUID | None = None
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
"""Schemas for applying heartbeat settings to board-group agents."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class BoardGroupHeartbeatApply(SQLModel):
|
||||
# Heartbeat cadence string understood by the OpenClaw gateway (e.g. "2m", "10m", "30m").
|
||||
"""Request payload for heartbeat policy updates."""
|
||||
|
||||
# Heartbeat cadence string understood by the OpenClaw gateway
|
||||
# (e.g. "2m", "10m", "30m").
|
||||
every: str
|
||||
# Optional heartbeat target (most deployments use "none").
|
||||
target: str | None = None
|
||||
@@ -15,6 +20,8 @@ class BoardGroupHeartbeatApply(SQLModel):
|
||||
|
||||
|
||||
class BoardGroupHeartbeatApplyResult(SQLModel):
|
||||
"""Result payload describing agents updated by a heartbeat request."""
|
||||
|
||||
board_group_id: UUID
|
||||
requested: dict[str, Any]
|
||||
updated_agent_ids: list[UUID]
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
"""Schemas for board-group memory create/read API payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
from app.schemas.common import NonEmptyStr
|
||||
from app.schemas.common import NonEmptyStr # noqa: TCH001
|
||||
|
||||
|
||||
class BoardGroupMemoryCreate(SQLModel):
|
||||
"""Payload for creating a board-group memory entry."""
|
||||
|
||||
# For writes, reject blank/whitespace-only content.
|
||||
content: NonEmptyStr
|
||||
tags: list[str] | None = None
|
||||
@@ -16,9 +20,12 @@ class BoardGroupMemoryCreate(SQLModel):
|
||||
|
||||
|
||||
class BoardGroupMemoryRead(SQLModel):
|
||||
"""Serialized board-group memory entry returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
board_group_id: UUID
|
||||
# For reads, allow legacy rows that may have empty content (avoid response validation 500s).
|
||||
# For reads, allow legacy rows that may have empty content
|
||||
# (avoid response validation 500s).
|
||||
content: str
|
||||
tags: list[str] | None = None
|
||||
source: str | None = None
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
"""Schemas for board-group create/update/read API operations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class BoardGroupBase(SQLModel):
|
||||
"""Shared board-group fields for create/read operations."""
|
||||
|
||||
name: str
|
||||
slug: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class BoardGroupCreate(BoardGroupBase):
|
||||
pass
|
||||
"""Payload for creating a board group."""
|
||||
|
||||
|
||||
class BoardGroupUpdate(SQLModel):
|
||||
"""Payload for partial board-group updates."""
|
||||
|
||||
name: str | None = None
|
||||
slug: str | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class BoardGroupRead(BoardGroupBase):
|
||||
"""Board-group payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
organization_id: UUID
|
||||
created_at: datetime
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
"""Schemas for board memory create/read API payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
from app.schemas.common import NonEmptyStr
|
||||
from app.schemas.common import NonEmptyStr # noqa: TCH001
|
||||
|
||||
|
||||
class BoardMemoryCreate(SQLModel):
|
||||
"""Payload for creating a board memory entry."""
|
||||
|
||||
# For writes, reject blank/whitespace-only content.
|
||||
content: NonEmptyStr
|
||||
tags: list[str] | None = None
|
||||
@@ -16,9 +20,12 @@ class BoardMemoryCreate(SQLModel):
|
||||
|
||||
|
||||
class BoardMemoryRead(SQLModel):
|
||||
"""Serialized board memory entry returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
board_id: UUID
|
||||
# For reads, allow legacy rows that may have empty content (avoid response validation 500s).
|
||||
# For reads, allow legacy rows that may have empty content
|
||||
# (avoid response validation 500s).
|
||||
content: str
|
||||
tags: list[str] | None = None
|
||||
source: str | None = None
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
"""Schemas for board create/update/read API operations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from typing import Self
|
||||
from uuid import UUID
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from pydantic import model_validator
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
_ERR_GOAL_FIELDS_REQUIRED = (
|
||||
"Confirmed goal boards require objective and success_metrics"
|
||||
)
|
||||
_ERR_GATEWAY_REQUIRED = "gateway_id is required"
|
||||
|
||||
|
||||
class BoardBase(SQLModel):
|
||||
"""Shared board fields used across create and read payloads."""
|
||||
|
||||
name: str
|
||||
slug: str
|
||||
gateway_id: UUID | None = None
|
||||
@@ -22,17 +31,25 @@ class BoardBase(SQLModel):
|
||||
|
||||
|
||||
class BoardCreate(BoardBase):
|
||||
"""Payload for creating a board."""
|
||||
|
||||
gateway_id: UUID
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_goal_fields(self) -> Self:
|
||||
if self.board_type == "goal" and self.goal_confirmed:
|
||||
if not self.objective or not self.success_metrics:
|
||||
raise ValueError("Confirmed goal boards require objective and success_metrics")
|
||||
"""Require goal details when creating a confirmed goal board."""
|
||||
if (
|
||||
self.board_type == "goal"
|
||||
and self.goal_confirmed
|
||||
and (not self.objective or not self.success_metrics)
|
||||
):
|
||||
raise ValueError(_ERR_GOAL_FIELDS_REQUIRED)
|
||||
return self
|
||||
|
||||
|
||||
class BoardUpdate(SQLModel):
|
||||
"""Payload for partial board updates."""
|
||||
|
||||
name: str | None = None
|
||||
slug: str | None = None
|
||||
gateway_id: UUID | None = None
|
||||
@@ -46,13 +63,16 @@ class BoardUpdate(SQLModel):
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_gateway_id(self) -> Self:
|
||||
"""Reject explicit null gateway IDs in patch payloads."""
|
||||
# Treat explicit null like "unset" is invalid for patch updates.
|
||||
if "gateway_id" in self.model_fields_set and self.gateway_id is None:
|
||||
raise ValueError("gateway_id is required")
|
||||
raise ValueError(_ERR_GATEWAY_REQUIRED)
|
||||
return self
|
||||
|
||||
|
||||
class BoardRead(BoardBase):
|
||||
"""Board payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
organization_id: UUID
|
||||
created_at: datetime
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Common reusable schema primitives and simple API response envelopes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated
|
||||
@@ -5,9 +7,12 @@ from typing import Annotated
|
||||
from pydantic import StringConstraints
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
# Reusable string type for request payloads where blank/whitespace-only values are invalid.
|
||||
# Reusable string type for request payloads where blank/whitespace-only values
|
||||
# are invalid.
|
||||
NonEmptyStr = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]
|
||||
|
||||
|
||||
class OkResponse(SQLModel):
|
||||
"""Standard success response payload."""
|
||||
|
||||
ok: bool = True
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
"""Structured error payload schemas used by API responses."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class BlockedTaskDetail(SQLModel):
|
||||
"""Error detail payload listing blocking dependency task identifiers."""
|
||||
|
||||
message: str
|
||||
blocked_by_task_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BlockedTaskError(SQLModel):
|
||||
"""Top-level blocked-task error response envelope."""
|
||||
|
||||
detail: BlockedTaskDetail
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
"""Schemas for gateway passthrough API request and response payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
from app.schemas.common import NonEmptyStr
|
||||
from app.schemas.common import NonEmptyStr # noqa: TCH001
|
||||
|
||||
|
||||
class GatewaySessionMessageRequest(SQLModel):
|
||||
"""Request payload for sending a message into a gateway session."""
|
||||
|
||||
content: NonEmptyStr
|
||||
|
||||
|
||||
class GatewayResolveQuery(SQLModel):
|
||||
"""Query parameters used to resolve which gateway to target."""
|
||||
|
||||
board_id: str | None = None
|
||||
gateway_url: str | None = None
|
||||
gateway_token: str | None = None
|
||||
@@ -17,6 +23,8 @@ class GatewayResolveQuery(SQLModel):
|
||||
|
||||
|
||||
class GatewaysStatusResponse(SQLModel):
|
||||
"""Aggregated gateway status response including session metadata."""
|
||||
|
||||
connected: bool
|
||||
gateway_url: str
|
||||
sessions_count: int | None = None
|
||||
@@ -28,20 +36,28 @@ class GatewaysStatusResponse(SQLModel):
|
||||
|
||||
|
||||
class GatewaySessionsResponse(SQLModel):
|
||||
"""Gateway sessions list response payload."""
|
||||
|
||||
sessions: list[object]
|
||||
main_session_key: str | None = None
|
||||
main_session: object | None = None
|
||||
|
||||
|
||||
class GatewaySessionResponse(SQLModel):
|
||||
"""Single gateway session response payload."""
|
||||
|
||||
session: object
|
||||
|
||||
|
||||
class GatewaySessionHistoryResponse(SQLModel):
|
||||
"""Gateway session history response payload."""
|
||||
|
||||
history: list[object]
|
||||
|
||||
|
||||
class GatewayCommandsResponse(SQLModel):
|
||||
"""Gateway command catalog and protocol metadata."""
|
||||
|
||||
protocol_version: int
|
||||
methods: list[str]
|
||||
events: list[str]
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
"""Schemas for gateway-main and lead-agent coordination endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from app.schemas.common import NonEmptyStr
|
||||
from app.schemas.common import NonEmptyStr # noqa: TCH001
|
||||
|
||||
|
||||
def _lead_reply_tags() -> list[str]:
|
||||
return ["gateway_main", "lead_reply"]
|
||||
|
||||
|
||||
def _user_reply_tags() -> list[str]:
|
||||
return ["gateway_main", "user_reply"]
|
||||
|
||||
|
||||
class GatewayLeadMessageRequest(SQLModel):
|
||||
"""Request payload for sending a message to a board lead agent."""
|
||||
|
||||
kind: Literal["question", "handoff"] = "question"
|
||||
correlation_id: str | None = None
|
||||
content: NonEmptyStr
|
||||
|
||||
# How the lead should reply (defaults are interpreted by templates).
|
||||
reply_tags: list[str] = Field(default_factory=lambda: ["gateway_main", "lead_reply"])
|
||||
reply_tags: list[str] = Field(default_factory=_lead_reply_tags)
|
||||
reply_source: str | None = "lead_to_gateway_main"
|
||||
|
||||
|
||||
class GatewayLeadMessageResponse(SQLModel):
|
||||
"""Response payload for a lead-message dispatch attempt."""
|
||||
|
||||
ok: bool = True
|
||||
board_id: UUID
|
||||
lead_agent_id: UUID | None = None
|
||||
@@ -27,15 +41,19 @@ class GatewayLeadMessageResponse(SQLModel):
|
||||
|
||||
|
||||
class GatewayLeadBroadcastRequest(SQLModel):
|
||||
"""Request payload for broadcasting a message to multiple board leads."""
|
||||
|
||||
kind: Literal["question", "handoff"] = "question"
|
||||
correlation_id: str | None = None
|
||||
content: NonEmptyStr
|
||||
board_ids: list[UUID] | None = None
|
||||
reply_tags: list[str] = Field(default_factory=lambda: ["gateway_main", "lead_reply"])
|
||||
reply_tags: list[str] = Field(default_factory=_lead_reply_tags)
|
||||
reply_source: str | None = "lead_to_gateway_main"
|
||||
|
||||
|
||||
class GatewayLeadBroadcastBoardResult(SQLModel):
|
||||
"""Per-board result entry for a lead broadcast operation."""
|
||||
|
||||
board_id: UUID
|
||||
lead_agent_id: UUID | None = None
|
||||
lead_agent_name: str | None = None
|
||||
@@ -44,6 +62,8 @@ class GatewayLeadBroadcastBoardResult(SQLModel):
|
||||
|
||||
|
||||
class GatewayLeadBroadcastResponse(SQLModel):
|
||||
"""Aggregate response for a lead broadcast operation."""
|
||||
|
||||
ok: bool = True
|
||||
sent: int = 0
|
||||
failed: int = 0
|
||||
@@ -51,16 +71,21 @@ class GatewayLeadBroadcastResponse(SQLModel):
|
||||
|
||||
|
||||
class GatewayMainAskUserRequest(SQLModel):
|
||||
"""Request payload for asking the end user via a main gateway agent."""
|
||||
|
||||
correlation_id: str | None = None
|
||||
content: NonEmptyStr
|
||||
preferred_channel: str | None = None
|
||||
|
||||
# How the main agent should reply back into Mission Control (defaults interpreted by templates).
|
||||
reply_tags: list[str] = Field(default_factory=lambda: ["gateway_main", "user_reply"])
|
||||
# How the main agent should reply back into Mission Control
|
||||
# (defaults interpreted by templates).
|
||||
reply_tags: list[str] = Field(default_factory=_user_reply_tags)
|
||||
reply_source: str | None = "user_via_gateway_main"
|
||||
|
||||
|
||||
class GatewayMainAskUserResponse(SQLModel):
|
||||
"""Response payload for user-question dispatch via gateway main agent."""
|
||||
|
||||
ok: bool = True
|
||||
board_id: UUID
|
||||
main_agent_id: UUID | None = None
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
"""Schemas for gateway CRUD and template-sync API payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from pydantic import field_validator
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class GatewayBase(SQLModel):
|
||||
"""Shared gateway fields used across create/read payloads."""
|
||||
|
||||
name: str
|
||||
url: str
|
||||
main_session_key: str
|
||||
@@ -16,11 +19,14 @@ class GatewayBase(SQLModel):
|
||||
|
||||
|
||||
class GatewayCreate(GatewayBase):
|
||||
"""Payload for creating a gateway configuration."""
|
||||
|
||||
token: str | None = None
|
||||
|
||||
@field_validator("token", mode="before")
|
||||
@classmethod
|
||||
def normalize_token(cls, value: Any) -> Any:
|
||||
def normalize_token(cls, value: object) -> str | None | object:
|
||||
"""Normalize empty/whitespace tokens to `None`."""
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
@@ -30,6 +36,8 @@ class GatewayCreate(GatewayBase):
|
||||
|
||||
|
||||
class GatewayUpdate(SQLModel):
|
||||
"""Payload for partial gateway updates."""
|
||||
|
||||
name: str | None = None
|
||||
url: str | None = None
|
||||
token: str | None = None
|
||||
@@ -38,7 +46,8 @@ class GatewayUpdate(SQLModel):
|
||||
|
||||
@field_validator("token", mode="before")
|
||||
@classmethod
|
||||
def normalize_token(cls, value: Any) -> Any:
|
||||
def normalize_token(cls, value: object) -> str | None | object:
|
||||
"""Normalize empty/whitespace tokens to `None`."""
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
@@ -48,6 +57,8 @@ class GatewayUpdate(SQLModel):
|
||||
|
||||
|
||||
class GatewayRead(GatewayBase):
|
||||
"""Gateway payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
organization_id: UUID
|
||||
token: str | None = None
|
||||
@@ -56,6 +67,8 @@ class GatewayRead(GatewayBase):
|
||||
|
||||
|
||||
class GatewayTemplatesSyncError(SQLModel):
|
||||
"""Per-agent error entry from a gateway template sync operation."""
|
||||
|
||||
agent_id: UUID | None = None
|
||||
agent_name: str | None = None
|
||||
board_id: UUID | None = None
|
||||
@@ -63,6 +76,8 @@ class GatewayTemplatesSyncError(SQLModel):
|
||||
|
||||
|
||||
class GatewayTemplatesSyncResult(SQLModel):
|
||||
"""Summary payload returned by gateway template sync endpoints."""
|
||||
|
||||
gateway_id: UUID
|
||||
include_main: bool
|
||||
reset_sessions: bool
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
"""Dashboard metrics schemas for KPI and time-series API responses."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from typing import Literal
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class DashboardSeriesPoint(SQLModel):
|
||||
"""Single numeric time-series point."""
|
||||
|
||||
period: datetime
|
||||
value: float
|
||||
|
||||
|
||||
class DashboardWipPoint(SQLModel):
|
||||
"""Work-in-progress point split by task status buckets."""
|
||||
|
||||
period: datetime
|
||||
inbox: int
|
||||
in_progress: int
|
||||
@@ -19,28 +25,38 @@ class DashboardWipPoint(SQLModel):
|
||||
|
||||
|
||||
class DashboardRangeSeries(SQLModel):
|
||||
"""Series payload for a single range/bucket combination."""
|
||||
|
||||
range: Literal["24h", "7d"]
|
||||
bucket: Literal["hour", "day"]
|
||||
points: list[DashboardSeriesPoint]
|
||||
|
||||
|
||||
class DashboardWipRangeSeries(SQLModel):
|
||||
"""WIP series payload for a single range/bucket combination."""
|
||||
|
||||
range: Literal["24h", "7d"]
|
||||
bucket: Literal["hour", "day"]
|
||||
points: list[DashboardWipPoint]
|
||||
|
||||
|
||||
class DashboardSeriesSet(SQLModel):
|
||||
"""Primary vs comparison pair for generic series metrics."""
|
||||
|
||||
primary: DashboardRangeSeries
|
||||
comparison: DashboardRangeSeries
|
||||
|
||||
|
||||
class DashboardWipSeriesSet(SQLModel):
|
||||
"""Primary vs comparison pair for WIP status series metrics."""
|
||||
|
||||
primary: DashboardWipRangeSeries
|
||||
comparison: DashboardWipRangeSeries
|
||||
|
||||
|
||||
class DashboardKpis(SQLModel):
|
||||
"""Topline dashboard KPI summary values."""
|
||||
|
||||
active_agents: int
|
||||
tasks_in_progress: int
|
||||
error_rate_pct: float
|
||||
@@ -48,6 +64,8 @@ class DashboardKpis(SQLModel):
|
||||
|
||||
|
||||
class DashboardMetrics(SQLModel):
|
||||
"""Complete dashboard metrics response payload."""
|
||||
|
||||
range: Literal["24h", "7d"]
|
||||
generated_at: datetime
|
||||
kpis: DashboardKpis
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
"""Schemas for organization, membership, and invite API payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class OrganizationRead(SQLModel):
|
||||
"""Organization payload returned by read endpoints."""
|
||||
|
||||
id: UUID
|
||||
name: str
|
||||
created_at: datetime
|
||||
@@ -14,14 +18,20 @@ class OrganizationRead(SQLModel):
|
||||
|
||||
|
||||
class OrganizationCreate(SQLModel):
|
||||
"""Payload for creating a new organization."""
|
||||
|
||||
name: str
|
||||
|
||||
|
||||
class OrganizationActiveUpdate(SQLModel):
|
||||
"""Payload for switching the active organization context."""
|
||||
|
||||
organization_id: UUID
|
||||
|
||||
|
||||
class OrganizationListItem(SQLModel):
|
||||
"""Organization list row for current user memberships."""
|
||||
|
||||
id: UUID
|
||||
name: str
|
||||
role: str
|
||||
@@ -29,6 +39,8 @@ class OrganizationListItem(SQLModel):
|
||||
|
||||
|
||||
class OrganizationUserRead(SQLModel):
|
||||
"""Embedded user fields included in organization member payloads."""
|
||||
|
||||
id: UUID
|
||||
email: str | None = None
|
||||
name: str | None = None
|
||||
@@ -36,6 +48,8 @@ class OrganizationUserRead(SQLModel):
|
||||
|
||||
|
||||
class OrganizationMemberRead(SQLModel):
|
||||
"""Organization member payload including board-level access overrides."""
|
||||
|
||||
id: UUID
|
||||
organization_id: UUID
|
||||
user_id: UUID
|
||||
@@ -49,16 +63,22 @@ class OrganizationMemberRead(SQLModel):
|
||||
|
||||
|
||||
class OrganizationMemberUpdate(SQLModel):
|
||||
"""Payload for partial updates to organization member role."""
|
||||
|
||||
role: str | None = None
|
||||
|
||||
|
||||
class OrganizationBoardAccessSpec(SQLModel):
|
||||
"""Board access specification used in member/invite mutation payloads."""
|
||||
|
||||
board_id: UUID
|
||||
can_read: bool = True
|
||||
can_write: bool = False
|
||||
|
||||
|
||||
class OrganizationBoardAccessRead(SQLModel):
|
||||
"""Board access payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
board_id: UUID
|
||||
can_read: bool
|
||||
@@ -68,12 +88,16 @@ class OrganizationBoardAccessRead(SQLModel):
|
||||
|
||||
|
||||
class OrganizationMemberAccessUpdate(SQLModel):
|
||||
"""Payload for replacing organization member access permissions."""
|
||||
|
||||
all_boards_read: bool = False
|
||||
all_boards_write: bool = False
|
||||
board_access: list[OrganizationBoardAccessSpec] = Field(default_factory=list)
|
||||
|
||||
|
||||
class OrganizationInviteCreate(SQLModel):
|
||||
"""Payload for creating an organization invite."""
|
||||
|
||||
invited_email: str
|
||||
role: str = "member"
|
||||
all_boards_read: bool = False
|
||||
@@ -82,6 +106,8 @@ class OrganizationInviteCreate(SQLModel):
|
||||
|
||||
|
||||
class OrganizationInviteRead(SQLModel):
|
||||
"""Organization invite payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
organization_id: UUID
|
||||
invited_email: str
|
||||
@@ -97,4 +123,6 @@ class OrganizationInviteRead(SQLModel):
|
||||
|
||||
|
||||
class OrganizationInviteAccept(SQLModel):
|
||||
"""Payload for accepting an organization invite token."""
|
||||
|
||||
token: str
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Shared pagination response type aliases used by API routes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
"""Schemas for souls-directory search and markdown fetch responses."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SoulsDirectorySoulRef(BaseModel):
|
||||
"""Reference metadata for a soul entry in the directory index."""
|
||||
|
||||
handle: str
|
||||
slug: str
|
||||
page_url: str
|
||||
@@ -11,10 +15,14 @@ class SoulsDirectorySoulRef(BaseModel):
|
||||
|
||||
|
||||
class SoulsDirectorySearchResponse(BaseModel):
|
||||
"""Response wrapper for directory search results."""
|
||||
|
||||
items: list[SoulsDirectorySoulRef]
|
||||
|
||||
|
||||
class SoulsDirectoryMarkdownResponse(BaseModel):
|
||||
"""Response payload containing rendered markdown for a soul."""
|
||||
|
||||
handle: str
|
||||
slug: str
|
||||
content: str
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
"""Schemas for task CRUD and task comment API payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Literal, Self
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from typing import Literal, Self
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from pydantic import field_validator, model_validator
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from app.schemas.common import NonEmptyStr
|
||||
from app.schemas.common import NonEmptyStr # noqa: TCH001
|
||||
|
||||
TaskStatus = Literal["inbox", "in_progress", "review", "done"]
|
||||
STATUS_REQUIRED_ERROR = "status is required"
|
||||
|
||||
|
||||
class TaskBase(SQLModel):
|
||||
"""Shared task fields used by task create/read payloads."""
|
||||
|
||||
title: str
|
||||
description: str | None = None
|
||||
status: TaskStatus = "inbox"
|
||||
@@ -23,10 +28,14 @@ class TaskBase(SQLModel):
|
||||
|
||||
|
||||
class TaskCreate(TaskBase):
|
||||
"""Payload for creating a task."""
|
||||
|
||||
created_by_user_id: UUID | None = None
|
||||
|
||||
|
||||
class TaskUpdate(SQLModel):
|
||||
"""Payload for partial task updates."""
|
||||
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
status: TaskStatus | None = None
|
||||
@@ -38,7 +47,8 @@ class TaskUpdate(SQLModel):
|
||||
|
||||
@field_validator("comment", mode="before")
|
||||
@classmethod
|
||||
def normalize_comment(cls, value: Any) -> Any:
|
||||
def normalize_comment(cls, value: object) -> object | None:
|
||||
"""Normalize blank comment strings to `None`."""
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, str) and not value.strip():
|
||||
@@ -47,12 +57,15 @@ class TaskUpdate(SQLModel):
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_status(self) -> Self:
|
||||
"""Ensure explicitly supplied status is not null."""
|
||||
if "status" in self.model_fields_set and self.status is None:
|
||||
raise ValueError("status is required")
|
||||
raise ValueError(STATUS_REQUIRED_ERROR)
|
||||
return self
|
||||
|
||||
|
||||
class TaskRead(TaskBase):
|
||||
"""Task payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
board_id: UUID | None
|
||||
created_by_user_id: UUID | None
|
||||
@@ -64,10 +77,14 @@ class TaskRead(TaskBase):
|
||||
|
||||
|
||||
class TaskCommentCreate(SQLModel):
|
||||
"""Payload for creating a task comment."""
|
||||
|
||||
message: NonEmptyStr
|
||||
|
||||
|
||||
class TaskCommentRead(SQLModel):
|
||||
"""Task comment payload returned from read endpoints."""
|
||||
|
||||
id: UUID
|
||||
message: str | None
|
||||
agent_id: UUID | None
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
"""User API schemas for create, update, and read operations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class UserBase(SQLModel):
|
||||
"""Common user profile fields shared across user payload schemas."""
|
||||
|
||||
clerk_user_id: str
|
||||
email: str | None = None
|
||||
name: str | None = None
|
||||
@@ -17,10 +21,12 @@ class UserBase(SQLModel):
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
pass
|
||||
"""Payload used to create a user record."""
|
||||
|
||||
|
||||
class UserUpdate(SQLModel):
|
||||
"""Payload for partial user profile updates."""
|
||||
|
||||
name: str | None = None
|
||||
preferred_name: str | None = None
|
||||
pronouns: str | None = None
|
||||
@@ -30,5 +36,7 @@ class UserUpdate(SQLModel):
|
||||
|
||||
|
||||
class UserRead(UserBase):
|
||||
"""Full user payload returned by API responses."""
|
||||
|
||||
id: UUID
|
||||
is_super_admin: bool
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
"""Composite read models assembled for board and board-group views."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from datetime import datetime # noqa: TCH003
|
||||
from uuid import UUID # noqa: TCH003
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from app.schemas.agents import AgentRead
|
||||
from app.schemas.approvals import ApprovalRead
|
||||
from app.schemas.board_groups import BoardGroupRead
|
||||
from app.schemas.board_memory import BoardMemoryRead
|
||||
from app.schemas.boards import BoardRead
|
||||
from app.schemas.agents import AgentRead # noqa: TCH001
|
||||
from app.schemas.approvals import ApprovalRead # noqa: TCH001
|
||||
from app.schemas.board_groups import BoardGroupRead # noqa: TCH001
|
||||
from app.schemas.board_memory import BoardMemoryRead # noqa: TCH001
|
||||
from app.schemas.boards import BoardRead # noqa: TCH001
|
||||
from app.schemas.tasks import TaskRead
|
||||
|
||||
|
||||
class TaskCardRead(TaskRead):
|
||||
"""Task read model enriched with assignee and approval counters."""
|
||||
|
||||
assignee: str | None = None
|
||||
approvals_count: int = 0
|
||||
approvals_pending_count: int = 0
|
||||
|
||||
|
||||
class BoardSnapshot(SQLModel):
|
||||
"""Aggregated board payload used by board snapshot endpoints."""
|
||||
|
||||
board: BoardRead
|
||||
tasks: list[TaskCardRead]
|
||||
agents: list[AgentRead]
|
||||
@@ -29,6 +35,8 @@ class BoardSnapshot(SQLModel):
|
||||
|
||||
|
||||
class BoardGroupTaskSummary(SQLModel):
|
||||
"""Task summary row used inside board-group snapshot responses."""
|
||||
|
||||
id: UUID
|
||||
board_id: UUID
|
||||
board_name: str
|
||||
@@ -44,11 +52,15 @@ class BoardGroupTaskSummary(SQLModel):
|
||||
|
||||
|
||||
class BoardGroupBoardSnapshot(SQLModel):
|
||||
"""Board-level rollup embedded within a board-group snapshot."""
|
||||
|
||||
board: BoardRead
|
||||
task_counts: dict[str, int] = Field(default_factory=dict)
|
||||
tasks: list[BoardGroupTaskSummary] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BoardGroupSnapshot(SQLModel):
|
||||
"""Top-level board-group snapshot response payload."""
|
||||
|
||||
group: BoardGroupRead | None = None
|
||||
boards: list[BoardGroupBoardSnapshot] = Field(default_factory=list)
|
||||
|
||||
Reference in New Issue
Block a user