Core
Tool calling
Functions, the round trip, and which models actually support it. This is the single most common reason existing agent code would not work against a new provider.
Pass tools and, optionally, tool_choice. Both are OpenAI’s shape and are forwarded verbatim.
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
reply = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": "What is the weather in Nairobi?"}],
tools=tools,
)
call = reply.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments)finish_reason is tool_calls and content is empty. That combination is the model asking you to
run something, not the model saying nothing.
The round trip
Send the result back in a message with role: "tool", carrying the tool_call_id from the call:
messages = [
{"role": "user", "content": "What is the weather in Nairobi?"},
reply.choices[0].message,
{
"role": "tool",
"tool_call_id": call.id,
"content": '{"temp_c": 24, "conditions": "clear"}',
},
]
final = client.chat.completions.create(model="llama-3.3-70b", messages=messages, tools=tools)
print(final.choices[0].message.content)Which models support it
Check Can do on Models, or supported_parameters on GET /v1/models. Sending
tools to a model that does not declare support is a 400 naming the parameter, before any
billable work.
Those declarations are measured by sending a real tool call to each model, not copied from a supplier’s catalogue. Suppliers over-claim.
Streaming
Tool calls stream. delta.tool_calls accumulates by index, arguments arriving a few characters at
a time, and finish_reason is tool_calls on the final frame. The SDKs reassemble this for you.