Files
hold-slayer/docs/call-flows.md
Robert Helewka dff21f7d5c Serve dashboard at /, version the REST API under /api/v1
The SvelteKit build was always made for the root (no base path); it
now mounts at / — registered last so /api/v1, /ws, /health, and /mcp
match first — and the JSON root endpoint is gone (its info lives in
/health and gateway_status). REST routers move from /api/* to
/api/v1/*; /ws and /health stay put; /mcp/ unchanged. Dashboard API
client, tests, README, and docs updated; dashboard rebuilt (build/ is
gitignored).

Verified live: / serves the UI, /api/v1 answers 200/401, the old
/api paths 404, MCP still lists 15 tools.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:57:45 -04:00

234 lines
6.0 KiB
Markdown

# Call Flows
Call flows are reusable IVR navigation trees that tell Hold Slayer exactly how to navigate a company's phone menu. Once a flow is learned (manually or via exploration), subsequent calls to the same number skip the LLM analysis and follow the stored steps directly.
## Data Model
### CallFlowStep
A single step in the IVR navigation:
```python
class CallFlowStep(BaseModel):
id: str # Unique step identifier
type: CallFlowStepType # DTMF, WAIT, LISTEN, HOLD, SPEAK, TRANSFER
description: str # Human-readable description
dtmf: Optional[str] = None # Digits to press (for DTMF steps)
timeout: float = 10.0 # Max seconds to wait
next_step: Optional[str] = None # ID of the next step
conditions: dict = {} # Conditional branching rules
metadata: dict = {} # Extra data (transcript patterns, etc.)
```
### Step Types
| Type | Purpose | Key Fields |
|------|---------|------------|
| `DTMF` | Press touch-tone digits | `dtmf="3"` |
| `WAIT` | Pause for a duration | `timeout=5.0` |
| `LISTEN` | Record + transcribe + decide | `timeout=15.0`, optional `dtmf` for hardcoded response |
| `HOLD` | Wait on hold, monitor for human | `timeout=7200` (max hold time) |
| `SPEAK` | Play audio to the call | `metadata={"audio_file": "greeting.wav"}` |
| `TRANSFER` | Bridge call to user's device | `metadata={"device": "sip_phone"}` |
### CallFlow
A complete IVR navigation tree:
```python
class CallFlow(BaseModel):
id: str # "chase_bank_main"
name: str # "Chase Bank — Main Menu"
company: Optional[str] # "Chase Bank"
phone_number: Optional[str] # "+18005551234"
description: Optional[str] # "Navigate to disputes department"
steps: list[CallFlowStep] # Ordered list of steps
created_at: datetime
updated_at: datetime
version: int = 1
tags: list[str] = [] # ["banking", "disputes"]
success_count: int = 0 # Times this flow succeeded
fail_count: int = 0 # Times this flow failed
```
## Example Call Flow
```json
{
"id": "chase_bank_disputes",
"name": "Chase Bank — Disputes",
"company": "Chase Bank",
"phone_number": "+18005551234",
"steps": [
{
"id": "wait_greeting",
"type": "WAIT",
"description": "Wait for greeting to finish",
"timeout": 5.0,
"next_step": "main_menu"
},
{
"id": "main_menu",
"type": "LISTEN",
"description": "Listen to main menu options",
"timeout": 15.0,
"next_step": "press_3"
},
{
"id": "press_3",
"type": "DTMF",
"description": "Press 3 for account services",
"dtmf": "3",
"next_step": "sub_menu"
},
{
"id": "sub_menu",
"type": "LISTEN",
"description": "Listen to account services sub-menu",
"timeout": 15.0,
"next_step": "press_1"
},
{
"id": "press_1",
"type": "DTMF",
"description": "Press 1 for disputes",
"dtmf": "1",
"next_step": "hold"
},
{
"id": "hold",
"type": "HOLD",
"description": "Wait on hold for disputes agent",
"timeout": 7200,
"next_step": "transfer"
},
{
"id": "transfer",
"type": "TRANSFER",
"description": "Transfer to user's phone"
}
]
}
```
## Call Flow Learner (`services/call_flow_learner.py`)
Automatically builds call flows from exploration data.
### How It Works
1. **Exploration mode** records "discoveries" — what the Hold Slayer encountered and did at each step
2. The learner converts discoveries into `CallFlowStep` objects
3. Steps are ordered and linked (`next_step` pointers)
4. The resulting `CallFlow` is saved for future calls
### Discovery Types
| Discovery | Becomes Step |
|-----------|-------------|
| Heard IVR prompt, pressed DTMF | `LISTEN``DTMF` |
| Detected hold music | `HOLD` |
| Detected silence (waiting) | `WAIT` |
| Heard speech (human) | `TRANSFER` |
| Sent DTMF digits | `DTMF` |
### Building a Flow
```python
learner = CallFlowLearner()
# After an exploration call completes:
discoveries = [
{"type": "wait", "duration": 3.0, "description": "Initial silence"},
{"type": "ivr_menu", "transcript": "Press 1 for billing...", "dtmf_sent": "1"},
{"type": "ivr_menu", "transcript": "Press 3 for disputes...", "dtmf_sent": "3"},
{"type": "hold", "duration": 480.0},
{"type": "human_detected", "transcript": "Thank you for calling..."},
]
flow = learner.build_flow(
discoveries=discoveries,
phone_number="+18005551234",
company="Chase Bank",
intent="dispute a charge",
)
# Returns a CallFlow with 5 steps: WAIT → LISTEN/DTMF → LISTEN/DTMF → HOLD → TRANSFER
```
### Merging Discoveries
When the same number is called again with exploration, new discoveries can be merged into the existing flow:
```python
updated_flow = learner.merge_discoveries(
existing_flow=flow,
new_discoveries=new_discoveries,
)
```
This handles:
- New menu options discovered
- Changed IVR structure
- Updated timing information
- Success/failure tracking
## REST API
### List Call Flows
```
GET /api/v1/call-flows
GET /api/v1/call-flows?company=Chase+Bank
GET /api/v1/call-flows?tag=banking
```
### Get Call Flow
```
GET /api/v1/call-flows/{flow_id}
```
### Create Call Flow
```
POST /api/v1/call-flows
Content-Type: application/json
{
"name": "Chase Bank — Disputes",
"company": "Chase Bank",
"phone_number": "+18005551234",
"steps": [ ... ]
}
```
### Update Call Flow
```
PUT /api/v1/call-flows/{flow_id}
Content-Type: application/json
{ ... updated flow ... }
```
### Delete Call Flow
```
DELETE /api/v1/call-flows/{flow_id}
```
### Learn Flow from Exploration
```
POST /api/v1/call-flows/learn
Content-Type: application/json
{
"call_id": "call_abc123",
"phone_number": "+18005551234",
"company": "Chase Bank"
}
```
This triggers the Call Flow Learner to build a flow from the call's exploration data.