minmo 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["**"]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+
21
+ - run: pip install -e ".[dev]"
22
+
23
+ - run: pytest -q
@@ -0,0 +1,28 @@
1
+ name: Format
2
+
3
+ on:
4
+ push:
5
+ branches: ["**"]
6
+
7
+ jobs:
8
+ black:
9
+ runs-on: ubuntu-latest
10
+ if: github.actor != 'github-actions[bot]'
11
+ permissions:
12
+ contents: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ ref: ${{ github.head_ref || github.ref_name }}
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.11"
21
+
22
+ - run: pip install black
23
+
24
+ - run: black minmo tests scripts
25
+
26
+ - uses: stefanzweifel/git-auto-commit-action@v5
27
+ with:
28
+ commit_message: "style: black auto-format"
@@ -0,0 +1,38 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.11"
16
+
17
+ - run: pip install build
18
+
19
+ - run: python -m build
20
+
21
+ - uses: actions/upload-artifact@v4
22
+ with:
23
+ name: dist
24
+ path: dist/
25
+
26
+ publish:
27
+ needs: build
28
+ runs-on: ubuntu-latest
29
+ environment: pypi
30
+ permissions:
31
+ id-token: write
32
+ steps:
33
+ - uses: actions/download-artifact@v4
34
+ with:
35
+ name: dist
36
+ path: dist/
37
+
38
+ - uses: pypa/gh-action-pypi-publish@release/v1
minmo-0.1.0/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ .worktrees/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ .venv/
6
+ .minmo/
7
+ .env
8
+ dist/
9
+ build/
10
+ *.egg-info/
minmo-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anas Elhaag
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
minmo-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.5
2
+ Name: minmo
3
+ Version: 0.1.0
4
+ Summary: Boilerplate-free SDK for building voice agents on AssemblyAI's Voice Agent API
5
+ Project-URL: Homepage, https://github.com/a-elhaag/minmo
6
+ Author: Anas Elhaag
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: click>=8.1
11
+ Requires-Dist: fastapi>=0.110
12
+ Requires-Dist: jsonschema>=4.0
13
+ Requires-Dist: openai>=1.30
14
+ Requires-Dist: pyngrok>=7.0
15
+ Requires-Dist: requests>=2.31
16
+ Requires-Dist: uvicorn>=0.29
17
+ Provides-Extra: dev
18
+ Requires-Dist: black>=24.0; extra == 'dev'
19
+ Requires-Dist: httpx>=0.27; extra == 'dev'
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Requires-Dist: responses>=0.25; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # minmo
25
+
26
+ [![CI](https://github.com/a-elhaag/minmo/actions/workflows/ci.yml/badge.svg)](https://github.com/a-elhaag/minmo/actions/workflows/ci.yml)
27
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
28
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](pyproject.toml)
29
+
30
+ **Boilerplate-free SDK for building voice agents** on [AssemblyAI's Voice Agent API](https://assemblyai.com/docs/voice-agents/voice-agent-api), with support for Hume and OpenAI Realtime too.
31
+
32
+ Define a prompt, register Python functions as tools, deploy. minmo handles
33
+ JSON-Schema generation, local tool hosting + tunneling, and talking to each
34
+ provider's REST API — so you write the assistant, not the plumbing.
35
+
36
+ 📖 **[Full documentation](https://a-elhaag.github.io/minmo/)**
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install -e .
42
+ ```
43
+
44
+ (Set `NGROK_AUTHTOKEN` in your environment if you plan to use `local=True` —
45
+ see [pyngrok's docs](https://pyngrok.readthedocs.io/) for how to get one.)
46
+
47
+ ## Quickstart
48
+
49
+ ```python
50
+ from minmo import VoiceAgent
51
+
52
+ agent = VoiceAgent(
53
+ prompt="You are a friendly voice assistant. Keep answers short.",
54
+ api_key="YOUR_ASSEMBLYAI_API_KEY",
55
+ )
56
+
57
+
58
+ @agent.tool
59
+ def get_weather(city: str) -> str:
60
+ """Get the current weather for a city."""
61
+ return f"It's sunny in {city}."
62
+
63
+
64
+ result = agent.deploy(local=True)
65
+ print(result) # the created AssemblyAI agent record, including its id
66
+ ```
67
+
68
+ That's it — `deploy(local=True)` starts a local tool server, tunnels it
69
+ publicly, and registers `get_weather` as a real tool your live voice agent
70
+ can call.
71
+
72
+ `result` always includes an `"info"` field — a plain-language paragraph
73
+ telling you whether the agent is hosted on the provider's servers or only
74
+ exists locally, and how to actually connect to it (usually via
75
+ `mint_token()`). Worth printing after every deploy, especially since this
76
+ differs per provider — see below.
77
+
78
+ ## Testing tool logic without burning session minutes
79
+
80
+ ```python
81
+ from minmo import VoiceAgent
82
+ from minmo.testing import simulate
83
+
84
+ agent = VoiceAgent(
85
+ prompt="You are a friendly voice assistant.",
86
+ api_key="YOUR_ASSEMBLYAI_API_KEY",
87
+ llm={"base_url": "https://api.openai.com/v1", "model": "gpt-4o-mini", "api_key": "YOUR_OPENAI_KEY"},
88
+ )
89
+
90
+
91
+ @agent.tool
92
+ def get_weather(city: str) -> str:
93
+ """Get the current weather for a city."""
94
+ return f"It's sunny in {city}."
95
+
96
+
97
+ result = simulate(agent, transcript=["What's the weather in Paris?"])
98
+ print(result["tool_calls"])
99
+ ```
100
+
101
+ `simulate()` calls the `llm` you configured directly (any OpenAI-compatible
102
+ endpoint) and runs tool calls against your local Python functions — no
103
+ AssemblyAI session, no phone call.
104
+
105
+ ## Client-side tools (no server, no tunnel)
106
+
107
+ `@agent.tool` defaults to `transport="http"` — minmo hosts the function
108
+ behind a URL AssemblyAI calls. Pass `transport="client"` instead to declare
109
+ a [client-side/function tool](https://assemblyai.com/docs/voice-agents/voice-agent-api/tools/client-side-tools):
110
+ no server, no tunnel, no `host_url` needed for that tool.
111
+
112
+ ```python
113
+ @agent.tool(transport="client")
114
+ def get_account_balance(account_id: str) -> str:
115
+ """Look up an account's balance."""
116
+ return "$42.00"
117
+ ```
118
+
119
+ minmo only builds the correct agent config for this — declaring the tool
120
+ as client-side. Running the actual session (the WebSocket connection that
121
+ receives `tool.call` and sends back `tool.result`) is your own code's job:
122
+ a browser app, a Twilio bridge, whatever already holds that live connection.
123
+ If every registered tool is `transport="client"`, `deploy()` skips the local
124
+ server and tunnel (or `host_url` requirement) entirely — nothing to host.
125
+
126
+ ## Providers
127
+
128
+ minmo supports AssemblyAI (default), Hume, and OpenAI Realtime. They don't
129
+ all host your agent the same way:
130
+
131
+ | Provider | Hosted on their servers? | How you use it |
132
+ |---|---|---|
133
+ | AssemblyAI | Yes — `deploy()` creates a persistent agent record | `mint_token()` for a client token, connect a voice session with it |
134
+ | Hume | Yes — `deploy()` creates/versions a config resource | `mint_token()` for an access token, start an EVI session with it |
135
+ | OpenAI Realtime | No — `deploy()` only builds a config in your process's memory | Call `mint_token()` right after, in the same process, to actually send it to OpenAI and get a session token |
136
+
137
+ Pass a different provider via `VoiceAgent(provider=..., provider_options=...)`;
138
+ see each provider's docstring in `minmo/providers/` for required options.
139
+
140
+ ## CLI
141
+
142
+ ```bash
143
+ minmo init # scaffold main.py, .env.example, requirements.txt
144
+ minmo deploy # import main.py, find the VoiceAgent, deploy it
145
+ minmo logs # print the most recent session log
146
+ ```
147
+
148
+ ## Deploying to a real server (not `local=True`)
149
+
150
+ ```python
151
+ agent.deploy(local=False, host_url="https://your-deployed-tool-server.example.com")
152
+ # or set MINMO_HOST_URL in the environment instead of passing host_url
153
+ ```
154
+
155
+ Your host must already be running the tool server — `local=True` is for
156
+ development; production tool hosting is up to you (minmo's local server
157
+ factory, `minmo.server.create_tool_server`, is reusable if you want to
158
+ deploy it yourself behind a real domain).
159
+
160
+ ## Development
161
+
162
+ ```bash
163
+ pip install -e ".[dev]"
164
+ pytest -q
165
+ ```
166
+
167
+ Pushes are auto-formatted with [Black](https://black.readthedocs.io/) via
168
+ GitHub Actions, and every push/PR runs the test suite across Python 3.10–3.12.
169
+
170
+ ## Contributing
171
+
172
+ Issues and PRs welcome. Keep changes small and covered by a test.
173
+
174
+ ## License
175
+
176
+ [MIT](LICENSE) © [Anas Elhaag](https://github.com/a-elhaag)
minmo-0.1.0/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # minmo
2
+
3
+ [![CI](https://github.com/a-elhaag/minmo/actions/workflows/ci.yml/badge.svg)](https://github.com/a-elhaag/minmo/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](pyproject.toml)
6
+
7
+ **Boilerplate-free SDK for building voice agents** on [AssemblyAI's Voice Agent API](https://assemblyai.com/docs/voice-agents/voice-agent-api), with support for Hume and OpenAI Realtime too.
8
+
9
+ Define a prompt, register Python functions as tools, deploy. minmo handles
10
+ JSON-Schema generation, local tool hosting + tunneling, and talking to each
11
+ provider's REST API — so you write the assistant, not the plumbing.
12
+
13
+ 📖 **[Full documentation](https://a-elhaag.github.io/minmo/)**
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install -e .
19
+ ```
20
+
21
+ (Set `NGROK_AUTHTOKEN` in your environment if you plan to use `local=True` —
22
+ see [pyngrok's docs](https://pyngrok.readthedocs.io/) for how to get one.)
23
+
24
+ ## Quickstart
25
+
26
+ ```python
27
+ from minmo import VoiceAgent
28
+
29
+ agent = VoiceAgent(
30
+ prompt="You are a friendly voice assistant. Keep answers short.",
31
+ api_key="YOUR_ASSEMBLYAI_API_KEY",
32
+ )
33
+
34
+
35
+ @agent.tool
36
+ def get_weather(city: str) -> str:
37
+ """Get the current weather for a city."""
38
+ return f"It's sunny in {city}."
39
+
40
+
41
+ result = agent.deploy(local=True)
42
+ print(result) # the created AssemblyAI agent record, including its id
43
+ ```
44
+
45
+ That's it — `deploy(local=True)` starts a local tool server, tunnels it
46
+ publicly, and registers `get_weather` as a real tool your live voice agent
47
+ can call.
48
+
49
+ `result` always includes an `"info"` field — a plain-language paragraph
50
+ telling you whether the agent is hosted on the provider's servers or only
51
+ exists locally, and how to actually connect to it (usually via
52
+ `mint_token()`). Worth printing after every deploy, especially since this
53
+ differs per provider — see below.
54
+
55
+ ## Testing tool logic without burning session minutes
56
+
57
+ ```python
58
+ from minmo import VoiceAgent
59
+ from minmo.testing import simulate
60
+
61
+ agent = VoiceAgent(
62
+ prompt="You are a friendly voice assistant.",
63
+ api_key="YOUR_ASSEMBLYAI_API_KEY",
64
+ llm={"base_url": "https://api.openai.com/v1", "model": "gpt-4o-mini", "api_key": "YOUR_OPENAI_KEY"},
65
+ )
66
+
67
+
68
+ @agent.tool
69
+ def get_weather(city: str) -> str:
70
+ """Get the current weather for a city."""
71
+ return f"It's sunny in {city}."
72
+
73
+
74
+ result = simulate(agent, transcript=["What's the weather in Paris?"])
75
+ print(result["tool_calls"])
76
+ ```
77
+
78
+ `simulate()` calls the `llm` you configured directly (any OpenAI-compatible
79
+ endpoint) and runs tool calls against your local Python functions — no
80
+ AssemblyAI session, no phone call.
81
+
82
+ ## Client-side tools (no server, no tunnel)
83
+
84
+ `@agent.tool` defaults to `transport="http"` — minmo hosts the function
85
+ behind a URL AssemblyAI calls. Pass `transport="client"` instead to declare
86
+ a [client-side/function tool](https://assemblyai.com/docs/voice-agents/voice-agent-api/tools/client-side-tools):
87
+ no server, no tunnel, no `host_url` needed for that tool.
88
+
89
+ ```python
90
+ @agent.tool(transport="client")
91
+ def get_account_balance(account_id: str) -> str:
92
+ """Look up an account's balance."""
93
+ return "$42.00"
94
+ ```
95
+
96
+ minmo only builds the correct agent config for this — declaring the tool
97
+ as client-side. Running the actual session (the WebSocket connection that
98
+ receives `tool.call` and sends back `tool.result`) is your own code's job:
99
+ a browser app, a Twilio bridge, whatever already holds that live connection.
100
+ If every registered tool is `transport="client"`, `deploy()` skips the local
101
+ server and tunnel (or `host_url` requirement) entirely — nothing to host.
102
+
103
+ ## Providers
104
+
105
+ minmo supports AssemblyAI (default), Hume, and OpenAI Realtime. They don't
106
+ all host your agent the same way:
107
+
108
+ | Provider | Hosted on their servers? | How you use it |
109
+ |---|---|---|
110
+ | AssemblyAI | Yes — `deploy()` creates a persistent agent record | `mint_token()` for a client token, connect a voice session with it |
111
+ | Hume | Yes — `deploy()` creates/versions a config resource | `mint_token()` for an access token, start an EVI session with it |
112
+ | OpenAI Realtime | No — `deploy()` only builds a config in your process's memory | Call `mint_token()` right after, in the same process, to actually send it to OpenAI and get a session token |
113
+
114
+ Pass a different provider via `VoiceAgent(provider=..., provider_options=...)`;
115
+ see each provider's docstring in `minmo/providers/` for required options.
116
+
117
+ ## CLI
118
+
119
+ ```bash
120
+ minmo init # scaffold main.py, .env.example, requirements.txt
121
+ minmo deploy # import main.py, find the VoiceAgent, deploy it
122
+ minmo logs # print the most recent session log
123
+ ```
124
+
125
+ ## Deploying to a real server (not `local=True`)
126
+
127
+ ```python
128
+ agent.deploy(local=False, host_url="https://your-deployed-tool-server.example.com")
129
+ # or set MINMO_HOST_URL in the environment instead of passing host_url
130
+ ```
131
+
132
+ Your host must already be running the tool server — `local=True` is for
133
+ development; production tool hosting is up to you (minmo's local server
134
+ factory, `minmo.server.create_tool_server`, is reusable if you want to
135
+ deploy it yourself behind a real domain).
136
+
137
+ ## Development
138
+
139
+ ```bash
140
+ pip install -e ".[dev]"
141
+ pytest -q
142
+ ```
143
+
144
+ Pushes are auto-formatted with [Black](https://black.readthedocs.io/) via
145
+ GitHub Actions, and every push/PR runs the test suite across Python 3.10–3.12.
146
+
147
+ ## Contributing
148
+
149
+ Issues and PRs welcome. Keep changes small and covered by a test.
150
+
151
+ ## License
152
+
153
+ [MIT](LICENSE) © [Anas Elhaag](https://github.com/a-elhaag)
@@ -0,0 +1,185 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Cookbook — minmo</title>
7
+ <meta name="description" content="Copy-paste recipes for common minmo voice agent patterns." />
8
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎙️</text></svg>" />
9
+ <link rel="stylesheet" href="style.css" />
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css" />
11
+ </head>
12
+ <body>
13
+
14
+ <header class="nav">
15
+ <div class="wrap nav-inner">
16
+ <a class="brand" href="index.html">minmo</a>
17
+ <nav class="links">
18
+ <a href="index.html#quickstart">Quickstart</a>
19
+ <a href="index.html#providers">Providers</a>
20
+ <a href="index.html#cli">CLI</a>
21
+ <a href="cookbook.html">Cookbook</a>
22
+ <a href="reference.html">Reference</a>
23
+ <a href="https://github.com/a-elhaag/minmo">GitHub</a>
24
+ </nav>
25
+ </div>
26
+ </header>
27
+
28
+ <main>
29
+
30
+ <section class="hero" id="top">
31
+ <div class="wrap">
32
+ <p class="eyebrow">recipes</p>
33
+ <h1>Cookbook</h1>
34
+ <p class="lede">
35
+ Small, copy-paste-ready patterns for things people build with minmo.
36
+ Each one assumes a <code>VoiceAgent</code> is already created — see the
37
+ <a href="index.html#quickstart">quickstart</a> if you need that part.
38
+ </p>
39
+ </div>
40
+ </section>
41
+
42
+ <section class="section" id="multi-tool">
43
+ <div class="wrap">
44
+ <h2>Multiple tools on one agent</h2>
45
+ <p>Register as many functions as you need — each becomes its own tool with its own JSON schema, inferred from the signature and docstring.</p>
46
+ <pre class="code-block" data-copy><button class="copy-btn" type="button">Copy</button><code class="language-python">@agent.tool
47
+ def get_weather(city: str) -> str:
48
+ """Get the current weather for a city."""
49
+ return f"It's sunny in {city}."
50
+
51
+
52
+ @agent.tool
53
+ def get_account_balance(account_id: str) -> str:
54
+ """Look up a customer's account balance by id."""
55
+ return "$42.00"
56
+
57
+
58
+ @agent.tool
59
+ def book_appointment(date: str, time: str, reason: str) -> str:
60
+ """Book an appointment on a given date and time."""
61
+ return f"Booked for {date} at {time}: {reason}"</code></pre>
62
+ </div>
63
+ </section>
64
+
65
+ <section class="section alt" id="mixed-transport">
66
+ <div class="wrap">
67
+ <h2>Mixing server-hosted and client-side tools</h2>
68
+ <p>A tool that only needs data your frontend already has (auth, session state) doesn't need a round trip to your server — declare it <code>transport="client"</code> and answer it yourself when the provider sends <code>tool.call</code> over the live session.</p>
69
+ <pre class="code-block" data-copy><button class="copy-btn" type="button">Copy</button><code class="language-python">@agent.tool # hosted by minmo, transport="http" is the default
70
+ def get_weather(city: str) -> str:
71
+ """Get the current weather for a city."""
72
+ return f"It's sunny in {city}."
73
+
74
+
75
+ @agent.tool(transport="client")
76
+ def get_account_balance(account_id: str) -> str:
77
+ """Look up the current user's balance — answered client-side."""
78
+ return "$42.00"</code></pre>
79
+ </div>
80
+ </section>
81
+
82
+ <section class="section" id="ci-testing">
83
+ <div class="wrap">
84
+ <h2>Asserting tool-call behavior in CI</h2>
85
+ <p><code>simulate()</code> drives your <code>llm</code> directly and checks which tools it picks — no live session, so it's cheap enough to run on every commit.</p>
86
+ <pre class="code-block" data-copy><button class="copy-btn" type="button">Copy</button><code class="language-python">from minmo.testing import simulate
87
+
88
+ result = simulate(agent, transcript=["What's the weather in Paris?"])
89
+ assert result["tool_calls"][0]["name"] == "get_weather"
90
+ assert result["tool_calls"][0]["arguments"]["city"] == "Paris"</code></pre>
91
+ </div>
92
+ </section>
93
+
94
+ <section class="section alt" id="hume-recipe">
95
+ <div class="wrap">
96
+ <h2>Switching to Hume</h2>
97
+ <p>Hume needs an extra <code>secret_key</code> (its OAuth client secret) alongside the usual <code>api_key</code>, and optionally a specific voice.</p>
98
+ <pre class="code-block" data-copy><button class="copy-btn" type="button">Copy</button><code class="language-python">from minmo import VoiceAgent
99
+ from minmo.providers.hume import HumeProvider
100
+
101
+ agent = VoiceAgent(
102
+ prompt="You are a friendly voice assistant.",
103
+ api_key="YOUR_HUME_API_KEY",
104
+ provider=HumeProvider(),
105
+ provider_options={
106
+ "secret_key": "YOUR_HUME_SECRET_KEY",
107
+ "voice": {"name": "ITO", "provider": "HUME_AI"},
108
+ },
109
+ )
110
+
111
+ result = agent.deploy()
112
+ print(result["info"])
113
+
114
+ token = agent.mint_token() # OAuth access token, start an EVI session with it</code></pre>
115
+ </div>
116
+ </section>
117
+
118
+ <section class="section" id="openai-realtime-recipe">
119
+ <div class="wrap">
120
+ <h2>Switching to OpenAI Realtime</h2>
121
+ <p>OpenAI Realtime doesn't host anything on their servers — <code>deploy()</code> just builds the config in memory, so call <code>mint_token()</code> right after, in the same process, to actually get a usable session token.</p>
122
+ <pre class="code-block" data-copy><button class="copy-btn" type="button">Copy</button><code class="language-python">from minmo import VoiceAgent
123
+ from minmo.providers.openai_realtime import OpenAIRealtimeProvider
124
+
125
+ agent = VoiceAgent(
126
+ prompt="You are a friendly voice assistant.",
127
+ api_key="YOUR_OPENAI_API_KEY",
128
+ provider=OpenAIRealtimeProvider(),
129
+ provider_options={"model": "gpt-4o-realtime-preview", "voice": "alloy"},
130
+ )
131
+
132
+ agent.deploy()
133
+ token = agent.mint_token() # send this session token to your client</code></pre>
134
+ </div>
135
+ </section>
136
+
137
+ <section class="section alt" id="session-logging">
138
+ <div class="wrap">
139
+ <h2>Logging sessions to disk</h2>
140
+ <p>Pass <code>log_path</code> when creating the agent, then call <code>log_session()</code> after a call ends (e.g. from your provider's webhook or end-of-call handler) to persist transcript + tool calls, and optionally react to it.</p>
141
+ <pre class="code-block" data-copy><button class="copy-btn" type="button">Copy</button><code class="language-python">agent = VoiceAgent(
142
+ prompt="You are a friendly voice assistant.",
143
+ api_key="YOUR_ASSEMBLYAI_API_KEY",
144
+ log_path="./sessions.jsonl",
145
+ on_session_end=lambda record: print("saved:", record["session_id"]),
146
+ )
147
+
148
+ # after a call ends:
149
+ agent.log_session(
150
+ session_id="sess_123",
151
+ transcript=[{"role": "user", "content": "What's the weather in Paris?"}],
152
+ tool_calls=[{"name": "get_weather", "arguments": {"city": "Paris"}}],
153
+ )</code></pre>
154
+ <p>Inspect the latest one anytime with <code>minmo logs</code> on the command line.</p>
155
+ </div>
156
+ </section>
157
+
158
+ <section class="section" id="prod-deploy">
159
+ <div class="wrap">
160
+ <h2>Deploying tool hosting yourself</h2>
161
+ <p><code>local=True</code> is for development — it spins up a local server and an ngrok tunnel. In production, run minmo's own server factory behind your real domain and point <code>deploy()</code> at it.</p>
162
+ <pre class="code-block" data-copy><button class="copy-btn" type="button">Copy</button><code class="language-python">from minmo.server import create_tool_server
163
+
164
+ app = create_tool_server(agent) # a FastAPI app — serve it with uvicorn/gunicorn
165
+
166
+ # elsewhere, once that server is live at your domain:
167
+ agent.deploy(local=False, host_url="https://tools.yourdomain.com")
168
+ # or export MINMO_HOST_URL=https://tools.yourdomain.com and drop host_url</code></pre>
169
+ </div>
170
+ </section>
171
+
172
+ </main>
173
+
174
+ <footer class="footer">
175
+ <div class="wrap footer-inner">
176
+ <p>MIT licensed. Created by <a href="https://github.com/a-elhaag">Anas Elhaag</a> (<a href="https://github.com/a-elhaag">@a-elhaag</a>).</p>
177
+ <p><a href="https://github.com/a-elhaag/minmo">github.com/a-elhaag/minmo</a></p>
178
+ </div>
179
+ </footer>
180
+
181
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
182
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
183
+ <script src="script.js"></script>
184
+ </body>
185
+ </html>