feat: complete phase 4 -- conversation replay API + analytics dashboard

- Replay models: StepType enum, ReplayStep, ReplayPage frozen dataclasses
- Checkpoint transformer: PostgresSaver JSONB -> structured timeline steps
- Replay API: GET /api/conversations (paginated), GET /api/replay/{thread_id}
- Analytics models: AgentUsage, InterruptStats, AnalyticsResult
- Analytics event recorder: Protocol + PostgresAnalyticsRecorder + NoOp
- Analytics queries: resolution_rate, agent_usage, escalation_rate, cost, interrupts
- Analytics API: GET /api/analytics?range=Xd with envelope response
- DB migration: analytics_events table + conversations column additions
- 74 new tests, 399 total passing, 92.87% coverage
This commit is contained in:
Yaojia Wang
2026-03-31 13:35:45 +02:00
parent a2f750269d
commit 33db5aeb10
26 changed files with 1903 additions and 23 deletions

View File

@@ -13,7 +13,7 @@ class TestMainModule:
assert app.title == "Smart Support"
def test_app_version(self) -> None:
assert app.version == "0.3.0"
assert app.version == "0.4.0"
def test_agents_yaml_path_exists(self) -> None:
assert AGENTS_YAML.name == "agents.yaml"
@@ -25,3 +25,11 @@ class TestMainModule:
def test_websocket_route_registered(self) -> None:
routes = [r.path for r in app.routes if hasattr(r, "path")]
assert "/ws" in routes
def test_replay_router_registered(self) -> None:
routes = [r.path for r in app.routes if hasattr(r, "path")]
assert any("replay" in p or "conversations" in p for p in routes)
def test_analytics_router_registered(self) -> None:
routes = [r.path for r in app.routes if hasattr(r, "path")]
assert any("analytics" in p for p in routes)