Skip to main content

Bring Your Agent

Already have a working Python agent? Connect it to a phone number in under 15 minutes.

Install

uv add unpod

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().
from unpod import AgentRunner, CallContext
from unpod.adapters.langchain import LangChainAdapter

# Your existing LangChain chain
from your_app import chain

async def entrypoint(ctx: CallContext) -> None:
    ctx.session.dialog_machine = LangChainAdapter(chain)
    await ctx.session.run()

Start Your Runner

AgentRunner requires UNPOD_API_KEY to be set in your environment. See the Setup Checklist for all required variables.
AgentRunner(
    entrypoint=entrypoint,
    agent_id="my-agent",  # must match agent_id in your Speech Pipe config
).start()
That’s it. Incoming calls to your Unpod number are now routed to your agent.

Supported Adapters

AdapterImportUse when
OpenAIAdapterunpod.adapters.openaiOpenAI AsyncOpenAI client — gpt-4o, gpt-4o-mini, etc.
AnthropicAdapterunpod.adapters.anthropicAnthropic AsyncAnthropic client — Claude models
LangChainAdapterunpod.adapters.langchainLangChain chain with .ainvoke() / .astream()
HTTPAdapterunpod.adapters.httpRemote agent API (any language)
SuperDialogAdapterunpod.adapters.superdialogsuperdialog DialogMachine / LLMAgent — or assign directly, auto-wrapping handled
CustomImplement DialogAdapter protocolAny Python object with turn, stream, assist

OpenAI

from openai import AsyncOpenAI
from unpod import AgentRunner, CallContext
from unpod.adapters.openai import OpenAIAdapter

client = AsyncOpenAI()

async def entrypoint(ctx: CallContext) -> None:
    ctx.session.dialog_machine = OpenAIAdapter(
        client=client,
        model="gpt-4o-mini",
        system_prompt="You are a helpful support agent. Be concise.",
    )
    await ctx.session.run()

AgentRunner(entrypoint=entrypoint, agent_id="my-agent").start()

Anthropic (Claude)

import anthropic
from unpod import AgentRunner, CallContext
from unpod.adapters.anthropic import AnthropicAdapter

client = anthropic.AsyncAnthropic()

async def entrypoint(ctx: CallContext) -> None:
    ctx.session.dialog_machine = AnthropicAdapter(
        client=client,
        model="claude-haiku-4-5-20251001",
        system_prompt="You are a helpful support agent. Be concise.",
    )
    await ctx.session.run()

AgentRunner(entrypoint=entrypoint, agent_id="my-agent").start()

LangChain Chain Requirements

LangChainAdapter expects your chain to accept {"messages": [...]} as input by default. This works with ChatPromptTemplate | ChatModel chains. If your chain uses a different input key, pass input_key:
# Chain expects {"input": "..."}
LangChainAdapter(chain, input_key="input")

Using Session Controls

Inside your entrypoint you can react to call events and control the call:
async def entrypoint(ctx: CallContext) -> None:
    @ctx.session.on("user_turn")
    async def _(text: str) -> None:
        print(f"User said: {text}")

    @ctx.session.on("call_end")
    async def _(reason: str) -> None:
        print(f"Call ended: {reason}")

    ctx.session.dialog_machine = LangChainAdapter(chain)
    await ctx.session.run()
Available hooks: call_start, user_turn, agent_turn, user_partial, interruption, call_end, error.

Writing a Custom Adapter

Implement three methods - no base class required:
class MyAdapter:
    async def turn(self, text: str, context: dict | None = None) -> str:
        """Return complete response. Called for non-streaming use."""
        return my_agent.respond(text)

    async def stream(self, text: str, context: dict | None = None):
        """Yield response tokens. THIS is the hot path used by session.run()."""
        async for token in my_agent.stream(text):
            yield token

    def assist(self, text: str) -> None:
        """Inject a system instruction before the next turn."""
        my_agent.set_instruction(text)
stream() is called on every user turn by session.run(). Implement it properly - a single-chunk fallback causes choppy audio.

Next Steps

Setup Checklist

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

Session Controls

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

SuperDialog

State machine framework for structured conversation flows

SuperDialog + Voice

Plug a DialogMachine directly into your AgentRunner session