diff --git a/backend/app/schemas/ai_session.py b/backend/app/schemas/ai_session.py index 973de98c..983eb113 100644 --- a/backend/app/schemas/ai_session.py +++ b/backend/app/schemas/ai_session.py @@ -228,6 +228,8 @@ class AISessionDetail(AISessionSummary): ticket_data: dict[str, Any] | None = None steps: list[AISessionStepResponse] = [] conversation_messages: list[dict[str, Any]] = [] # Chat sessions store messages here + is_branching: bool = False + active_branch_id: str | None = None model_config = {"from_attributes": True} diff --git a/backend/app/schemas/session_branch.py b/backend/app/schemas/session_branch.py new file mode 100644 index 00000000..deebaca2 --- /dev/null +++ b/backend/app/schemas/session_branch.py @@ -0,0 +1,83 @@ +"""Pydantic schemas for session branches and fork points.""" +from __future__ import annotations +from typing import Any +from uuid import UUID +from datetime import datetime +from pydantic import BaseModel, Field + + +class BranchCreate(BaseModel): + label: str = Field(..., max_length=200) + status: str = "untried" + + +class BranchUpdate(BaseModel): + status: str = Field(..., pattern="^(active|dead_end|solved|untried|revived)$") + status_reason: str | None = None + + +class BranchResponse(BaseModel): + id: UUID + session_id: UUID + parent_branch_id: UUID | None + fork_point_step_id: UUID | None + branch_order: int + label: str + status: str + status_reason: str | None + status_changed_at: datetime | None + context_summary: dict[str, Any] | None + evidence_from_branch_id: UUID | None + evidence_description: str | None + step_count: int = 0 + created_at: datetime + updated_at: datetime + model_config = {"from_attributes": True} + + +class BranchTreeResponse(BaseModel): + branches: list[BranchResponse] + active_branch_id: UUID | None + + +class ForkOption(BaseModel): + label: str = Field(..., max_length=200) + description: str = Field(..., max_length=500) + + +class ForkCreateRequest(BaseModel): + fork_reason: str = Field(..., min_length=5, max_length=2000) + options: list[ForkOption] = Field(..., min_length=2, max_length=10) + + +class ForkPointResponse(BaseModel): + id: UUID + session_id: UUID + parent_branch_id: UUID + trigger_step_id: UUID | None + fork_reason: str + options: list[dict[str, Any]] + created_at: datetime + model_config = {"from_attributes": True} + + +class BranchSwitchResponse(BaseModel): + active_branch_id: UUID + branch: BranchResponse + conversation_messages: list[dict[str, Any]] + + +class ReviveRequest(BaseModel): + evidence_from_branch_id: UUID + evidence_description: str = Field(..., min_length=5, max_length=2000) + + +class BranchMessageRequest(BaseModel): + message: str = Field(..., min_length=1, max_length=8000) + upload_ids: list[UUID] = Field(default_factory=list, max_length=10) + + +class BranchMessageResponse(BaseModel): + content: str + branch_id: UUID + step_id: UUID | None = None diff --git a/backend/app/schemas/session_handoff.py b/backend/app/schemas/session_handoff.py new file mode 100644 index 00000000..da1a52cb --- /dev/null +++ b/backend/app/schemas/session_handoff.py @@ -0,0 +1,57 @@ +"""Pydantic schemas for session handoffs.""" +from __future__ import annotations +from typing import Any +from uuid import UUID +from datetime import datetime +from pydantic import BaseModel, Field + + +class HandoffCreateRequest(BaseModel): + intent: str = Field(..., pattern="^(park|escalate)$") + engineer_notes: str | None = None + priority: str = Field("normal", pattern="^(normal|elevated)$") + + +class HandoffResponse(BaseModel): + id: UUID + session_id: UUID + handed_off_by: UUID + intent: str + source_branch_id: UUID | None + snapshot: dict[str, Any] + ai_assessment: str | None + ai_assessment_data: dict[str, Any] | None + artifacts: list[dict[str, Any]] | None + engineer_notes: str | None + priority: str + claimed_by: UUID | None + claimed_at: datetime | None + psa_note_pushed: bool + notification_sent: bool + created_at: datetime + model_config = {"from_attributes": True} + + +class HandoffClaimRequest(BaseModel): + pass + + +class HandoffBriefingResponse(BaseModel): + briefing: str + handoff: HandoffResponse + + +class QueueItemResponse(BaseModel): + handoff_id: UUID + session_id: UUID + intent: str + problem_summary: str | None + problem_domain: str | None + priority: str + handed_off_by_name: str | None + engineer_notes: str | None + branch_count: int = 0 + created_at: datetime + claimed_by: UUID | None + claimed_at: datetime | None + model_config = {"from_attributes": True} diff --git a/backend/app/schemas/session_resolution.py b/backend/app/schemas/session_resolution.py new file mode 100644 index 00000000..126c93fb --- /dev/null +++ b/backend/app/schemas/session_resolution.py @@ -0,0 +1,42 @@ +"""Pydantic schemas for session resolution outputs.""" +from __future__ import annotations +from typing import Any +from uuid import UUID +from datetime import datetime +from pydantic import BaseModel, Field + + +class ResolutionOutputResponse(BaseModel): + id: UUID + session_id: UUID + output_type: str + generated_content: str + structured_data: dict[str, Any] | None + edited_content: str | None + status: str + pushed_to: str | None + pushed_at: datetime | None + pushed_reference: str | None + generated_by_model: str + created_at: datetime + updated_at: datetime + model_config = {"from_attributes": True} + + +class ResolutionOutputEditRequest(BaseModel): + edited_content: str = Field(..., min_length=1) + + +class ResolutionOutputPushRequest(BaseModel): + destination: str = Field(..., pattern="^(psa|kb_library|clipboard|email)$") + + +class ResolutionOutputPushResponse(BaseModel): + output_id: UUID + status: str + pushed_to: str + pushed_reference: str | None = None + + +class AllResolutionOutputsResponse(BaseModel): + outputs: list[ResolutionOutputResponse]