The three layers
Python 3.11 or later throughout.
The agent layer stands alone. You can profile cost and latency without operating any
infrastructure beyond your own agent process, and most people stay there. The SFU and SIP
layers assume you already run the deployment under test, which is usually a different job
and often a different person.
The SIP layer works differently
VoiceGateway does not place calls and will not become a load generator. It is the evidence and reporting layer: something else drives the load, and this reads what it left behind.You run the generator against your own trunk, then hand VoiceGateway the artifacts.
voicegw loadtest import ingests and correlates them; voicegw loadtest report <run_id>
judges the gates and writes the evidence bundle.
How the layers join
Each layer is written by its own path: LiveKit webhooks fillcalls and call_legs, the
node scrape fills node_samples, and attach() writes sessions and requests. No single
row joins all three. The joins are pairwise and deliberately conservative:
GET /api/correlation reports the deployment-wide join rate so you can see how complete
the picture is instead of assuming it.
The per-call waterfall
The dashboard renders six layers per call. Two carry a number.
The unmeasured rows render their reason rather than an empty bar. Per-modality inference
timing does exist, from
attach(), on the cost and latency views. It is not joined into
this waterfall.
What it does not do
- It does not generate SIP load. Not one call.
- It does generate SFU traffic when you ask:
voicegw livekit latencyplaces real agent turns,voicegw livekit sfu --loadconnects synthetic participants. Both are opt-in. - It does not provision infrastructure.
- It does not claim a causal join. Node overlap means a sample fell inside a call’s window, not that the node served that call.