- 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
53 lines
1.3 KiB
Python
53 lines
1.3 KiB
Python
"""Value objects for conversation replay."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
|
|
|
|
class StepType(str, Enum):
|
|
"""Types of steps in a conversation replay."""
|
|
|
|
user_message = "user_message"
|
|
supervisor_routing = "supervisor_routing"
|
|
tool_call = "tool_call"
|
|
tool_result = "tool_result"
|
|
agent_response = "agent_response"
|
|
interrupt = "interrupt"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ReplayStep:
|
|
"""A single step in a conversation replay."""
|
|
|
|
step: int
|
|
type: StepType
|
|
timestamp: str
|
|
content: str = ""
|
|
agent: str | None = None
|
|
tool: str | None = None
|
|
params: dict | None = None
|
|
result: dict | None = None
|
|
reasoning: str | None = None
|
|
tokens: int | None = None
|
|
duration_ms: int | None = None
|
|
|
|
def __post_init__(self) -> None:
|
|
# Store params as a frozen copy to prevent mutation from the outside
|
|
if self.params is not None:
|
|
object.__setattr__(self, "params", dict(self.params))
|
|
if self.result is not None:
|
|
object.__setattr__(self, "result", dict(self.result))
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ReplayPage:
|
|
"""A paginated page of replay steps for a conversation thread."""
|
|
|
|
thread_id: str
|
|
total_steps: int
|
|
page: int
|
|
per_page: int
|
|
steps: tuple[ReplayStep, ...]
|