CallBridge
Autonomous outbound phone-negotiation agent that places calls on behalf of a human client, intercepts constraint violations in real time, stalls the receptionist with a natural holding phrase, escalates a Red Decision Card to the human, and speaks an approved counter-offer back — all through a FastAPI backend with WebSocket and REST interfaces.
Primary Purpose
CallBridge solves the problem of high-stakes, constraint-bound phone negotiations that humans find stressful or error-prone — such as booking medical appointments under time and budget limits, placing food orders with specific restrictions, or negotiating service terms. The system acts as a fully autonomous AI caller: it accepts a natural-language goal from the user, parses it into a structured rule-set (budgets, time windows, forbidden disclosures), and conducts the entire outbound conversation on the user's behalf. The real-world value is that it eliminates the cognitive load of live negotiation while guaranteeing that user-defined hard limits are never breached without explicit human sign-off.
Control Flow & Architecture
Requests enter through a FastAPI server (app.py) which maintains an in-memory session store (sessiondb). On session creation, the user's raw prompt is forwarded to engine.extract_rules_from_prompt(), which calls the Groq LLM with a structured system prompt and json_object response format to produce an ExtractedRules Pydantic model. During the call, each receptionist utterance arrives either as text via POST /evaluate-turn or as raw audio via POST /audio-turn — the audio path first passes through engine.transcribe_audio() (AssemblyAI primary, Groq Whisper fallback) before joining the same evaluation path. engine.evaluate_caller_turn() then builds a context window from the last 8 transcript turns plus the decision ledger and sends it to the LLM, which returns a TurnDecision. Depending on that decision, the session transitions to IN_PROGRESS (safe reply), DECISION_REQUIRED (dealbreaker), or COMPLETED (call confirmed). A parallel WebSocket endpoint (/ws/call/{session_id}) mirrors the full state machine for streaming frontends using named JSON events.
Invariants & Edge Cases
The most critical invariant is the dealbreaker interception: if the LLM sets is_dealbreaker=true, the session is immediately frozen at DECISION_REQUIRED and a stalling phrase is returned for the agent to speak aloud — the call cannot proceed until the human explicitly approves a counter-action via /user-action followed by /approve. An approved override is appended to the decision_ledger, and the LLM is explicitly instructed to treat ledger entries as authoritative — preventing previously approved alternatives from being re-flagged as violations. Audio transcription failures degrade gracefully: AssemblyAI errors are caught and logged, falling back to Groq Whisper; if both fail the endpoint returns a 500 with a structured error body rather than silently dropping the turn. TTS playback failures in tts_player.py are swallowed entirely (bare except pass) so a speaker driver issue never crashes the client. Empty or silent audio turns return a canned 'couldn't hear you' draft response without invoking the LLM, avoiding wasted inference on empty input.
Core Technology Stack
Entry Point
Primary File
app.py
Execution Type
asgi_server
Run Command
uvicorn app:app --reload --port 8000
Lifecycle Summary
On startup, FastAPI registers all routes and CORS middleware. A client POSTs to /api/session/create with a natural-language prompt; engine.extract_rules_from_prompt() calls the Groq LLM to produce an ExtractedRules object stored in sessiondb. During the call, each receptionist utterance is evaluated by engine.evaluate_caller_turn() which returns a TurnDecision. Dealbreakers freeze the session at DECISION_REQUIRED; the human picks a counter-action via /user-action, engine.generate_staged_draft() produces a bilingual response, and /approve resumes the call. At call end, /complete calls engine.generate_call_summary() for a structured audit report.
Interactive Architecture Flowchart
Click on any flowchart node to jump directly to its matching execution card
Step-by-Step Execution Pipeline
Lifecycle stages, module boundaries, IO contracts, and code anchors
Session Creation & Rule Extraction
Validates the raw prompt is non-empty, generates a UUID session ID (8 chars), calls engine.extract_rules_from_prompt() to parse constraints via LLM, then stores the full session dict in sessiondb.
rules = extract_rules_from_prompt(clean_prompt, request.user_language)LLM Goal Parsing
Builds a structured system prompt and calls client.chat.completions.create() with response_format={'type': 'json_object'} at temperature=0.1. Parses the returned JSON and validates through the ExtractedRules Pydantic model.
response = client.chat.completions.create(model=get_model(), messages=[...], response_format={"type": "json_object"}, temperature=0.1)Receptionist Turn Evaluation
Assembles a context-rich system prompt including the last 8 history turns, decision ledger of approved overrides, constraint rules, and ASSIST-mode addendum if active. Calls Groq at temperature=0.1 with json_object response format.
raw_json = json.loads(response.choices[0].message.content)
return TurnDecision(**raw_json)Audio Transcription
Primary path: AssemblyAI Transcriber (if ASSEMBLYAI_API_KEY is set). Fallback: Groq Whisper via client.audio.transcriptions.create() using model='whisper-large-v3-turbo'. Result feeds into evaluate_caller_turn().
transcriber = aai.Transcriber()
transcript = transcriber.transcribe(audio_stream)Dealbreaker Escalation & Staged Draft
When TurnDecision.is_dealbreaker=True the session enters DECISION_REQUIRED. The human picks an option; generate_staged_draft() sends it to the LLM which produces a bilingual polite counter-offer. The english_response is stored as pending_draft and a ledger entry is appended.
staged = generate_staged_draft(session, action)
session["pending_draft"] = staged.english_responseApproval & TTS Playback
POST /approve clears pending_draft, appends agent turn to transcript_history, sets status to IN_PROGRESS, and returns speak_text. The test client calls speak_text_aloud() which synthesises with edge_tts.Communicate, saves a temp MP3, and plays it via a PowerShell subprocess blocking until playback completes.
communicate = edge_tts.Communicate(text, voice)
await communicate.save(temp_file)
await asyncio.to_thread(play_audio_file, temp_file)Post-Call Audit Summary
Formats the complete conversation as a plain-text block and sends it with task constraints and ledger to the LLM at temperature=0.1. The model extracts confirmed agreements, open items, and produces an executive headline.
raw_json = json.loads(response.choices[0].message.content)
return FinalSummary(**raw_json)Core Data Models
| Field | Type | Description |
|---|---|---|
| raw_prompt | str | Natural-language goal description from the human client. |
| user_language | Optional[str] | Preferred language for translated responses. Defaults to 'English'. |
| mode | Optional[str] | 'delegate' — AI negotiates autonomously; 'assist' — pauses for human approval on every commitment. |
| Field | Type | Description |
|---|---|---|
| intent | str | Primary action, e.g. 'schedule_appointment'. |
| target_business | str | Business being called, e.g. 'Dr. Smith Dental Clinic'. |
| action_details | Optional[str] | Specifics of the request. |
| opening_phrase | Optional[str] | Natural first sentence CallBridge speaks when the receptionist picks up. |
| constraints | List[RuleConstraint] | Parsed budget/time/quantity limits with parameter, operator, value, and unit. |
| forbidden_disclosures | List[str] | Information the agent must never share (credit card, SSN, etc.). |
| special_notes | Optional[str] | Additional freeform notes from the user's prompt. |
| Field | Type | Description |
|---|---|---|
| is_dealbreaker | bool | True when the receptionist violates a constraint or requests forbidden data. |
| call_completed | bool | True when the booking/order is confirmed or call ends. |
| violation_parameter | Optional[str] | The violated constraint name, e.g. 'price'. |
| violation_reason | Optional[str] | Human-readable Red Card reason. |
| immediate_stalling_phrase | Optional[str] | Phrase spoken immediately to keep receptionist waiting. |
| suggested_user_options | Optional[List[str]] | 2-4 actionable choices surfaced on the Human-in-the-Loop UI card. |
| draft_response | Optional[str] | Next agent utterance (safe turn) or initial counter-offer text (dealbreaker). |
| Field | Type | Description |
|---|---|---|
| english_response | str | Exact English phrase agent will speak after approval. |
| translated_response | str | Same phrase in the user's preferred language for verification. |
| action_summary | str | One-line ledger entry, e.g. 'Counter-offer drafted for Sunday appointment'. |
| Field | Type | Description |
|---|---|---|
| outcome_headline | str | Bold executive headline of call result. |
| outcome_subtext | str | One-sentence logistics/payment status description. |
| confirmed_items | List[str] | Facts explicitly agreed upon during the conversation. |
| unresolved_items | List[str] | Open questions or ambiguities the call did not resolve. |
| Field | Type | Description |
|---|---|---|
| chosen_action | str | Option selected by the human from the Red Decision Card. |
| custom_instruction | Optional[str] | Free-text instruction if the user chose 'Type my own response'. |
File System Directory
| File | Role | LoC |
|---|---|---|
| ./ All application source lives flat in the project root — no subdirectory layering. | ||
| app.py | FastAPI application — route handlers, session state machine, CORS middleware | 345 |
| engine.py | LLM orchestration — Groq client, all prompt-building and inference calls, AssemblyAI + Whisper transcription | 260 |
| models.py | Pydantic v2 data models for all request/response contracts | 125 |
| tts_player.py | Edge TTS synthesis and PowerShell-based audio playback | 68 |
| test_mic_caller.py | Interactive CLI test harness — mic recording, HTTP orchestration, human-in-the-loop decision prompts | 261 |
| list_models.py | Standalone debug script to enumerate available Groq models | 24 |
Setup & Verification Playbook
Prerequisites
- ›Python >= 3.10
- ›pip
- ›Git
- ›Windows (TTS playback uses PowerShell MediaPlayer)
- ›Groq API key
- ›AssemblyAI API key (optional)
git clone https://github.com/Shariq-duet/CallBridge.git && cd CallBridgepython -m venv venv && venv\Scripts\activatepip install --upgrade pip && pip install -r requirements.txtcopy .env.example .env
# Edit .env: set GROQ_API_KEY, GROQ_MODEL, ASSEMBLYAI_API_KEYuvicorn app:app --reload --port 8000curl http://127.0.0.1:8000/api/modelspython test_mic_caller.pystart http://127.0.0.1:8000/docs