Skip to main content
Already have a working agent? This is your page. You wrap your brain in a DialogAdapter, assign it to ctx.session.dialog_machine, and Unpod handles the rest of the call - telephony, speech-to-text, text-to-speech, audio transport. Your Agent stays text-in/text-out. If you don’t have an agent yet, start with the Quickstart instead - it builds the minimal brain first.

Install

Write your entrypoint

The entrypoint is an async function called once per call. Set your agent on ctx.session.dialog_machine and call await ctx.session.run().
AgentRunner requires UNPOD_API_KEY in your environment, and the agent_id you pass must match your Speech Pipe’s agent_id - see IDs You’ll Meet. The Setup Checklist lists every variable.
Incoming calls to your Unpod number are now routed to your agent.

Streaming is the hot path

This is the single most important thing on this page. During a live call, session.run() calls your adapter’s stream() on every user turn and pipes tokens straight to the voice bridge for synthesis. turn() is never called by the framework during live calls.
If your adapter implements turn() properly but fakes stream() with a single-chunk fallback (await the full response, yield it once), the caller hears silence until the entire response is generated, then a long monologue - choppy, high-latency audio with no error message anywhere. Implement real token streaming in stream().
The bundled adapters differ here - check the table below before choosing: OpenAIAdapter, AnthropicAdapter, and LangChainAdapter stream real tokens from their providers; HTTPAdapter cannot stream (one HTTP round-trip, one chunk), so expect a latency penalty proportional to your response length.

The DialogAdapter protocol

Any object with these three methods is a valid brain - no base class required (the protocol is runtime-checkable):
Auto-wrapping: assigning a superdialog DialogMachine or LLMAgent directly to ctx.session.dialog_machine wraps it in a SuperDialogAdapter for you. Anything else must satisfy the protocol above, or the setter raises TypeError.

Supported adapters

OpenAI

Anthropic (Claude)

Model name formats differ by layer: direct adapters take the provider’s raw model name (claude-haiku-4-5-20251001, gpt-4o-mini); superdialog brains take a provider/model URI (anthropic/claude-haiku-4-5). Both are correct in their context.

LangChain

LangChainAdapter expects your chain to accept {"messages": [...]} as input by default. This works with ChatPromptTemplate | ChatModel chains. The adapter keeps conversation history across turns and streams via .astream(). If your chain uses a different input key, pass input_key:

HTTP endpoint

HTTPAdapter lets you keep your agent in any language behind an HTTP API. Each user turn becomes one POST:
Request body your endpoint receives, and the response it must return:
HTTPAdapter does not stream - the whole reply arrives as one chunk, so the caller waits for your full HTTP round-trip before hearing anything. Fine for short replies; for long-form answers prefer an in-process adapter.

Using session controls

Inside your entrypoint you can react to call events and control the call:
Common hooks: call_start, user_turn, agent_turn, user_partial, interruption, call_end, error (the registry also accepts tool_call, tool_result, silence, state, metric, llm_call, and turn_complete). See Session for the full control surface.

Writing a custom adapter

Implement the three protocol methods - no base class required:

Next steps

Setup Checklist

One-time resource provisioning: agent, number, voice profile

Session Controls

say(), transfer(), recording controls during live calls

SuperDialog

Conversation runtime: playbooks (default) and flow graphs

SuperDialog + Voice

Plug a SuperDialog agent directly into your AgentRunner session