"""Pydantic schemas for FlowPilot analytics dashboard.""" from __future__ import annotations from typing import Optional, Any from datetime import datetime from pydantic import BaseModel class MTTRDataPoint(BaseModel): date: str mttr_minutes: float session_count: int class DomainBreakdown(BaseModel): domain: str total: int resolved: int escalated: int resolution_rate: float class ConfidenceBreakdown(BaseModel): guided_sessions: int guided_resolution_rate: float exploring_sessions: int exploring_resolution_rate: float discovery_sessions: int discovery_resolution_rate: float class DomainCoverage(BaseModel): domain: str flow_count: int session_count: int guided_rate: float class KnowledgeCoverage(BaseModel): total_flows: int ai_generated_flows: int total_proposals_pending: int proposals_approved_this_period: int proposals_rejected_this_period: int coverage_by_domain: list[DomainCoverage] = [] class PsaMetrics(BaseModel): ticket_link_rate: float auto_push_success_rate: float auto_push_retry_success_rate: float total_time_entries_logged: int total_hours_logged: float class CoverageDomainRow(BaseModel): domain: str flow_count: int session_count: int resolution_rate: float escalation_rate: float guided_rate: float avg_resolution_minutes: float | None = None class CoverageResponse(BaseModel): domains: list[CoverageDomainRow] unmapped_session_count: int total_domains: int class FlowQualityRow(BaseModel): flow_id: str name: str tree_type: str usage_count: int success_rate: float | None = None last_matched_at: datetime | None = None avg_confidence: float | None = None quality_score: float class FlowQualityResponse(BaseModel): flows: list[FlowQualityRow] top_performers: list[FlowQualityRow] needs_attention: list[FlowQualityRow] class PsaFunnel(BaseModel): total_sessions: int linked_to_ticket: int doc_pushed: int time_entry_logged: int class PsaDailyTrend(BaseModel): date: str entries: int hours: float class EnhancedPsaMetrics(BaseModel): total_time_entries: int total_hours_logged: float avg_hours_per_session: float push_funnel: PsaFunnel daily_trend: list[PsaDailyTrend] class FlowPilotDashboard(BaseModel): period: str total_sessions: int resolved_sessions: int escalated_sessions: int abandoned_sessions: int resolution_rate: float avg_steps_to_resolution: float avg_session_duration_minutes: float avg_rating: float | None = None mttr_minutes: float | None = None mttr_trend: list[MTTRDataPoint] = [] sessions_by_domain: list[DomainBreakdown] = [] confidence_breakdown: ConfidenceBreakdown knowledge_coverage: KnowledgeCoverage psa_metrics: PsaMetrics | None = None class EscalationMetrics(BaseModel): """In-product time-to-first-action metric for the Escalation Mode wedge. NOTE: this is the *in-product* metric (post-claim time-to-first-action). The "minutes recovered" sales claim requires a manual baseline measurement of the pre-Escalation-Mode verbal-handoff time. See docs/plans/2026-04-27-escalation-mode-wedge-design.md for the two-metric framing — do not roll this number alone into "minutes recovered." """ period: str n_handoffs_claimed: int n_handoffs_with_action: int avg_seconds_to_first_action: float | None = None median_seconds_to_first_action: float | None = None p95_seconds_to_first_action: float | None = None metric_definition: str = ( "elapsed_seconds(first ai_session_step in session where " "created_at > SessionHandoff.claimed_at) — measures post-claim activity " "lag, NOT verbal-handoff savings. Pair with manual baseline." )