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

# Tool calling

> Receive function calls, run application logic, and return results to the model.

Current Chat ↔ Responses function-tool conversion does not copy strict. Native declarations, conversion, and upstream defaults do not establish equivalent guarantees. Validate tool arguments in the application. See [tool selection and parallel controls](/en/guides/tool-controls).

Your application executes tools. The model returns a function name and arguments; the gateway relays or converts the protocol. Declaring a tool does not make the gateway execute your local function.

## Complete the loop

1. Send `tools` using a model that supports functions and the requested options.
2. Inspect the assistant's `tool_calls`; handle an ordinary reply if there are none.
3. Validate names and arguments, then execute an allowed application function.
4. Append the original assistant message and a matching `role: tool` result for each call, then request another reply.
5. Bound rounds, execution time, and total work because the model can request more tools.

## Runnable example: local multiplication

Prepare the [TypeScript SDK environment](/en/guides/typescript), save as `tool-calling.mts`, and run `node tool-calling.mts`. This example exposes only a pure calculation, `multiply`, and makes at most three model requests. Its `strict: true` requires strict tool-argument support in the model and channel.

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

const { API_BASE_URL, API_KEY, API_MODEL } = process.env;
if (!API_BASE_URL || !API_KEY || !API_MODEL) {
  throw new Error('Set API_BASE_URL, API_KEY, and API_MODEL first.');
}
const client = new OpenAI({
  baseURL: API_BASE_URL.replace(/\/+$/, ''),
  apiKey: API_KEY,
  timeout: 60_000,
  maxRetries: 0,
});
const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
  { role: 'user', content: 'What is 7 times 8? Use multiply.' },
];
const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [{
  type: 'function',
  function: {
    name: 'multiply',
    description: 'Multiply two finite numbers.',
    strict: true,
    parameters: {
      type: 'object',
      properties: { a: { type: 'number' }, b: { type: 'number' } },
      required: ['a', 'b'],
      additionalProperties: false,
    },
  },
}];

try {
  let completed = false;
  for (let round = 0; round < 3; round++) {
    const result = await client.chat.completions.create({ model: API_MODEL, messages, tools });
    const choice = result.choices[0];
    if (!choice || choice.message.refusal) throw new Error('No usable reply.');
    const calls = choice.message.tool_calls ?? [];
    if (calls.length === 0) {
      if (choice.finish_reason !== 'stop' || !choice.message.content) {
        throw new Error('Incomplete text reply.');
      }
      console.log(choice.message.content);
      completed = true;
      break;
    }
    if (choice.finish_reason !== 'tool_calls') throw new Error('Incomplete tool calls.');
    messages.push(choice.message);
    for (const call of calls) {
      if (call.type !== 'function' || call.function.name !== 'multiply' || !call.id) {
        throw new Error('Unsupported tool call.');
      }
      const args = JSON.parse(call.function.arguments);
      if (!args || Array.isArray(args) || Object.keys(args).length !== 2
        || !Number.isFinite(args.a) || !Number.isFinite(args.b)) {
        throw new Error('Invalid tool arguments.');
      }
      const value = args.a * args.b;
      if (!Number.isFinite(value)) throw new Error('Invalid tool result.');
      messages.push({ role: 'tool', tool_call_id: call.id, content: JSON.stringify({ value }) });
    }
  }
  if (!completed) throw new Error('Tool round limit reached.');
} catch (error) {
  console.error(error instanceof OpenAI.APIError && error.status
    ? `Request failed (HTTP ${error.status}). Check the request logs.`
    : 'Tool workflow failed: check completion, tool names, arguments, and round limit.');
  process.exitCode = 1;
}
```

The example handles multiple calls in one reply, returning each result with its `tool_call_id`. A model can also answer directly; final text depends on the model. Unknown functions, invalid arguments, unfinished output, or the round limit cause a nonzero exit.

`function.arguments` is a JSON string, not trusted code. Validate application constraints even with strict mode. Tools that access or change real data must enforce the current user's permissions independently; generated arguments do not grant access. Do not implement this example with `eval`, arbitrary shell execution, or unrestricted dynamic function dispatch.

## Chat Completions versus Responses

| Item | Chat Completions | Responses |
| - | - | - |
| Function definition | `tools[].function.name` / `parameters` | Function `name` / `parameters` are at the tool object's top level |
| Model call | `message.tool_calls[]` | `function_call` items in `output` |
| Returned result | `role: tool`, linked by `tool_call_id` | `type: function_call_output`, linked by `call_id`, result in `output` |

A Responses `call_id` differs from the output item's own `id`. Do not send the Chat messages above unchanged to Responses. Preserve any other output items and context required by the model.

Validate the non-streaming loop first. In a stream, `arguments` can arrive in fragments: collect them by call index/ID and parse only when complete. Do not execute a function once per fragment.

[Chat Completions reference](/en/api-reference/chat-completions) · [Streaming](/en/guides/streaming)

## Python: a Responses function-calling loop

Save as `responses-tools.py`, configure the [Python SDK](/en/guides/python), and run `python responses-tools.py`. Choose a model and channel supporting Responses, strict function arguments, and stateless history replay. Requests also use `store: false` and `include: ["reasoning.encrypted_content"]`; see [conversations](/en/guides/conversations) for compatibility requirements.

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

history = [{"role": "user", "content": "What is 7 times 8? Use multiply."}]
tools = [{
    "type": "function", "name": "multiply", "description": "Multiply two finite numbers.",
    "strict": True,
    "parameters": {
        "type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}},
        "required": ["a", "b"], "additionalProperties": False,
    },
}]
seen_call_ids = set()
with OpenAI(base_url=os.environ["API_BASE_URL"].rstrip("/"),
            api_key=os.environ["API_KEY"], timeout=60.0, max_retries=0) as client:
    try:
        for round_index in range(3):
            result = client.responses.create(
                model=os.environ["API_MODEL"], input=history, tools=tools,
                instructions="Use multiply for arithmetic, then answer briefly.",
                store=False, include=["reasoning.encrypted_content"],
            )
            if result.status != "completed":
                raise ValueError("Response did not complete.")
            calls = []
            for item in result.output:
                if item.type == "function_call":
                    if getattr(item, "status", None) not in (None, "completed"):
                        raise ValueError("Function call did not complete.")
                    calls.append(item)
                elif item.type == "message":
                    if item.status != "completed" or any(part.type != "output_text" for part in item.content):
                        raise ValueError("Refusal or incomplete message.")
                elif item.type != "reasoning":
                    raise ValueError("Unsupported output item.")
            if not calls:
                if not result.output_text.strip():
                    raise ValueError("No final text.")
                print(result.output_text)
                break
            if round_index == 2 or len(calls) > 4:
                raise ValueError("Tool round or call limit reached.")
            outputs = []
            for call in calls:
                if (call.name != "multiply" or not isinstance(call.call_id, str)
                        or not call.call_id.strip() or call.call_id in seen_call_ids):
                    raise ValueError("Unknown tool or invalid call ID.")
                args = json.loads(call.arguments)
                if not isinstance(args, dict) or set(args) != {"a", "b"}:
                    raise ValueError("Invalid tool arguments.")
                if any(type(value) not in (int, float) or not math.isfinite(value) for value in args.values()):
                    raise ValueError("Expected finite numbers.")
                value = args["a"] * args["b"]
                if not math.isfinite(value):
                    raise ValueError("Invalid tool result.")
                seen_call_ids.add(call.call_id)
                outputs.append({
                    "type": "function_call_output", "call_id": call.call_id,
                    "output": json.dumps({"value": value}, allow_nan=False),
                })
            history.extend(item.model_dump(exclude_none=True) for item in result.output)
            history.extend(outputs)
    except APIStatusError as error:
        raise SystemExit(f"Request failed (HTTP {error.status_code}). No automatic retry.")
    except APIConnectionError:
        raise SystemExit("Connection failed. No automatic retry.")
    except (ValueError, TypeError, AttributeError, OverflowError):
        raise SystemExit("Tool workflow failed: check output, call IDs, arguments, and limits.")
```

The example allows at most three model requests and four function calls per round. If the final round still requests tools, it fails before executing them. It preserves full output items, associates results using `call_id`, and rejects repeated call IDs, unknown tools, booleans masquerading as numbers, non-finite values, and overflow. Multiplication has no external side effects; business tools need authorization, idempotency, and execution timeouts. The 60-second request timeout is not a deadline for the entire workflow.

Reference: [OpenAI function calling](https://developers.openai.com/api/docs/guides/function-calling).

## Native protocols and streaming tools

[Claude Messages](/en/guides/native-claude) and [Gemini generateContent](/en/guides/native-gemini) provide dedicated native protocol examples. For argument assembly and function execution, see [Chat / Responses streaming tools](/en/guides/streaming-tools).

Runnable native SSE and tool loops for [Claude](/en/guides/native-claude-streaming) and [Gemini](/en/guides/native-gemini-streaming) include completion validation and signature preservation.

[简体中文](/zh/guides/tool-calling)


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