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.
This commit is contained in:
70
services/call_persistence.py
Normal file
70
services/call_persistence.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
Call Persistence — Writes completed calls and their transcript chunks
|
||||
to the database when CallManager.end_call() fires.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from db.database import CallRecord, TranscriptChunk, get_session_factory
|
||||
from models.call import ActiveCall, CallStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def persist_call_on_end(call: ActiveCall, final_status: CallStatus) -> None:
|
||||
"""Insert a CallRecord and any transcript chunks for `call`.
|
||||
|
||||
Wired into CallManager via _on_call_ended in gateway.start().
|
||||
"""
|
||||
try:
|
||||
async with get_session_factory()() as session:
|
||||
record = CallRecord(
|
||||
id=call.id,
|
||||
direction=call.direction,
|
||||
remote_number=call.remote_number,
|
||||
status=final_status.value,
|
||||
mode=call.mode.value,
|
||||
intent=call.intent,
|
||||
started_at=call.started_at,
|
||||
ended_at=datetime.now(),
|
||||
duration=int(call.duration),
|
||||
hold_time=int(call.hold_time),
|
||||
device_used=call.device,
|
||||
call_flow_id=call.call_flow_id,
|
||||
classification_timeline=[
|
||||
{
|
||||
"timestamp": c.timestamp,
|
||||
"audio_type": c.audio_type.value,
|
||||
"confidence": c.confidence,
|
||||
}
|
||||
for c in call.classification_history
|
||||
],
|
||||
metadata_={"services": list(call.services)},
|
||||
)
|
||||
session.add(record)
|
||||
|
||||
# Each transcript chunk gets its own row with a sequence number
|
||||
# so the dashboard can render them in order with click-to-seek.
|
||||
for seq, text in enumerate(call.transcript_chunks):
|
||||
speaker = "unknown"
|
||||
payload = text
|
||||
if ":" in text:
|
||||
head, rest = text.split(":", 1)
|
||||
head = head.strip().lower()
|
||||
if head in {"caller", "agent", "receptionist", "caller_message"}:
|
||||
speaker = head if head != "caller_message" else "caller"
|
||||
payload = rest.strip()
|
||||
session.add(TranscriptChunk(
|
||||
id=f"tc_{uuid.uuid4().hex[:10]}",
|
||||
call_id=call.id,
|
||||
seq=seq,
|
||||
t_offset_ms=0,
|
||||
speaker=speaker,
|
||||
text=payload,
|
||||
))
|
||||
|
||||
await session.commit()
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not persist call {call.id}: {e}")
|
||||
Reference in New Issue
Block a user