# Create a chat completion Source: https://docs.inceptionlabs.ai/api-reference/chat/create-a-chat-completion https://api.inceptionlabs.ai/openapi.json post /v1/chat/completions Generate a chat completion from a sequence of messages. Returns a `ChatCompletion` object, or a server-sent events stream of `ChatCompletionChunk` deltas when `stream=true`. Supports tool calling, structured output via `response_format`, and reasoning controls. # Create a code edit completion Source: https://docs.inceptionlabs.ai/api-reference/edit/create-a-code-edit-completion https://api.inceptionlabs.ai/openapi.json post /v1/edit/completions Generate an edit to a code region given the surrounding context. The request must contain a single user message whose content includes the required edit prompt tags (e.g. `<|code_to_edit|>`, `<|cursor|>`, `<|current_file_content|>`). Returns an `EditCompletion` object. Streaming and tool calling are not supported on this endpoint. # Create a fill-in-the-middle completion Source: https://docs.inceptionlabs.ai/api-reference/fim/create-a-fill-in-the-middle-completion https://api.inceptionlabs.ai/openapi.json post /v1/fim/completions Generate a code completion given a `prompt` (prefix) and optional `suffix`. Designed for IDE-style inline completion. Returns a `FimCompletion` object, or a server-sent events stream of `FimCompletionChunk` deltas when `stream=true`. Tool calling and function calling are not supported. # List available models Source: https://docs.inceptionlabs.ai/api-reference/models/list-available-models https://api.inceptionlabs.ai/openapi.json get /v1/models List the models currently available through the API. # List chat models Source: https://docs.inceptionlabs.ai/api-reference/models/list-chat-models https://api.inceptionlabs.ai/openapi.json get /v1/chat/completions/models List models available for chat completions. # List Edit models Source: https://docs.inceptionlabs.ai/api-reference/models/list-edit-models https://api.inceptionlabs.ai/openapi.json get /v1/edit/completions/models List models available for edit completions. # List FIM models Source: https://docs.inceptionlabs.ai/api-reference/models/list-fim-models https://api.inceptionlabs.ai/openapi.json get /v1/fim/completions/models List models available for fill-in-the-middle completions. # Autocomplete (FIM) Source: https://docs.inceptionlabs.ai/capabilities/fim Generate inline code autocomplete suggestions with the Inception fill-in-the-middle (FIM) endpoint, powered by the Mercury Edit 2 code model. Fill-in-the-middle (FIM) generates code that fits between existing code. You send the code **before** the cursor as `prompt` (the prefix) and the code **after** the cursor as `suffix`; Mercury Edit 2 returns the text that belongs in between. This is what powers inline autocomplete in an editor. ## FIM vs. Next Edit | | Autocomplete (FIM) | [Next Edit](/capabilities/next-edit) | | ------------------- | ---------------------------------------------- | ------------------------------------------------------------------------- | | Endpoint | `POST /v1/fim/completions` | `POST /v1/edit/completions` | | Question it answers | "What belongs between these two known points?" | "What should I change next in this file?" | | Inputs | `prompt` prefix and optional `suffix` | Current file, editable region, edit history, and recently viewed snippets | | Best for | Inline completion as the user types | Predictive multi-line edits informed by recent context | FIM has no separate fields for edit history or other open files. Add any extra context directly to `prompt`, such as imports, related types, or a short description of the intended code. Use [Next Edit](/capabilities/next-edit) when the suggestion should incorporate edit history or recently viewed snippets. Set your [API key](https://platform.inceptionlabs.ai/dashboard/api-keys) as an environment variable and install the SDK. ```bash macOS / Linux theme={null} export INCEPTION_API_KEY="your_api_key_here" ``` ```cmd Windows (Command Prompt) theme={null} set INCEPTION_API_KEY=your_api_key_here ``` ```powershell Windows (PowerShell) theme={null} $env:INCEPTION_API_KEY="your_api_key_here" ``` ```bash Python theme={null} pip install inceptionai ``` ```bash TypeScript theme={null} npm install inceptionai ``` In the example below, the prefix defines `factorial` up to `return n * ` and the suffix is the call that uses it. Mercury Edit 2 fills the gap — e.g. `factorial(n - 1)`. ```bash cURL theme={null} curl https://api.inceptionlabs.ai/v1/fim/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $INCEPTION_API_KEY" \ -d '{ "model": "mercury-edit-2", "prompt": "def factorial(n):\n if n <= 1:\n return 1\n return n * ", "suffix": "\n\nprint(factorial(5))", "max_tokens": 1000 }' ``` ```python Python theme={null} from inceptionai import Inception client = Inception() # reads INCEPTION_API_KEY from the environment completion = client.fim.completions.create( model="mercury-edit-2", prompt="def factorial(n):\n if n <= 1:\n return 1\n return n * ", suffix="\n\nprint(factorial(5))", max_tokens=1000, ) # The completion is the code that fills the gap, e.g. "factorial(n - 1)" print(completion.choices[0].text) ``` ```typescript TypeScript theme={null} import Inception from 'inceptionai'; const client = new Inception(); // reads INCEPTION_API_KEY from the environment const completion = await client.fim.completions.create({ model: 'mercury-edit-2', prompt: 'def factorial(n):\n if n <= 1:\n return 1\n return n * ', suffix: '\n\nprint(factorial(5))', max_tokens: 1000, }); // The completion is the code that fills the gap, e.g. "factorial(n - 1)" console.log(completion.choices[0]?.text); ``` # Next Edit Source: https://docs.inceptionlabs.ai/capabilities/next-edit Predict a developer's next code edit with the Inception Next Edit endpoint and Mercury Edit 2 using recent snippets, file context, and edit history. Predict a developer's next code edit with Mercury Edit 2. Provide recent snippets, the current file with an editable region, and edit history — the [request format](#next-edit-request-format) is documented below. Set your [API key](https://platform.inceptionlabs.ai/dashboard/api-keys) as an environment variable and install the SDK. ```bash macOS / Linux theme={null} export INCEPTION_API_KEY="your_api_key_here" ``` ```cmd Windows (Command Prompt) theme={null} set INCEPTION_API_KEY=your_api_key_here ``` ```powershell Windows (PowerShell) theme={null} $env:INCEPTION_API_KEY="your_api_key_here" ``` ```bash Python theme={null} pip install inceptionai ``` ```bash TypeScript theme={null} npm install inceptionai ``` ```bash cURL theme={null} curl https://api.inceptionlabs.ai/v1/edit/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $INCEPTION_API_KEY" \ -d "{ \"model\": \"mercury-edit-2\", \"messages\": [ { \"role\": \"user\", \"content\": \"<|recently_viewed_code_snippets|>\\n\\n<|/recently_viewed_code_snippets|>\\n\\n<|current_file_content|>\\ncurrent_file_path: solver.py\\n'''\\nfunction: flagAllNeighbors\\n----------\\nThis function marks each of the covered neighbors of the cell at the given row\\n<|code_to_edit|>\\nand col as flagged.\\n'''\\ndef flagAllNeighbors(board<|cursor|>, row, col): \\n for r, c in b.getNeighbors(row, col):\\n if b.isValid(r, c):\\n b.flag(r, c)\\n\\n<|/code_to_edit|>\\n<|/current_file_content|>\\n\\n<|edit_diff_history|>\\n--- /c:/Users/test/testing/solver.py\\n+++ /c:/Users/test/testing/solver.py\\n@@ -6,1 +6,1 @@\\n-def flagAllNeighbors(b, row, col): \\n+def flagAllNeighbors(board, row, col): \\n\\n<|/edit_diff_history|>\" } ] }" ``` ```python Python theme={null} from inceptionai import Inception client = Inception() # reads INCEPTION_API_KEY from the environment content = """<|recently_viewed_code_snippets|> <|/recently_viewed_code_snippets|> <|current_file_content|> current_file_path: solver.py ''' function: flagAllNeighbors ---------- This function marks each of the covered neighbors of the cell at the given row <|code_to_edit|> and col as flagged. ''' def flagAllNeighbors(board<|cursor|>, row, col): for r, c in b.getNeighbors(row, col): if b.isValid(r, c): b.flag(r, c) <|/code_to_edit|> <|/current_file_content|> <|edit_diff_history|> --- /c:/Users/test/testing/solver.py +++ /c:/Users/test/testing/solver.py @@ -6,1 +6,1 @@ -def flagAllNeighbors(b, row, col): +def flagAllNeighbors(board, row, col): <|/edit_diff_history|>""" completion = client.edit.completions.create( model="mercury-edit-2", messages=[{"role": "user", "content": content}], ) print(completion.choices[0].message.content) ``` ```typescript TypeScript theme={null} import Inception from 'inceptionai'; const client = new Inception(); // reads INCEPTION_API_KEY from the environment const content = `<|recently_viewed_code_snippets|> <|/recently_viewed_code_snippets|> <|current_file_content|> current_file_path: solver.py ''' function: flagAllNeighbors ---------- This function marks each of the covered neighbors of the cell at the given row <|code_to_edit|> and col as flagged. ''' def flagAllNeighbors(board<|cursor|>, row, col): for r, c in b.getNeighbors(row, col): if b.isValid(r, c): b.flag(r, c) <|/code_to_edit|> <|/current_file_content|> <|edit_diff_history|> --- /c:/Users/test/testing/solver.py +++ /c:/Users/test/testing/solver.py @@ -6,1 +6,1 @@ -def flagAllNeighbors(b, row, col): +def flagAllNeighbors(board, row, col): <|/edit_diff_history|>`; const completion = await client.edit.completions.create({ model: 'mercury-edit-2', messages: [{ role: 'user', content }], }); console.log(completion.choices[0]?.message.content); ``` ## Next Edit Request Format Mercury Edit expects next-edit requests to contain 3 sections of code contents: recently viewed snippets, the current file with the editable region, and a time-ordered edit history. The primary purpose of the recently viewed snippets and edit history is to provide context to Mercury Edit so that it can better understand the user's current intent in modifying code. The recently viewed snippets should be formatted as: ```text theme={null} <|recently_viewed_code_snippets|> <|recently_viewed_code_snippet|> code_snippet_file_path: [SNIPPET FILE PATH 1] [SNIPPET 1 CODE] <|/recently_viewed_code_snippet|> <|recently_viewed_code_snippet|> code_snippet_file_path: [SNIPPET FILE PATH 2] [SNIPPET 2 CODE] <|/recently_viewed_code_snippet|> <|/recently_viewed_code_snippets|> ``` Each snippet should correspond to a piece of code (or entire code files) that a user has recently viewed. If you do not wish to use recently viewed snippets, please add `<|recently_viewed_code_snippets|>\n\n<|/recently_viewed_code_snippets|>` with no contents inside the tags. The current file content should be formatted as: ```text theme={null} <|current_file_content|> current_file_path: [CURRENT FILE PATH] [CODE ABOVE EDITABLE REGION] <|code_to_edit|> [EDITABLE REGION CODE] <|/code_to_edit|> [CODE BELOW EDITABLE REGION] <|/current_file_content|> ``` Mercury Edit will return an updated version of the editable region in its response (e.g., completing a function) enclosed in triple backticks. The edit history should be formatted as: ```text theme={null} <|edit_diff_history|> --- [EDITED FILE PATH 1] +++ [EDITED FILE PATH 1] [DIFF HUNK HEADER 1 WITH @@] [DIFF LINES 1] --- [EDITED FILE PATH 2] +++ [EDITED FILE PATH 2] [DIFF HUNK HEADER 2 WITH @@] [DIFF LINES 2] <|/edit_diff_history|> ``` As seen above, each edit should follow unidiff formatting. It is important to make sure that the bottommost edits in the prompt correspond to the most recent edits made by the user. If you do not wish to use edit history, please add `<|edit_diff_history|>\n\n<|/edit_diff_history|>` with no contents inside the tags. ## Next Edit Prompting Best Practices The following best practices will help you get the most relevant suggestions out of Mercury Edit while maintaining low latency. Include **3–5 snippets** of roughly 20 lines each, centered around the user's recent cursor positions. These should be focused code excerpts — not full files. For more advanced implementations, consider using AST nodes at cursor locations or code RAG to surface relevant type definitions and method implementations across the codebase. Always provide the **latest state** of code at each snippet location, as stale snippets can cause the model to suggest reverting recent changes. Both full declarations and outlines (e.g., type signatures and docstrings) are acceptable snippet formats. Include at least the **last 3–5 user edits**, ordered chronologically with the most recent edit last. Edits should be **range-based** — if a user made multiple modifications in the same area, combine them into a single unidiff rather than many granular diffs. The edit history is often the strongest signal for identifying user intent. Ensuring the final entry corresponds to the user's most recent edit can significantly improve suggestion quality. Pass in the **entire current file** so the model can draw on imports, function signatures, and surrounding code. For extremely large files, trim distant regions while preserving the area around the editable region. The editable region directly determines output token count and dominates latency. We recommend starting with **10–15 lines** (\~100–150 tokens) and tuning from there. At \~1,000 tokens/second decoding speed, an upper bound of around 25 lines (\~250 tokens) is practical depending on network latency. For selecting which lines to include, a simple approach is to center the region around the cursor: `[currentLine - 5, currentLine + 10]`. To suggest edits beyond the cursor vicinity: * **Parallel requests**: Fire multiple requests with disjoint editable regions and check which ones produce meaningful diffs. * **Linter-guided selection**: Wrap editable regions around locations where the linter identifies errors. We recommend making the maximum editable region size configurable so you can tune the tradeoff between suggestion scope and latency. # Reasoning Efforts Source: https://docs.inceptionlabs.ai/capabilities/reasoning-efforts Choose how much Mercury 2 reasons before responding, from near-instant answers to deeper multi-step reasoning. ## Reasoning effort levels `reasoning_effort` controls how much the model deliberates before answering. It applies to `mercury-2` on the chat completions endpoint and accepts four values: | Value | Latency | Best for | | ------------------ | -------- | ---------------------------------------------- | | `instant` | Lowest | Voice, realtime turns, high-volume automations | | `low` | Low | Latency-sensitive chat and tool-calling | | `medium` (default) | Balanced | General-purpose use | | `high` | Highest | Complex, multi-step reasoning | If `reasoning_effort` is omitted, the model uses `medium`. # Streaming & Diffusion Source: https://docs.inceptionlabs.ai/capabilities/streaming Stream Mercury 2 chat completions or enable the diffusion mode to visualize how the model iteratively denoises text into the final answer. Inception API supports streaming output and diffusion effect modes: * **Streaming:** Get responses block-by-block for instant feedback—ideal for chat and live applications. A "block" is a chunk of refined text the model emits as it decodes, rather than one token at a time. * **Diffusing:** Optionally visualize how noisy outputs are refined into final text, showcasing the model's iterative denoising process. Set your [API key](https://platform.inceptionlabs.ai/dashboard/api-keys) as an environment variable and install the SDK. ```bash macOS / Linux theme={null} export INCEPTION_API_KEY="your_api_key_here" ``` ```cmd Windows (Command Prompt) theme={null} set INCEPTION_API_KEY=your_api_key_here ``` ```powershell Windows (PowerShell) theme={null} $env:INCEPTION_API_KEY="your_api_key_here" ``` ```bash Python theme={null} pip install inceptionai ``` ```bash TypeScript theme={null} npm install inceptionai ``` ## Streaming Stream the response block-by-block as it is generated. ```bash cURL theme={null} curl https://api.inceptionlabs.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $INCEPTION_API_KEY" \ -d '{ "model": "mercury-2", "messages": [ {"role": "user", "content": "What is a diffusion model?"} ], "max_tokens": 1000, "stream": true }' ``` ```python Python theme={null} from inceptionai import Inception client = Inception() # reads INCEPTION_API_KEY from the environment stream = client.chat.completions.create( model="mercury-2", messages=[{"role": "user", "content": "What is a diffusion model?"}], max_tokens=1000, stream=True, ) for chunk in stream: print(chunk.choices[0].delta.content or "", end="", flush=True) ``` ```typescript TypeScript theme={null} import Inception from 'inceptionai'; const client = new Inception(); // reads INCEPTION_API_KEY from the environment const stream = await client.chat.completions.create({ model: 'mercury-2', messages: [{ role: 'user', content: 'What is a diffusion model?' }], max_tokens: 1000, stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta.content ?? ''); } ``` ## Diffusion Set `diffusing` to `true` to stream the model's intermediate denoising steps. Each chunk contains the full text as it is refined into the final answer. ```bash cURL theme={null} curl https://api.inceptionlabs.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $INCEPTION_API_KEY" \ -d '{ "model": "mercury-2", "messages": [ {"role": "user", "content": "What is a diffusion model?"} ], "max_tokens": 1000, "stream": true, "diffusing": true }' ``` ```python Python theme={null} from inceptionai import Inception client = Inception() # reads INCEPTION_API_KEY from the environment stream = client.chat.completions.create( model="mercury-2", messages=[{"role": "user", "content": "What is a diffusion model?"}], max_tokens=1000, stream=True, diffusing=True, ) for chunk in stream: # In diffusing mode each chunk is the full text as it is denoised print(chunk.choices[0].delta.content or "", flush=True) ``` ```typescript TypeScript theme={null} import Inception from 'inceptionai'; const client = new Inception(); // reads INCEPTION_API_KEY from the environment const stream = await client.chat.completions.create({ model: 'mercury-2', messages: [{ role: 'user', content: 'What is a diffusion model?' }], max_tokens: 1000, stream: true, diffusing: true, }); for await (const chunk of stream) { // In diffusing mode each chunk is the full text as it is denoised console.log(chunk.choices[0]?.delta.content ?? ''); } ``` The diffusing effect is best shown by overwriting the displayed text on each step. Here is an example in a web app using JavaScript: ```javascript theme={null} const reader = response.body.getReader(); const decoder = new TextDecoder(); let fullContent = ''; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { const trimmed = line.trim(); if (!trimmed || !trimmed.startsWith('data: ')) continue; if (trimmed === 'data: [DONE]') continue; const jsonStr = trimmed.substring(6); if (!jsonStr.startsWith('{')) continue; try { const data = JSON.parse(jsonStr); for (const choice of data.choices || []) { if (choice.delta && choice.delta.content !== null && choice.delta.content !== undefined) { fullContent = choice.delta.content || ''; contentElement.textContent = fullContent; } } } catch (error) { console.error('Parsing error:', error); } } } ``` # Structured Outputs Source: https://docs.inceptionlabs.ai/capabilities/structured-outputs Constrain Mercury 2 chat completions to a JSON schema using structured outputs, so your application receives strictly typed, machine-parseable responses. Use JSON schemas to enforce structured data output with specific properties, types, and constraints. Set your [API key](https://platform.inceptionlabs.ai/dashboard/api-keys) as an environment variable and install the SDK. ```bash macOS / Linux theme={null} export INCEPTION_API_KEY="your_api_key_here" ``` ```cmd Windows (Command Prompt) theme={null} set INCEPTION_API_KEY=your_api_key_here ``` ```powershell Windows (PowerShell) theme={null} $env:INCEPTION_API_KEY="your_api_key_here" ``` ```bash Python theme={null} pip install inceptionai ``` ```bash TypeScript theme={null} npm install inceptionai ``` ```python Python theme={null} from inceptionai import Inception client = Inception() # reads INCEPTION_API_KEY from the environment response_schema = { "name": "Sentiment", "strict": True, "schema": { "type": "object", "properties": { "sentiment": { "type": "string", "enum": ["positive", "negative", "neutral"] }, "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, "key_phrases": { "type": "array", "items": {"type": "string"} } }, "required": ["sentiment", "confidence", "key_phrases"] }, } completion = client.chat.completions.create( model="mercury-2", messages=[ { "role": "user", "content": "Analyze the sentiment of this text: 'I absolutely love this feature! It works perfectly and saves me so much time.'" } ], response_format={"type": "json_schema", "json_schema": response_schema}, ) print(completion.choices[0].message.content) ``` ```typescript TypeScript theme={null} import Inception from 'inceptionai'; const client = new Inception(); // reads INCEPTION_API_KEY from the environment const responseSchema = { name: 'Sentiment', strict: true, schema: { type: 'object', properties: { sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] }, confidence: { type: 'number', minimum: 0, maximum: 1 }, key_phrases: { type: 'array', items: { type: 'string' } }, }, required: ['sentiment', 'confidence', 'key_phrases'], }, }; const completion = await client.chat.completions.create({ model: 'mercury-2', messages: [ { role: 'user', content: "Analyze the sentiment of this text: 'I absolutely love this feature! It works perfectly and saves me so much time.'", }, ], response_format: { type: 'json_schema', json_schema: responseSchema }, }); console.log(completion.choices[0]?.message.content); ``` # Tool Use Source: https://docs.inceptionlabs.ai/capabilities/tool-use Use tool calling on the Mercury 2 chat completions endpoint to let the model invoke functions, fetch data, and orchestrate multi-step agent workflows. Mercury 2 supports OpenAI-compatible tool calling on the chat completions endpoint. You define functions the model may call; when the model decides a tool is needed, it returns a `tool_call` with JSON arguments. You then execute the function and send the result back so the model can produce a final answer. Set your [API key](https://platform.inceptionlabs.ai/dashboard/api-keys) as an environment variable and install the SDK. ```bash macOS / Linux theme={null} export INCEPTION_API_KEY="your_api_key_here" ``` ```cmd Windows (Command Prompt) theme={null} set INCEPTION_API_KEY=your_api_key_here ``` ```powershell Windows (PowerShell) theme={null} $env:INCEPTION_API_KEY="your_api_key_here" ``` ```bash Python theme={null} pip install inceptionai ``` ```bash TypeScript theme={null} npm install inceptionai ``` ## Request a tool call Pass your function definitions in `tools`. The model returns a `tool_call` when it needs one. ```bash cURL theme={null} curl https://api.inceptionlabs.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $INCEPTION_API_KEY" \ -d '{ "model": "mercury-2", "messages": [{"role": "user", "content": "What'"'"'s the weather like in San Francisco?"}], "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather in a given location", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "City and state, e.g., '"'"'San Francisco, CA'"'"'"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}}, "required": ["location", "unit"]}}}] }' ``` ```python Python theme={null} from inceptionai import Inception client = Inception() # reads INCEPTION_API_KEY from the environment tools = [{ "type": "function", "function": { "name": "get_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "City and state, e.g., 'San Francisco, CA'" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] } }, "required": ["location", "unit"] } } }] completion = client.chat.completions.create( model="mercury-2", messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}], tools=tools, ) tool_call = completion.choices[0].message.tool_calls[0].function print(f"Function called: {tool_call.name}") print(f"Arguments: {tool_call.arguments}") ``` ```typescript TypeScript theme={null} import Inception from 'inceptionai'; const client = new Inception(); // reads INCEPTION_API_KEY from the environment const tools = [ { type: 'function' as const, function: { name: 'get_weather', description: 'Get the current weather in a given location', parameters: { type: 'object', properties: { location: { type: 'string', description: "City and state, e.g., 'San Francisco, CA'" }, unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }, }, required: ['location', 'unit'], }, }, }, ]; const completion = await client.chat.completions.create({ model: 'mercury-2', messages: [{ role: 'user', content: "What's the weather like in San Francisco?" }], tools, }); const toolCall = completion.choices[0]?.message.tool_calls?.[0]?.function; console.log(`Function called: ${toolCall?.name}`); console.log(`Arguments: ${toolCall?.arguments}`); ``` ## Return the tool result Execute the requested function, then append the assistant message and a `tool` message (matched by `tool_call_id`) and call the API again. The model uses the result to produce its final response. Repeat this loop for multi-step agent workflows. The examples below reuse the `tools` definition from above. ```python Python theme={null} import json from inceptionai import Inception client = Inception() def get_weather(location: str, unit: str) -> str: # Call your real weather API here. return json.dumps({"location": location, "unit": unit, "temperature": 18}) messages = [{"role": "user", "content": "What's the weather like in San Francisco?"}] # First request — the model asks for a tool call. completion = client.chat.completions.create( model="mercury-2", messages=messages, tools=tools, ) message = completion.choices[0].message tool_call = message.tool_calls[0] args = json.loads(tool_call.function.arguments) # Execute the function and send the result back. result = get_weather(**args) messages.append(message.model_dump(exclude_none=True)) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result, }) final = client.chat.completions.create( model="mercury-2", messages=messages, tools=tools, ) print(final.choices[0].message.content) ``` ```typescript TypeScript theme={null} import Inception from 'inceptionai'; const client = new Inception(); function getWeather(location: string, unit: string): string { // Call your real weather API here. return JSON.stringify({ location, unit, temperature: 18 }); } const messages = [ { role: 'user' as const, content: "What's the weather like in San Francisco?" }, ]; // First request — the model asks for a tool call. const completion = await client.chat.completions.create({ model: 'mercury-2', messages, tools, }); const message = completion.choices[0].message; const toolCall = message.tool_calls![0]; const args = JSON.parse(toolCall.function.arguments); // Execute the function and send the result back. const result = getWeather(args.location, args.unit); const finalMessages = [ ...messages, message, { role: 'tool' as const, tool_call_id: toolCall.id, content: result, }, ]; const final = await client.chat.completions.create({ model: 'mercury-2', messages: finalMessages, tools, }); console.log(final.choices[0]?.message.content); ``` ## Tool calling parameters * `tool_choice` Use `tool_choice` to enforce the tool calling behavior that you desire. By default, `tool_choice` is set to `"auto"`. This means that the model will choose to either emit tool calls or output text. If set to `"none"`, tool calls are prevented. If set to `"required"`, the model is forced to emit a tool call in that turn. Use this to enforce the behavior you desire. Just add `tool_choice` to the payload to use it. See below for an example: ```python theme={null} payload = { "model": MODEL, "messages": messages, "tools": TOOLS, "tool_choice": "auto", # <- add this line: "auto" or "required" or "none" } body = requests.post(API_URL, headers=HEADERS, json=payload).json() ``` * `strict` Use `strict` to force the model to output the correct tool calling schema that you have specified. We recommend setting this field to `True` for best results. To do this, add `"strict": True` to your tool definition. See below for an example. ```python theme={null} { "type": "function", "function": { "name": "list_directory", "description": "List the entries (files and subdirectories) inside a directory.", "strict": True, # <- add this line here "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "Absolute directory path, e.g. '/repo'.", }, }, "required": ["path"], "additionalProperties": False, }, }, } ``` ## Coding agent tool calling demo See how you can use tool calling with Mercury 2 with the following simple coding agent example. We assume a small codebase of the following structure: ```python theme={null} ### structure ### /repo ├── main.py └── stats.py ################# ### stats.py ### def compute_average(values): """Return the arithmetic mean of a list of numbers.""" if not values: return 0.0 return sum(values) / len(values) ################# ### main.py ### from stats import compute_average print(compute_average([1, 2, 3])) ################# ``` ### Define tools First, define your tools. Here, we define two tools for a read-only coding agent: `list_directory` and `read_file`. ```python theme={null} TOOLS = [ { "type": "function", "function": { "name": "list_directory", "description": "List the entries (files and subdirectories) inside a directory.", "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "Absolute directory path, e.g. '/repo'.", }, }, "required": ["path"], "additionalProperties": False, }, }, }, { "type": "function", "function": { "name": "read_file", "description": "Return the full text contents of a file.", "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "Absolute file path, e.g. '/repo/stats.py'.", }, }, "required": ["path"], "additionalProperties": False, }, }, }, ] ``` ### Prompt your model We prompt the model to search the code base for a specific function and return its definition to the user. ```python theme={null} SYSTEM_PROMPT = ( "You are a coding assistant with access to a small filesystem via two tools: " "`list_directory` and `read_file`. Use tools to explore the repo before answering. " "When you have enough information, reply in plain text with the answer." ) USER_PROMPT = ( "The repo lives at `/repo`. Find the definition of the function " "`compute_average` and quote its body back to me." ) messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": USER_PROMPT}, ] ``` ### Run your agent ```python theme={null} # setup API_URL = "https://api.inceptionlabs.ai/v1/chat/completions" MODEL = "mercury-2" HEADERS = { "Authorization": f"Bearer {os.environ['INCEPTION_API_KEY']}", "Content-Type": "application/json", } # agent loop for turn in range(3): payload = { "model": MODEL, "messages": messages, "tools": TOOLS, } body = requests.post(API_URL, headers=HEADERS, json=payload).json() # 1. parse body for content and tool_calls. # 2. execute tools in tool_calls. # 3. append model output and tool call results to messages. ``` ### Results When we run the above code, we get the following results: **Step 1:** Model's tool call: ```python theme={null} {'name': 'list_directory', 'arguments': '{"path": "/repo"}'} ``` Tool call output: ```python theme={null} {"path": "/repo", "entries": ["main.py", "stats.py", "README.md"]} ``` **Step 2:** Model's tool call: ```python theme={null} {'name': 'read_file', 'arguments': '{"path": "/repo/stats.py"}'} ``` Tool call output: ```python theme={null} {"path": "/repo/stats.py", "contents": "def compute_average(values):\n \"\"\"Return the arithmetic mean of a list of numbers.\"\"\"\n if not values:\n return 0.0\n return sum(values) / len(values)\n"} ``` **Step 3:** Model's output: ````text theme={null} Here is the definition of `compute_average` from **/repo/stats.py**: ```python def compute_average(values): """Return the arithmetic mean of a list of numbers.""" if not values: return 0.0 return sum(values) / len(values) ``` ```` See the full demo's code in [this cookbook](/cookbooks/coding-agent-mercury-tool-calling). Currently, the Inception API only supports sequential tool calling. Parallel tool calling will be supported soon! # Coding Agent: Mercury + Tool Calling Source: https://docs.inceptionlabs.ai/cookbooks/coding-agent-mercury-tool-calling A minimal, runnable coding agent built on Mercury 2 tool calling. This cookbook is the runnable companion to the [tool use demo](/capabilities/tool-use#coding-agent-tool-calling-demo). You can run it with the following command: ```bash theme={null} export INCEPTION_API_KEY="your_api_key_here" python coding-agent.py ``` ## Code ```python coding-agent.py theme={null} """ Mercury 2 tool-calling example. Usage: export INCEPTION_API_KEY=... python coding-agent.py """ import json import os import requests API_URL = "https://api.inceptionlabs.ai/v1/chat/completions" MODEL = "mercury-2" HEADERS = { "Authorization": f"Bearer {os.environ['INCEPTION_API_KEY']}", "Content-Type": "application/json", } # --------------------------------------------------------------------------- # Mock filesystem the "tools" will pretend to read. # --------------------------------------------------------------------------- FAKE_FS = { "/repo": ["main.py", "stats.py"], "/repo/stats.py": ( "def compute_average(values):\n" " \"\"\"Return the arithmetic mean of a list of numbers.\"\"\"\n" " if not values:\n" " return 0.0\n" " return sum(values) / len(values)\n" ), "/repo/main.py": ( "from stats import compute_average\n" "\n" "print(compute_average([1, 2, 3]))\n" ), } def run_list_directory(path: str) -> str: entries = FAKE_FS.get(path) if entries is None or not isinstance(entries, list): return json.dumps({"error": f"not a directory: {path}"}) return json.dumps({"path": path, "entries": entries}) def run_read_file(path: str) -> str: contents = FAKE_FS.get(path) if contents is None or isinstance(contents, list): return json.dumps({"error": f"not a file: {path}"}) return json.dumps({"path": path, "contents": contents}) TOOL_RUNNERS = { "list_directory": lambda args: run_list_directory(args["path"]), "read_file": lambda args: run_read_file(args["path"]), } # --------------------------------------------------------------------------- # Tool schemas — the OpenAI tools[] format (which Mercury 2 also accepts). # --------------------------------------------------------------------------- TOOLS = [ { "type": "function", "function": { "name": "list_directory", "description": "List the entries (files and subdirectories) inside a directory.", "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "Absolute directory path, e.g. '/repo'.", }, }, "required": ["path"], "additionalProperties": False, }, }, }, { "type": "function", "function": { "name": "read_file", "description": "Return the full text contents of a file.", "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "Absolute file path, e.g. '/repo/stats.py'.", }, }, "required": ["path"], "additionalProperties": False, }, }, }, ] SYSTEM_PROMPT = ( "You are a coding assistant with access to a small filesystem via two tools: " "`list_directory` and `read_file`. Use tools to explore the repo before answering. " "When you have enough information, reply in plain text with the answer." ) USER_PROMPT = ( "The repo lives at `/repo`. Find the definition of the function " "`compute_average` and quote its body back to me." ) def chat_completion(messages): """POST to the Inception chat/completions endpoint and return the JSON body.""" payload = { "model": MODEL, "messages": messages, "tools": TOOLS, } resp = requests.post(API_URL, headers=HEADERS, json=payload) resp.raise_for_status() return resp.json() def main(): messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": USER_PROMPT}, ] for turn in range(3): print(f"\n========== turn {turn} ==========") body = chat_completion(messages) choice = body["choices"][0] msg = choice["message"] finish = choice.get("finish_reason") tool_calls = msg.get("tool_calls") or [] print(f"finish_reason: {finish}") if tool_calls: # add tool calls to messages history messages.append({ "role": "assistant", "content": msg.get("content"), "tool_calls": tool_calls, }) for tc in tool_calls: print(f"tc: {tc}") fn = tc["function"] print(f"fn: {fn}") name = fn["name"] raw_args = fn["arguments"] print(f" tool_call id={tc['id']}") print(f" name: {name}") print(f" args: {raw_args}") try: parsed_args = json.loads(raw_args) except json.JSONDecodeError: parsed_args = {} runner = TOOL_RUNNERS.get(name) if runner is None: result = json.dumps({"error": f"unknown tool {name}"}) else: result = runner(parsed_args) print(f" result: {result}") # add tool result to messages history messages.append({ "role": "tool", "tool_call_id": tc["id"], "content": result, }) else: # answer — loop is done. print(f"assistant output:\n{msg.get('content')}") break if __name__ == "__main__": main() ``` # Voice Customer Support Agent: Mercury + Tool Calling + Voice Source: https://docs.inceptionlabs.ai/cookbooks/customer-support-agent-mercury Build a voice-enabled dental clinic support agent that uses Mercury 2 tool calling to read and write a live appointment database, with ElevenLabs speech-to-text and text-to-speech. This cookbook builds a customer support agent for a dental clinic on top of [Mercury 2](/get-started/models), Inception's diffusion large language model. The agent understands natural-language requests, calls tools to read and write a live appointment database, and holds a real-time voice conversation using [ElevenLabs](https://elevenlabs.io) for speech-to-text and text-to-speech. By the end you will have a working agent that can: * Look up a patient and their upcoming appointments * Show open appointment slots for a given day or provider * Book, reschedule, and cancel appointments via real mutations against a database * Do all of the above out loud, over streamed audio Because Mercury 2 generates tokens with a diffusion process rather than autoregressively, it returns tool calls and final answers faster than comparable models. For a voice customer support agent, where every extra 100 ms is audible, this difference is critical. Directly via Google Colab or download the `.ipynb` to run it locally in Jupyter ## Architecture Voice customer support architecture: caller audio to ElevenLabs STT to Mercury 2, which loops with the clinic SQLite database via tool calls and results, then ElevenLabs TTS to spoken reply. The clinic database is an in-memory SQLite instance seeded with providers, patients, and an availability grid. Two API keys (INCEPTION\_API\_KEY, ELEVENLABS\_API\_KEY) are required to run this demo. ## 1. Setup Install the Inception SDK, the ElevenLabs SDK, and a few audio helpers. ```python theme={null} %pip install -q "inceptionai" "elevenlabs>=2.15.0" \ "numpy>=1.26.0" "scipy>=1.16.2" "pydub>=0.25.1" ``` You need two API keys: * `INCEPTION_API_KEY` — from the [Inception dashboard](https://platform.inceptionlabs.ai) (new accounts get 100M free tokens) * `ELEVENLABS_API_KEY` — from the [ElevenLabs dashboard](https://elevenlabs.io/app/settings/api-keys) The next cell reads them from the environment, and prompts you for any that aren't already set. Nothing is written to disk. ```python theme={null} import os import json import getpass import time import sqlite3 from datetime import datetime, timedelta from inceptionai import Inception for var in ("INCEPTION_API_KEY", "ELEVENLABS_API_KEY"): if not os.environ.get(var): os.environ[var] = getpass.getpass(f"{var}: ") client = Inception(api_key=os.environ["INCEPTION_API_KEY"]) MERCURY_MODEL = "mercury-2" print("Inception client ready. Model:", MERCURY_MODEL) ``` ## 2. A mock clinic database We spin up an in-memory SQLite database for a fabricated dental clinic: **Diffusion Dental**. It contains three tables: * `providers` — the dentists and hygienists * `patients` — existing patient records * `appointments` — a grid of 30-minute slots, each `available`, `booked`, or `cancelled` The availability grid is generated relative to the moment you run the cell. It starts with whatever is left of today and rolls forward over the next five business days, skipping weekends and any hour that has already passed. A couple of slots start out pre-booked so availability looks realistic. This is a placeholder for demo purposes; you can plug in your own custom db here. ```python theme={null} CLINIC_HOURS = (9, 10, 11, 14, 15, 16) # 30-min slots, morning and afternoon BUSINESS_DAYS = 5 # how many open days to put on the calendar def build_clinic_db(now=None): """Create and seed an in-memory clinic database. Returns a live connection. The availability grid is built relative to `now` (default: the current date and time), so the calendar is always current no matter when you run this. Slots start with whatever is left of today and roll forward over the next few business days; times that have already passed are never offered. """ now = now or datetime.now() conn = sqlite3.connect(":memory:") conn.row_factory = sqlite3.Row cur = conn.cursor() cur.executescript( """ CREATE TABLE providers ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, specialty TEXT NOT NULL ); CREATE TABLE patients ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, phone TEXT NOT NULL, email TEXT ); CREATE TABLE appointments ( id INTEGER PRIMARY KEY, patient_id INTEGER, provider_id INTEGER NOT NULL, start_time TEXT NOT NULL, -- ISO 8601, e.g. 2026-08-17T14:00 duration_min INTEGER NOT NULL DEFAULT 30, reason TEXT, status TEXT NOT NULL DEFAULT 'available', -- available | booked | cancelled FOREIGN KEY (patient_id) REFERENCES patients(id), FOREIGN KEY (provider_id) REFERENCES providers(id) ); """ ) # Deliberately cartoonish names - nobody should mistake this for real PHI. cur.executemany("INSERT INTO providers VALUES (?,?,?)", [ (1, "Dr. Ada Molar", "General Dentistry"), (2, "Dr. Reed Wyre", "Orthodontics"), (3, "Dr. Pearl Flossmore", "Hygiene & Cleaning"), ]) cur.executemany("INSERT INTO patients VALUES (?,?,?,?)", [ (1, "Penny Placeholder", "+1-415-555-0101", "penny@example.com"), (2, "Sam Sampleton", "+1-415-555-0102", "sam@example.com"), (3, "Molly Mockford", "+1-415-555-0103", "molly@example.com"), ]) # Walk forward from today, collecting BUSINESS_DAYS weekdays that still have # open hours left. Weekends are skipped, and so is the rest of today once the # clinic has closed - that day simply doesn't count toward the total. rows, appt_id = [], 1 day = now.replace(hour=0, minute=0, second=0, microsecond=0) days_added = 0 while days_added < BUSINESS_DAYS: if day.weekday() < 5: # Mon-Fri starts = [day.replace(hour=h, minute=0) for h in CLINIC_HOURS] starts = [s for s in starts if s > now] # never offer the past if starts: for start in starts: for provider_id in (1, 2, 3): rows.append((appt_id, None, provider_id, start.strftime("%Y-%m-%dT%H:%M"), 30, None, "available")) appt_id += 1 days_added += 1 day += timedelta(days=1) cur.executemany("INSERT INTO appointments VALUES (?,?,?,?,?,?,?)", rows) # Pre-book a couple of slots so the calendar isn't empty. Picked by position # rather than by hard-coded date, so they land on real slots on any run. cur.execute("""UPDATE appointments SET patient_id=1, reason='Cleaning', status='booked' WHERE id = (SELECT id FROM appointments WHERE provider_id=3 AND status='available' ORDER BY start_time LIMIT 1)""") cur.execute("""UPDATE appointments SET patient_id=2, reason='Braces adjustment', status='booked' WHERE id = (SELECT id FROM appointments WHERE provider_id=2 AND status='available' AND start_time LIKE '%T14:00' ORDER BY start_time LIMIT 1 OFFSET 1)""") conn.commit() return conn conn = build_clinic_db() # Peek at the seed data. print("Providers:") for r in conn.execute("SELECT * FROM providers"): print(" ", dict(r)) first, last = conn.execute("SELECT MIN(start_time), MAX(start_time) FROM appointments").fetchone() n_avail = conn.execute("SELECT COUNT(*) FROM appointments WHERE status='available'").fetchone()[0] n_booked = conn.execute("SELECT COUNT(*) FROM appointments WHERE status='booked'").fetchone()[0] print(f"\nCalendar runs {first} -> {last}") print(f"Slots: {n_avail} available, {n_booked} booked") ``` ## 3. Define the support tools Each tool is a plain Python function that reads or writes the database. We keep the functions small and return dictionaries, which then get handed back to Mercury 2 as the tool result. We include a mix of **read** tools (`find_patient`, `list_available_slots`, `get_appointments_for_patient`) and **write** tools (`book_appointment`, `reschedule_appointment`, `cancel_appointment`). The write tools validate state first: you can't book a slot that's already taken, or cancel an appointment that doesn't exist. If you have a different database setup, you can customize these functions to match your database-specific query logic. If you do, make sure you update your tool schemas (next section) accordingly. ```python theme={null} _TITLES = {"dr", "doctor", "mr", "mrs", "ms", "prof"} def _provider_id_from_name(conn, provider_name): """Resolve a provider from however the caller referred to them. Records hold the full name ("Dr. Pearl Flossmore") but a caller on the phone will just say a surname, so match on the distinctive words and ignore titles. """ if not provider_name: return None words = [w for w in provider_name.lower().replace(".", " ").split() if w not in _TITLES] if not words: return None q = "SELECT id FROM providers WHERE " + " AND ".join(["LOWER(name) LIKE ?"] * len(words)) row = conn.execute(q, [f"%{w}%" for w in words]).fetchone() return row["id"] if row else None def find_patient(conn, name=None, phone=None): """Look up a patient by (partial) name and/or phone number.""" q, params = "SELECT * FROM patients WHERE 1=1", [] if name: q += " AND name LIKE ?"; params.append(f"%{name}%") if phone: q += " AND phone LIKE ?"; params.append(f"%{phone}%") rows = [dict(r) for r in conn.execute(q, params).fetchall()] return {"patients": rows, "count": len(rows)} def list_available_slots(conn, date=None, provider_name=None): """List open appointment slots, optionally filtered by date (YYYY-MM-DD) and/or provider surname.""" q = ("SELECT a.id, a.start_time, a.duration_min, p.name AS provider, p.specialty " "FROM appointments a JOIN providers p ON p.id = a.provider_id " "WHERE a.status = 'available'") params = [] if date: q += " AND a.start_time LIKE ?"; params.append(f"{date}%") pid = _provider_id_from_name(conn, provider_name) if provider_name and pid is None: return {"error": f"No provider matching '{provider_name}'."} if pid: q += " AND a.provider_id = ?"; params.append(pid) q += " ORDER BY a.start_time LIMIT 20" rows = [dict(r) for r in conn.execute(q, params).fetchall()] return {"available_slots": rows, "count": len(rows)} def book_appointment(conn, slot_id, patient_name, patient_phone, reason=None): """Book an available slot for a patient, creating the patient if new.""" slot = conn.execute("SELECT * FROM appointments WHERE id=?", (slot_id,)).fetchone() if slot is None: return {"error": f"No slot with id {slot_id}."} if slot["status"] != "available": return {"error": f"Slot {slot_id} is not available (status: {slot['status']})."} existing = conn.execute("SELECT * FROM patients WHERE phone=?", (patient_phone,)).fetchone() if existing: patient_id = existing["id"] else: cur = conn.execute("INSERT INTO patients (name, phone) VALUES (?, ?)", (patient_name, patient_phone)) patient_id = cur.lastrowid conn.execute("UPDATE appointments SET patient_id=?, reason=?, status='booked' WHERE id=?", (patient_id, reason, slot_id)) conn.commit() confirmed = conn.execute( "SELECT a.id, a.start_time, p.name AS provider " "FROM appointments a JOIN providers p ON p.id=a.provider_id WHERE a.id=?", (slot_id,)).fetchone() return {"status": "booked", "confirmation": dict(confirmed), "patient_id": patient_id} def reschedule_appointment(conn, appointment_id, new_slot_id): """Move a booking to a new available slot, freeing the old one.""" old = conn.execute("SELECT * FROM appointments WHERE id=?", (appointment_id,)).fetchone() if old is None or old["status"] != "booked": return {"error": f"Appointment {appointment_id} is not an active booking."} new = conn.execute("SELECT * FROM appointments WHERE id=?", (new_slot_id,)).fetchone() if new is None or new["status"] != "available": return {"error": f"Slot {new_slot_id} is not available."} conn.execute("UPDATE appointments SET patient_id=?, reason=?, status='booked' WHERE id=?", (old["patient_id"], old["reason"], new_slot_id)) conn.execute("UPDATE appointments SET patient_id=NULL, reason=NULL, status='available' WHERE id=?", (appointment_id,)) conn.commit() return {"status": "rescheduled", "from_id": appointment_id, "to_id": new_slot_id} def cancel_appointment(conn, appointment_id): """Cancel a booking and return the slot to the available pool.""" appt = conn.execute("SELECT * FROM appointments WHERE id=?", (appointment_id,)).fetchone() if appt is None or appt["status"] != "booked": return {"error": f"Appointment {appointment_id} is not an active booking."} conn.execute("UPDATE appointments SET patient_id=NULL, reason=NULL, status='available' WHERE id=?", (appointment_id,)) conn.commit() return {"status": "cancelled", "appointment_id": appointment_id} def get_appointments_for_patient(conn, name=None, phone=None): """List a patient's active (booked) appointments.""" q = ("SELECT a.id, a.start_time, a.reason, a.status, p.name AS provider " "FROM appointments a " "JOIN patients pt ON pt.id = a.patient_id " "JOIN providers p ON p.id = a.provider_id " "WHERE a.status='booked'") params = [] if name: q += " AND pt.name LIKE ?"; params.append(f"%{name}%") if phone: q += " AND pt.phone LIKE ?"; params.append(f"%{phone}%") q += " ORDER BY a.start_time" rows = [dict(r) for r in conn.execute(q, params).fetchall()] return {"appointments": rows, "count": len(rows)} print("Tool functions defined.") ``` ### Tool schemas Mercury 2 uses the OpenAI-compatible function-calling format. Each schema tells the model the tool's name, what it does, and its parameters. This is how the model decides *which* tool to reach for and *how* to fill in the arguments. You can also plug in custom tool schemas; just make sure they are compatible with the support tools you've defined above. ```python theme={null} TOOLS = [ { "type": "function", "function": { "name": "find_patient", "description": "Look up an existing patient by name and/or phone number.", "strict": True, "parameters": { "type": "object", "properties": { "name": {"type": "string", "description": "Full or partial patient name"}, "phone": {"type": "string", "description": "Full or partial phone number"}, }, }, }, }, { "type": "function", "function": { "name": "list_available_slots", "description": ("List OPEN appointment slots. Filter by date (format " "YYYY-MM-DD) and/or provider surname. Call this before booking " "so you can offer the patient a real, open slot id."), "strict": True, "parameters": { "type": "object", "properties": { "date": {"type": "string", "description": "Day to search, YYYY-MM-DD"}, "provider_name": {"type": "string", "description": "Provider surname, e.g. 'Flossmore'"}, }, }, }, }, { "type": "function", "function": { "name": "book_appointment", "description": ("Book a specific available slot for a patient. Requires the " "slot_id (from list_available_slots), the patient's name, and " "their phone number."), "strict": True, "parameters": { "type": "object", "properties": { "slot_id": {"type": "integer", "description": "id of an available slot"}, "patient_name": {"type": "string"}, "patient_phone": {"type": "string"}, "reason": {"type": "string", "description": "Reason for the visit"}, }, "required": ["slot_id", "patient_name", "patient_phone"], }, }, }, { "type": "function", "function": { "name": "reschedule_appointment", "description": "Move an existing booking to a new available slot.", "strict": True, "parameters": { "type": "object", "properties": { "appointment_id": {"type": "integer", "description": "id of the current booking"}, "new_slot_id": {"type": "integer", "description": "id of the new available slot"}, }, "required": ["appointment_id", "new_slot_id"], }, }, }, { "type": "function", "function": { "name": "cancel_appointment", "description": "Cancel an existing booking and free the slot.", "strict": True, "parameters": { "type": "object", "properties": { "appointment_id": {"type": "integer", "description": "id of the booking to cancel"}, }, "required": ["appointment_id"], }, }, }, { "type": "function", "function": { "name": "get_appointments_for_patient", "description": "List a patient's upcoming (booked) appointments by name and/or phone.", "strict": True, "parameters": { "type": "object", "properties": { "name": {"type": "string"}, "phone": {"type": "string"}, }, }, }, }, ] # Map tool names to the functions that run them (bound to our connection). TOOL_RUNNERS = { "find_patient": lambda **kw: find_patient(conn, **kw), "list_available_slots": lambda **kw: list_available_slots(conn, **kw), "book_appointment": lambda **kw: book_appointment(conn, **kw), "reschedule_appointment": lambda **kw: reschedule_appointment(conn, **kw), "cancel_appointment": lambda **kw: cancel_appointment(conn, **kw), "get_appointments_for_patient": lambda **kw: get_appointments_for_patient(conn, **kw), } print(f"{len(TOOLS)} tools registered.") ``` ## 4. The agent loop The loop is the heart of a tool-calling agent: 1. Send the conversation **plus the tool schemas** to Mercury 2. 2. If the reply contains `tool_calls`, run each one locally and append the result as a `role: "tool"` message. 3. Repeat until Mercury returns a normal text answer. Using this workflow, the loop naturally handles multi-step requests like "reschedule my cleaning to Thursday" (look up the booking → find a Thursday slot → move it). We query mercury-2 on low reasoning effort: real time customer service is a very latency-sensitive use-case where Mercury's ability to complete complicated tool-calling tasks at ultra-low latencies shines. ```python theme={null} TODAY = datetime.now() SYSTEM_PROMPT = f"""You are the friendly virtual receptionist for Diffusion Dental. You help callers check availability and book, reschedule, or cancel appointments. Guidelines: - Today's date is {TODAY.strftime("%A, %B")} {TODAY.day}, {TODAY.year}. The clinic is open Monday-Friday. - The clinic's providers go by Molar (general dentistry), Wyre (orthodontics), and Flossmore (hygiene and cleaning). - Always use the tools to look up real data; never invent slot ids, times, or patient records. - Before booking, call list_available_slots and offer the caller a real open slot. - To book you need the caller's name and phone number - ask if you don't have them. - Confirm the concrete date, time, and provider back to the caller after any change. - Keep replies short and conversational - they may be read aloud over the phone.""" def run_agent(user_message, history=None, max_turns=6, verbose=True): """Run one user turn through the tool-calling loop. Returns (reply_text, history).""" messages = history or [{"role": "system", "content": SYSTEM_PROMPT}] messages.append({"role": "user", "content": user_message}) if verbose: print(f"👤 USER: {user_message}\n") for _ in range(max_turns): t0 = time.time() response = client.chat.completions.create( model=MERCURY_MODEL, reasoning_effort="low", messages=messages, tools=TOOLS, tool_choice="auto", ) dt = time.time() - t0 msg = response.choices[0].message # No tool calls => this is the final answer for this turn. if not msg.tool_calls: messages.append({"role": "assistant", "content": msg.content}) if verbose: print(f"🦷 AGENT ({dt:.2f}s): {msg.content}") return msg.content, messages # Otherwise, run each requested tool and feed the results back. messages.append(msg.model_dump(exclude_none=True)) for tool_call in msg.tool_calls: name = tool_call.function.name args = json.loads(tool_call.function.arguments) if verbose: print(f" 🔧 tool_call ({dt:.2f}s): {name}({json.dumps(args)})") try: result = TOOL_RUNNERS[name](**args) except Exception as e: # never crash the loop result = {"error": f"{type(e).__name__}: {e}"} if verbose: print(f" ↳ {json.dumps(result)[:200]}") messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result), }) return "Sorry, I couldn't complete that. Let me connect you to the front desk.", messages print(f"Agent loop ready. Today is {TODAY.strftime('%A, %B')} {TODAY.day}, {TODAY.year}.") ``` ## 5. Text Customer Service Agent Let's walk a caller through a full support flow. We keep `history` between calls so the agent remembers the conversation. ### 5a. Check availability ```python theme={null} reply, history = run_agent( "Hi, what times are open with Dr. Flossmore on Wednesday for a cleaning?" ) ``` ### 5b. Book a slot (a real database write) ```python theme={null} reply, history = run_agent( "Let's do the earliest one. My name is Taylor Testcase, phone 415-555-0199.", history=history, ) ``` Let's confirm the write actually landed in the database. The slot Taylor took should now be `booked`, and Taylor should exist as a patient. ```python theme={null} print("Taylor's bookings:") print(json.dumps(get_appointments_for_patient(conn, name="Taylor"), indent=2)) ``` ### 5c. Reschedule ```python theme={null} reply, history = run_agent( "Actually something came up - can you move that to Friday instead?", history=history, ) ``` ### 5d. Cancel ```python theme={null} reply, history = run_agent( "You know what, I'll just cancel it for now. Thanks!", history=history, ) ``` Every one of those turns was a real read or write against the SQLite database. The agent discovered slot ids and appointment ids purely through tool calls, exactly as it would against a production scheduling system behind an API. ## 6. Real-time voice with ElevenLabs Now we give the agent a voice. The pipeline is: **caller audio → ElevenLabs STT → Mercury 2 (tool loop) → ElevenLabs TTS → spoken reply** We use ElevenLabs `scribe_v1` for transcription and `eleven_turbo_v2_5` for low-latency speech. To keep the notebook self-contained (no microphone needed), we *simulate* the caller by synthesizing their line with a different voice, then feed that audio through the real STT → agent → TTS path. ```python theme={null} from elevenlabs import ElevenLabs from IPython.display import Audio, display eleven = ElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"], base_url="https://api.elevenlabs.io") CALLER_VOICE = "21m00Tcm4TlvDq8ikWAM" # Rachel - stands in for the caller AGENT_VOICE = "IKne3meq5aSn9XLyUdCD" # Charlie - the clinic receptionist TTS_MODEL = "eleven_turbo_v2_5" STT_MODEL = "scribe_v1" def synth(text, voice_id, path): """Synthesize `text` to an mp3 file and return the raw bytes.""" audio = eleven.text_to_speech.convert( voice_id=voice_id, model_id=TTS_MODEL, text=text, output_format="mp3_44100_128", ) data = b"".join(audio) with open(path, "wb") as f: f.write(data) return data print("ElevenLabs client ready.") ``` ### 6a. Simulate the caller We synthesize the caller's spoken request. In a real deployment this audio would come from a phone line or the browser's microphone. ```python theme={null} caller_line = "Hi, I'd like to book a checkup with Dr. Flossmore on Wednesday afternoon." caller_audio = synth(caller_line, CALLER_VOICE, "caller.mp3") print(f"Simulated caller said: {caller_line!r}") display(Audio("caller.mp3")) ``` ### 6b. Transcribe with `scribe_v1` Speech-to-text, timed. This is the first hop of the latency budget. ```python theme={null} t0 = time.time() transcript = eleven.speech_to_text.convert( model_id=STT_MODEL, file=open("caller.mp3", "rb"), ) stt_latency = time.time() - t0 caller_text = transcript.text print(f"📝 Transcript ({stt_latency:.2f}s): {caller_text}") ``` ### 6c. Stream the spoken reply, sentence by sentence The trick for low perceived latency (borrowed from the [voice assistant cookbook](/cookbooks/realtime-voice-assistant)): don't wait for the whole answer. Buffer Mercury's streamed text until a sentence boundary, then synthesize that sentence while the next one is still generating. Here we run the **tool loop** for the caller's request, then stream the final answer into ElevenLabs TTS one sentence at a time. ```python theme={null} import re def speak_streaming(text_chunks, voice_id, out_path): """Consume an iterator of text chunks, synthesize each complete sentence as it arrives, and concatenate the audio into a single mp3.""" audio_parts, buffer = [], "" sentence_end = re.compile(r"(.+?[.!?])(\s|$)") def flush(sentence): sentence = sentence.strip() if not sentence: return chunk = eleven.text_to_speech.convert( voice_id=voice_id, model_id=TTS_MODEL, text=sentence, output_format="mp3_44100_128", ) audio_parts.append(b"".join(chunk)) for piece in text_chunks: buffer += piece while True: m = sentence_end.match(buffer) if not m: break flush(m.group(1)) buffer = buffer[m.end():] flush(buffer) # trailing text with no terminal punctuation data = b"".join(audio_parts) with open(out_path, "wb") as f: f.write(data) return data ``` ### 6d. Put it together: one full voice turn `voice_turn` takes caller audio and returns the agent's spoken reply, printing a latency breakdown for each stage. ```python theme={null} def voice_turn(audio_path, history=None, agent_voice=AGENT_VOICE, out_path="agent_reply.mp3"): timings = {} # 1. Speech -> text t0 = time.time() transcript = eleven.speech_to_text.convert(model_id=STT_MODEL, file=open(audio_path, "rb")) caller_text = transcript.text timings["stt"] = time.time() - t0 # 2. Run the tool-calling agent (may make several Mercury calls) t0 = time.time() reply_text, history = run_agent(caller_text, history=history, verbose=False) timings["agent"] = time.time() - t0 # 3. Text -> streamed speech (synthesize sentence by sentence) t0 = time.time() speak_streaming(iter([reply_text]), agent_voice, out_path) timings["tts"] = time.time() - t0 print(f"👤 Caller: {caller_text}") print(f"🦷 Agent: {reply_text}\n") print("⏱ latency " f"STT {timings['stt']:.2f}s | agent {timings['agent']:.2f}s | " f"TTS {timings['tts']:.2f}s | total {sum(timings.values()):.2f}s") return history, out_path history_voice, reply_path = voice_turn("caller.mp3") display(Audio(reply_path)) ``` ### 6e. Keep the conversation going Because `voice_turn` threads `history` back through, the agent remembers context across spoken turns. Let's confirm the booking out loud. ```python theme={null} follow_up = synth( "Yes please book it. I'm Dana Decoy, my number is 415-555-0288.", CALLER_VOICE, "caller2.mp3", ) history_voice, reply_path = voice_turn("caller2.mp3", history=history_voice) display(Audio(reply_path)) ``` ## Tips ### Getting tool calls right * **Put your data conventions where Mercury can read them.** The model can only match your schema if you tell it what your schema looks like, either in tool descriptions or in the system prompt. For example, the line *"The clinic's providers go by Molar (general dentistry), Wyre (orthodontics), and Flossmore (hygiene and cleaning)"* is what makes Mercury pass `Flossmore` (not `Dr. Pearl Flossmore`) when a caller asks for "Doctor Flossmore." The same goes for id formats, enum values, and date formats: if a tool wants `2026-08-19`, say so in the tool's `description`, not just as `{"type": "string"}` in the schema. The `description` fields are prompt text, and they are where the model looks when filling arguments. * **Then make the tool tolerant anyway.** Prompt guidance reduces malformed arguments; it never eliminates them, and speech-to-text will always run the risk of transcribing speech incorrectly, especially names. That is why `_provider_id_from_name` strips titles and matches on distinctive words. State the contract in the prompt, enforce it in the tool, and return a structured `{"error": ...}` the model can read and recover from. ### Model sampling * **Adjust reasoning effort for your use case.** For real-time voice use cases, we recommend *reasoning effort low*, which runs at ultra-low latencies while still delivering a pleasant conversational experience and the complex tool calls needed for a streamlined customer service agent. ### Conversation state * **Manage context length as history grows.** Every turn appends the full text response and tool call JSON, increasing prompt size and driving up latency for very long-running conversations. However, most of that history goes stale fast. In order to improve latency over very long conversations, consider incorporating a compaction step in your voice loop, using Mercury-2 to summarize your conversation history, maintaining a smooth conversation experience while saving on tokens and latency. * **Validate every model-supplied id against the database.** The write tools here check state before mutating: you cannot book a taken slot or cancel an appointment that does not exist. Keep that discipline in production: treat slot ids and appointment ids from the model as untrusted input, because a plausible-looking hallucinated id could silently corrupt the loop. ## Resources * [Tool use with Mercury 2](/capabilities/tool-use) * [SQL agent cookbook](/cookbooks/natural-language-sql-agent) * [Voice assistant cookbook](/cookbooks/realtime-voice-assistant) * [Models & pricing](/get-started/models) # Natural Language SQL Agent: Mercury + Tool Calling Source: https://docs.inceptionlabs.ai/cookbooks/natural-language-sql-agent Build a natural language SQL agent that uses Mercury 2. This cookbook demonstrates how to build a natural language database query system using Mercury 2's tool calling capabilities. The full loop — natural language question, SQL generation, execution, and formatted answer — completes in under 2 seconds. We walk you through how to: 1. Load a SQLite database (sample or your own) 2. Auto-detect the schema and define a SQL tool for Mercury 2 3. Translate natural language questions into SQL using tool calling 4. Execute queries and return natural language answers 5. Measure end-to-end latency at each step Open the notebook in Google Colab or download the `.ipynb` to run locally. It builds its sample database in memory — no extra files needed. *** ## Dependencies Install packages and import the libraries required for the Mercury 2 API and SQLite: ```python theme={null} %pip install \ "inceptionai" \ "python-dotenv>=1.0.0" ``` ```python theme={null} import os import json import time import sqlite3 from inceptionai import Inception from dotenv import load_dotenv ``` ## API Keys Set up your Inception Labs API key. 1. Create a `.env` file in this directory 2. Add your API key to the `.env`: * `INCEPTION_API_KEY` The key will be automatically loaded from the `.env` file. ```python theme={null} # Load environment variables from .env file load_dotenv() INCEPTION_API_KEY = os.getenv("INCEPTION_API_KEY") ``` ## Initialize Client Initialize the Inception (Mercury 2) client: ```python theme={null} assert INCEPTION_API_KEY is not None, ( "ERROR: INCEPTION_API_KEY not found. Please create a .env file and add your API key." ) client = Inception(api_key=INCEPTION_API_KEY) MERCURY_MODEL = "mercury-2" ``` ## Load Database We create a small in-memory e-commerce database with three tables — `customers`, `products`, and `orders`. If you want to use your own SQLite database instead, replace this cell with `db = sqlite3.connect("your_file.db")`. ```python theme={null} # To use your own SQLite database instead, replace this cell with: # db = sqlite3.connect("path/to/your/database.db") db = sqlite3.connect(":memory:") cursor = db.cursor() cursor.executescript(""" CREATE TABLE customers ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL, signup_date TEXT NOT NULL ); CREATE TABLE products ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, category TEXT NOT NULL, price REAL NOT NULL, stock INTEGER NOT NULL ); CREATE TABLE orders ( id INTEGER PRIMARY KEY, customer_id INTEGER NOT NULL, product_id INTEGER NOT NULL, quantity INTEGER NOT NULL, total_amount REAL NOT NULL, status TEXT NOT NULL, order_date TEXT NOT NULL, FOREIGN KEY (customer_id) REFERENCES customers(id), FOREIGN KEY (product_id) REFERENCES products(id) ); INSERT INTO customers VALUES (1, 'Alice Johnson', 'alice@example.com', '2024-01-15'); INSERT INTO customers VALUES (2, 'Bob Smith', 'bob@example.com', '2024-02-20'); INSERT INTO customers VALUES (3, 'Sarah Lee', 'sarah@example.com', '2024-03-10'); INSERT INTO customers VALUES (4, 'Mike Chen', 'mike@example.com', '2024-04-05'); INSERT INTO customers VALUES (5, 'Emma Wilson', 'emma@example.com', '2024-05-12'); INSERT INTO products VALUES (1, 'Wireless Headphones', 'Electronics', 79.99, 45); INSERT INTO products VALUES (2, 'Running Shoes', 'Sports', 129.99, 30); INSERT INTO products VALUES (3, 'Coffee Maker', 'Home & Kitchen', 49.99, 60); INSERT INTO products VALUES (4, 'Backpack', 'Accessories', 59.99, 25); INSERT INTO products VALUES (5, 'Yoga Mat', 'Sports', 29.99, 80); INSERT INTO products VALUES (6, 'Desk Lamp', 'Home & Kitchen', 34.99, 55); INSERT INTO products VALUES (7, 'Water Bottle', 'Accessories', 19.99, 100); INSERT INTO products VALUES (8, 'Bluetooth Speaker', 'Electronics', 99.99, 35); INSERT INTO orders VALUES (1, 1, 1, 1, 79.99, 'delivered', '2025-01-10'); INSERT INTO orders VALUES (2, 1, 3, 2, 99.98, 'delivered', '2025-01-15'); INSERT INTO orders VALUES (3, 2, 2, 1, 129.99, 'shipped', '2025-02-01'); INSERT INTO orders VALUES (4, 3, 5, 2, 59.98, 'delivered', '2025-02-10'); INSERT INTO orders VALUES (5, 3, 1, 1, 79.99, 'delivered', '2025-02-14'); INSERT INTO orders VALUES (6, 4, 8, 1, 99.99, 'pending', '2025-03-01'); INSERT INTO orders VALUES (7, 4, 4, 1, 59.99, 'pending', '2025-03-05'); INSERT INTO orders VALUES (8, 5, 6, 2, 69.98, 'shipped', '2025-03-08'); INSERT INTO orders VALUES (9, 2, 7, 3, 59.97, 'delivered', '2025-03-10'); INSERT INTO orders VALUES (10, 1, 8, 1, 99.99, 'pending', '2025-03-12'); INSERT INTO orders VALUES (11, 5, 3, 1, 49.99, 'delivered', '2025-01-20'); INSERT INTO orders VALUES (12, 3, 7, 2, 39.98, 'shipped', '2025-03-14'); """) db.commit() ``` ## Inspect Schema We introspect the schema directly from the database, so the tool calling setup automatically adapts to whatever tables are loaded: ```python theme={null} cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND sql IS NOT NULL") DB_SCHEMA = "\n\n".join(row[0] for row in cursor.fetchall()) print("Detected schema:") print(DB_SCHEMA) ``` ## Define the SQL Tool We define a single tool — `run_sql_query` — that Mercury 2 can call to execute SQL against our database. The tool description includes the auto-detected schema so Mercury knows what tables and columns are available: ```python theme={null} tools = [ { "type": "function", "function": { "name": "run_sql_query", "description": f"Execute a read-only SQL query against the SQLite database. Schema:\n{DB_SCHEMA}", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "The SQL SELECT query to execute", } }, "required": ["query"], }, }, } ] def run_sql_query(query: str) -> str: """Execute a SQL query and return results as a formatted string.""" try: cursor.execute(query) columns = [desc[0] for desc in cursor.description] if cursor.description else [] rows = cursor.fetchall() if not rows: return "No results found." result = " | ".join(columns) + "\n" result += "-" * len(result) + "\n" for row in rows: result += " | ".join(str(val) for val in row) + "\n" return result except Exception as e: return f"SQL Error: {e}" tool_functions = {"run_sql_query": run_sql_query} ``` ## Query Helper This function handles the full tool-calling loop: 1. Send the user's natural language question to Mercury 2 with the tool definition 2. Mercury generates a `run_sql_query` tool call with SQL 3. We execute the SQL and send results back to Mercury 4. Mercury returns a natural language answer We time each step to show the latency breakdown. ```python theme={null} def ask_database(question: str) -> dict: """ Full tool-calling loop: question -> Mercury tool call -> SQL exec -> Mercury answer. Returns timing breakdown and the final answer. """ messages = [{"role": "user", "content": question}] # Step 1: Mercury generates a tool call t0 = time.time() response = client.chat.completions.create( model=MERCURY_MODEL, messages=messages, tools=tools, ) t1 = time.time() assistant_message = response.choices[0].message tool_call = assistant_message.tool_calls[0] function_name = tool_call.function.name function_args = json.loads(tool_call.function.arguments) print(f"Generated SQL: {function_args['query']}") # Step 2: Execute the SQL t2 = time.time() result = tool_functions[function_name](**function_args) t3 = time.time() print(f"Query result:\n{result}") # Step 3: Send tool result back to Mercury for a natural language answer messages.append(assistant_message.model_dump(exclude_none=True)) messages.append( { "role": "tool", "tool_call_id": tool_call.id, "content": result, } ) t4 = time.time() response = client.chat.completions.create( model=MERCURY_MODEL, messages=messages, ) t5 = time.time() final_answer = response.choices[0].message.content timing = { "tool_call_generation": round(t1 - t0, 3), "sql_execution": round(t3 - t2, 3), "answer_generation": round(t5 - t4, 3), "total": round(t5 - t0, 3), } return {"answer": final_answer, "sql": function_args["query"], "timing": timing} ``` ## Natural Language Database Queries Let's run several queries and measure the full round-trip latency. Each query goes through: NL question, SQL generation, execution, and NL answer. ### Query 1: Aggregation *"Who are the top 3 customers by total spending?"* ```python theme={null} result = ask_database("Who are the top 3 customers by total spending?") print(f"\nAnswer: {result['answer']}") print(f"\nLatency breakdown:") for step, duration in result["timing"].items(): print(f" {step}: {duration}s") ``` ### Query 2: Filtering *"Which orders are still pending?"* ```python theme={null} result = ask_database("Which orders are still pending?") print(f"\nAnswer: {result['answer']}") print(f"\nLatency breakdown:") for step, duration in result["timing"].items(): print(f" {step}: {duration}s") ``` ### Query 3: Join + Lookup *"Show me all orders placed by Sarah Lee."* ```python theme={null} result = ask_database("Show me all orders placed by Sarah Lee.") print(f"\nAnswer: {result['answer']}") print(f"\nLatency breakdown:") for step, duration in result["timing"].items(): print(f" {step}: {duration}s") ``` ### Query 4: Analytics *"What is the most popular product category by number of items sold?"* ```python theme={null} result = ask_database("What is the most popular product category by number of items sold?") print(f"\nAnswer: {result['answer']}") print(f"\nLatency breakdown:") for step, duration in result["timing"].items(): print(f" {step}: {duration}s") ``` ### Query 5: Revenue *"What's our total revenue from delivered orders?"* ```python theme={null} result = ask_database("What's our total revenue from delivered orders?") print(f"\nAnswer: {result['answer']}") print(f"\nLatency breakdown:") for step, duration in result["timing"].items(): print(f" {step}: {duration}s") ``` ## Latency Summary Mercury 2's diffusion-based architecture generates tokens \~5x faster than traditional autoregressive LLMs. In the queries above, the full tool-calling loop — understanding the question, generating SQL, executing it, and summarizing results in natural language — consistently completes in around 1-2 seconds. SQL execution itself is near-instant; the time is spent on two Mercury inference calls (tool call generation + answer generation), each typically under 1 second. This makes Mercury 2 fast enough to power **real-time, interactive database assistants** where users expect instant answers to ad-hoc questions — no waiting, no loading spinners. # Realtime Voice Assistant: Mercury + ElevenLabs Source: https://docs.inceptionlabs.ai/cookbooks/realtime-voice-assistant Stream Mercury 2 in ElevenLabs for a low-latency voice assistant. This notebook demonstrates how to build a low-latency voice assistant using Mercury 2 for real-time intelligence combined with ElevenLabs' speech-to-text and text-to-speech models. We walk you through how to: 1. Convert text to speech using ElevenLabs TTS 2. Convert audio to text using ElevenLabs speech-to-text 3. Generate realtime responses with Mercury 2 4. Optimize latency using Mercury 2 streaming Directly via Google Colab or download the `.ipynb` to run it locally in Jupyter *** ## Dependencies Install packages and import the libraries required for the Mercury 2 SDK, ElevenLabs, and audio playback: ```python theme={null} %pip install \ "inceptionai" \ "elevenlabs>=2.15.0" \ "python-dotenv>=1.0.0" ``` ```python theme={null} import io import os import time import elevenlabs from dotenv import load_dotenv from inceptionai import Inception from IPython.display import Audio ``` ## API Keys Set up your API keys for both ElevenLabs and Inception Labs. 1. Create a `.env` in this directory 2. Add your API keys to the `.env`: * `INCEPTION_API_KEY` * `ELEVENLABS_API_KEY` The keys will be automatically loaded from the `.env` file. ```python theme={null} # Load environment variables from .env file load_dotenv() INCEPTION_API_KEY = os.getenv("INCEPTION_API_KEY") ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY") ``` ## Initialize Clients Create the ElevenLabs and Inception (Mercury 2) clients: ```python theme={null} assert INCEPTION_API_KEY is not None, ( "ERROR: INCEPTION_API_KEY not found. Please copy .env.example to .env and add your API keys." ) assert ELEVENLABS_API_KEY is not None, ( "ERROR: ELEVENLABS_API_KEY not found. Please copy .env.example to .env and add your API keys." ) mercury_client = Inception(api_key=INCEPTION_API_KEY) elevenlabs_client = elevenlabs.ElevenLabs( api_key=ELEVENLABS_API_KEY, base_url="https://api.elevenlabs.io" ) MERCURY_MODEL = "mercury-2" VOICE_ID = "21m00Tcm4TlvDq8ikWAM" # Rachel def mercury_completion(user_text, max_tokens=1000, temperature=0.75): return mercury_client.chat.completions.create( model=MERCURY_MODEL, messages=[{"role": "user", "content": user_text}], max_tokens=max_tokens, temperature=temperature, reasoning_effort="low", ) def mercury_stream(user_text, max_tokens=1000, temperature=0.75): stream = mercury_client.chat.completions.create( model=MERCURY_MODEL, messages=[{"role": "user", "content": user_text}], max_tokens=max_tokens, temperature=temperature, reasoning_effort="low", stream=True, ) for chunk in stream: if not chunk.choices: continue text = chunk.choices[0].delta.content if text: yield text ``` ## Create Input Audio We can simulate a user audio input using ElevenLabs text-to-speech model: ```python theme={null} audio = elevenlabs_client.text_to_speech.convert( voice_id=VOICE_ID, output_format="mp3_44100_128", model_id="eleven_v3", text="Hi, Mercury. ", ) audio_data = io.BytesIO() for chunk in audio: audio_data.write(chunk) Audio(audio_data.getvalue()) ``` ## Transcribe Speech into Text We then transcribe the audio input using ElevenLabs' speech-to-text model. We'll also measure the transcription latency: ```python theme={null} audio_data.seek(0) start_time = time.time() transcription = elevenlabs_client.speech_to_text.convert(file=audio_data, model_id="scribe_v1") end_time = time.time() transcription_time = end_time - start_time print(f"Transcribed text: {transcription.text}") print(f"Transcription time: {transcription_time:.2f} seconds") ``` ## Get a Response from Mercury 2 We send the transcribed text to Mercury 2 and measure the response time. We're using `mercury-2` for fast reasoning responses: ```python theme={null} start_time = time.time() completion = mercury_completion(transcription.text, max_tokens=1000, temperature=0.75) mercury_response = completion.choices[0].message.content end_time = time.time() non_streaming_response_time = end_time - start_time print(mercury_response) print(f"\nResponse time: {non_streaming_response_time:.2f} seconds") ``` ## Real-time with Streaming Improve response latency by using Mercury 2 streaming. This allows us to receive the first tokens more quickly, reducing user-perceived latency: ```python theme={null} start_time = time.time() first_token_time = None mercury_full_response = "" for text in mercury_stream(transcription.text, max_tokens=1000, temperature=0.75): mercury_full_response += text print(text, end="", flush=True) if first_token_time is None: first_token_time = time.time() streaming_time_to_first_token = first_token_time - start_time print( f"\n\nStreaming time to first token: {streaming_time_to_first_token:.2f} seconds - reducing response wait by {(non_streaming_response_time - streaming_time_to_first_token) * 100 / non_streaming_response_time:.2f}%" ) ``` We can also stream the text-to-speech response to reduce silence: ```python theme={null} start_time = time.time() first_audio_chunk_time = None audio_buffer = io.BytesIO() audio_generator = elevenlabs_client.text_to_speech.stream( voice_id=VOICE_ID, output_format="mp3_44100_128", text=mercury_full_response, model_id="eleven_turbo_v2_5", ) for chunk in audio_generator: if first_audio_chunk_time is None: first_audio_chunk_time = time.time() audio_buffer.write(chunk) streaming_tts_time_to_first_chunk = first_audio_chunk_time - start_time print(f"Streaming TTS time to first audio chunk: {streaming_tts_time_to_first_chunk:.2f} seconds") Audio(audio_buffer.getvalue()) ``` ## Streaming Mercury 2 Directly to TTS We've optimized Mercury 2 streaming and TTS separately. Now we can stream the model response and convert it to audio as soon as we have complete sentences, demarcated by `!`, `.`, `?`. ```python theme={null} import re sentence_pattern = re.compile(r"[.!?]+") sentence_buffer = "" audio_chunks = [] start_time = time.time() first_audio_time = None for text in mercury_stream(transcription.text, max_tokens=1000, temperature=0.75): print(text, end="", flush=True) sentence_buffer += text if sentence_pattern.search(sentence_buffer): sentences = sentence_pattern.split(sentence_buffer) # Process all complete sentences (all but the last element) for i in range(len(sentences) - 1): complete_sentence = sentences[i].strip() if complete_sentence: audio_gen = elevenlabs_client.text_to_speech.stream( voice_id=VOICE_ID, output_format="mp3_44100_128", text=complete_sentence, model_id="eleven_turbo_v2_5", ) sentence_audio = io.BytesIO() for chunk in audio_gen: if first_audio_time is None: first_audio_time = time.time() sentence_audio.write(chunk) audio_chunks.append(sentence_audio.getvalue()) sentence_buffer = sentences[-1] if sentence_buffer.strip(): audio_gen = elevenlabs_client.text_to_speech.stream( voice_id=VOICE_ID, output_format="mp3_44100_128", text=sentence_buffer.strip(), model_id="eleven_turbo_v2_5", ) sentence_audio = io.BytesIO() for chunk in audio_gen: if first_audio_time is None: first_audio_time = time.time() sentence_audio.write(chunk) audio_chunks.append(sentence_audio.getvalue()) sentence_streaming_time_to_first_audio = first_audio_time - start_time print(f"\n\nTime to first audio: {sentence_streaming_time_to_first_audio:.2f} seconds") combined_pcm = b"".join(audio_chunks) Audio(combined_pcm) ``` In this sentence-by-sentence approach, each chunk is synthesized independently, so the audio can sound less natural than a fully context-aware streaming setup. To offload the voice loop to a managed platform, see how to set up Mercury 2 with the [ElevenLabs Agents platform](/integrations/elevenlabs). # Search Agent: Mercury + Web Search Source: https://docs.inceptionlabs.ai/cookbooks/search-agent-mercury Build an iterative web search agent that plans, searches, and synthesizes cited answers with Mercury.