Tool calling
A model can ask your code to run a function, such as looking up an order or querying a database, and then answer with the result. The model never runs anything itself: it returns the function's name and arguments, your code runs it, and you send the result back in the next request.
Use a model whose page on Models lists tool calling under Capabilities. A model without it ignores tools and answers in text, without an error.
This sample is a file we run against the service before publishing:
# pip install openai
# Tool calling: the model asks for a function, your code runs it and sends the
# result back, and the model answers with it. Any chat model whose page lists
# tool calling works the same way.
import json
import os
from openai import OpenAI
client = OpenAI(
base_url=os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1"),
api_key=os.environ["OCEANTOKEN_API_KEY"],
)
def get_weather(city):
# Your own code runs here: a database, an internal API, anything.
return {"city": city, "temperature_c": 18, "sky": "cloudy"}
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "The current weather in a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string", "description": "City name, for example Lisbon"}},
"required": ["city"],
},
},
}
]
messages = [{"role": "user", "content": "What is the weather in Lisbon right now?"}]
message = client.chat.completions.create(model="openai/gpt-5-mini", messages=messages, tools=tools).choices[0].message
called = False
# While the model asks for tools, run each one and send the results back.
while message.tool_calls:
called = True
messages.append({
"role": "assistant",
"content": message.content,
"tool_calls": [call.model_dump() for call in message.tool_calls],
})
for call in message.tool_calls:
result = get_weather(**json.loads(call.function.arguments))
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
message = client.chat.completions.create(model="openai/gpt-5-mini", messages=messages, tools=tools).choices[0].message
# A model that ignores `tools` answers without calling them, and that is not an error.
if not called:
raise SystemExit("the model answered without calling the tool")
print(message.content)
How the loop works
- Describe your functions in
tools: aname, adescriptionthe model reads to decide when to call it, andparametersas a JSON Schema. - The model asks for a call. The answer's
finish_reasonistool_calls, andchoices[0].message.tool_callslists each call with anid, the functionnameand itsargumentsas a JSON string. - Run it and send the result back. Append the model's message with its
tool_calls, then onetoolmessage per call, carrying the call'stool_call_idand your result as a string. - Ask again with the same
tools. The model answers, or asks for more calls; repeat untiltool_callsis empty.
Arguments come from the model, so validate them before you act on them, exactly as you would user input.
Choosing when the model calls
tool_choice controls it:
| Value | Effect |
|---|---|
"auto" (default) | The model decides. |
"required" | The model must call at least one tool. |
"none" | The model must answer in text. |
{"type": "function", "function": {"name": "get_weather"}} | The model must call this function. |
Several calls at once
A model whose page lists parallel tool calls can ask for several calls in one message, for example the weather in two cities. Run each and send one tool message per tool_call_id, in any order; the loop above already does. Send "parallel_tool_calls": false to get one call at a time.
Streaming
With stream=True, a call arrives in pieces under choices[0].delta.tool_calls: the first piece of each call carries its index, id and name, and the following pieces add to arguments. Join the pieces by index, and run the calls once the stream ends with finish_reason tool_calls. SDK helpers such as the Vercel AI SDK do this for you (Call tools).
With the Anthropic Messages API
On /v1/messages tools take the Anthropic shape, for every chat model: tools with name, description and input_schema; the model answers with tool_use blocks and stop_reason tool_use; you send the results back as tool_result blocks in a user message. The Anthropic SDK works unchanged with the base URL https://api.oceantoken.ai.
Costs
Tool definitions are part of the prompt and are billed as input tokens on every request that carries them, and each round trip is a separate request. Long, unchanging tool lists are a good fit for prompt caching.
Next
- Structured output when you need JSON but no function call.
- Coding agents: models tested with agents that call tools on every turn.