Tool Use
Define tools, let the model call them, and feed results back. Build agents, function-calling workflows, and structured outputs across every model.
A language model on its own can't check the weather, query your database, or book a meeting — it can only produce text. Tool use (also called function calling) closes that gap: you describe named functions, the model decides which to call and with what arguments, your code runs them, and you feed the results back for a final answer. AnyRouter normalizes tool use to the OpenAI schema, so every tool-capable model in the catalog works through the same request shape — swap the model id and your code is unchanged.
Overview
The loop has four steps: define tools, send them with your request, run whatever the model calls, and return the results as tool messages. The model then composes a natural-language answer from those results.
const tools = [
{
type: "function",
function: {
name: "get_weather",
description: "Return the current weather for a city.",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["city"],
},
},
},
]
How it works
Sending tools with a request
Pass your tools alongside the messages. tool_choice: "auto" lets the model decide whether to call one.
const response = await client.chat.completions.create({
model: "openai/gpt-5.4-mini",
messages: [{ role: "user", content: "What's the weather in Tokyo?" }],
tools,
tool_choice: "auto",
})
Handling tool calls
When the model decides to call a tool, the response carries tool_calls instead of plain content, and finish_reason is tool_calls:
{
"choices": [
{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_01ABC",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Tokyo\",\"unit\":\"celsius\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}
Run the tool, then send the result back as a tool role message. Include the original user message and the assistant's tool-call message so the model has full context:
const toolCall = response.choices[0].message.tool_calls![0]
const args = JSON.parse(toolCall.function.arguments)
const weather = await getWeather(args.city, args.unit)
const followup = await client.chat.completions.create({
model: "openai/gpt-5.4-mini",
messages: [
{ role: "user", content: "What's the weather in Tokyo?" },
response.choices[0].message,
{
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(weather),
},
],
tools,
})
The model sees the tool result and composes a natural-language answer.
Forcing or disabling a tool
tool_choice controls whether and which tool the model may call:
// Force a specific function
tool_choice: { type: "function", function: { name: "get_weather" } }
Use "none" to disable tool use entirely and force plain-text output, or "auto" (the default when tools are present) to let the model decide.
Parallel tool calls
Modern models can emit multiple tool calls in a single turn. Iterate over the full tool_calls array, run them in parallel, and include one tool message per call in your follow-up request.
const calls = response.choices[0].message.tool_calls ?? []
const results = await Promise.all(
calls.map(async (call) => ({
role: "tool" as const,
tool_call_id: call.id,
content: JSON.stringify(await runTool(call)),
})),
)
Always match every tool_call_id with exactly one tool message in the follow-up. A missing tool result leaves the model waiting on a call it already made and usually produces an error or a degraded answer.
Structured outputs
When you want JSON back without an actual side effect, use response_format instead of tools:
response_format: { type: "json_object" }
For guaranteed conformance, provide a JSON schema:
response_format: {
type: "json_schema",
json_schema: {
name: "weather_report",
schema: { /* JSON Schema */ },
strict: true,
},
}
Configure
Tool use needs no dashboard setup — it's part of every request. To adopt it:
- Define your tools as an array of
{ type: "function", function: { name, description, parameters } }objects, using JSON Schema forparameters. - Send them with
toolsand atool_choiceon your chat completion. - Loop on
finish_reason: "tool_calls": run each call, append atoolmessage pertool_call_id, and re-send until the model returns a normal completion. - Swap models freely — any tool-capable model in the catalog accepts the same request shape.
Related
- Presets — bundle
toolsandtool_choiceinto a reusable config. - Request Logs — inspect tool-call arguments and results per request.
- API Reference — the full chat completions schema.