open source ยท MIT licensed

Voice agents, without the boilerplate.

Define a prompt, register Python functions as tools, deploy. minmo handles JSON-Schema generation, local tool hosting + tunneling, and talking to AssemblyAI, Hume, or OpenAI Realtime โ€” so you write the assistant, not the plumbing.

pip install -e .

Quickstart

Three moving parts: a prompt, a tool, a deploy call.

from minmo import VoiceAgent

agent = VoiceAgent(
    prompt="You are a friendly voice assistant. Keep answers short.",
    api_key="YOUR_ASSEMBLYAI_API_KEY",
)


@agent.tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"It's sunny in {city}."


result = agent.deploy(local=True)
print(result)  # the created AssemblyAI agent record, including its id

deploy(local=True) starts a local tool server, tunnels it publicly, and registers get_weather as a real tool your live voice agent can call. The returned result always includes an info field โ€” plain language on whether the agent lives on the provider's servers and how to connect to it.

Test tool logic without burning session minutes

from minmo import VoiceAgent
from minmo.testing import simulate

agent = VoiceAgent(
    prompt="You are a friendly voice assistant.",
    api_key="YOUR_ASSEMBLYAI_API_KEY",
    llm={"base_url": "https://api.openai.com/v1", "model": "gpt-4o-mini", "api_key": "YOUR_OPENAI_KEY"},
)


@agent.tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"It's sunny in {city}."


result = simulate(agent, transcript=["What's the weather in Paris?"])
print(result["tool_calls"])

simulate() calls the llm you configured directly (any OpenAI-compatible endpoint) and runs tool calls against your local Python functions โ€” no AssemblyAI session, no phone call.

Client-side tools

@agent.tool defaults to transport="http" โ€” minmo hosts the function behind a URL the provider calls. Pass transport="client" to declare a client-side/function tool instead: no server, no tunnel, no host_url needed.

@agent.tool(transport="client")
def get_account_balance(account_id: str) -> str:
    """Look up an account's balance."""
    return "$42.00"

If every registered tool is client-side, deploy() skips the local server and tunnel entirely โ€” nothing to host.

Providers

minmo supports three backends. They don't all host your agent the same way:

ProviderHosted on their servers?How you use it
AssemblyAI Yes โ€” deploy() creates a persistent agent record mint_token() for a client token, connect a voice session with it
Hume Yes โ€” deploy() creates/versions a config resource mint_token() for an access token, start an EVI session with it
OpenAI Realtime No โ€” deploy() only builds a config in memory Call mint_token() right after, same process, to get a session token

Pass a different provider via VoiceAgent(provider=..., provider_options=...).

CLI

minmo init     # scaffold main.py, .env.example, requirements.txt
minmo deploy   # import main.py, find the VoiceAgent, deploy it
minmo logs     # print the most recent session log

Deploying to a real server

agent.deploy(local=False, host_url="https://your-deployed-tool-server.example.com")
# or set MINMO_HOST_URL in the environment instead of passing host_url

local=True is for development; production tool hosting is up to you. minmo.server.create_tool_server is reusable if you want to deploy it yourself behind a real domain.