Frameworks
OpenRTC is a thin operational layer (registration, routing, shared prewarm, observability, drain) over a voice runtime. That runtime is a backend, chosen when you build the pool:import openrtc pulls neither framework. Each backend lives behind its own
extra and is imported only when you select it:
Selecting a backend whose framework is not installed raises a clear error with
the
pip install openrtc[...] hint. Serving the pipecat backend (run()) also
needs openrtc[pipecat-serve], which adds pipecat’s FastAPI runner.
What every backend shares
Whichever runtime you pick, OpenRTC gives you the same operator control plane:- Registration and routing.
pool.add(name, ...)registers an agent; each call is routed to one by the same precedence (custom router, then job / room metadata, then room-name prefix, then first registered). See Routing. - Shared prewarm. The worker loads the VAD and turn model once and shares them across every session, instead of building them per call.
- Observability. Every session emits the same start / end signals to the observers you pass to the pool, so metrics and cost / quality lanes stay wired across frameworks.
add, get, remove, and
list_agents.
livekit backend (default)
Agents are standardlivekit.agents.Agent subclasses, and the provider arguments
pass through to the session. This is the model in
Getting Started and
Coming from livekit-agents.
pipecat backend
A pipecat agent registers as a pipeline builder: a callable that, given the call’s view, returns the pipecat processors for that call. The builder owns the transport and the STT / LLM / TTS services (so the pool’s provider arguments, which are livekit-only, do not apply here). OpenRTC wraps routing, shared prewarm, and observability around it. The call view (PipecatCallView) carries the worker’s shared prewarm, so the
builder attaches the shared VAD and turn analyzer instead of building its own:
Registration side by side
- livekit
- pipecat
Serving
pool.run() serves calls over a transport via pipecat’s runner (a FastAPI
server). Install the serving extra and call run():
/start endpoint; OpenRTC routes it
(body["agent"]), builds the observed session, and runs one pipeline per call
with the shared prewarm. Host and port come from pipecat’s environment
(RUNNER_HOST / RUNNER_PORT). During a drain the worker declines new calls and
lets in-flight ones finish.
From a directory of @agent_config-marked builders, the CLI does the same:
openrtc serve ./agents
starts pipecat’s runner (default http://localhost:7860), which serves a browser
test client at that URL. Open it, start a call, and confirm the agent responds and
that OpenRTC emits a session start / end for the call. This live check is the one
step the automated suite cannot cover (the assembly is otherwise verified end to
end in process); everything up to accepting a real transport connection is tested.
Status and boundaries
The pipecat backend’s per-call path (registration, routing, shared prewarm, dispatch, and observability) is complete and verified against real pipecat pipelines.AgentPool(backend="pipecat").run() serves calls over a transport
via pipecat’s runner (behind openrtc[pipecat-serve]): it registers a
per-connection bot that routes the call and runs the observed session, so one
worker serves many calls under OpenRTC’s routing, prewarm, and observability. The
one remaining boundary is a genuinely live transport connection (WebRTC / Daily /
telephony), covered by a manual / integration smoke rather than the unit suite,
the same way the livekit backend does not re-test its live network front in
process. The livekit backend is complete and is the default.
Hot reload requires the livekit backend (requesting it on pipecat raises a clear
error). Session introspection is a livekit-backend feature as well; see
Hot reload and
Session introspection.
