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.