mira-llm is the language model our voice agents run on, offered on its own.
It answers on OpenAI’s POST /v1/chat/completions, so the stock openai
package and any framework with an OpenAI LLM integration can call it. You
change the base URL, the key and the model name, and the rest of your code
stays as it is.
It was built for voice agents in India. It does best with the Hinglish real
customers speak, Hindi and English in one sentence, in either script, and it
calls tools the OpenAI way, with a tool_calls array.
If you build agents on this platform, you don’t need this page: your agents
already run on this model.
On the production address, a key from a workspace that has not gone live gets
403 production_workspace_required.
1. Get a key
Open the console, go to Developers and press Create key. The key is ask_live_ secret and the console shows
it only once. It is the same key you use for text to speech, speech to text
and the rest of the v2 API. See
Get a key for the first-time email check.
Call the API from your server. Never put the key in a web page or a mobile
app. There is no browser token for this route.
2. One request
200 OK
"stream": true, the whole reply arrives as one JSON document once
the model has finished. That suits work nobody is waiting on, such as
classification, extraction or a summary after a call. When a person is waiting,
stream the reply.
model must be mira-llm, and model in the response is always mira-llm.
Any other value, including one an OpenAI client carries over such as
gpt-4o-mini, returns 400 invalid_request with a message that names the
model to use.
3. Stream it
Add"stream": true. The reply comes back as server-sent events
(Content-Type: text/event-stream), one chat.completion.chunk per data:
line:
200 OK · text/event-stream
- The first chunk usually carries only
role, with empty content. Start your timer, or your speech, on the first chunk that has text. - Join the
delta.contentpieces in order. The last chunk carries an emptydeltaand thefinish_reason. - The stream ends with the line
data: [DONE], which is not JSON. Check for it before you parse a line. - If the model stops part-way, the stream ends without a
finish_reasonand withoutdata: [DONE]. Treat that reply as incomplete.
4. Call tools
Send atools array of function schemas. When the model decides a function
applies, it answers with tool_calls instead of text and stops with
finish_reason: "tool_calls":
200 OK
function.arguments is a JSON-encoded string, as on OpenAI. Parse it
(json.loads, JSON.parse) before you read a field, and handle a parse
failure: a model can produce malformed JSON.
To finish the turn, run the function yourself, then send the conversation
again with two more messages: the assistant message exactly as it came back,
tool_calls and all, and a tool message with the matching tool_call_id and
your result as a string. The model reads the result and answers in text:
The two messages you add
tool_choice takes auto, none, required, or one function as
{"type": "function", "function": {"name": "book_slot"}}. Leave it at auto
on a live call: forcing a call on a turn that was only “हाँ ठीक है” makes the
model invent arguments. Use required only when the turn cannot end any other
way.
Tool calls also stream. With "stream": true they arrive as
delta.tool_calls fragments, each with an index: the name in one chunk,
pieces of the arguments string in the next. Join the fragments that share an
index before you parse the arguments. The Python example below does this.
5. Python
The stockopenai package, with the base URL and key changed. This example
streams the reply, joins tool-call fragments on their index, runs the
function and sends the result back:
6. Pipecat
Pipecat’sOpenAILLMService works with a base URL change, the same swap the
TTS quickstart describes for speech:
llm where your LLM service was, between the user context aggregator and
your TTS service. Three things to know on a real call:
- Pipecat sometimes adds
developermessages, for example when the tool set changes mid-call or a function runs withcancel_on_interruption=False. This API refuses adevelopermessage with400 invalid_request. The subclass above has Pipecat send those messages asusermessages instead. - A long call passes the conversation limits. Pipecat keeps the whole conversation in its context, and each turn adds at least two messages, so after about ten turns a request goes over 20 messages and is refused. Trim the context as the call goes on: keep the system message and the latest turns. Pipecat’s context summarization can do this if you set its thresholds below our limits.
- Only some settings reach the model. Pipecat also sends
top_p,seed, the penalties andstream_options; we ignore them.temperatureandmax_tokenswork. See What the API reads.
model= to the
constructor still works but is deprecated in favour of
settings=...Settings(model=...).
What the API reads
We read seven fields and ignore everything else, so a body written for another provider still runs, with fewer of its settings in effect:top_p, stop, seed, n, frequency_penalty, presence_penalty,
response_format, parallel_tool_calls, stream_options,
max_completion_tokens and user are ignored. So is the name field on a
message.
Limits
Each refusal says which limit you hit.
Price
Free while the model is in beta. A request does not check or debit your wallet, and it writes no ledger row. The request rate limit still applies.Your request log
Every request your keys make is logged, whether it completed or failed. Open the console’s Language Model page and choose Logs. Each row shows the time and request id, a preview of the last user message, the time to the first token, the total time, the tokens in and out when the model reported them, tool calls, and the status. Requests from the console’s playground are in the same table, tagged. Pick 24 hours, 7 days or 30 days. The log holds your workspace’s latest 1,000 requests. It stores metadata and the first 140 characters of the last user message, never the whole conversation. Requests refused for a missing, unknown or revoked key, or for the rate limit, are not logged. The log is in the console only. There is no API for it yet.Try it in the console
The Playground tab on the same page is a chat with the model. You can set a system prompt and switch three demo tools on or off (book_slot,
check_order_status and send_payment_link). When the model calls one, the
playground shows the arguments it extracted and the raw messages going each
way. The tool results are simulated: nothing is booked, looked up or sent.
Code for this message gives you the cURL command for what you just sent.
Postman
The language model collection has three requests: a chat completion, one with tools (with the follow-up turn after the function has run) and a streaming one, each with a worked example response. Import it with the sandbox environment, or the production environment once you have gone live, and setapi_key. See
Postman collection for how to import.
When something fails
Errors use the same envelope as the rest of the API:
A stream that has already started cannot change its status code. If the model
fails after the first chunk, the stream just ends, without
data: [DONE].
TTS quickstart
Speak the model’s reply with Mira TTS.
Pipecat
Mirai’s own Pipecat services for speech.