feat: add cockpit triage backend foundation (Phase 1)

- Migration 071: add client_name, asset_name, issue_category,
  triage_hypothesis, evidence_items columns to ai_sessions
- TriageUpdate schema for AI-inferred header updates in chat responses
- QuestionItem.options field for quick-reply buttons
- PATCH /ai-sessions/{id}/triage endpoint for manual header edits
- POST /ai-sessions/{id}/handoff-draft streaming endpoint for conclude modal
- Structured handoff fields (root_cause, steps_taken, recommendations)
  on resolve/escalate requests, passed through to ResolutionOutputGenerator
- Triage fields exposed in AISessionDetail response for session resume

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-04-01 22:30:48 +00:00
parent cb750d52f6
commit 15781baeb7
5 changed files with 270 additions and 4 deletions

View File

@@ -102,12 +102,20 @@ class ResolveSessionRequest(BaseModel):
resolution_action: str | None = None
session_rating: int | None = Field(None, ge=1, le=5)
session_feedback: str | None = None
# Structured handoff fields (from cockpit conclude modal)
root_cause: str | None = None
steps_taken: list[str] | None = None
recommendations: str | None = None
class EscalateSessionRequest(BaseModel):
"""Escalate a session to another engineer."""
escalation_reason: str = Field(..., min_length=5, max_length=2000)
escalated_to_id: UUID | None = None
# Structured handoff fields (from cockpit conclude modal)
root_cause: str | None = None
steps_taken: list[str] | None = None
recommendations: str | None = None
class DocumentationStep(BaseModel):
@@ -231,6 +239,12 @@ class AISessionDetail(AISessionSummary):
pending_task_lane: dict[str, Any] | None = None
is_branching: bool = False
active_branch_id: str | None = None
# Triage / cockpit header fields
client_name: str | None = None
asset_name: str | None = None
issue_category: str | None = None
triage_hypothesis: str | None = None
evidence_items: list[dict[str, Any]] | None = None
model_config = {"from_attributes": True}
@@ -276,6 +290,16 @@ class QuestionItem(BaseModel):
"""A question the AI needs answered by the engineer."""
text: str
context: str = ""
options: list[str] | None = None # quick-reply button labels; null = free-text input
class TriageUpdate(BaseModel):
"""AI-inferred triage metadata returned with chat responses."""
client_name: str | None = None
asset_name: str | None = None
issue_category: str | None = None
triage_hypothesis: str | None = None
evidence_items: list[dict[str, Any]] | None = None # appends to existing list
class ChatMessageResponse(BaseModel):
@@ -285,6 +309,7 @@ class ChatMessageResponse(BaseModel):
fork: ForkMetadata | None = None
actions: list[ActionItem] | None = None
questions: list[QuestionItem] | None = None
triage_update: TriageUpdate | None = None
class SaveTaskLaneRequest(BaseModel):
@@ -307,3 +332,24 @@ class AISessionSearchResult(BaseModel):
created_at: datetime
model_config = {"from_attributes": True}
# ── Triage / Cockpit ──
class TriagePatchRequest(BaseModel):
"""Update triage metadata on a session (incident header fields)."""
client_name: str | None = None
asset_name: str | None = None
issue_category: str | None = None
triage_hypothesis: str | None = None
evidence_items: list[dict[str, Any]] | None = None
class TriagePatchResponse(BaseModel):
"""Updated triage metadata after a PATCH."""
id: UUID
client_name: str | None = None
asset_name: str | None = None
issue_category: str | None = None
triage_hypothesis: str | None = None
evidence_items: list[dict[str, Any]] | None = None