API guides

Tool calling

A model proposes a tool call; your application decides whether and how to execute it. Send the result back to the model so it can finish its answer. Choose a model that supports tools.

The tool loop

01
DescribeProvide a named tool and a JSON schema for its arguments.
02
Validate & runInspect the requested name and arguments. Only execute allowed operations.
03
ReturnAttach the tool result to the matching tool_call_id and ask the model to continue.

Complete example

Install the OpenAI Python SDK and set STRAITLY_API_KEY. This example uses a harmless local lookup with mock data. It never executes model-generated shell commands or reaches an external service.

Python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.straitly.ai/v1",
    api_key=os.environ["STRAITLY_API_KEY"],
)

tools = [{
    "type": "function",
    "function": {
        "name": "get_office_hours",
        "description": "Get office hours for a city.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string", "enum": ["San Francisco"]}},
            "required": ["city"],
            "additionalProperties": False,
        },
    },
}]
messages = [{"role": "user", "content": "When is the San Francisco office open?"}]

import json

for _ in range(4):
    reply = client.chat.completions.create(
        model="claude-sonnet-5", messages=messages, tools=tools,
    ).choices[0].message
    if not reply.tool_calls:
        print(reply.content)
        break
    messages.append(reply.model_dump(exclude_none=True))
    for call in reply.tool_calls:
        args = json.loads(call.function.arguments)
        if call.function.name != "get_office_hours":
            raise ValueError("Unknown tool")
        if not isinstance(args, dict) or args != {"city": "San Francisco"}:
            raise ValueError("Invalid arguments")
        result = {"hours": "09:00-17:00", "timezone": "America/Los_Angeles", "mock": True}
        messages.append({
            "role": "tool", "tool_call_id": call.id,
            "content": json.dumps(result),
        })
else:
    raise RuntimeError("Tool-call limit reached")

Execute safely

  • Allowlist tool names and validate argument types, allowed values, and permissions. Never eval the arguments.
  • Require confirmation for destructive or sensitive operations. A model request is not user authorization.
  • Set a maximum number of tool rounds and handle malformed arguments without retrying forever.
  • Deduplicate side effects when retrying requests. Do not run a payment, email, or write operation twice because a response timed out.

Streaming tools

Tool-call arguments may arrive in fragments. Accumulate them by index or call ID and wait for a complete call before parsing and executing. Do not treat each delta as a separate invocation.