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.
71 lines
2.6 KiB
Python
71 lines
2.6 KiB
Python
"""
|
|
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}")
|