OhMyGPT

Tool Calling

Let a model call your functions on Chat Completions, Responses or Anthropic Messages.

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

1. Send the tools

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):

{
  "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

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:

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)
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

tool_choiceEffect
"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

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:

{
  "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

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:

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:

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

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:

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:

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

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.

On this page