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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user