Chat Completions · tool calling

OpenAI Chat Completions function calling with a compatible gateway

Use the Chat Completions tools shape, not the Responses one. This guide covers field layout, follow-up turns and what a 401 probe does not prove.

What Chat Completions tool calling does

The model does not run your code. It returns structured tool_calls; your application validates arguments, runs an allowlisted function, and sends a role: tool message back. Treat model-produced arguments as untrusted input.

StepChat Completions fieldYour application
1. Definetools[].type = function and tools[].functionExpose only approved functions
2. Requestchoices[0].message.tool_calls[]Read id, function.name, function.arguments
3. ExecuteNo external action is performed by the modelAuthorize, validate, run and record the call
4. ContinueAppend assistant message + role: tool with matching tool_call_idAsk for the final answer

Verify the route, key and model first

Check GET /v1/models, then use an exact model ID returned to your key. On August 4, 2026, production probes against MonkeyAI showed:

Those results only prove route and authentication reachability. They do not prove that a specific model supports tools, strict mode or parallel calls.

curl -sS https://monkeytheme.com/v1/models \
  -H "Authorization: Bearer ***"
No customer key was used to complete a live tool round-trip in this review. Test the exact model and schema in a non-production workflow before allowing write, payment, message or account actions.

Send a minimal Chat Completions tools definition

In Chat Completions, function fields sit under tools[].function. OpenAI OpenAPI names this shape ChatCompletionTool. Name length is limited to 64 characters and should use letters, numbers, underscores or dashes.

curl -sS https://monkeytheme.com/v1/chat/completions \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "MODEL_ID_FROM_LIST",
  "messages": [
    {"role": "user", "content": "What is the weather in Paris?"}
  ],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Return current weather for a city.",
      "strict": true,
      "parameters": {
        "type": "object",
        "properties": {
          "city": {"type": "string"}
        },
        "required": ["city"],
        "additionalProperties": false
      }
    }
  }],
  "tool_choice": "auto"
}'

With strict mode, list every property in required and set additionalProperties to false on each object. OpenAI notes that Chat Completions remains non-strict by default unless you set strict: true.

Parse tool_calls and return tool results

When the model chooses tools, the assistant message usually has empty or null content, finish_reason is often tool_calls, and message.tool_calls carries one or more calls. Append that assistant message, then one tool message per call ID.

import json
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_KEY",
    base_url="https://monkeytheme.com/v1",
)

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Return current weather for a city.",
        "strict": True,
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
            "additionalProperties": False,
        },
    },
}]

messages = [{"role": "user", "content": "What is the weather in Paris?"}]
first = client.chat.completions.create(
    model="MODEL_ID_FROM_LIST",
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

assistant = first.choices[0].message
messages.append(assistant)

for call in assistant.tool_calls or []:
    args = json.loads(call.function.arguments)
    result = dispatch_approved_function(call.function.name, args)
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps(result),
    })

final = client.chat.completions.create(
    model="MODEL_ID_FROM_LIST",
    messages=messages,
    tools=tools,
)
print(final.choices[0].message.content)

dispatch_approved_function is application code, not an SDK helper. OpenAI also warns that function.arguments is a JSON string and may be invalid or invent fields outside your schema, so validate before execution.

tool_choice and parallel_tool_calls

ControlChat Completions valueEffect
Auto"tool_choice": "auto"Default when tools are present: zero, one or more tools
Required"tool_choice": "required"Model must call one or more tools
None"tool_choice": "none"No tool call; text only
Named tool{"type":"function","function":{"name":"get_weather"}}Force that function
Parallel off"parallel_tool_calls": falseAt most one tool call in the turn

Do not copy the Responses-only named shape {"type":"function","name":"..."} into Chat Completions. The named Chat Completions choice nests the name under function.

Do not mix Chat Completions and Responses shapes

DetailChat CompletionsResponses
Endpoint/v1/chat/completions/v1/responses
Function definitiontools[].function.{name,description,parameters,strict}Function fields sit directly on the tools item
Returned callchoices[0].message.tool_calls[]output[] items with type function_call
Correlation fieldtool_call_id on the tool messagecall_id on function_call_output
Result message{"role":"tool", ...}function_call_output input item

If a client or SDK example targets Responses, rewrite the schema before pointing it at /v1/chat/completions.

Put safety checks around the executor

Sources and related guides

FAQ

Does the model execute my function?

No. The model returns structured tool_calls. Your application decides whether to run them and sends role: tool results back.

Does a 401 response prove tool calling works?

No. It only proves the request reached authentication. Use a real key and an exact model ID to test tools.

Where do Chat Completions function fields go?

Under tools[].function. Do not place name/description/parameters at the tools item root the way Responses does.

How do I correlate a tool result?

Copy message.tool_calls[].id into the next role: tool message as tool_call_id. Missing or mismatched IDs break the follow-up turn.

Can one assistant message contain several tool calls?

Yes. OpenAI allows zero, one or multiple calls unless you set parallel_tool_calls to false. Iterate every call.