Skip to main content

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:
from openrtc import AgentPool

pool = AgentPool()                    # livekit (the default)
pool = AgentPool(backend="pipecat")   # pipecat
import openrtc pulls neither framework. Each backend lives behind its own extra and is imported only when you select it:
BackendInstallAgent shape
livekit (default)openrtc[livekit]livekit.agents.Agent subclass
pipecatopenrtc[pipecat]pipeline builder callable
Selecting a backend whose framework is not installed raises a clear error with the pip install openrtc[...] hint.

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.
The registration surface is the same on both: add, get, remove, and list_agents.

livekit backend (default)

Agents are standard livekit.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:
from openrtc import AgentPool
from openrtc.backends.pipecat import PipecatCallView


def support(view: PipecatCallView):
    # transport and services are yours (any pipecat transport / services);
    # the shared VAD and turn model come from the pool's prewarm.
    transport = make_transport(vad_analyzer=view.prewarmed.vad)
    return [
        transport.input(),
        stt,
        view.prewarmed.turn,
        llm,
        tts,
        transport.output(),
    ]


pool = AgentPool(backend="pipecat")
pool.add("support", support)

Registration side by side

from livekit.agents import Agent
from livekit.plugins import openai
from openrtc import AgentPool

class Support(Agent):
    def __init__(self) -> None:
        super().__init__(instructions="Help callers.")

pool = AgentPool()  # backend="livekit"
pool.add(
    "support",
    Support,
    stt=openai.STT(),
    llm=openai.responses.LLM(),
    tts=openai.TTS(),
)

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. The transport-serving front (accepting live calls over a transport and running a session per connection) is not yet wired: AgentPool(backend="pipecat").run() raises NotImplementedError documenting that boundary. 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.