Skip to main content
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 a sk_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
Without "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.content pieces in order. The last chunk carries an empty delta and the finish_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_reason and without data: [DONE]. Treat that reply as incomplete.
If you feed the reply to a text-to-speech service, send it on a sentence at a time, or use the TTS WebSocket, which takes tokens as they arrive and finds the sentence ends itself.

4. Call tools

Send a tools 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 stock openai 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’s OpenAILLMService works with a base URL change, the same swap the TTS quickstart describes for speech:
Put 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 developer messages, for example when the tool set changes mid-call or a function runs with cancel_on_interruption=False. This API refuses a developer message with 400 invalid_request. The subclass above has Pipecat send those messages as user messages 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 and stream_options; we ignore them. temperature and max_tokens work. See What the API reads.
This page was checked against Pipecat 1.8.1, where passing 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 set api_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.