docs: document SIP_ENGINE and correct config tables against the models
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 44s
CVE Scan & Docker Build / build-and-push (push) Successful in 1m53s

Started as the SIP_ENGINE row flagged in the last commit. Cross-checking the
tables against config.py mechanically (rather than by eye) turned up more,
including two entries that were actively wrong.

Corrections:
- GATEWAY_RTP_PORT_MIN/MAX and GATEWAY_HOST are documented in
  configuration.md but do not exist — no code reads them and they are absent
  from .env.example. Setting them today does nothing. Replaced with the real
  GATEWAY_SIP_ fields (host/port/domain).
- GATEWAY_SIP_PORT was documented as 5080 in two places; the code and
  .env.example both say 5060.
- DATABASE_URL was documented with a SQLite default. There is none, and
  startup exits if it is unset.

Additions — every env var the models accept is now documented somewhere
(verified bidirectionally: nothing in the models undocumented, nothing
documented that the models reject):
- Server section: HOST, PORT, DEBUG, LOG_LEVEL, LOG_FORMAT
- Safety section: MAX_CONCURRENT_CALLS, USE_MOCK_SIP, SIP_ENGINE
- Receptionist section (configuration.md had none, though seven vars exist)

Structural staleness, from the PR #8 media-plane work:
- core/pjsua_engine.py was absent from the component list and file tree; so
  were dial_plan.py (the emergency guard) and sip_engine.py.
- architecture.md's banner still read "media plane in transition". The engine
  landed; it is now two selectable engines with the audio consequence stated.
- Tech Stack described "single-process async architecture" — the
  simplification CLAUDE.md explicitly calls out. Now points at the threading
  model, since there are three execution contexts.
- The Asterisk lab shipped in PR #8 with its own README but nothing linked to
  it. Linked from the test section and both doc indexes.
- CLAUDE.md's "no structured JSON logging" gap is closed; test count was 146
  across 16 files, now 189 across 19. The other listed gaps (no /metrics, no
  rate limiting, no health-probe log filter) were re-verified and still hold.

Deliberately not hardcoding a test count in the README — that is the same
staleness this commit is clearing up. All internal links and anchors verified
to resolve; 189 tests pass; lint unchanged at its 216 baseline.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 17:57:31 -04:00
parent 1644999bcb
commit e2051f7486
5 changed files with 85 additions and 22 deletions

View File

@@ -52,8 +52,9 @@ You give it a phone number and an intent ("dispute a charge on my December state
## What's Implemented
### Core Engine
- **Sippy B2BUA Engine** (`core/sippy_engine.py`) — SIP call control, DTMF, bridging, conference, trunk registration
- **PJSUA2 Media Pipeline** (`core/media_pipeline.py`) — Audio routing, recording ports, conference bridge, WAV playback (stub mode until the `pjsua2` bindings are installed — see note below)
- **Sippy B2BUA Engine** (`core/sippy_engine.py`) — SIP call control, DTMF, bridging, conference, trunk registration. Signalling only: no audio reaches the classifier on this path
- **PJSUA2 SIP Engine** (`core/pjsua_engine.py`) — Places the call itself so it owns the dialog, which is the only way PJSUA2 will surface RTP. Select with `SIP_ENGINE=pjsua2`; see [docs/architecture.md](docs/architecture.md#media-plane-why-pjsua2-places-the-call)
- **PJSUA2 Media Pipeline** (`core/media_pipeline.py`) — Audio routing, capture ports, conference bridge, WAV playback (stub mode until the `pjsua2` bindings are installed — see note below)
- **Call Manager** (`core/call_manager.py`) — Active call state tracking, lifecycle management
- **Event Bus** (`core/event_bus.py`) — Async pub/sub with per-subscriber queues, type filtering, history
@@ -99,8 +100,12 @@ hold-slayer/
├── config.py # Pydantic settings from .env
├── core/
│ ├── gateway.py # Top-level gateway orchestrator
│ ├── sippy_engine.py # Sippy B2BUA SIP engine
│ ├── dial_plan.py # Emergency-number guard + number normalisation
│ ├── sip_engine.py # SIPEngine ABC + MockSIPEngine
│ ├── sippy_engine.py # Sippy B2BUA SIP engine (signalling only)
│ ├── pjsua_engine.py # PJSUA2 SIP engine (call control + media)
│ ├── media_pipeline.py # PJSUA2 audio routing
│ ├── logging_config.py # Text/JSON log formatting
│ ├── call_manager.py # Active call state management
│ └── event_bus.py # Async pub/sub event bus
├── services/
@@ -213,6 +218,15 @@ uvicorn main:app --host 0.0.0.0 --port 8000
pytest tests/ -v
```
No external services required — the suite runs against SQLite (`aiosqlite`) and
the mock SIP engine, so it needs neither a trunk nor PostgreSQL.
For the parts a unit test cannot reach — real SIP signalling, RTP, IVR
navigation against a live switch — there is an **Asterisk lab**: a fake PSTN
that answers calls, plays hold music, and runs scripted IVR menus, so call
paths can be exercised without dialling a real number or incurring telephony
charges. See [tests/lab/README.md](tests/lab/README.md).
## Docker
A single image bundles the FastAPI process and the built dashboard (the node
@@ -373,13 +387,19 @@ All configuration is via environment variables (see `.env.example`):
| `OWNER_NAME` | Casdoor username of the single operator (owner) | — (required if SSO on) |
| `PUBLIC_BASE_URL` | Public base URL for OAuth discovery (else derived from headers) | — |
| `MAX_CONCURRENT_CALLS` | Cap on simultaneous outbound calls | `4` |
| `HOST` | Bind address (off-loopback requires SSO enabled) | `0.0.0.0` |
| `PORT` | Bind port | `8000` |
| `DEBUG` | SQLAlchemy echo + uvicorn reload | `false` |
| `LOG_LEVEL` | Root log level (`debug`/`info`/`warning`/`error`) | `info` |
| `LOG_FORMAT` | `text` (human-readable) or `json` (structured, for Loki) | `text` |
| `USE_MOCK_SIP` | Run the mock SIP engine — no real calls. Must be asked for | `false` |
| `SIP_ENGINE` | `sippy` (signalling only) or `pjsua2` (call control + media) | `sippy` |
| `NOTIFY_SMS_NUMBER` | SMS notification number (optional) | — |
| `SIP_TRUNK_HOST` | Your SIP provider hostname | — |
| `SIP_TRUNK_USERNAME` | SIP auth username | — |
| `SIP_TRUNK_PASSWORD` | SIP auth password | — |
| `SIP_TRUNK_DID` | Your phone number (E.164) | — |
| `GATEWAY_SIP_PORT` | Port for device registration | `5080` |
| `GATEWAY_SIP_PORT` | Port for device registration | `5060` |
| `SPEACHES_URL` | Speaches/Whisper STT endpoint | `http://localhost:22070` |
| `LLM_BASE_URL` | OpenAI-compatible LLM endpoint | `http://localhost:11434/v1` |
| `LLM_MODEL` | Model name for IVR analysis | `llama3` |
@@ -393,11 +413,11 @@ All configuration is via environment variables (see `.env.example`):
## Tech Stack
- **Python 3.12+** + **asyncio** — Single-process async architecture
- **Python 3.12+** + **asyncio** — Single process. Not single-threaded: the SIP stacks run their own event loops on separate OS threads, crossed only through defined funnels ([docs/architecture.md](docs/architecture.md#threading-model))
- **FastAPI** — REST API + WebSocket server
- **SvelteKit** — Dashboard UI (built static, served by FastAPI at `/`)
- **Sippy B2BUA** — SIP call control and DTMF
- **PJSUA2** — Media pipeline, conference bridge, recording, WAV playback
- **Sippy B2BUA** — SIP call control and DTMF (`SIP_ENGINE=sippy`, signalling only)
- **PJSUA2** — Call control + media: conference bridge, capture ports, recording, WAV playback (`SIP_ENGINE=pjsua2`)
- **Speaches** (Whisper) — Speech-to-text
- **Rhema** (Kokoro) — Text-to-speech (OpenAI-compatible `/v1/audio/speech`)
- **Ollama / vLLM / OpenAI** — LLM for IVR menu analysis and receptionist intent capture
@@ -410,6 +430,9 @@ Full documentation is in [`/docs`](docs/README.md):
- [Architecture](docs/architecture.md) — System design, data flow, threading model
- [Core Engine](docs/core-engine.md) — SIP engine, media pipeline, call manager, event bus
- [Dial Plan](docs/dial-plan.md) — Number normalisation and the emergency-number guard
- [PJSUA2 Build](docs/pjsua2-build.md) — Building the bindings (not pip-installable)
- [Asterisk Lab](tests/lab/README.md) — The fake PSTN used for media validation
- [Hold Slayer Service](docs/hold-slayer-service.md) — IVR navigation, hold detection, human detection
- [Audio Classifier](docs/audio-classifier.md) — Waveform analysis, feature extraction, classification
- [Services](docs/services.md) — LLM client, transcription, recording, analytics, notifications