Skip to main content
Function calling (also known as tool use) lets the model request the execution of an external function while generating a reply — for actions like querying a database, calling an API, or reading/writing files. OpenPAI fully supports all three protocols, and they interoperate.

OpenAI protocol

from openai import OpenAI
client = OpenAI(api_key="sk-...", base_url="https://openp.ai/v1")

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

resp = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "Weather in Shanghai"}],
    tools=tools,
)

call = resp.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments)
After executing the function, send the result back:
messages = [
    {"role": "user", "content": "Weather in Shanghai"},
    resp.choices[0].message,            # assistant message with tool_calls
    {"role": "tool", "tool_call_id": call.id, "content": "Shanghai 26°C clear"},
]
final = client.chat.completions.create(model="gpt-5.5", messages=messages)
print(final.choices[0].message.content)

tool_choice

ValueBehavior
"auto" (default)The model decides on its own
"none"Force no tool call
"required"Must call some tool
{"type": "function", "function": {"name": "x"}}Call a specific tool

parallel_tool_calls

Newer models support calling multiple tools in parallel by default; tool_calls in the response is an array — execute each and return them all at once. To disable parallelism, set parallel_tool_calls: false.

Claude protocol

from anthropic import Anthropic
client = Anthropic(api_key="sk-...", base_url="https://openp.ai")

resp = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    tools=[{
        "name": "get_weather",
        "description": "Get weather",
        "input_schema": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    }],
    messages=[{"role": "user", "content": "Weather in Shanghai"}],
)

# Find the tool_use block
for block in resp.content:
    if block.type == "tool_use":
        result = call_my_weather(block.input["city"])
        followup = client.messages.create(
            model="claude-opus-4-8",
            max_tokens=1024,
            messages=[
                {"role": "user", "content": "Weather in Shanghai"},
                {"role": "assistant", "content": resp.content},
                {"role": "user", "content": [{
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                }]},
            ],
        )

tool_choice (Claude)

{"type": "auto"}                      // default
{"type": "any"}                       // must call some tool
{"type": "tool", "name": "x"}         // a specific tool
{"type": "none"}                      // disable tools

Gemini protocol

from google import genai
from google.genai import types

client = genai.Client(api_key="sk-...", http_options={"base_url": "https://openp.ai"})

weather_tool = types.Tool(function_declarations=[
    types.FunctionDeclaration(
        name="get_weather",
        description="Get weather",
        parameters={
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    )
])

resp = client.models.generate_content(
    model="gemini-3.1-pro-preview",
    contents="Weather in Shanghai",
    config=types.GenerateContentConfig(tools=[weather_tool]),
)

for part in resp.candidates[0].content.parts:
    if part.function_call:
        print(part.function_call.name, part.function_call.args)
Return the result:
function_response = types.Part(function_response=types.FunctionResponse(
    name="get_weather",
    response={"city": "Shanghai", "temp": 26}
))

followup = client.models.generate_content(
    model="gemini-3.1-pro-preview",
    contents=[
        types.Content(role="user", parts=[types.Part(text="Weather in Shanghai")]),
        resp.candidates[0].content,
        types.Content(role="user", parts=[function_response]),
    ],
)

Protocol interoperability

When you call Claude / Gemini models via OpenPAI’s OpenAI-compatible endpoint, OpenPAI automatically converts the Anthropic / Google tool-calling structures to OpenAI’s tools / tool_calls, and vice versa. You don’t need to worry about protocol differences — just pick one style.

Best practices

Detailed descriptions

The more specific a tool’s description, the better the model decides when to call it and what arguments to pass.

Strict schema

Use required and additionalProperties: false to keep the model from passing stray fields.

Handle errors gracefully

When a tool fails, return the error as a tool_result so the model can retry or change strategy.

Parallel calls

Execute multiple independent tools in parallel in one call to reduce latency.