Runbook
Install
uv sync --group dev --group voice # one venv; Pipecat 1.7.0 with the extras the bot and the eval harness need
make db # Postgres on :5433 — Docker when the daemon is up, a local cluster otherwise
cp .env.example .env # then fill DEEPGRAM_API_KEY, MURF_API_KEY, AWS credentials, SARVAM_API_KEY
.env may be read and edited; it is never printed and never committed.
AWS_REGION must be ap-south-1: Bedrock first-token p50 is 519 ms there
and 12,109 ms in us-east-1 (OBSERVED 2026-08-26).
Run the bot
poc/pc-web/server/run.sh # the live bot, http://localhost:7860/studio
poc/pc-web/server/run.sh -t eval --port 7861 # headless, for the regression suite; tags sessions `eval`
run.sh sources .env, pins the region, the model, the voice and the STT
endpoint, and derives ORI_RUN_TAG from the transport flag so an eval run can
never join the live corpus. Do not restart the :7860 bot to run an eval; use
:7861.
Every knob
All of them are read once into BotConfig (poc/pc-web/server/config.py) and
written into the session header, so a latency number on disk always names the
configuration that produced it.
| env var | default | what it does |
|---|---|---|
ORI_DEEPGRAM_URL |
wss://api.in.deepgram.com/v2/listen |
the STT socket; see below |
ORI_EOT_THRESHOLD |
0.8 |
Flux end-of-turn confidence; 0.7 cut him off mid-number |
AWS_BEDROCK_MODEL |
google.gemma-3-27b-it |
the prose model |
AWS_REGION |
ap-south-1 |
never a US region |
LIVE_VOICE |
hi-IN-shweta |
Murf voice, chosen by ear |
ORI_RUN_TAG |
live |
eval keeps a run out of the corpus and disables the idle timeout |
ORI_HOLD_S |
1.5 |
how long a cut-off figure waits for its continuation |
ORI_PLAYOUT_GRACE_S |
0.4 |
audio the browser is still playing after the server says it stopped |
ORI_HANGUP_WATCHDOG_S |
20 |
armed to hang up but no audio ever finished: end anyway |
ORI_EVAL_PONG_TIMEOUT |
180 |
the eval socket's patience for a grader running Whisper on this CPU |
OTEL_EXPORTER_OTLP_ENDPOINT |
unset | tracing is off unless a collector exists |
The India STT endpoint
api.in.deepgram.com terminates in AWS ap-south-2 (Hyderabad).
OBSERVED from Mumbai, same minute, n=8: TCP p50 38 ms vs 270 ms to
api.deepgram.com; websocket handshake 303 ms vs 1,175 ms. A websocket
pays that round trip repeatedly, so roughly 258 ms of the old 679 ms STT p50
was geography.
It is undocumented: absent from Deepgram's docs, changelog and status page.
No SLA, no incident visibility. Its TLS certificate was issued 2026-09-08 and
expires 2026-12-07. It is data-plane only (/v1/projects returns 404).
Our key and flux-general-multi are accepted on the handshake.
If calls go deaf — the studio page shows a red service_error line and the
session record carries the row — flip back with no code change:
ORI_DEEPGRAM_URL=wss://api.deepgram.com/v2/listen poc/pc-web/server/run.sh
STT settings that ship, with the measurement behind each: language_hints
["hi","en"] (callers code-switch); numerals=True (Flux renders Hinglish
numbers in English, where numerals works: 7/8 → 8/8 rates recovered, 0 wrong
either way, n=14); no keyterms (14/14 byte-identical transcripts with and
without, and a hallucinated हज़ार next to a truck size is a wrong rate).
Verify
.venv/bin/python -m pytest tests/ -q # the suite
.venv/bin/lint-imports # "domain is pure": 2 contracts kept
.venv/bin/ruff check app tests poc/pc-web/server scripts # lint
make mutate # mutation testing over the money path and the router
set -a; . ./.env; set +a; .venv/bin/python poc/e2e.py # end to end, a fresh bot per scenario
Measure
.venv/bin/python poc/analysis/mine_calls.py # every live call: intents, endings, latency, feedback
.venv/bin/python poc/analysis/replay_calls.py # replay live calls through the current brain
.venv/bin/python poc/analysis/perturb.py # the router's brittleness under five perturbations
.venv/bin/python poc/analysis/mine_words.py # the model's vocabulary and what the voice is handed
.venv/bin/python poc/converse_bakeoff.py # 12 models × 3 passes × 27 calls, two judges
.venv/bin/python poc/bakeoff.py # the gross-failure screen (leaks, stray slots, parroting)
.venv/bin/python poc/rate_call.py # score a call by ear
Results land under app/data/asr/ (gitignored); the ones worth keeping are
copied to docs/experiments/<date>-<topic>/.
Publish these docs
./deploy/docs.sh infra # once: bucket, certificate, CloudFront, DNS for docs.logistics.citesearch.in
./deploy/docs.sh site # build docs/ into site-build/ and sync
Separate from deploy/deploy.sh, which owns the load-booking demo and must
not be run casually — it mutates live AWS resources.