Tool calling
Describe functions your own code can run; the model decides when to ask for one, and your code decides whether and how to actually run it. tAI never executes a tool on your behalf.
tAI hands back one model turn at a time and stops there. It never calls a tool itself, never loops, and never sees the result of a tool call unless you send it back in a follow-up request. Your own code owns the entire execution loop: deciding whether to actually run what the model asked for, running it, and continuing the conversation with the result.
Defining tools
Pass a tools array on the request, using the same shape OpenAI's function-calling API uses:
{
"model": "tai-4.1",
"messages": [{"role": "user", "content": "Ankara'da hava nasıl?"}],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}
]
}
description matters more than it might seem: it's the model's only information about what a tool does and when it's appropriate to reach for it. A vague or missing description leads to a tool being called at the wrong moments, or not called when it should be. parameters is a JSON Schema object describing the arguments the tool accepts; the model's own arguments in a tool call are generated to match that schema.
Receiving a tool call
When the model decides to call a tool instead of replying directly, the response's finish_reason is "tool_calls" and message.tool_calls is populated instead of (or alongside) message.content:
{
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_a1b2c3",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"city\": \"Ankara\"}"}
}]
},
"finish_reason": "tool_calls"
}]
}
function.arguments is a JSON-encoded string, not a nested object; parse it before using it. The model can ask for more than one tool call in the same turn if the request has multiple tools defined and the task calls for it, so tool_calls is always an array, even when it contains exactly one entry.
In streaming mode, the same information arrives as a delta.tool_calls chunk followed by a finish chunk with "finish_reason": "tool_calls"; see Streaming for the general chunk shapes this builds on.
Running it and continuing the conversation
What happens next is entirely up to your code. Decide whether the requested call is something you're willing to run (this is exactly the point where an application typically applies its own permission model, sandboxing, or user confirmation, before executing anything). If you run it, send the result back as a new message with role: "tool", referencing the original call by its id:
{
"model": "tai-4.1",
"messages": [
{"role": "user", "content": "Ankara'da hava nasıl?"},
{"role": "assistant", "content": null, "tool_calls": [{"id": "call_a1b2c3", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\": \"Ankara\"}"}}]},
{"role": "tool", "tool_call_id": "call_a1b2c3", "content": "18°C, parçalı bulutlu"}
],
"tools": [/* same tools array as before */]
}
The model then continues from there, usually replying to the original question using the tool result, though it may also ask for another tool call if the task needs it. Keep sending the same tools array on every request in the conversation, not just the first one; the model has no memory of what tools were available on an earlier turn beyond what's in the current request.
Declining to run a call
Nothing requires you to actually execute what the model asked for. If you decide not to (the request is out of scope, needs confirmation you don't have yet, or simply isn't something your application supports), send a tool message explaining that instead of a real result, and the model will factor that into its next reply, typically by explaining the limitation to the user rather than assuming the action happened.
Why this design, specifically
Some agent frameworks bundle tool execution into the API call itself: you register a tool once, and the provider's own infrastructure runs it internally, looping until a final reply is ready. tAI deliberately doesn't do this for your own custom tools. Your tools are things only your code knows how to run, often touching your own infrastructure, your own credentials, your own filesystem; there's no way for tAI to safely execute them even if it wanted to. What tAI provides instead is exactly one thing: the decision of when a tool should be called and with what arguments, produced from a single model turn. Everything after that, execution, looping, guarding against a runaway sequence of calls, is squarely your application's responsibility, with the exact amount of caution and scoping appropriate for what your tools can actually do.