Skip to main content

Open-source voice AI SDK — connect any AI agent to real phone calls in 4 lines of code

Project description

Patter Python SDK

Connect AI agents to phone numbers in four lines of code

PyPI MIT License Python 3.11+

QuickstartFeaturesConfigurationVoice ModesAPI ReferenceContributing


Patter is the open-source SDK that gives your AI agent a phone number. Point it at any function that returns a string, and Patter handles the rest: telephony, speech-to-text, text-to-speech, and real-time audio streaming. You build the agent — we connect it to the phone.

Quickstart

pip install getpatter

Set the env vars your carrier and engine need:

export TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export TWILIO_AUTH_TOKEN=your_auth_token
export OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx

Four lines of Python:

from getpatter import Patter, Twilio, OpenAIRealtime

phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(engine=OpenAIRealtime(), system_prompt="You are a friendly receptionist for Acme Corp.", first_message="Hello! How can I help?")
await phone.serve(agent, tunnel=True)

tunnel=True spawns a Cloudflare tunnel and points your Twilio number at it. In production, pass webhook_url="api.prod.example.com" to the constructor instead.

Features

Feature Method Example
Inbound calls phone.serve(agent) Answer calls as an AI
Outbound calls + AMD phone.call(to, machine_detection=True) Place calls with voicemail detection
Tool calling agent(tools=[Tool(...)]) Agent calls external APIs mid-conversation
Custom STT + TTS agent(stt=DeepgramSTT(), tts=ElevenLabsTTS()) Bring your own voice providers
Dynamic variables agent(variables={...}) Personalize prompts per caller
Pluggable LLM agent(llm=AnthropicLLM()) 5 built-in providers: OpenAI, Anthropic, Groq, Cerebras, Google
Custom LLM (any model) serve(on_message=handler) Route to anything — local llama.cpp, internal gateways, etc.
Call recording serve(recording=True) Record all calls
Call transfer transfer_call (auto-injected) Transfer to a human
Voicemail drop call(voicemail_message="...") Play message on voicemail
Phone-as-a-tool (external agents) PatterTool(phone, agent).execute(...) Drop into LangChain / OpenAI Assistants / Hermes / MCP

Configuration

Environment variables

Every provider reads its credentials from the environment by default. Pass api_key="..." to any constructor to override.

Variable Used by
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN Twilio() carrier
TELNYX_API_KEY, TELNYX_CONNECTION_ID, TELNYX_PUBLIC_KEY (optional) Telnyx() carrier
PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN Plivo() carrier — Auth Token doubles as the V3 webhook signature key
OPENAI_API_KEY OpenAIRealtime, getpatter.stt.whisper.STT, getpatter.tts.openai.TTS
ELEVENLABS_API_KEY, ELEVENLABS_AGENT_ID ElevenLabsConvAI, getpatter.tts.elevenlabs.TTS
DEEPGRAM_API_KEY getpatter.stt.deepgram.STT
CARTESIA_API_KEY getpatter.stt.cartesia.STT, getpatter.tts.cartesia.TTS
RIME_API_KEY getpatter.tts.rime.TTS
LMNT_API_KEY getpatter.tts.lmnt.TTS
SONIOX_API_KEY getpatter.stt.soniox.STT
SPEECHMATICS_API_KEY getpatter.stt.speechmatics.STT
ASSEMBLYAI_API_KEY getpatter.stt.assemblyai.STT
ANTHROPIC_API_KEY AnthropicLLM / getpatter.llm.anthropic.LLM
GROQ_API_KEY GroqLLM / getpatter.llm.groq.LLM
CEREBRAS_API_KEY CerebrasLLM / getpatter.llm.cerebras.LLM
GEMINI_API_KEY (or GOOGLE_API_KEY) GoogleLLM / getpatter.llm.google.LLM
cp .env.example .env
# Edit .env with your API keys

Other carriers: Telnyx and Plivo are both fully supported alternatives to Twilio. All three carriers receive equal support for inbound DTMF, transfer, AMD, status callbacks, recording, voicemail drop, and metrics. Plivo additionally supports native DTMF send over the media WebSocket — a capability Twilio Media Streams lacks. Plivo's Auth Token doubles as the V3 webhook signature key (no separate public key, unlike Telnyx Ed25519).

Voice Modes

Mode Latency Quality Best For
OpenAI Realtime Lowest High Fluid, low-latency conversations
Pipeline (STT + LLM + TTS) Low High Independent control over STT and TTS
ElevenLabs ConvAI Low High ElevenLabs-managed conversation flow

API Reference

Patter constructor

Patter(
    carrier: Twilio | Telnyx | Plivo,
    phone_number: str,
    webhook_url: str = "",         # Public hostname (no scheme). Mutually exclusive with tunnel=...
    tunnel: CloudflareTunnel | Static | Ngrok | None = None,
    pricing: dict | None = None,
)
Parameter Type Description
carrier Twilio / Telnyx / Plivo Carrier instance. Reads env vars by default.
phone_number str Your phone number in E.164 format.
webhook_url str Public hostname your local server is reachable on. Use instead of tunnel=.
tunnel instance CloudflareTunnel(), Static(hostname=...), or Ngrok().

phone.agent()

phone.agent(
    system_prompt: str,
    engine: OpenAIRealtime | ElevenLabsConvAI | None = None,   # default OpenAIRealtime()
    stt: STTProvider | None = None,                            # e.g. DeepgramSTT()
    tts: TTSProvider | None = None,                            # e.g. ElevenLabsTTS()
    voice: str = "alloy",
    model: str = "gpt-4o-mini-realtime-preview",
    language: str = "en",
    first_message: str = "",
    tools: list[Tool] | None = None,
    guardrails: list[Guardrail] | None = None,
    variables: dict | None = None,
    ...,
)

Pass engine= for end-to-end mode, stt= + tts= for pipeline mode. Both arguments may take plain adapter instances (e.g. DeepgramSTT()) that read their API key from the environment.

phone.serve()

await phone.serve(
    agent: Agent,
    port: int = 8000,
    tunnel: bool = False,          # shortcut for Patter(tunnel=CloudflareTunnel())
    dashboard: bool = True,
    recording: bool = False,
    on_call_start: Callable | None = None,
    on_call_end: Callable | None = None,
    on_transcript: Callable | None = None,
    on_message: Callable | str | None = None,
    voicemail_message: str = "",
    dashboard_token: str = "",
)

phone.call()

await phone.call(
    to: str,
    agent: Agent | None = None,            # required
    from_number: str = "",
    first_message: str = "",
    machine_detection: bool = False,
    voicemail_message: str = "",
    ring_timeout: int | None = None,
)

STT / TTS catalog

Flat re-exports (short form):

from getpatter import (
    Twilio, Telnyx, Plivo,
    OpenAIRealtime, ElevenLabsConvAI,
    # STT / TTS classes live in namespaced modules — see below.
)

Namespaced imports (one module per provider):

from getpatter.stt import deepgram, whisper, openai_transcribe, cartesia, soniox, speechmatics, assemblyai
from getpatter.tts import elevenlabs, openai as openai_tts, cartesia as cartesia_tts, rime, lmnt

stt = deepgram.STT()                                  # reads DEEPGRAM_API_KEY
stt = openai_transcribe.STT()                         # gpt-4o-transcribe — ~10× faster than Whisper
tts = elevenlabs.TTS(voice_id="sarah")                # reads ELEVENLABS_API_KEY
tts = elevenlabs.TTS.for_twilio(voice_id="sarah")     # μ-law @ 8 kHz native — no resample on Twilio

Examples

Inbound calls — default engine

import asyncio
from getpatter import Patter, Twilio, OpenAIRealtime

async def main() -> None:
    phone = Patter(carrier=Twilio(), phone_number="+15550001234")
    agent = phone.agent(
        engine=OpenAIRealtime(),
        system_prompt="You are a helpful customer service agent.",
        first_message="Hello! How can I help?",
    )
    await phone.serve(
        agent,
        tunnel=True,
        on_call_start=lambda data: print(f"Call from {data['caller']}"),
        on_call_end=lambda data: print("Call ended"),
    )

asyncio.run(main())

Custom voice — Deepgram STT + ElevenLabs TTS

from getpatter import Patter, Twilio
from getpatter.stt import deepgram
from getpatter.tts import elevenlabs

phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
    stt=deepgram.STT(),              # reads DEEPGRAM_API_KEY
    tts=elevenlabs.TTS(voice="sarah"),    # reads ELEVENLABS_API_KEY
    system_prompt="You are a helpful voice assistant.",
)
await phone.serve(agent, tunnel=True)

Pipeline mode — pick STT, LLM, TTS independently

from getpatter import Patter, Twilio, DeepgramSTT, AnthropicLLM, ElevenLabsTTS

phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
    stt=DeepgramSTT(),                     # reads DEEPGRAM_API_KEY
    llm=AnthropicLLM(),                    # reads ANTHROPIC_API_KEY
    tts=ElevenLabsTTS(voice_id="sarah"),   # reads ELEVENLABS_API_KEY
    system_prompt="You are a helpful voice assistant.",
)
await phone.serve(agent, tunnel=True)

Available LLM providers: OpenAILLM, AnthropicLLM, GroqLLM, CerebrasLLM, GoogleLLM. Tool calling works across all five. For fully custom logic, drop llm= and pass an on_message callback to serve() instead.

Tool calling

from getpatter import Patter, Twilio, OpenAIRealtime, Tool, tool

@tool
async def check_availability(date: str) -> dict:
    """Check appointment availability for a given ISO date."""
    return {"available": True}

phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
    engine=OpenAIRealtime(),
    system_prompt="You are a booking assistant.",
    tools=[check_availability],
)
await phone.serve(agent, tunnel=True)

Outbound calls

from getpatter import Patter, Twilio, OpenAIRealtime

phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
    engine=OpenAIRealtime(),
    system_prompt="You are making reminder calls.",
    first_message="Hi, this is a reminder from Acme Corp.",
)

await phone.serve(agent, tunnel=True)
await phone.call(to="+14155551234", agent=agent)

Dynamic variables

agent = phone.agent(
    engine=OpenAIRealtime(),
    system_prompt="You are helping {customer_name}, account #{account_id}.",
    first_message="Hi {customer_name}! How can I help you today?",
    variables={"customer_name": "Jane", "account_id": "A-789"},
)

Contributing

Pull requests are welcome.

cd libraries/python && pip install -e ".[dev]" && pytest tests/ -v

Please open an issue before submitting large changes so we can discuss the approach first.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

getpatter-0.6.5.tar.gz (4.7 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

getpatter-0.6.5-py3-none-any.whl (4.6 MB view details)

Uploaded Python 3

File details

Details for the file getpatter-0.6.5.tar.gz.

File metadata

  • Download URL: getpatter-0.6.5.tar.gz
  • Upload date:
  • Size: 4.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for getpatter-0.6.5.tar.gz
Algorithm Hash digest
SHA256 7268c3aab8729edf741c3de7f0f8813bf7bcac759bdcf85f7e257a6f29a7e4d3
MD5 8e3287cb5bb43588d1d5fc225df57e61
BLAKE2b-256 24c9c0a654d3bb6f06974fbd085deb952998472c02d053971d945887ebce0737

See more details on using hashes here.

Provenance

The following attestation bundles were made for getpatter-0.6.5.tar.gz:

Publisher: release.yml on PatterAI/Patter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file getpatter-0.6.5-py3-none-any.whl.

File metadata

  • Download URL: getpatter-0.6.5-py3-none-any.whl
  • Upload date:
  • Size: 4.6 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for getpatter-0.6.5-py3-none-any.whl
Algorithm Hash digest
SHA256 f046b3becd82be49fe217fd4614f73ef4e8f3a839b137a336e4fefc8da461c83
MD5 5a169d24e31a31c172224814b0c25290
BLAKE2b-256 d02ac09da4efd994d92dce85b1a938a7b114ef8c7cb2f0fa7cb10de996465251

See more details on using hashes here.

Provenance

The following attestation bundles were made for getpatter-0.6.5-py3-none-any.whl:

Publisher: release.yml on PatterAI/Patter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page