# Tool calling

> Describe your functions, let the model decide when to call them, run them yourself and send back the results.

Source: https://www.lyber.ai/docs/capabilities/tool-calling/ · Markdown: https://www.lyber.ai/docs/md/capabilities/tool-calling.md

Tool calling uses the OpenAI format. The model never runs anything: it returns the function name and JSON arguments, your code runs the function, and you continue the conversation with the result.

## The loop

1. Send your messages with a `tools` array.
2. When the reply has `finish_reason: "tool_calls"`, run each call in `message.tool_calls`.
3. Append the assistant message, then one `role: "tool"` message per call with its `tool_call_id` and result.
4. Send the conversation again. Repeat until the model answers in `content`.

Python (`main.py`):

```python
import json
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1",
    api_key=os.environ["LYBER_API_KEY"],
)

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather for a city.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]
messages = [{"role": "user", "content": "What's the weather in Buenos Aires?"}]

first = client.chat.completions.create(
    model="minimax-m3.1-flash-abliterated", messages=messages, tools=tools, max_tokens=2048,
)
reply = first.choices[0].message
messages.append(reply)

for call in reply.tool_calls or []:
    args = json.loads(call.function.arguments)
    result = {"city": args["city"], "temperature_c": 21}  # your code runs here
    messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})

final = client.chat.completions.create(
    model="minimax-m3.1-flash-abliterated", messages=messages, tools=tools, max_tokens=2048,
)
print(final.choices[0].message.content)
```

TypeScript (`main.ts`):

```ts
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1",
  apiKey: process.env.LYBER_API_KEY,
});

const tools = [{
  type: "function" as const,
  function: {
    name: "get_weather",
    description: "Current weather for a city.",
    parameters: {
      type: "object",
      properties: { city: { type: "string" } },
      required: ["city"],
    },
  },
}];
const messages: OpenAI.ChatCompletionMessageParam[] = [
  { role: "user", content: "What's the weather in Buenos Aires?" },
];

const first = await client.chat.completions.create({
  model: "minimax-m3.1-flash-abliterated", messages, tools, max_tokens: 2048,
});
const reply = first.choices[0].message;
messages.push(reply);

for (const call of reply.tool_calls ?? []) {
  if (call.type !== "function") continue;
  const args = JSON.parse(call.function.arguments);
  const result = { city: args.city, temperature_c: 21 }; // your code runs here
  messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) });
}

const final = await client.chat.completions.create({
  model: "minimax-m3.1-flash-abliterated", messages, tools, max_tokens: 2048,
});
console.log(final.choices[0].message.content);
```

## Choosing tools

| tool_choice | Effect |
| --- | --- |
| `"auto"` | The model decides. This is the default when you send tools. |
| `"none"` | The model answers in text and calls nothing. |
| `"required"` | The model must call at least one tool. |
| `{"type": "function", "function": {"name": "get_weather"}}` | The model must call this function. |

## Streaming tool calls

With `stream: true`, each tool call arrives in pieces in `delta.tool_calls`. The first piece carries the call's `id` and `function.name`; later pieces with the same `index` carry fragments of `function.arguments`. Concatenate them and parse the JSON when the stream finishes with `finish_reason: "tool_calls"`.

## Limits

- Up to 32 tools per request, all `type: "function"`. Names are 1–128 characters and must be unique; `parameters` must be a JSON Schema object.
- Up to 32 tool calls in one assistant message.
- `parallel_tool_calls` is not accepted on Chat Completions and returns `400`. The model may still return several calls at once; handle every entry in `tool_calls`.
- Server-side tools such as web search or code execution are not available. Run them in your own code and pass the results back as tool messages.
