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

# Tool calling and function execution

> Enable LLMs to call external functions with structured parameters. Build AI agents that interact with APIs, databases, and external services.

**Use Cases**

* Connecting models to live data (databases, calendars, internal APIs) without prompt hacks.
* Agents that take actions on behalf of users: create tickets, send emails, run queries.
* Multi-step workflows where the model decides which tools to invoke and in what order.
* Replacing brittle regex parsing with structured function calls for data extraction.

***

## Quick Start

Enable AI models to call external functions with structured parameters.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://api.orq.ai/v3/router/responses \
    -H "Authorization: Bearer $ORQ_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.ORQ_API_KEY,
    baseURL: "https://api.orq.ai/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("ORQ_API_KEY"),
      base_url="https://api.orq.ai/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://api.orq.ai/v3/router/chat/completions \
    -H "Authorization: Bearer $ORQ_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.ORQ_API_KEY,
    baseURL: "https://api.orq.ai/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("ORQ_API_KEY"),
      base_url="https://api.orq.ai/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)
  ```
</CodeGroup>

## Configuration

### Tool Definition

**Responses API** (`/v3/router/responses`): flat shape:

| Parameter     | Type         | Required | Description                |
| ------------- | ------------ | -------- | -------------------------- |
| `type`        | `"function"` | Yes      | Tool type                  |
| `name`        | string       | Yes      | Function identifier        |
| `description` | string       | Yes      | What the function does     |
| `parameters`  | object       | Yes      | JSON Schema for parameters |

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

| Parameter              | Type         | Required | Description                |
| ---------------------- | ------------ | -------- | -------------------------- |
| `type`                 | `"function"` | Yes      | Tool type                  |
| `function.name`        | string       | Yes      | Function identifier        |
| `function.description` | string       | Yes      | What the function does     |
| `function.parameters`  | object       | Yes      | JSON Schema for parameters |

### Tool Choice Options

| Value                                               | Behavior                        |
| --------------------------------------------------- | ------------------------------- |
| `"auto"`                                            | Model decides when to use tools |
| `"none"`                                            | Disable tool usage              |
| `"required"`                                        | Force tool usage                |
| `{type: "function", function: {name: "tool_name"}}` | Force specific tool             |

### Tool Message Format

<Note>
  This format applies to the Chat Completions endpoint (`/v3/router/chat/completions`). On the Responses API, tool results use `type: "function_call_output"`, `call_id`, and `output` instead.
</Note>

When providing tool results back to the model, use the `tool` role:

| Parameter      | Type           | Required | Description                                   |
| -------------- | -------------- | -------- | --------------------------------------------- |
| `role`         | `"tool"`       | Yes      | Message role for tool results                 |
| `tool_call_id` | string \| null | Yes      | ID of the tool call being responded to        |
| `content`      | string         | Yes      | JSON-stringified result of the tool execution |

<Note>
  The `tool_call_id` can be `null` in certain scenarios, such as when tool results are being provided without a corresponding tool call from the model, or when working with providers that don't require tool call IDs.
</Note>

## Code examples

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://api.orq.ai/v3/router/responses \
    -H "Authorization: Bearer $ORQ_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.ORQ_API_KEY,
    baseURL: "https://api.orq.ai/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("ORQ_API_KEY"),
      base_url="https://api.orq.ai/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://api.orq.ai/v3/router/chat/completions \
    -H "Authorization: Bearer $ORQ_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.ORQ_API_KEY,
    baseURL: "https://api.orq.ai/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("ORQ_API_KEY"),
      base_url="https://api.orq.ai/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)
  ```
</CodeGroup>

## Function Execution Patterns

### Basic Tool Handler

A registry that maps tool names to handler functions, replacing per-call switch statements with a single dispatch path.

<CodeGroup>
  ```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])
  ```
</CodeGroup>

### Parallel Tool Execution

When the model returns multiple tool calls in one turn, execute them concurrently to reduce latency.

<CodeGroup>
  ```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)
  ```
</CodeGroup>

## Advanced Use Cases

### Database Integration

Expose SQL access as a tool so the model can query data directly. Always sanitize queries before execution.

<CodeGroup>
  ```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()}
  ```
</CodeGroup>

### API Integration

Expose external service actions (email, calendar, notifications) as tools the model can invoke during a conversation.

<CodeGroup>
  ```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}")
  ```
</CodeGroup>

### Multi-Steps Workflows

Run the model in a loop until it produces a final text response, enabling multi-step agent behavior without streaming.

<CodeGroup>
  ```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.ORQ_API_KEY,
    baseURL: "https://api.orq.ai/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("ORQ_API_KEY"),
      base_url="https://api.orq.ai/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)
          }
  ```
</CodeGroup>

## Error Handling

Return structured error objects in tool outputs so the model can report failures or retry with corrected arguments.

<CodeGroup>
  ```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)}),
          }
  ```
</CodeGroup>

## Best Practices

### Tool design

* Use clear, descriptive function names.
* Provide detailed parameter descriptions.
* Include examples in descriptions.
* Make tools idempotent when possible.

### Schema design

<CodeGroup>
  ```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"]
  }
  ```
</CodeGroup>

### Security considerations

* Never expose destructive operations directly.
* Validate all inputs thoroughly.
* Use allowlists for sensitive operations.
* Implement proper authentication.
* Log all tool executions.

## Troubleshooting

**Tool not being called**

* Check tool descriptions are clear.

* Verify parameter schemas are correct.

* Ensure tool\_choice is set appropriately.

* Try more explicit prompts.
  **Invalid arguments**

* Validate JSON Schema thoroughly.

* Add parameter examples.

* Check required fields are marked.

* Simplify complex parameter structures.

**Execution failures**

* Implement proper error handling.
* Add timeout protection.
* Validate inputs before execution.
* Return structured error messages.

## Limitations

| Limitation            | Impact                       | Workaround                   |
| --------------------- | ---------------------------- | ---------------------------- |
| **Tool limit**        | Max \~20 tools per request   | Group related functions      |
| **Parameter size**    | Large schemas may fail       | Simplify parameter structure |
| **Execution time**    | Tools block response         | Use async patterns           |
| **Error propagation** | Failures can break workflow  | Implement error recovery     |
| **Model differences** | Varying tool calling quality | Test across models           |

## Performance Optimization

### Tool caching

Cache results from read-only tools to avoid redundant external calls within a session.

<CodeGroup>
  ```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
  ```
</CodeGroup>

### Batch operations

Accept arrays of inputs in a single tool to reduce the number of model turns required for bulk operations.

<CodeGroup>
  ```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"]
      }
  }
  ```
</CodeGroup>
