Relay mode
The gateway runs tools in relay mode — it never executes them. It declares your tool definitions to the model, returns the call the model wants to make, and stops. You run the tool and send the result back on the next turn. A typical loop:- Send a request with
tools(and optionallytool_choice). - The model responds with one or more tool calls and a tool-call finish reason instead of final text.
- Run each tool yourself and append the result to the conversation.
- Send the updated conversation back; the model continues (more tool calls, or a final text answer).
Chat Completions
Request —tools[] are function definitions; tool_choice is
auto (default), none, required, or { "type": "function", "function": { "name": "..." } }.
tool_calls and finish_reason is
tool_calls. Each call’s function.arguments is a JSON-encoded string.
tool message
whose tool_call_id matches, then send the conversation again:
delta.tool_calls[] fragments. The first
fragment for a call carries id, type, and function.name; later fragments
stream function.arguments text. The index field correlates fragments of the
same call.
Responses API
Request — Responses flattens the function fields onto each tool.tool_choice
is auto / none / required / { "type": "function", "name": "..." }.
function_call output item with a call_id and
a JSON-string arguments:
function_call_output input item referencing the
same call_id:
response.output_item.added event opens the function_call
item, response.function_call_arguments.delta events stream the argument text,
and response.function_call_arguments.done closes it.
Messages (Anthropic)
Request —tools[] use input_schema; tool_choice is { "type": "auto" },
{ "type": "any" }, { "type": "tool", "name": "..." }, or { "type": "none" }.
(A bare claude-... model id routes to Anthropic; any provider/model id also
works here — see SDKs & integrations.)
tool_use content block (with a parsed input object) and
stop_reason: "tool_use":
user turn containing a tool_result block
referencing the tool_use_id:
tool_use block opens with a content_block_start, its input
streams as content_block_delta events of type input_json_delta
(partial_json), and closes with content_block_stop.
Across all three surfaces, arguments arrive as JSON text (a string for
OpenAI-style calls, streamed
partial_json for Anthropic). Parse them yourself
before running the tool, and tolerate empty/partial fragments while streaming.Gemini thought signatures
Gemini 3 models attach an opaque signature to the first function call of each step and reject the tool-result turn unless it comes back verbatim. The gateway round-trips it for you: on Chat Completions the tool call carriesextra_content.google.thought_signature (Google’s OpenAI-compat mapping — in
streaming it rides a tool_calls fragment), and on the Responses API the
function_call item carries the same extra_content field. Send the tool
call back exactly as you received it — clients that echo the assistant
message unchanged (the OpenAI SDK and Vercel AI SDK loops already do) need no
extra code. The Gemini-native endpoint uses Google’s own Part.thoughtSignature
unchanged.
