> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aurous-labs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools and function calling

> Define callable tools and let the model decide when to invoke them.

`POST /v1/chat/completions` accepts the standard OpenAI `tools` array. The model emits structured `tool_calls` in its response when it decides a tool is the right move; your code runs the tool and round-trips the result back as a `role: "tool"` message.

## Tool schema

Each tool is a function declaration with a JSON-schema parameter object:

```jsonc theme={null}
{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "Return the current weather for a city.",
    "parameters": {
      "type": "object",
      "properties": {
        "city": { "type": "string", "description": "City name, e.g. 'Berlin'." },
        "unit": { "type": "string", "enum": ["c", "f"], "default": "c" }
      },
      "required": ["city"]
    }
  }
}
```

The `tool_choice` field controls how the model picks a tool:

| Value                                                   | Behavior                                                                                                                     |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `"auto"` (default if `tools` is set)                    | Model decides per turn whether to call a tool.                                                                               |
| `"none"`                                                | Disable tool calls for this turn.                                                                                            |
| `"required"`                                            | Force the model to call at least one tool. Returns `400 tool_choice_required_unsupported` if the model lacks the capability. |
| `{ "type": "function", "function": { "name": "..." } }` | Force a specific tool.                                                                                                       |

## Single-turn example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.aurous-labs.com/v1/chat/completions \
    -H "Authorization: Bearer $AUROUS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "aurous-grow-2.0-pro",
      "messages": [
        { "role": "user", "content": "What is the weather in Berlin?" }
      ],
      "tools": [
        {
          "type": "function",
          "function": {
            "name": "get_weather",
            "description": "Return the current weather for a city.",
            "parameters": {
              "type": "object",
              "properties": {
                "city": { "type": "string" },
                "unit": { "type": "string", "enum": ["c", "f"] }
              },
              "required": ["city"]
            }
          }
        }
      ],
      "tool_choice": "auto",
      "max_tokens": 512
    }'
  ```

  ```typescript Node.js (OpenAI SDK) theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    baseURL: "https://api.aurous-labs.com/v1",
    apiKey: process.env.AUROUS_API_KEY!,
  });

  const tools = [
    {
      type: "function" as const,
      function: {
        name: "get_weather",
        description: "Return the current weather for a city.",
        parameters: {
          type: "object",
          properties: {
            city: { type: "string" },
            unit: { type: "string", enum: ["c", "f"] },
          },
          required: ["city"],
        },
      },
    },
  ];

  const first = await client.chat.completions.create({
    model: "aurous-grow-2.0-pro",
    messages: [{ role: "user", content: "What is the weather in Berlin?" }],
    tools,
    tool_choice: "auto",
    max_tokens: 512,
  });

  const call = first.choices[0].message.tool_calls?.[0];
  console.log(call?.function.name, call?.function.arguments);
  // → "get_weather" '{"city":"Berlin","unit":"c"}'
  ```

  ```python Python (OpenAI SDK) theme={null}
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.aurous-labs.com/v1",
      api_key="al_live_xxxxxxxxxxxxxxxx",
  )

  tools = [
      {
          "type": "function",
          "function": {
              "name": "get_weather",
              "description": "Return the current weather for a city.",
              "parameters": {
                  "type": "object",
                  "properties": {
                      "city": {"type": "string"},
                      "unit": {"type": "string", "enum": ["c", "f"]},
                  },
                  "required": ["city"],
              },
          },
      }
  ]

  first = client.chat.completions.create(
      model="aurous-grow-2.0-pro",
      messages=[{"role": "user", "content": "What is the weather in Berlin?"}],
      tools=tools,
      tool_choice="auto",
      max_tokens=512,
  )

  call = first.choices[0].message.tool_calls[0]
  print(call.function.name, call.function.arguments)
  # → "get_weather" '{"city":"Berlin","unit":"c"}'
  ```
</CodeGroup>

## Round-tripping the tool result

After the model emits a tool call, run the tool locally, then re-call `chat.completions.create` with the original assistant message AND a new `role: "tool"` message carrying the result:

```typescript Node.js (OpenAI SDK) theme={null}
// Step 1: model returned a tool_call.
const assistantMessage = first.choices[0].message;
const call = assistantMessage.tool_calls![0];
const args = JSON.parse(call.function.arguments);

// Step 2: run the tool (your code).
const weather = await fetchWeather(args.city, args.unit);
//   → { tempC: 14, condition: "cloudy" }

// Step 3: send the tool result back as a follow-up turn.
const followup = await client.chat.completions.create({
  model: "aurous-grow-2.0-pro",
  messages: [
    { role: "user", content: "What is the weather in Berlin?" },
    assistantMessage, // contains tool_calls
    {
      role: "tool",
      tool_call_id: call.id,
      content: JSON.stringify(weather),
    },
  ],
  max_tokens: 512,
});

console.log(followup.choices[0].message.content);
// → "It's 14°C and cloudy in Berlin right now."
```

The `tool_call_id` on the `role: "tool"` message must match the `id` field on the assistant's `tool_calls` entry. The model may emit multiple tool calls in a single turn — round-trip them all in one follow-up.

## Parallel tool calls

If the model emits two or more tool calls in a single turn (`message.tool_calls.length > 1`), run them in parallel and send all results back in the next request:

```typescript theme={null}
const calls = assistantMessage.tool_calls!;
const results = await Promise.all(
  calls.map(async (c) => ({
    role: "tool" as const,
    tool_call_id: c.id,
    content: JSON.stringify(await dispatchTool(c.function.name, c.function.arguments)),
  })),
);

const final = await client.chat.completions.create({
  model: "aurous-grow-2.0-pro",
  messages: [
    { role: "user", content: "Compare Berlin and Tokyo weather." },
    assistantMessage,
    ...results, // one tool message per call
  ],
});
```

## Forcing a specific tool

Pin the tool the model must call by name:

```jsonc theme={null}
{
  "tools": [...],
  "tool_choice": {
    "type": "function",
    "function": { "name": "get_weather" }
  }
}
```

Combined with `response_format`, this is the building block for "extract structured data from text" flows: define a tool whose parameters match the schema you want and force it.

## Streaming and tools

When `stream: true`, tool-call arguments arrive as `delta.tool_calls[*].function.arguments` fragments that you must concatenate per `id` until you see `finish_reason: "tool_calls"`. The OpenAI Node and Python SDKs both expose `tool_calls` accumulators on their streaming helpers — prefer those over hand-parsing deltas.

## Errors

* `tool_choice_required_unsupported` (400) — you passed `tool_choice: "required"` to a model whose `aurous_metadata.capabilities` doesn't include `tool_choice_required`. List models via [`GET /v1/models`](/api-reference/openapi#tag/models) to check capability flags before setting this.
* `response_format_too_large` (400) — the JSON schema in `response_format` exceeds the platform's payload cap. Trim the schema (fewer properties, shorter descriptions) and retry.
* `response_format_too_deep` (400) — the JSON schema nests deeper than the parser's cap. Flatten nested definitions or pull them into `$defs` references.
