# Tool Calling (/docs/tool-calling)

You describe your functions in the request. When the model wants one, it returns the function name and JSON arguments instead of a final answer. Your code runs the function, sends the result back, and the model continues. The model never runs anything itself.

Write your tools once, in the shape of the endpoint you already use: Chat Completions, Responses or Anthropic Messages. The same definitions then work with every model on that endpoint, so moving a tool loop from GPT to Claude or GLM is a one-line model change. You can test the whole loop for free with `ohmygpt/mock`.

## Chat Completions [#chat-completions]

### 1. Send the tools [#1-send-the-tools]

```bash
curl https://api.ohmygpt.com/v1/chat/completions \
  -H "Authorization: Bearer $OHMYGPT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4-nano",
    "messages": [{"role": "user", "content": "What is the weather in Paris?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }]
  }'
```

The model answers with `finish_reason: "tool_calls"` and the call it wants (trimmed):

```json
{
  "choices": [
    {
      "index": 0,
      "finish_reason": "tool_calls",
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_wwN71lk5akb6aFGYQv9f234b",
            "type": "function",
            "function": { "name": "get_weather", "arguments": "{\"city\":\"Paris\"}" }
          }
        ]
      }
    }
  ]
}
```

`arguments` is a JSON string. Parse it before you use it.

### 2. Return the result [#2-return-the-result]

Append the assistant message unchanged, then one `tool` message per call with the matching `tool_call_id`, and send the conversation again with the same `tools`. The full loop in Python:

```python
import json
import os

from openai import OpenAI

client = OpenAI(api_key=os.environ["OHMYGPT_API_KEY"], base_url="https://api.ohmygpt.com/v1")

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a city.",
            "parameters": {
                "type": "object",
                "properties": {"city": {"type": "string", "description": "City name, e.g. Paris"}},
                "required": ["city"],
            },
        },
    }
]


def get_weather(city: str) -> dict:
    return {"city": city, "temperature_c": 18, "condition": "cloudy"}


messages = [{"role": "user", "content": "What's the weather in Paris?"}]

response = client.chat.completions.create(model="openai/gpt-5.4-nano", messages=messages, tools=tools)
message = response.choices[0].message
messages.append(message)

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

final = client.chat.completions.create(model="openai/gpt-5.4-nano", messages=messages, tools=tools)
print(final.choices[0].message.content)
```

```text
In **Paris**, it’s currently **cloudy** with a temperature of **18°C**.
```

The same program with `model="anthropic/claude-haiku-4-5"` prints `The weather in Paris is currently **cloudy** with a temperature of **18°C** (about 64°F).`

### Control tool use [#control-tool-use]

| `tool_choice` | Effect |
|---|---|
| `"auto"` (default) | The model decides whether to call a tool |
| `"none"` | The model answers in text |
| `"required"` | The model must call at least one tool |
| `{"type": "function", "function": {"name": "get_time"}}` | The model must call `get_time` |

## Test the loop for free [#test-the-loop-for-free]

`ohmygpt/mock` answers deterministically. When the request has tools, it calls the first one, or the one `tool_choice` names, with every required argument set to its zero value. After a tool result, it replies with text. The first request above with `"model": "ohmygpt/mock"` returns:

```json
{
  "id": "chatcmpl-ohmygpt-mock",
  "object": "chat.completion",
  "model": "mock",
  "choices": [
    {
      "index": 0,
      "finish_reason": "tool_calls",
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_mock_1",
            "type": "function",
            "function": { "name": "get_weather", "arguments": "{\"city\":\"\"}" }
          }
        ]
      }
    }
  ],
  "usage": { "prompt_tokens": 73, "completion_tokens": 8, "total_tokens": 81 }
}
```

The mock is available once your account has been topped up.

## Responses [#responses]

Tools are flat (`name` and `parameters` sit next to `type`). The model's call arrives as a `function_call` item in `output`; send the result back as a `function_call_output` item with the same `call_id`:

```bash
curl https://api.ohmygpt.com/v1/responses \
  -H "Authorization: Bearer $OHMYGPT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ohmygpt/mock",
    "input": [
      {"role": "user", "content": "What is the weather in Paris?"},
      {"type": "function_call", "call_id": "call_mock_1", "name": "get_weather", "arguments": "{\"city\":\"Paris\"}"},
      {"type": "function_call_output", "call_id": "call_mock_1", "output": "{\"temperature_c\":18,\"condition\":\"cloudy\"}"}
    ],
    "tools": [{
      "type": "function",
      "name": "get_weather",
      "description": "Get the current weather for a city.",
      "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
    }]
  }'
```

The first turn's output item looks like this:

```json
{ "type": "function_call", "id": "fc_0", "call_id": "call_mock_1", "name": "get_weather", "arguments": "{\"city\":\"\"}", "status": "completed" }
```

## Anthropic Messages [#anthropic-messages]

Tools use `input_schema`. The model's call is a `tool_use` content block with `stop_reason: "tool_use"`; send the result in the next user message as a `tool_result` block with the same ID:

```bash
curl https://api.ohmygpt.com/v1/messages \
  -H "x-api-key: $OHMYGPT_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ohmygpt/mock",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "What is the weather in Paris?"},
      {"role": "assistant", "content": [{"type": "tool_use", "id": "call_mock_1", "name": "get_weather", "input": {"city": "Paris"}}]},
      {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "call_mock_1", "content": "{\"temperature_c\":18,\"condition\":\"cloudy\"}"}]}
    ],
    "tools": [{
      "name": "get_weather",
      "description": "Get the current weather for a city.",
      "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
    }]
  }'
```

The first turn's content block looks like this:

```json
{ "type": "tool_use", "id": "call_mock_1", "name": "get_weather", "input": { "city": "" } }
```

## Streaming [#streaming]

With `"stream": true`, the call arrives in pieces: the name first, then the arguments as string fragments (`tool_calls[].function.arguments` deltas on Chat Completions, `response.function_call_arguments.delta` events on Responses, `input_json_delta` events on Messages). Concatenate the fragments and parse the JSON once the stream ends.
