Skip to content

Tool calling

Tool calling lets the model ask your code to do something, such as look up an order or run a query, and then use the result in its answer. Belarel relays the call to you. It never executes your tools: your code runs them, on your side, and sends the result back in the next request.

Belarel can also run one tool itself: the provider’s native web search, which you turn on per request.

Terminal window
curl https://api.belarel.com/v1/chat/completions \
-H "Authorization: Bearer $BELAREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5",
"messages": [{"role": "user", "content": "Where is order 7731?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Returns the shipping status of an order.",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"]
}
}
}]
}'
  • Only type: "function" tools are read.
  • Up to 24 tools per request. Entries beyond the 24th are dropped without an error — the model never sees them — and skipped entries (below) still count toward the 24. A name is 1 to 64 letters, digits, _ or -.
  • parameters is a JSON Schema object of at most 16,000 characters. If you omit it, the tool takes an empty object.
  • An invalid entry (wrong type, bad or duplicate name, oversized schema) is skipped, not refused. The model simply does not see it.
  • parallel_tool_calls is accepted and has no effect.
tool_choice Effect
Omitted or "auto" The model decides whether to call a tool.
"none" Passed to the model: your tools stay declared, so a conversation that already contains tool calls still reads correctly, and the model answers without calling any.
"required", or a named function 400 unsupported_parameter, param: "tool_choice". Belarel cannot force a tool call. If one must happen, check finish_reason and handle the case where the model answered in text.

The same values apply on the Responses API.

If you send tools to a model that cannot call them (belarel.capabilities.tools: false in GET /v1/models), the call is refused before the model runs, so it costs nothing:

{
"error": {
"message": "Model \"<model-id>\" cannot call tools",
"type": "invalid_request_error",
"code": "model_lacks_tools",
"param": "tools"
}
}

Pick a model whose belarel.capabilities.tools is true and whose supported_parameters include tools, or send the request without tools.

When the model calls one of your tools, the turn ends:

  • finish_reason is tool_calls.
  • message.tool_calls holds each call: id, type: "function", function.name and function.arguments (a JSON string).

To continue, send the whole conversation again, adding:

  1. the assistant message, with its tool_calls;
  2. one role: "tool" message per call, with the matching tool_call_id and the result as content. The content is read as text: serialize objects to JSON.

When streaming, each call arrives in one chunk with complete arguments. See Streaming.

Tools use the flat Responses shape: {"type": "function", "name", "description", "parameters"}. A call comes back as a function_call output item with a call_id. Send it back in input, followed by a function_call_output item with the same call_id and your result as output. Tool types other than function and web_search return 400 unsupported_parameter. See Responses API.

Web search lets the model ground its answer in current web results. The search runs at the model’s provider, not in your code.

Dialect How to turn it on
Chat Completions "web_search": true in the request body (a Belarel extension). OpenAI’s web_search_options is not supported and returns 400 unsupported_parameter.
Responses {"type": "web_search"} (or web_search_preview) in tools.
Terminal window
curl https://api.belarel.com/v1/responses \
-H "Authorization: Bearer $BELAREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "openai/gpt-5", "input": "What changed in the latest TLS RFC?",
"tools": [{"type": "web_search"}]}'

Which models. Web search is available on models whose belarel.capabilities.web_search is true in GET /v1/models. It uses each provider’s native search, for OpenAI, Anthropic, Google, xAI and Perplexity models. Perplexity models search on every turn.

When it is refused. Belarel never returns an ungrounded answer as if it had searched. It refuses instead:

Code HTTP Meaning What to do
web_search_not_allowed 403 Web search is not available for this model in your organization. Pick a model with web_search: true, or ask your organization admin.
web_search_unavailable 400 This model has no provider-native search where it is served. Pick another model.

Sources. On Responses, the message’s output_text carries url_citation annotations, and a web_search_call item marks that a search ran. On Chat Completions, sources arrive while streaming, as chunks with a top-level citations array of {url, title}. A non-streamed chat completion does not include them. Stream, or use the Responses API, if you need sources.

Do not name your own tools web_search or google_search when search is on. A tool of yours whose name collides with the search tool is dropped.