Files
hold-slayer/models/events.py
Robert Helewka 63f1a270bb feat: add call history API endpoints and TTS service client
Adds read-only access to persisted call records for the dashboard
and implements a client for the Rhema text-to-speech service.

- api/call_history.py: New router providing paged call lists
  and detailed call records with transcript metadata.
- services/tts.py: Async client for OpenAI-compatible TTS
  endpoints (Rhema/Kokoro) used for call-flow steps.
2026-05-22 06:28:33 -04:00

83 lines
2.5 KiB
Python

"""
Event models — Real-time events published via WebSocket and event bus.
These events drive the dashboard, notifications, and MCP updates.
"""
from datetime import datetime
from enum import Enum
from typing import Any, Optional
from pydantic import BaseModel, Field
class EventType(str, Enum):
"""Types of events the gateway can emit."""
# Call lifecycle
CALL_INITIATED = "call.initiated"
CALL_RINGING = "call.ringing"
CALL_CONNECTED = "call.connected"
CALL_ENDED = "call.ended"
CALL_FAILED = "call.failed"
# Hold Slayer
IVR_STEP = "holdslayer.ivr_step"
IVR_DTMF_SENT = "holdslayer.dtmf_sent"
HOLD_DETECTED = "holdslayer.hold_detected"
HUMAN_DETECTED = "holdslayer.human_detected"
TRANSFER_STARTED = "holdslayer.transfer_started"
TRANSFER_COMPLETE = "holdslayer.transfer_complete"
# Audio
AUDIO_CLASSIFIED = "audio.classified"
TRANSCRIPT_CHUNK = "audio.transcript_chunk"
SPEAK_PLAYED = "audio.speak_played"
# AI Receptionist
RECEPTIONIST_GREETING = "receptionist.greeting"
RECEPTIONIST_LISTENING = "receptionist.listening"
RECEPTIONIST_CAPTURED_INTENT = "receptionist.captured_intent"
RECEPTIONIST_ROUTING = "receptionist.routing"
RECEPTIONIST_MESSAGE_SAVED = "receptionist.message_saved"
RECEPTIONIST_REJECTED = "receptionist.rejected"
# Routing
ROUTING_RULE_MATCHED = "routing.rule_matched"
ROUTING_DEVICE_DND = "routing.device_dnd"
# Device
DEVICE_REGISTERED = "device.registered"
DEVICE_ONLINE = "device.online"
DEVICE_OFFLINE = "device.offline"
# System
GATEWAY_STARTED = "system.gateway_started"
GATEWAY_STOPPING = "system.gateway_stopping"
ERROR = "system.error"
# SIP Trunk
SIP_TRUNK_REGISTERED = "sip.trunk.registered"
SIP_TRUNK_REGISTRATION_FAILED = "sip.trunk.registration_failed"
SIP_TRUNK_UNREGISTERED = "sip.trunk.unregistered"
class GatewayEvent(BaseModel):
"""A real-time event from the gateway."""
type: EventType
call_id: Optional[str] = None
timestamp: datetime = Field(default_factory=datetime.now)
data: dict[str, Any] = Field(default_factory=dict)
message: Optional[str] = None # Human-readable description
def to_ws_message(self) -> dict:
"""Serialize for WebSocket transmission."""
return {
"type": self.type.value,
"call_id": self.call_id,
"timestamp": self.timestamp.isoformat(),
"data": self.data,
"message": self.message,
}