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

# Tool calls

> Define tools, validate model arguments, execute safely, and return results.

Tool calls let the model choose functions executed by your application. The model only generates the call intent and parameters; it does not access databases, send emails, or execute commands for you.

## Call flow

1. Send tool names, descriptions, and JSON parameter structures in the request.
2. The model returns a tool call and parameters.
3. The application validates parameters, permissions, and user intent.
4. The application executes the tool and records audit information.
5. Return the tool result to the model in a follow-up message to get the final answer.

## Request example

```json theme={null}
{
  "model": "YOUR_APIONE_MODEL_ID",
  "messages": [{"role": "user", "content": "Check the weather in Shanghai today"}],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get weather for a city",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"],
          "additionalProperties": false
        }
      }
    }
  ],
  "tool_choice": "auto"
}
```

Send to `POST https://api.apione.ai/v1/chat/completions`. Only use these fields when the target model and route support tool calls; check model parameters first.

## Parsing and validation

The response content may be empty; tool requests are usually in `choices[].message.tool_calls`. Do not directly execute model-generated strings: parse JSON first, then validate tool names, field types, enums, lengths, and permissions.

## Safety boundaries

* Use an allowlist for tool names; reject unknown tools outright.
* File, command, payment, deletion, and external messaging are high-risk operations that should require extra confirmation.
* Recalculate user permissions on the server side; do not trust user IDs or amounts passed by the model.
* Set execution timeouts, maximum return sizes, and rate limits.
* Remove keys, internal error stacks, and irrelevant personal data from tool results.

## Common issues

| Symptom | Handling |
| - | - |
| `content` is empty | Check whether `tool_calls` was returned |
| Parameters are not valid JSON | Have the application catch parse errors and ask the model to retry |
| Tool is called repeatedly | Set a maximum round count and detect loops with identical parameters |
| Model calls a nonexistent tool | Enforce an allowlist and return an understandable error |
| Tool result is too long | Summarize, paginate, or return only necessary fields |

## Complete Python example

The example executes a temperature conversion in code, then sends the result back to the model. First install dependencies, then follow [Quickstart](https://apione.ai/docs/en/quickstart.md) to set `APIONE_API_KEY` and a tool-capable `APIONE_MODEL`. Save as `tool_demo.py` and run `python tool_demo.py`.

```bash theme={null}
python -m pip install openai
```

```python theme={null}
import json
import math
import os
from openai import OpenAI, APIStatusError, APIConnectionError

tools = [{"type": "function", "function": {
    "name": "celsius_to_fahrenheit",
    "description": "Convert Celsius to Fahrenheit.",
    "parameters": {"type": "object", "properties": {
        "celsius": {"type": "number", "minimum": -273.15, "maximum": 10000}
    }, "required": ["celsius"], "additionalProperties": False}
}}]

def execute(name, arguments):
    if name != "celsius_to_fahrenheit":
        return {"error": "Unknown tool"}
    try:
        args = json.loads(arguments)
        if not isinstance(args, dict) or set(args) != {"celsius"}:
            raise ValueError("Expected celsius only")
        value = args["celsius"]
        if isinstance(value, bool) or not isinstance(value, (int, float)):
            raise ValueError("Expected a number")
        if not math.isfinite(value) or not -273.15 <= value <= 10000:
            raise ValueError("Temperature out of range")
        return {"celsius": value, "fahrenheit": round(value * 9 / 5 + 32, 6)}
    except (ValueError, TypeError):
        return {"error": "Invalid tool arguments"}

messages = [{"role": "user", "content": "Use the tool to convert 25 Celsius to Fahrenheit."}]
try:
    with OpenAI(base_url="https://api.apione.ai/v1",
                api_key=os.environ["APIONE_API_KEY"],
                timeout=60, max_retries=0) as client:
        for round_number in range(4):
            raw = client.chat.completions.with_raw_response.create(
                model=os.environ["APIONE_MODEL"], messages=messages,
                tools=tools, tool_choice="auto")
            ids = {name: raw.headers[name] for name in
                   ("X-Request-ID", "X-Maxx-Trace-Id", "X-Maxx-Request-ID")
                   if name in raw.headers}
            print("request identifiers:", ids)
            result = raw.parse()
            if not result.choices:
                raise RuntimeError("No choices returned")
            message = result.choices[0].message
            calls = message.tool_calls or []
            if not calls:
                print(message.content)
                break
            if len(calls) > 8:
                raise RuntimeError("Too many tool calls")
            messages.append(message.model_dump(exclude_none=True))
            for call in calls:
                output = execute(call.function.name, call.function.arguments)
                messages.append({"role": "tool", "tool_call_id": call.id,
                                 "content": json.dumps(output)})
        else:
            raise RuntimeError("Tool-call round limit reached")
except APIStatusError as error:
    print("HTTP status:", error.status_code)
    headers = error.response.headers
    print("request identifiers:", {name: headers[name] for name in
          ("X-Request-ID", "X-Maxx-Trace-Id", "X-Maxx-Request-ID") if name in headers})
    raise SystemExit(1) from None
except APIConnectionError:
    print("Connection failed: no HTTP response or server request ID received.")
    raise SystemExit(1) from None
```

The local function returns 77 degrees Fahrenheit for 25 degrees Celsius; the model's final wording depends on the actual response. Each tool result's `tool_call_id` corresponds to the original call's `id`; you must retain the tool call message and all results. The example limits execution to four rounds and eight tool calls per round, with SDK retries disabled. Charges are based on actual request records.

Next: [Structured output](https://apione.ai/docs/en/api/structured-output.md), [Model parameters](https://apione.ai/docs/en/models/parameters.md), [Troubleshooting](https://apione.ai/docs/en/support/errors.md), [Request reliability](https://apione.ai/docs/en/api/request-reliability.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.