feat: add initial Hold Slayer AI telephony gateway implementation
Complete project scaffolding and core implementation of an AI-powered telephony system that calls companies, navigates IVR menus, waits on hold, and transfers to the user when a human answers. Key components: - FastAPI server with REST API, WebSocket, and MCP (SSE) interfaces - SIP/VoIP call management via PJSUA2 with RTP audio streaming - LLM-powered IVR navigation using OpenAI/Anthropic with tool calling - Hold detection service combining audio analysis and silence detection - Real-time STT (Whisper/Deepgram) and TTS (OpenAI/Piper) pipelines - Call recording with per-channel and mixed audio capture - Event bus (asyncio pub/sub) for real-time client updates - Web dashboard with live call monitoring - SQLite persistence via SQLAlchemy with call history and analytics - Notification support (email, SMS, webhook, desktop) - Docker Compose deployment with Opal VoIP and Opal Media containers - Comprehensive test suite with unit, integration, and E2E tests - Simplified .gitignore and full project documentation in README
This commit is contained in:
60
models/contact.py
Normal file
60
models/contact.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""
|
||||
Contact models — People and organizations you call.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class PhoneNumber(BaseModel):
|
||||
"""A phone number associated with a contact."""
|
||||
|
||||
number: str # E.164 format
|
||||
label: str = "main" # main, mobile, work, home, fax, etc.
|
||||
primary: bool = False
|
||||
|
||||
|
||||
class ContactBase(BaseModel):
|
||||
"""Shared contact fields."""
|
||||
|
||||
name: str
|
||||
phone_numbers: list[PhoneNumber]
|
||||
category: Optional[str] = None # personal / business / service
|
||||
routing_preference: Optional[str] = None # how to handle their calls
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class Contact(ContactBase):
|
||||
"""Full contact model."""
|
||||
|
||||
id: str
|
||||
call_count: int = 0
|
||||
last_call: Optional[datetime] = None
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
@property
|
||||
def primary_number(self) -> Optional[str]:
|
||||
"""Get the primary phone number."""
|
||||
for pn in self.phone_numbers:
|
||||
if pn.primary:
|
||||
return pn.number
|
||||
return self.phone_numbers[0].number if self.phone_numbers else None
|
||||
|
||||
|
||||
class ContactCreate(ContactBase):
|
||||
"""Request model for creating a contact."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ContactUpdate(BaseModel):
|
||||
"""Request model for updating a contact."""
|
||||
|
||||
name: Optional[str] = None
|
||||
phone_numbers: Optional[list[PhoneNumber]] = None
|
||||
category: Optional[str] = None
|
||||
routing_preference: Optional[str] = None
|
||||
notes: Optional[str] = None
|
||||
Reference in New Issue
Block a user