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

# Tool calling

> Let the model call functions that you define

Tool calling, also called function calling, lets the model ask your code to run a function and use the result in its answer. You describe the functions in the request; the model decides when to call one and with which arguments; your code runs it and sends the result back.

## How it works

<Steps>
  <Step title="Define tools">
    Send `tools` with your request, each with a `name`, a `description`, and JSON Schema `parameters`.
  </Step>

  <Step title="The model calls a tool">
    If the model decides to use a tool, the response has `finish_reason: "tool_calls"` and one or more entries in `message.tool_calls`, each with an `id`, the function `name`, and JSON `arguments`.
  </Step>

  <Step title="Run the tool and return the result">
    Append the assistant message to the conversation, then add one `tool` message per call, directly after it, with the call's `id` as `tool_call_id` and the result as `content`.
  </Step>

  <Step title="The model answers">
    Send the conversation again. The model uses the results to answer, or calls more tools.
  </Step>
</Steps>

## Example

<CodeGroup>
  ```python Python theme={null}
  import json
  import os
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.reflection.ai/openai/v1",
      api_key=os.environ["REFLECTION_API_KEY"],
  )

  tools = [
      {
          "type": "function",
          "function": {
              "name": "get_weather",
              "description": "Get the current weather in a city.",
              "parameters": {
                  "type": "object",
                  "properties": {
                      "city": {"type": "string", "description": "City name, such as Paris"},
                  },
                  "required": ["city"],
              },
          },
      }
  ]


  def get_weather(city: str) -> dict:
      return {"city": city, "temperature_c": 18, "conditions": "sunny"}


  messages = [{"role": "user", "content": "What's the weather in Paris?"}]

  while True:
      completion = client.chat.completions.create(
          model="Beam-501B-A23B",
          messages=messages,
          tools=tools,
      )
      choice = completion.choices[0]
      if choice.finish_reason != "tool_calls":
          break

      # Return the assistant turn as received, including its tool calls.
      messages.append(choice.message)
      for call in choice.message.tool_calls:
          # Arguments are model-generated: validate them before use.
          try:
              arguments = json.loads(call.function.arguments)
          except json.JSONDecodeError:
              arguments = None
          city = arguments.get("city") if isinstance(arguments, dict) else None
          if isinstance(city, str) and city:
              result = get_weather(city)
          else:
              result = {"error": "Expected a non-empty string argument named city."}
          messages.append(
              {
                  "role": "tool",
                  "tool_call_id": call.id,
                  "content": json.dumps(result),
              }
          )

  print(choice.message.content)
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    baseURL: "https://api.reflection.ai/openai/v1",
    apiKey: process.env.REFLECTION_API_KEY,
  });

  const tools: OpenAI.ChatCompletionTool[] = [
    {
      type: "function",
      function: {
        name: "get_weather",
        description: "Get the current weather in a city.",
        parameters: {
          type: "object",
          properties: {
            city: { type: "string", description: "City name, such as Paris" },
          },
          required: ["city"],
        },
      },
    },
  ];

  function parseCity(json: string): string | null {
    try {
      const { city } = JSON.parse(json);
      return typeof city === "string" && city ? city : null;
    } catch {
      return null;
    }
  }

  function getWeather(city: string) {
    return { city, temperature_c: 18, conditions: "sunny" };
  }

  const messages: OpenAI.ChatCompletionMessageParam[] = [
    { role: "user", content: "What's the weather in Paris?" },
  ];

  while (true) {
    const completion = await client.chat.completions.create({
      model: "Beam-501B-A23B",
      messages,
      tools,
    });
    const choice = completion.choices[0];
    if (choice.finish_reason !== "tool_calls") {
      console.log(choice.message.content);
      break;
    }

    // Return the assistant turn as received, including its tool calls.
    messages.push(choice.message);
    for (const call of choice.message.tool_calls ?? []) {
      if (call.type !== "function") continue;
      // Arguments are model-generated: validate them before use.
      const city = parseCity(call.function.arguments);
      const result = city
        ? getWeather(city)
        : { error: "Expected a non-empty string argument named city." };
      messages.push({
        role: "tool",
        tool_call_id: call.id,
        content: JSON.stringify(result),
      });
    }
  }
  ```
</CodeGroup>

The first response contains the tool call:

```json theme={null}
"message": {
  "role": "assistant",
  "content": null,
  "refusal": null,
  "tool_calls": [
    {
      "id": "call-7f3a9c2e-5b1d-4e8a-9c36-2f0d8b4a71e5",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"city\": \"Paris\"}"
      }
    }
  ]
},
"finish_reason": "tool_calls"
```

<Warning>
  `arguments` is a JSON string generated by the model. Parse it, and validate it before acting on it, especially for tools that change data or spend money.
</Warning>

## Control tool use

`tool_choice` controls whether the model calls a tool:

| Value | Behavior |
| - | - |
| `"auto"` | The model decides whether to call tools. |
| `"none"` | The model does not call tools. |
| `"required"` | The model must call at least one tool. |
| `{"type": "function", "function": {"name": "get_weather"}}` | The model must call the named function. |

Set `parallel_tool_calls` to `false` to allow at most one tool call per turn. `tool_choice` and `parallel_tool_calls` are allowed only when `tools` is supplied.

## Strict mode

Set `function.strict` to `true` on a tool to enable strict schema adherence for its arguments. Its `parameters` must then follow the rules for [strict structured outputs](/structured-outputs#strict-mode): every object sets `additionalProperties: false` and requires all of its properties, and some JSON Schema keywords aren't supported.

## Limits

* Up to 128 tools per request.
* Function names are 1 to 64 characters.

## Tips

* Write descriptions for the model: say what the tool does and when to use it, and describe each parameter.
* Keep the assistant message that made the tool calls in the conversation, unchanged, including any `reasoning_content`. See [Reasoning](/reasoning#reasoning-in-multi-turn-conversations).
* If you build the assistant message yourself, give each tool call its `id`, `type: "function"`, and a `function` with `name` and `arguments`, and give each `tool` message a `tool_call_id`. A missing field returns a `400` error whose `param` names it, such as `messages[1].tool_calls[0].function.arguments`.
* Send exactly one `tool` message for each tool call, even when the tool fails; describe the error in `content` so the model can recover.
* Put the `tool` messages directly after the assistant message that made the calls, in any order, before any other message. A call without a result, a result for a call that isn't in that assistant message, a second result for the same call, or two calls with the same `id` returns a `400` error whose `param` names the message.
* When [streaming](/streaming#streaming-tool-calls), tool calls arrive in fragments that you accumulate by `index`.
