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

# Tool Calling

> Enable models to call functions and use external tools

## Overview

Tool calling (also known as function calling) enables AI models to request the execution of external functions and tools. Instead of trying to generate structured data directly, the model can indicate it wants to call a function with specific parameters, your code executes the function, and then you send the result back to continue the conversation.

**Use tool calling when you need to:**

* Access real-time data (weather, stock prices, databases)
* Perform calculations or data processing
* Interact with external APIs and services
* Execute actions on behalf of users (send emails, create calendar events)
* Retrieve information the model doesn't have in its training data

## Prerequisites

Before using tool calling, ensure you have:

* A Concentrate AI API key ([get one here](https://concentrate.ai))
* A model that supports tool calling (refer to the [models endpoint](/docs/api-reference/endpoint/list-models) to see models that support tool calling)
* An understanding of JSON Schema for defining function parameters

## Quick Start

Here's a basic example of tool calling with a weather function:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.concentrate.ai/v1/responses \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "model": "gpt-5.2",
      "input": "What is the weather in San Francisco?",
      "tools": [
        {
          "type": "function",
          "name": "get_weather",
          "description": "Get current weather for a location",
          "parameters": {
            "type": "object",
            "properties": {
              "location": {
                "type": "string",
                "description": "City and state, e.g. San Francisco, CA"
              },
              "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "Temperature unit"
              }
            },
            "required": ["location"],
            "additionalProperties": false
          }
        }
      ]
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.concentrate.ai/v1/responses",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={
          "model": "gpt-5.2",
          "input": "What is the weather in San Francisco?",
          "tools": [{
              "type": "function",
              "name": "get_weather",
              "description": "Get current weather for a 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"],
                  "additionalProperties": False
              }
          }]
      }
  )

  data = response.json()
  print(data)
  ```

  ```typescript TypeScript theme={null}
  interface WeatherTool {
    type: "function";
    name: string;
    description: string;
    parameters: {
      type: "object";
      properties: Record<string, unknown>;
      required: string[];
    };
  }

  const response = await fetch("https://api.concentrate.ai/v1/responses", {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "gpt-5.2",
      input: "What is the weather in San Francisco?",
      tools: [{
        type: "function",
        name: "get_weather",
        description: "Get current weather for a 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"]
        }
      }]
    })
  });

  const data = await response.json();
  console.log(data);
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
  )

  func main() {
      payload := map[string]interface{}{
          "model": "gpt-5.2",
          "input": "What is the weather in San Francisco?",
          "tools": []map[string]interface{}{
              {
                  "type":        "function",
                  "name":        "get_weather",
                  "description": "Get current weather for a location",
                  "parameters": map[string]interface{}{
                      "type": "object",
                      "properties": map[string]interface{}{
                          "location": map[string]interface{}{
                              "type":        "string",
                              "description": "City and state, e.g. San Francisco, CA",
                          },
                          "unit": map[string]interface{}{
                              "type": "string",
                              "enum": []string{"celsius", "fahrenheit"},
                          },
                      },
                      "required": []string{"location"},
                  },
              },
          },
      }

      jsonData, _ := json.Marshal(payload)
      req, _ := http.NewRequest("POST", "https://api.concentrate.ai/v1/responses", bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()

      var result map[string]interface{}
      json.NewDecoder(resp.Body).Decode(&result)
      fmt.Println(result)
  }
  ```
</CodeGroup>

## Defining Tools

Each tool is defined using the `FunctionTool` schema. Here are the properties:

| Property        | Type    | Required | Description                                                   |
| --------------- | ------- | -------- | ------------------------------------------------------------- |
| `type`          | string  | Yes      | Must be `"function"`                                          |
| `name`          | string  | Yes      | Function name (alphanumeric, underscores, dots, hyphens only) |
| `description`   | string  | No       | Clear description of what the function does                   |
| `parameters`    | object  | Yes      | JSON Schema defining the function's parameters                |
| `strict`        | boolean | No       | Enable strict schema validation (default: `true`)             |
| `cache_control` | object  | No       | Cache this tool definition for better performance             |

### Tool Name Pattern

Tool names must match the pattern: `^[a-zA-Z0-9_.-]+$`

Valid names:

* `get_weather`
* `send_email`
* `user.get_profile`
* `calculate-sum`

Invalid names:

* `get weather` (contains space)
* `send@email` (contains @)

### Parameters as JSON Schema

The `parameters` field uses JSON Schema to define the function's inputs. This helps the model understand what data to provide.

```json theme={null}
{
  "parameters": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City and state, e.g. San Francisco, CA"
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "description": "Temperature unit"
      },
      "include_forecast": {
        "type": "boolean",
        "description": "Include 5-day forecast"
      }
    },
    "required": ["location"]
  }
}
```

**Best practices for parameters:**

* Always include `description` fields to help the model understand the purpose
* Use `enum` for fields with fixed options
* Mark essential parameters in the `required` array
* Keep schemas simple and focused on the task

### Strict Mode

By default, `strict: true` is enabled. This ensures the model's output strictly follows your schema.

<Warning>
  When `strict` is `true`, your `parameters` schema **must** include `"additionalProperties": false`. Omitting it will return a `400 Bad Request` error. This is a common mistake.
</Warning>

```json theme={null}
{
  "type": "function",
  "name": "create_user",
  "parameters": {
    "type": "object",
    "properties": {
      "name": { "type": "string" },
      "email": { "type": "string" }
    },
    "required": ["name", "email"],
    "additionalProperties": false
  },
  "strict": true
}
```

With `strict: true`, the model will only include `name` and `email` in its output—no additional properties. Set `strict: false` if you don't want to enforce `additionalProperties`.

### Cache Control

Cache tool definitions to improve performance and reduce costs:

```json theme={null}
{
  "type": "function",
  "name": "get_weather",
  "description": "Get current weather for a location",
  "parameters": { ... },
  "cache_control": {
    "type": "ephemeral",
    "ttl": "5m"
  }
}
```

See [Prompt Caching](/docs/api-reference/endpoint/prompt-caching) for more details.

## Tool Choice Modes

Control which tools the model can use with the `tool_choice` parameter:

### None - Disable Tool Calling

```json theme={null}
{
  "tool_choice": "none"
}
```

The model will respond normally without calling any tools, even if they're available.

### Auto - Let Model Decide (Default)

```json theme={null}
{
  "tool_choice": "auto"
}
```

The model decides whether to call a tool based on the user's input. This is the default behavior.

### Required - Force Tool Use

```json theme={null}
{
  "tool_choice": "required"
}
```

The model must call at least one tool before responding. Useful when you always need a function call.

### Specific Tool - Force Specific Function

```json theme={null}
{
  "tool_choice": {
    "type": "function",
    "name": "get_weather"
  }
}
```

The model must call the specified tool. Use this when you know exactly which function should be called.

### Allowed Tools - Limit to Subset

```json theme={null}
{
  "tool_choice": {
    "type": "allowed_tools",
    "mode": "auto",
    "tools": [
      { "type": "function", "name": "get_weather" },
      { "type": "function", "name": "get_forecast" }
    ]
  }
}
```

Restrict the model to only use specific tools from your full tool list. The `mode` can be:

* `"auto"` - Model can use these tools or skip them
* `"required"` - Model must use at least one of these tools

## Parallel Tool Calls

Enable the model to call multiple tools simultaneously:

```json theme={null}
{
  "parallel_tool_calls": true
}
```

**When to enable:**

* Multiple independent operations (e.g., get weather for multiple cities)
* No dependencies between tool calls
* Want faster results through parallelization

**When to disable:**

* Sequential operations where order matters
* Tool calls depend on each other's results
* Want more predictable, step-by-step execution

**Example of parallel tool calls:**

```python theme={null}
# User asks: "What's the weather in SF, NYC, and London?"
# Model can call get_weather three times in parallel:
{
  "output": [
    {"type": "function_call", "call_id": "call_1", "name": "get_weather", "arguments": '{"location": "San Francisco, CA"}'},
    {"type": "function_call", "call_id": "call_2", "name": "get_weather", "arguments": '{"location": "New York, NY"}'},
    {"type": "function_call", "call_id": "call_3", "name": "get_weather", "arguments": '{"location": "London, UK"}'}
  ]
}
```

## Multi-Turn Workflow

Tool calling typically follows this pattern:

1. **User sends a message** with tools available
2. **Model responds with function\_call** indicating it wants to use a tool
3. **You execute the function** in your application
4. **You send the result back** with `function_call_output`
5. **Model provides final response** using the tool result

Here's a complete example:

<CodeGroup>
  ```python Python - Complete Workflow theme={null}
  import requests
  import json

  API_URL = "https://api.concentrate.ai/v1/responses"
  HEADERS = {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
  }

  # Define your tools
  tools = [{
      "type": "function",
      "name": "get_weather",
      "description": "Get current weather for a location",
      "parameters": {
          "type": "object",
          "properties": {
              "location": {"type": "string"},
              "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
          },
          "required": ["location"]
      }
  }]

  # Step 1: Initial request
  response1 = requests.post(API_URL, headers=HEADERS, json={
      "model": "gpt-5.2",
      "input": "What's the weather like in San Francisco?",
      "tools": tools
  })

  data1 = response1.json()
  print("Step 1 - Model response:", data1)

  # Step 2: Check if model wants to call a tool
  function_call = None
  for item in data1["output"]:
      if item["type"] == "function_call":
          function_call = item
          break

  if function_call:
      # Step 3: Execute the function
      args = json.loads(function_call["arguments"])

      # Your actual function implementation
      def get_weather(location, unit="fahrenheit"):
          # In reality, you'd call a weather API
          return {
              "location": location,
              "temperature": 72,
              "unit": unit,
              "conditions": "Sunny"
          }

      result = get_weather(**args)
      print("Step 2 - Function result:", result)

      # Step 4: Send result back to model
      response2 = requests.post(API_URL, headers=HEADERS, json={
          "model": "gpt-5.2",
          "input": [
              {"role": "user", "content": "What's the weather like in San Francisco?"},
              function_call,  # Include the function call
              {
                  "type": "function_call_output",
                  "call_id": function_call["call_id"],
                  "output": json.dumps(result)
              }
          ],
          "tools": tools
      })

      data2 = response2.json()
      print("Step 3 - Final response:", data2)

      # Extract the assistant's final message
      for item in data2["output"]:
          if item["type"] == "message" and item["role"] == "assistant":
              print("\nAssistant:", item["content"][0]["text"])
  ```

  ```typescript TypeScript - Complete Workflow theme={null}
  interface FunctionCall {
    type: "function_call";
    call_id: string;
    name: string;
    arguments: string;
  }

  const API_URL = "https://api.concentrate.ai/v1/responses";
  const HEADERS = {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json"
  };

  const tools = [{
    type: "function",
    name: "get_weather",
    description: "Get current weather for a location",
    parameters: {
      type: "object",
      properties: {
        location: { type: "string" },
        unit: { type: "string", enum: ["celsius", "fahrenheit"] }
      },
      required: ["location"]
    }
  }];

  // Step 1: Initial request
  const response1 = await fetch(API_URL, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      model: "gpt-5.2",
      input: "What's the weather like in San Francisco?",
      tools
    })
  });

  const data1 = await response1.json();
  console.log("Step 1 - Model response:", data1);

  // Step 2: Check if model wants to call a tool
  const functionCall = data1.output.find(
    (item: any) => item.type === "function_call"
  ) as FunctionCall | undefined;

  if (functionCall) {
    // Step 3: Execute the function
    const args = JSON.parse(functionCall.arguments);

    // Your actual function implementation
    function getWeather(location: string, unit = "fahrenheit") {
      // In reality, you'd call a weather API
      return {
        location,
        temperature: 72,
        unit,
        conditions: "Sunny"
      };
    }

    const result = getWeather(args.location, args.unit);
    console.log("Step 2 - Function result:", result);

    // Step 4: Send result back to model
    const response2 = await fetch(API_URL, {
      method: "POST",
      headers: HEADERS,
      body: JSON.stringify({
        model: "gpt-5.2",
        input: [
          { role: "user", content: "What's the weather like in San Francisco?" },
          functionCall,  // Include the function call
          {
            type: "function_call_output",
            call_id: functionCall.call_id,
            output: JSON.stringify(result)
          }
        ],
        tools
      })
    });

    const data2 = await response2.json();
    console.log("Step 3 - Final response:", data2);

    // Extract the assistant's final message
    const assistantMessage = data2.output.find(
      (item: any) => item.type === "message" && item.role === "assistant"
    );
    console.log("\nAssistant:", assistantMessage.content[0].text);
  }
  ```
</CodeGroup>

## Streaming with Tools

When streaming is enabled, tool calls are delivered incrementally:

### Event Types

* `response.function_call_arguments.delta` - Incremental tool arguments as they're generated
* `response.function_call_arguments.done` - Complete tool call with final arguments

### Example

```typescript TypeScript - Streaming Tool Calls theme={null}
const response = await fetch("https://api.concentrate.ai/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "gpt-5.2",
    input: "What's the weather in San Francisco?",
    tools: [{
      type: "function",
      name: "get_weather",
      description: "Get current weather",
      parameters: {
        type: "object",
        properties: {
          location: { type: "string" }
        },
        required: ["location"]
      }
    }],
    stream: true
  })
});

const reader = response.body!.getReader();
const decoder = new TextDecoder();

let functionCallBuffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value);
  const lines = chunk.split("\n").filter(line => line.trim());

  for (const line of lines) {
    if (line.startsWith("data: ")) {
      const data = JSON.parse(line.slice(6));

      if (data.event === "response.function_call_arguments.delta") {
        functionCallBuffer += data.data.arguments;
        console.log("Building arguments:", functionCallBuffer);
      }

      if (data.event === "response.function_call_arguments.done") {
        console.log("Complete function call:", data.data);
        // Execute your function here
      }
    }
  }
}
```

See [Streaming](/docs/api-reference/endpoint/streaming) for more details on streaming events.

## Advanced Patterns

### Error Handling

When a tool execution fails, use the `is_error` flag:

```python theme={null}
{
  "type": "function_call_output",
  "call_id": "call_abc123",
  "output": json.dumps({"error": "Weather API unavailable"}),
  "is_error": True
}
```

The model will receive the error and can respond appropriately (e.g., apologizing or suggesting alternatives).

### Caching Tool Definitions

For frequently used tools, cache their definitions:

```json theme={null}
{
  "tools": [
    {
      "type": "function",
      "name": "get_weather",
      "description": "Get current weather for a location",
      "parameters": { ... },
      "cache_control": {
        "type": "ephemeral",
        "ttl": "1h"
      }
    }
  ]
}
```

This reduces costs and improves latency for repeated requests with the same tools.

## Provider Support

Tool calling is supported across all major providers:

| Provider         | Supported | Notes                                 |
| ---------------- | --------- | ------------------------------------- |
| OpenAI           | ✅ Yes     | Full support including parallel calls |
| Anthropic        | ✅ Yes     | Full support with Claude models       |
| Azure OpenAI     | ✅ Yes     | Same as OpenAI                        |
| AWS Bedrock      | ✅ Yes     | Supported on compatible models        |
| Google Vertex AI | ✅ Yes     | Supported on Gemini models            |
| Cohere           | ✅ Yes     | Supported on Command models           |
| Mistral          | ✅ Yes     | Supported on recent models            |

Check specific model capabilities using the [List Models](/docs/api-reference/endpoint/list-models) endpoint.

## Best Practices

### Schema Design

1. **Keep it simple** - Only include necessary parameters
2. **Clear descriptions** - Help the model understand what each field does
3. **Use enums** - For fields with fixed options
4. **Validate inputs** - Always validate tool arguments before execution
5. **Handle errors gracefully** - Return meaningful error messages

### Security Considerations

1. **Validate all inputs** - Never trust model-generated arguments blindly
2. **Limit permissions** - Tools should have minimal necessary permissions
3. **Rate limiting** - Implement rate limits on expensive operations
4. **Audit logs** - Log all tool executions for monitoring
5. **Sanitize outputs** - Clean tool results before sending back to model

### Performance Optimization

1. **Cache definitions** - Use `cache_control` for frequently used tools
2. **Parallel calls** - Enable when tools are independent
3. **Lazy loading** - Only load tools when needed
4. **Timeout handling** - Set reasonable timeouts for tool execution
5. **Batch operations** - Combine multiple related calls when possible

## Troubleshooting

### Model doesn't call the tool

**Problem:** Model responds with text instead of calling the tool.

**Solutions:**

* Make the tool description clearer and more specific
* Use `tool_choice: "required"` to force tool usage
* Ensure the user's input clearly requires the tool's functionality
* Check if the model supports tool calling

### Invalid arguments

**Problem:** Model provides incorrect or incomplete arguments.

**Solutions:**

* Add detailed descriptions to parameter fields
* Use `strict: true` to enforce schema validation
* Include examples in parameter descriptions
* Mark essential fields as `required`

### Tool not found error

**Problem:** `"Tool 'xyz' not found"` error.

**Solutions:**

* Verify tool name matches exactly (case-sensitive)
* Ensure tool name follows the pattern: `^[a-zA-Z0-9_.-]+$`
* Check that tools array is included in the request

### Streaming issues

**Problem:** Tool calls not appearing in streaming mode.

**Solutions:**

* Listen for `response.function_call_arguments.delta` and `.done` events
* Buffer the arguments until the `.done` event
* Check that `stream: true` is set in the request

***

## Related Pages

<CardGroup cols={2}>
  <Card title="Create Response" icon="code" href="/docs/api-reference/endpoint/create-response">
    Main API endpoint for generating responses
  </Card>

  <Card title="Request Parameters" icon="sliders" href="/docs/api-reference/endpoint/request-parameters">
    Complete parameter reference
  </Card>

  <Card title="Streaming" icon="water" href="/docs/api-reference/endpoint/streaming">
    Real-time response streaming
  </Card>

  <Card title="Prompt Caching" icon="database" href="/docs/api-reference/endpoint/prompt-caching">
    Cache tool definitions for better performance
  </Card>
</CardGroup>
