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

# Chamada de ferramentas e execução de funções

> Permita que LLMs chamem funções externas com parâmetros estruturados. Crie agentes de IA que interajam com APIs, bancos de dados e serviços externos.

**Casos de uso**

* Conectar modelos a dados em tempo real (bancos de dados, calendários, APIs internas) sem gambiarras no prompt.
* Agentes que executam ações em nome dos usuários: criar tickets, enviar e-mails, executar consultas.
* Fluxos de várias etapas em que o modelo decide quais ferramentas invocar e em que ordem.
* Substituir a análise frágil com regex por chamadas de função estruturadas na extração de dados.

***

## Início rápido

Permita que modelos de IA chamem funções externas com parâmetros estruturados.

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://router.toqan.ai/eu/v3/router/responses \
  -H "Authorization: Bearer $TOQAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4",
    "input": "What is the weather in NYC?",
    "tools": [{
      "type": "function",
      "name": "get_weather",
      "description": "Get current weather for a location",
      "parameters": {
        "type": "object",
        "properties": {
          "location": { "type": "string", "description": "City and state" },
          "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
        },
        "required": ["location"]
      }
    }],
    "tool_choice": "auto"
  }'
```

```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.TOQAN_API_KEY,
  baseURL: "https://router.toqan.ai/eu/v3/router",
});

async function getWeather(location: string, unit = "celsius") {
  // Your implementation here - fetch from a weather API, etc.
  return { location, temperature: 22, unit, conditions: "sunny" };
}

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

const response = await client.responses.create({
  model: "openai/gpt-5.4",
  input: "What's the weather in NYC?",
  tools,
  tool_choice: "auto",
});

const toolCall = response.output.find((item) => item.type === "function_call");
if (toolCall && toolCall.type === "function_call") {
  const args = JSON.parse(toolCall.arguments);
  const result = await getWeather(args.location, args.unit);
  const callId = toolCall.call_id;

  // Pattern: previous_response_id - the router maintains conversation state server-side
  const finalResponse = await client.responses.create({
    model: "openai/gpt-5.4",
    previous_response_id: response.id,
    input: [{
      type: "function_call_output",
      call_id: callId,
      output: JSON.stringify(result),
    }],
  });

  console.log(finalResponse.output_text);
}
```

```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from openai import OpenAI
import json
import os

client = OpenAI(
    api_key=os.environ.get("TOQAN_API_KEY"),
    base_url="https://router.toqan.ai/eu/v3/router",
)

def get_weather(location: str, unit: str = "celsius") -> dict:
    # Your implementation here - fetch from a weather API, etc.
    return {"location": location, "temperature": 22, "unit": unit, "conditions": "sunny"}

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

response = client.responses.create(
    model="openai/gpt-5.4",
    input="What's the weather in NYC?",
    tools=tools,
    tool_choice="auto",
)

tool_call = next((item for item in response.output if item.type == "function_call"), None)
if tool_call:
    args = json.loads(tool_call.arguments)
    result = get_weather(args["location"], args.get("unit", "celsius"))

    # Pattern: previous_response_id - the router maintains conversation state server-side
    final_response = client.responses.create(
        model="openai/gpt-5.4",
        previous_response_id=response.id,
        input=[{
            "type": "function_call_output",
            "call_id": tool_call.call_id,
            "output": json.dumps(result),
        }],
    )

    print(final_response.output_text)
```

```bash cURL (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://router.toqan.ai/eu/v3/router/chat/completions \
  -H "Authorization: Bearer $TOQAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4",
    "messages": [{"role": "user", "content": "What is the weather in NYC?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get current weather for a location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": { "type": "string", "description": "City and state" },
            "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
          },
          "required": ["location"]
        }
      }
    }],
    "tool_choice": "auto"
  }'
```

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.TOQAN_API_KEY,
  baseURL: "https://router.toqan.ai/eu/v3/router",
});

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

async function getWeather(location: string, unit = "celsius") {
  // Your implementation here - fetch from a weather API, etc.
  return { location, temperature: 22, unit, conditions: "sunny" };
}

const response = await client.chat.completions.create({
  model: "openai/gpt-5.4",
  messages: [{ role: "user", content: "What's the weather in NYC?" }],
  tools,
  tool_choice: "auto",
});

if (response.choices[0].message.tool_calls) {
  const toolCall = response.choices[0].message.tool_calls[0];
  const args = JSON.parse(toolCall.function.arguments);
  const result = await getWeather(args.location, args.unit);

  const finalResponse = await client.chat.completions.create({
    model: "openai/gpt-5.4",
    messages: [
      { role: "user", content: "What's the weather in NYC?" },
      response.choices[0].message,
      {
        role: "tool",
        tool_call_id: toolCall.id,
        content: JSON.stringify(result),
      },
    ],
  });

  console.log(finalResponse.choices[0].message.content);
}
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
from openai import OpenAI
import json
import os

client = OpenAI(
    api_key=os.environ.get("TOQAN_API_KEY"),
    base_url="https://router.toqan.ai/eu/v3/router",
)

def get_weather(location: str, unit: str = "celsius") -> dict:
    # Your implementation here - fetch from a weather API, etc.
    return {"location": location, "temperature": 22, "unit": unit, "conditions": "sunny"}

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

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

response = client.chat.completions.create(
    model="openai/gpt-5.4",
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

if response.choices[0].message.tool_calls:
    tool_call = response.choices[0].message.tool_calls[0]
    args = json.loads(tool_call.function.arguments)
    result = get_weather(args["location"], args.get("unit", "celsius"))

    messages.append(response.choices[0].message)
    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": json.dumps(result),
    })

    final_response = client.chat.completions.create(
        model="openai/gpt-5.4",
        messages=messages,
    )

    print(final_response.choices[0].message.content)
```

## Configuração

### Definição da ferramenta

**Responses API** (`/v3/router/responses`): formato plano:

| Parâmetro | Tipo | Obrigatório | Descrição |
| - | - | - | - |
| `type` | `"function"` | Sim | Tipo da ferramenta |
| `name` | string | Sim | Identificador da função |
| `description` | string | Sim | O que a função faz |
| `parameters` | object | Sim | JSON Schema dos parâmetros |

**Chat Completions** (`/v3/router/chat/completions`): wrapper `function` aninhado:

| Parâmetro | Tipo | Obrigatório | Descrição |
| - | - | - | - |
| `type` | `"function"` | Sim | Tipo da ferramenta |
| `function.name` | string | Sim | Identificador da função |
| `function.description` | string | Sim | O que a função faz |
| `function.parameters` | object | Sim | JSON Schema dos parâmetros |

### Opções de escolha de ferramenta

| Valor | Comportamento |
| - | - |
| `"auto"` | O modelo decide quando usar ferramentas |
| `"none"` | Desativa o uso de ferramentas |
| `"required"` | Força o uso de ferramentas |
| `{type: "function", function: {name: "tool_name"}}` | Força uma ferramenta específica |

### Formato da mensagem de ferramenta

> [!NOTE]
>
> Este formato se aplica ao endpoint de Chat Completions (`/v3/router/chat/completions`). Na Responses API, os resultados das ferramentas usam `type: "function_call_output"`, `call_id` e `output`.

Ao devolver os resultados das ferramentas ao modelo, use a role `tool`:

| Parâmetro | Tipo | Obrigatório | Descrição |
| - | - | - | - |
| `role` | `"tool"` | Sim | Role da mensagem para resultados de ferramentas |
| `tool_call_id` | string \| null | Sim | ID da chamada de ferramenta que está sendo respondida |
| `content` | string | Sim | Resultado da execução da ferramenta serializado em JSON |

> [!NOTE]
>
> O `tool_call_id` pode ser `null` em determinados cenários, como quando os resultados das ferramentas são fornecidos sem uma chamada de ferramenta correspondente do modelo ou ao trabalhar com provedores que não exigem IDs de chamada de ferramenta.

## Exemplos de código

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://router.toqan.ai/eu/v3/router/responses \
  -H "Authorization: Bearer $TOQAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4",
    "input": "What is the weather like in San Francisco?",
    "tools": [
      {
        "type": "function",
        "name": "get_weather",
        "description": "Get the current weather for a location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The city and state, e.g. San Francisco, CA"
            },
            "unit": {
              "type": "string",
              "enum": ["celsius", "fahrenheit"],
              "description": "The temperature unit"
            }
          },
          "required": ["location"]
        }
      }
    ],
    "tool_choice": "auto"
  }'
```

```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.TOQAN_API_KEY,
  baseURL: "https://router.toqan.ai/eu/v3/router",
});

const tools = [
  {
    type: "function" as const,
    name: "get_weather",
    description: "Get the current weather for a location",
    parameters: {
      type: "object",
      properties: {
        location: {
          type: "string",
          description: "The city and state, e.g. San Francisco, CA",
        },
        unit: {
          type: "string",
          enum: ["celsius", "fahrenheit"],
          description: "The temperature unit",
        },
      },
      required: ["location"],
    },
  },
];

const response = await client.responses.create({
  model: "openai/gpt-5.4",
  input: "What's the weather like in San Francisco?",
  tools,
  tool_choice: "auto",
});

const toolCall = response.output.find((item) => item.type === "function_call");
if (toolCall && toolCall.type === "function_call") {
  const args = JSON.parse(toolCall.arguments);

  const weatherResult = {
    temperature: 72,
    unit: "fahrenheit",
    description: "Sunny with light clouds",
  };

  // Pattern: spread response.output - the full conversation history is sent client-side
  const finalResponse = await client.responses.create({
    model: "openai/gpt-5.4",
    input: [
      ...response.output,
      {
        type: "function_call_output",
        call_id: toolCall.call_id,
        output: JSON.stringify(weatherResult),
      },
    ],
  });

  console.log(finalResponse.output_text);
}
```

```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from openai import OpenAI
import json
import os

client = OpenAI(
    api_key=os.environ.get("TOQAN_API_KEY"),
    base_url="https://router.toqan.ai/eu/v3/router",
)

tools = [
    {
        "type": "function",
        "name": "get_weather",
        "description": "Get the current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA",
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "The temperature unit",
                },
            },
            "required": ["location"],
        },
    }
]

response = client.responses.create(
    model="openai/gpt-5.4",
    input="What's the weather like in San Francisco?",
    tools=tools,
    tool_choice="auto",
)

tool_call = next((item for item in response.output if item.type == "function_call"), None)
if tool_call:
    arguments = json.loads(tool_call.arguments)

    weather_result = {
        "temperature": 72,
        "unit": "fahrenheit",
        "description": "Sunny with light clouds",
    }

    final_response = client.responses.create(
        model="openai/gpt-5.4",
        input=[
            *response.output,
            {
                "type": "function_call_output",
                "call_id": tool_call.call_id,
                "output": json.dumps(weather_result),
            },
        ],
    )

    print(final_response.output_text)
```

```bash cURL (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://router.toqan.ai/eu/v3/router/chat/completions \
  -H "Authorization: Bearer $TOQAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4",
    "messages": [
      {
        "role": "user",
        "content": "What is the weather like in San Francisco?"
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "Get the current weather for a location",
          "parameters": {
            "type": "object",
            "properties": {
              "location": {
                "type": "string",
                "description": "The city and state, e.g. San Francisco, CA"
              },
              "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "The temperature unit"
              }
            },
            "required": ["location"]
          }
        }
      }
    ],
    "tool_choice": "auto"
  }'
```

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.TOQAN_API_KEY,
  baseURL: "https://router.toqan.ai/eu/v3/router",
});

const tools = [
  {
    type: "function" as const,
    function: {
      name: "get_weather",
      description: "Get the current weather for a location",
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "The city and state, e.g. San Francisco, CA",
          },
          unit: {
            type: "string",
            enum: ["celsius", "fahrenheit"],
            description: "The temperature unit",
          },
        },
        required: ["location"],
      },
    },
  },
];

const messages = [
  { role: "user" as const, content: "What's the weather like in San Francisco?" },
];

const response = await client.chat.completions.create({
  model: "openai/gpt-5.4",
  messages,
  tools,
  tool_choice: "auto",
});

if (response.choices[0].message.tool_calls) {
  const toolCall = response.choices[0].message.tool_calls[0];
  const args = JSON.parse(toolCall.function.arguments);

  const weatherResult = {
    temperature: 72,
    unit: "fahrenheit",
    description: "Sunny with light clouds",
  };

  messages.push(response.choices[0].message);
  messages.push({
    role: "tool" as const,
    tool_call_id: toolCall.id,
    content: JSON.stringify(weatherResult),
  });

  const finalResponse = await client.chat.completions.create({
    model: "openai/gpt-5.4",
    messages,
  });

  console.log(finalResponse.choices[0].message.content);
}
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
from openai import OpenAI
import json
import os

client = OpenAI(
    api_key=os.environ.get("TOQAN_API_KEY"),
    base_url="https://router.toqan.ai/eu/v3/router",
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA",
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "The temperature unit",
                    },
                },
                "required": ["location"],
            },
        },
    }
]

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

response = client.chat.completions.create(
    model="openai/gpt-5.4",
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

if response.choices[0].message.tool_calls:
    tool_call = response.choices[0].message.tool_calls[0]
    arguments = json.loads(tool_call.function.arguments)

    weather_result = {
        "temperature": 72,
        "unit": "fahrenheit",
        "description": "Sunny with light clouds",
    }

    messages.append(response.choices[0].message)
    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": json.dumps(weather_result),
    })

    final_response = client.chat.completions.create(
        model="openai/gpt-5.4",
        messages=messages,
    )

    print(final_response.choices[0].message.content)
```

## Padrões de execução de funções

### Handler básico de ferramentas

Um registro que mapeia nomes de ferramentas para funções handler, substituindo instruções switch por chamada por um único caminho de despacho.

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
class ToolHandler {
  constructor() {
    this.tools = new Map();
  }

  register(name, func, schema) {
    this.tools.set(name, { func, schema });
  }

  async execute(toolCall) {
    const tool = this.tools.get(toolCall.function.name);
    if (!tool) throw new Error(`Unknown tool: ${toolCall.function.name}`);

    const args = JSON.parse(toolCall.function.arguments);
    const result = await tool.func(args);

    return {
      role: "tool",
      tool_call_id: toolCall.id,
      content: JSON.stringify(result),
    };
  }
}

// Stubs - replace with your actual implementations
const weatherSchema = { type: "object", properties: { location: { type: "string" } }, required: ["location"] };
const searchSchema = { type: "object", properties: { query: { type: "string" } }, required: ["query"] };
const getWeatherAPI = async ({ location }) => ({ temperature: 72, condition: "sunny", location });
const searchWebAPI = async ({ query }) => ({ results: [`Result for: ${query}`] });
const response = { choices: [{ message: { tool_calls: [] } }] };

const handler = new ToolHandler();
handler.register("get_weather", getWeatherAPI, weatherSchema);
handler.register("search_web", searchWebAPI, searchSchema);

// Execute tool calls
const toolResults = await Promise.all(
  response.choices[0].message.tool_calls.map((call) => handler.execute(call)),
);
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import json
import asyncio

async def get_weather_api(args: dict) -> dict: return {"temperature": 72, "condition": "sunny", "location": args["location"]}  # replace with your weather API
async def search_web_api(args: dict) -> dict: return {"results": [f"Result for: {args['query']}"]}  # replace with your search API

class ToolHandler:
    def __init__(self):
        self._tools = {}

    def register(self, name: str, func):
        self._tools[name] = func

    async def execute(self, tool_call) -> dict:
        func = self._tools.get(tool_call.function.name)
        if not func:
            raise ValueError(f"Unknown tool: {tool_call.function.name}")
        args = json.loads(tool_call.function.arguments)
        result = await func(args)
        return {"role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result)}

handler = ToolHandler()
handler.register("get_weather", get_weather_api)
handler.register("search_web", search_web_api)

async def execute_all_tools(message):
    return await asyncio.gather(*[handler.execute(call) for call in message.tool_calls])
```

### Execução paralela de ferramentas

Quando o modelo retornar várias chamadas de ferramentas em um único turno, execute-as simultaneamente para reduzir a latência.

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
async function getWeatherAsync(args: Record<string, unknown>) { return { temperature: 72, condition: "sunny" }; } // replace with your weather API
async function searchProductsAsync(args: Record<string, unknown>) { return { products: [] as unknown[] }; } // replace with your product search
async function checkInventoryAsync(args: Record<string, unknown>) { return { inStock: true }; } // replace with your inventory API

// Replace with your actual response and messages array
const response = { choices: [{ message: { role: "assistant" as const, content: null as string | null, tool_calls: [] as Array<{ function: { name: string; arguments: string }; id: string }> } }] }; // e.g., client.chat.completions.create(...)
let messages: Array<{ role: string; tool_call_id?: string; content: string | null }> = [];

const dispatch: Record<string, (args: Record<string, unknown>) => Promise<unknown>> = {
  get_weather: getWeatherAsync,
  search_products: searchProductsAsync,
  check_inventory: checkInventoryAsync,
};

async function executeToolsParallel(
  toolCalls: Array<{ function: { name: string; arguments: string }; id: string }>,
) {
  return Promise.all(
    toolCalls.map(async (call) => {
      const args = JSON.parse(call.function.arguments) as Record<string, unknown>;
      const fn = dispatch[call.function.name] ?? (() => Promise.resolve({ error: `Unknown: ${call.function.name}` }));
      const result = await fn(args);
      return { role: "tool" as const, tool_call_id: call.id, content: JSON.stringify(result) };
    }),
  );
}

if (response.choices[0].message.tool_calls) {
  messages.push(response.choices[0].message);
  const toolResults = await executeToolsParallel(response.choices[0].message.tool_calls);
  messages = [...messages, ...toolResults];
}
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import asyncio
import json

async def get_weather_async(args): return {"temperature": 72, "condition": "sunny"}  # replace with your weather API
async def search_products_async(args): return {"products": []}  # replace with your product search
async def check_inventory_async(args): return {"in_stock": True}  # replace with your inventory API

# Replace these with your actual API call and conversation history
response = None  # e.g. client.chat.completions.create(model=..., messages=messages, tools=[...])
messages: list = []

async def execute_tools_parallel(tool_calls):
    async def execute_single_tool(tool_call):
        function_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)

        # Route to appropriate function
        if function_name == "get_weather":
            result = await get_weather_async(arguments)
        elif function_name == "search_products":
            result = await search_products_async(arguments)
        elif function_name == "check_inventory":
            result = await check_inventory_async(arguments)
        else:
            result = {"error": f"Unknown function: {function_name}"}

        return {
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps(result)
        }

    # Execute all tools concurrently
    results = await asyncio.gather(
        *[execute_single_tool(call) for call in tool_calls]
    )

    return results

# Usage
if response.choices[0].message.tool_calls:
    messages.append(response.choices[0].message)
    tool_results = await execute_tools_parallel(
        response.choices[0].message.tool_calls
    )

    # Add to conversation
    messages.extend(tool_results)
```

## Casos de uso avançados

### Integração com banco de dados

Exponha o acesso SQL como uma ferramenta para que o modelo possa consultar dados diretamente. Sempre sanitize as consultas antes da execução.

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
const sanitizeSql = (q: string) => q; // replace with your SQL sanitizer
const getDbConnection = () => null as unknown as { execute: (q: string) => Promise<{ fetchAll: () => unknown[] }> }; // replace with your DB client

const db = getDbConnection();

const tools = [
  {
    type: "function" as const,
    function: {
      name: "query_database",
      description: "Query the customer database",
      parameters: {
        type: "object",
        properties: {
          query: { type: "string", description: "SQL query to execute" },
          limit: { type: "integer", description: "Maximum number of results" },
        },
        required: ["query"],
      },
    },
  },
];

async function queryDatabase(args: { query: string; limit?: number }) {
  const query = sanitizeSql(args.query);
  const limit = args.limit ?? 10;
  const results = await db.execute(`${query} LIMIT ${limit}`); // limit is integer-typed in the schema, safe to interpolate directly
  return { results: results.fetchAll() };
}
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
sanitize_sql = lambda q: q  # replace with your SQL sanitizer

def get_db_connection():
    pass  # replace with your database client, e.g. from myapp.db import get_db_connection

db = get_db_connection()

tools = [
    {
        "type": "function",
        "function": {
            "name": "query_database",
            "description": "Query the customer database",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "SQL query to execute"
                    },
                    "limit": {
                        "type": "integer",
                        "description": "Maximum number of results"
                    }
                },
                "required": ["query"]
            }
        }
    }
]

async def query_database(args):
    query = sanitize_sql(args["query"])
    limit = args.get("limit", 10)

    results = await db.execute(f"{query} LIMIT {limit}")
    return {"results": results.fetchall()}
```

### Integração com API

Exponha ações de serviços externos (e-mail, calendário, notificações) como ferramentas que o modelo pode invocar durante uma conversa.

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
const apiTools = [
  {
    type: "function",
    function: {
      name: "send_email",
      description: "Send an email to a recipient",
      parameters: {
        type: "object",
        properties: {
          to: { type: "string", description: "Email address" },
          subject: { type: "string", description: "Email subject" },
          body: { type: "string", description: "Email content" },
        },
        required: ["to", "subject", "body"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "create_calendar_event",
      description: "Create a calendar event",
      parameters: {
        type: "object",
        properties: {
          title: { type: "string" },
          start_time: { type: "string", format: "date-time" },
          duration: { type: "integer", description: "Duration in minutes" },
          attendees: { type: "array", items: { type: "string" } },
        },
        required: ["title", "start_time"],
      },
    },
  },
];

const emailAPI = { send: async (args: Record<string, unknown>) => ({ messageId: "msg_001" }) }; // replace with your email client
const calendarAPI = { createEvent: async (args: Record<string, unknown>) => ({ eventId: "evt_001" }) }; // replace with your calendar client

const executeApiTool = async (toolCall) => {
  const { name } = toolCall.function;
  const args = JSON.parse(toolCall.function.arguments);

  switch (name) {
    case "send_email":
      return await emailAPI.send(args);
    case "create_calendar_event":
      return await calendarAPI.createEvent(args);
    default:
      throw new Error(`Unknown API tool: ${name}`);
  }
};
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import json

# Replace with your actual API clients
class EmailAPI:
    async def send(self, args: dict) -> dict: return {"message_id": "msg_001"}

class CalendarAPI:
    async def create_event(self, args: dict) -> dict: return {"event_id": "evt_001"}

email_api = EmailAPI()
calendar_api = CalendarAPI()

api_tools = [
    {
        "type": "function",
        "function": {
            "name": "send_email",
            "description": "Send an email to a recipient",
            "parameters": {
                "type": "object",
                "properties": {
                    "to": {"type": "string", "description": "Email address"},
                    "subject": {"type": "string", "description": "Email subject"},
                    "body": {"type": "string", "description": "Email content"},
                },
                "required": ["to", "subject", "body"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "create_calendar_event",
            "description": "Create a calendar event",
            "parameters": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "start_time": {"type": "string", "format": "date-time"},
                    "duration": {"type": "integer", "description": "Duration in minutes"},
                    "attendees": {"type": "array", "items": {"type": "string"}},
                },
                "required": ["title", "start_time"],
            },
        },
    },
]

async def execute_api_tool(tool_call) -> dict:
    args = json.loads(tool_call.function.arguments)
    match tool_call.function.name:
        case "send_email":
            return await email_api.send(args)
        case "create_calendar_event":
            return await calendar_api.create_event(args)
        case _:
            raise ValueError(f"Unknown API tool: {tool_call.function.name}")
```

### Fluxos de várias etapas

Execute o modelo em loop até que ele produza uma resposta final em texto, permitindo um comportamento de agente em várias etapas sem streaming.

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { OpenAI } from "openai";
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";

const client = new OpenAI({
  apiKey: process.env.TOQAN_API_KEY,
  baseURL: "https://router.toqan.ai/eu/v3/router",
});

class WorkflowEngine {
  private tools: Record<string, { func: (args: Record<string, unknown>) => Promise<unknown>; schema: ChatCompletionTool }> = {};

  register(name: string, func: (args: Record<string, unknown>) => Promise<unknown>, schema: ChatCompletionTool) {
    this.tools[name] = { func, schema };
  }

  async executeWorkflow(initialPrompt: string, maxSteps = 10): Promise<string> {
    const conversation: ChatCompletionMessageParam[] = [{ role: "user", content: initialPrompt }];

    for (let step = 0; step < maxSteps; step++) {
      const response = await client.chat.completions.create({
        model: "openai/gpt-5.4",
        messages: conversation,
        tools: Object.values(this.tools).map((t) => t.schema),
        tool_choice: "auto",
      });

      conversation.push(response.choices[0].message);

      if (!response.choices[0].message.tool_calls) {
        return response.choices[0].message.content ?? "";
      }

      for (const call of response.choices[0].message.tool_calls) {
        conversation.push(await this.executeTool(call));
      }
    }
    return "Workflow exceeded maximum steps";
  }

  private async executeTool(toolCall: { function: { name: string; arguments: string }; id: string }): Promise<ChatCompletionMessageParam> {
    const args = JSON.parse(toolCall.function.arguments) as Record<string, unknown>;
    const tool = this.tools[toolCall.function.name];
    const result = tool ? await tool.func(args) : { error: `Unknown tool: ${toolCall.function.name}` };
    return { role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result) };
  }
}
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
from openai import AsyncOpenAI
import os
import json

client = AsyncOpenAI(
    api_key=os.environ.get("TOQAN_API_KEY"),
    base_url="https://router.toqan.ai/eu/v3/router",
)

class WorkflowEngine:
    def __init__(self):
        self.tools = {}
        self.conversation = []

    def register_tool(self, name, func, schema):
        self.tools[name] = {"func": func, "schema": schema}

    async def execute_workflow(self, initial_prompt, max_steps=10):
        self.conversation = [{"role": "user", "content": initial_prompt}]

        for step in range(max_steps):
            response = await client.chat.completions.create(
                model="openai/gpt-5.4",
                messages=self.conversation,
                tools=[v["schema"] for v in self.tools.values()],
                tool_choice="auto"
            )

            self.conversation.append(response.choices[0].message)

            # Check if tools need to be executed
            if response.choices[0].message.tool_calls:
                for tool_call in response.choices[0].message.tool_calls:
                    result = await self.execute_tool(tool_call)
                    self.conversation.append(result)
            else:
                # No tools called, workflow complete
                return response.choices[0].message.content

        return "Workflow exceeded maximum steps"

    async def execute_tool(self, tool_call):
        tool_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)

        if tool_name in self.tools:
            result = await self.tools[tool_name]["func"](arguments)
        else:
            result = {"error": f"Unknown tool: {tool_name}"}

        return {
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps(result)
        }
```

## Tratamento de erros

Retorne objetos de erro estruturados nas saídas das ferramentas para que o modelo possa relatar falhas ou tentar novamente com argumentos corrigidos.

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
const validateArgs = (name: string, args: Record<string, unknown>) => ({ valid: true, errors: [] as string[] }); // replace with your validator
const executeFunction = async (name: string, args: Record<string, unknown>): Promise<unknown> => ({}); // replace with your dispatcher

const safeToolExecution = async (toolCall) => {
  try {
    const args = JSON.parse(toolCall.function.arguments);

    // Validate arguments
    const validation = validateArgs(toolCall.function.name, args);
    if (!validation.valid) {
      return {
        role: "tool",
        tool_call_id: toolCall.id,
        content: JSON.stringify({
          error: "Invalid arguments",
          details: validation.errors,
        }),
      };
    }

    // Execute with timeout
    const result = await Promise.race([
      executeFunction(toolCall.function.name, args),
      new Promise((_, reject) =>
        setTimeout(() => reject(new Error("Tool execution timeout")), 30000),
      ),
    ]);

    return {
      role: "tool",
      tool_call_id: toolCall.id,
      content: JSON.stringify(result),
    };
  } catch (error) {
    console.error(`Tool execution failed: ${error.message}`);

    return {
      role: "tool",
      tool_call_id: toolCall.id,
      content: JSON.stringify({
        error: "Tool execution failed",
        message: error.message,
      }),
    };
  }
};
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import json
import asyncio

def validate_args(name: str, args: dict) -> dict: return {"valid": True, "errors": []}  # replace with your validator
async def execute_function(name: str, args: dict): return {}  # replace with your dispatcher

async def safe_tool_execution(tool_call) -> dict:
    try:
        args = json.loads(tool_call.function.arguments)

        validation = validate_args(tool_call.function.name, args)
        if not validation["valid"]:
            return {
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps({"error": "Invalid arguments", "details": validation["errors"]}),
            }

        result = await asyncio.wait_for(
            execute_function(tool_call.function.name, args),
            timeout=30,
        )
        return {"role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result)}
    except Exception as e:
        return {
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps({"error": "Tool execution failed", "message": str(e)}),
        }
```

## Boas práticas

### Design de ferramentas

* Use nomes de função claros e descritivos.
* Forneça descrições detalhadas dos parâmetros.
* Inclua exemplos nas descrições.
* Torne as ferramentas idempotentes sempre que possível.

### Design de schemas

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "object",
  "properties": {
    "location": {
      "type": "string",
      "description": "City and state (e.g., 'San Francisco, CA')",
      "examples": ["New York, NY", "London, UK"]
    },
    "units": {
      "type": "string",
      "enum": ["metric", "imperial"],
      "description": "Temperature unit system",
      "default": "metric"
    }
  },
  "required": ["location"]
}
```

### Considerações de segurança

* Nunca exponha operações destrutivas diretamente.
* Valide todas as entradas minuciosamente.
* Use listas de permissões para operações sensíveis.
* Implemente uma autenticação adequada.
* Registre em log todas as execuções de ferramentas.

## Solução de problemas

**A ferramenta não está sendo chamada**

* Verifique se as descrições das ferramentas estão claras.
* Confirme se os schemas dos parâmetros estão corretos.
* Garanta que tool\_choice esteja definido adequadamente.
* Experimente prompts mais explícitos.

**Argumentos inválidos**

* Valide o JSON Schema minuciosamente.
* Adicione exemplos de parâmetros.
* Verifique se os campos obrigatórios estão marcados.
* Simplifique estruturas de parâmetros complexas.

**Falhas de execução**

* Implemente um tratamento de erros adequado.
* Adicione proteção por timeout.
* Valide as entradas antes da execução.
* Retorne mensagens de erro estruturadas.

## Limitações

| Limitação | Impacto | Alternativa |
| - | - | - |
| **Limite de ferramentas** | Máximo de \~20 ferramentas por solicitação | Agrupe funções relacionadas |
| **Tamanho dos parâmetros** | Schemas grandes podem falhar | Simplifique a estrutura dos parâmetros |
| **Tempo de execução** | As ferramentas bloqueiam a resposta | Use padrões assíncronos |
| **Propagação de erros** | Falhas podem interromper o fluxo | Implemente recuperação de erros |
| **Diferenças entre modelos** | Qualidade variável na chamada de ferramentas | Teste com vários modelos |

## Otimização de desempenho

### Cache de ferramentas

Armazene em cache os resultados de ferramentas somente leitura para evitar chamadas externas redundantes em uma sessão.

```typescript TypeScript (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
class CachedToolExecutor {
  constructor() {
    this.cache = new Map();
    this.cacheTTL = 300000; // 5 minutes
  }

  getCacheKey(toolCall) {
    return `${toolCall.function.name}:${toolCall.function.arguments}`;
  }

  async executeFunction(toolCall) {
    throw new Error("executeFunction must be implemented in a subclass");
  }

  async execute(toolCall) {
    const key = this.getCacheKey(toolCall);
    const cached = this.cache.get(key);

    if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
      return cached.result;
    }

    const result = await this.executeFunction(toolCall);
    this.cache.set(key, { result, timestamp: Date.now() });

    return result;
  }
}
```

```python Python (Chat Completions) theme={"theme":{"light":"github-light","dark":"github-dark"}}
import time
import json

class CachedToolExecutor:
    def __init__(self, cache_ttl: int = 300):
        self._cache: dict = {}
        self._cache_ttl = cache_ttl

    def _cache_key(self, tool_call) -> str:
        return f"{tool_call.function.name}:{tool_call.function.arguments}"

    async def execute_function(self, tool_call) -> dict:
        raise NotImplementedError("Implement in subclass")

    async def execute(self, tool_call) -> dict:
        key = self._cache_key(tool_call)
        entry = self._cache.get(key)
        if entry and (time.time() - entry["timestamp"]) < self._cache_ttl:
            return entry["result"]
        result = await self.execute_function(tool_call)
        self._cache[key] = {"result": result, "timestamp": time.time()}
        return result
```

### Operações em lote

Aceite arrays de entradas em uma única ferramenta para reduzir o número de turnos do modelo necessários em operações em massa.

```typescript TypeScript (Responses) theme={"theme":{"light":"github-light","dark":"github-dark"}}
const getWeather = (location: string) => ({ temperature: 72, condition: "sunny", location }); // replace with your weather API

const getWeatherBatch = (locations: string[]) =>
  Object.fromEntries(locations.map((loc) => [loc, getWeather(loc)]));

const getWeatherBatchTool = {
  type: "function" as const,
  name: "get_weather_batch",
  description: "Get weather for multiple locations in a single call",
  parameters: {
    type: "object",
    properties: {
      locations: { type: "array", items: { type: "string" }, description: "List of city names" },
    },
    required: ["locations"],
  },
};
```

```python Python (Responses) theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Pattern fragment: add client setup and get_weather stub before using
# Instead of multiple individual calls
def get_weather_batch(locations):
    return {loc: get_weather(loc) for loc in locations}

# Tool that accepts multiple inputs
get_weather_batch_tool = {
    "type": "function",
    "name": "get_weather_batch",
    "description": "Get weather for multiple locations in a single call",
    "parameters": {
        "type": "object",
        "properties": {
            "locations": {
                "type": "array",
                "items": {"type": "string"}
            }
        },
        "required": ["locations"]
    }
}
```


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