docs/Tool CallingCore

Client Tools & Function Calling

Enable models to interact with custom databases, external APIs, and local code via strict JSON schemas.


How Tool Calling Works

Tool calling (also known as function calling) gives an LLM the ability to interact with external tools, databases, and APIs. The LLM **does not execute the tool directly**. Instead, it generates a structured JSON payload identifying the tool to call along with typed parameters. Your application inspects this request, executes the function locally, and returns the output to the LLM in a follow-up turn. The model then synthesizes the result into a natural response for the user. Unlocked AI standardizes the tool calling interface across all model providers (Anthropic Claude, OpenAI, Google Gemini, and DeepSeek) using the OpenAI tools specification.
Standard 3-Step Execution Cycle
1. **Request with Tools**: You supply your tools schema alongside the user's message. 2. **Client Execution**: The model returns `tool_calls`. You run the matching local code. 3. **Submit Tool Results**: You send the tool output back as `role: 'tool'` with `tool_call_id` to receive the final answer.

Step 1: Inference Request with Tools Schema

Define your available functions inside the `tools` array using standard JSON Schema declarations. You must provide `type: "function"`, `name`, `description`, and a `parameters` object schema:
{
  "model": "claude-sonnet-5.5",
  "messages": [
    {
      "role": "user",
      "content": "What is the stock price and volume for NVDA right now?"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "lookup_stock_quote",
        "description": "Fetch real-time stock ticker price, volume, and day range",
        "parameters": {
          "type": "object",
          "properties": {
            "symbol": {
              "type": "string",
              "description": "The stock ticker symbol, e.g. NVDA, AAPL, MSFT"
            },
            "currency": {
              "type": "string",
              "enum": ["USD", "EUR", "GBP"],
              "description": "Target currency code"
            }
          },
          "required": ["symbol"],
          "additionalProperties": false
        }
      }
    }
  ],
  "tool_choice": "auto"
}

Step 2: Model Proposes Tool Call & Client Executes

When the model determines a tool call is needed, it responds with `finish_reason: "tool_calls"` and includes a `tool_calls` array on the assistant message. Each item contains an identifier (`id`), function name, and JSON-encoded arguments:
{
  "id": "gen_8e7f9a2b",
  "object": "chat.completion",
  "model": "claude-sonnet-5.5",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_nvda_99812",
            "type": "function",
            "function": {
              "name": "lookup_stock_quote",
              "arguments": "{"symbol": "NVDA", "currency": "USD"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}

Step 3: Submit Tool Results Back to the Gateway

Append the assistant's message (containing `tool_calls`) and a new message with `role: "tool"`, matching `tool_call_id: "call_nvda_99812"`, and the `content` string with your tool output. The model will synthesize the final response:
Include Tools Array on Follow-Up
Always re-include the `tools` array on subsequent turns if you want the model to have the option to make additional or chaining tool calls.
{
  "model": "claude-sonnet-5.5",
  "messages": [
    {
      "role": "user",
      "content": "What is the stock price and volume for NVDA right now?"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_nvda_99812",
          "type": "function",
          "function": {
            "name": "lookup_stock_quote",
            "arguments": "{"symbol": "NVDA", "currency": "USD"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_nvda_99812",
      "content": "{"symbol": "NVDA", "price": 142.50, "volume": "54.2M", "change": "+3.4%"}"
    }
  ],
  "tools": [ /* Include the same tools array for schema validation */ ]
}

Controlling Tool Usage with tool_choice

You can control how and when tools are called using the `tool_choice` parameter:
ModeSyntaxBehavior
auto (Default)"tool_choice": "auto"Model decides whether to call a tool or reply directly with text.
none"tool_choice": "none"Prevents the model from generating any tool calls; forces plain text.
required"tool_choice": "required"Forces the model to call at least one tool before responding.
specific function{"type": "function", "function": {"name": "lookup_stock_quote"}}Forces the model to call the designated function specifically.

Interleaved Thinking Between Tool Calls

Reasoning frontier models like **Claude Sonnet 5.5**, **OpenAI o3**, and **DeepSeek R1** support interleaved thinking. When faced with complex multi-step problems, the model reasons through the initial prompt, selects an information-gathering tool, evaluates the tool's output with internal chain-of-thought logic, and then decides whether another tool is needed before returning its final answer. This delivers superior accuracy for autonomous agents, automated root-cause analysis, and deep data extraction.

Complete End-to-End Python Example

Here is a complete, runnable script executing the entire tool call cycle in Python:
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://unlockedai.net/v1",
    api_key="<YOUR_UNLOCKED_API_KEY>",
)

# 1. Define local Python function
def lookup_weather(location: str):
    # Simulated weather lookup
    return json.dumps({"location": location, "temperature": "71°F", "condition": "Partly Cloudy"})

# 2. Define schema
tools = [
    {
        "type": "function",
        "function": {
            "name": "lookup_weather",
            "description": "Get current weather condition for a location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "City name, e.g. San Francisco, CA"}
                },
                "required": ["location"]
            }
        }
    }
]

messages = [{"role": "user", "content": "What is the weather like in Austin, TX?"}]

# 3. First API call
response = client.chat.completions.create(
    model="claude-sonnet-5.5",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

msg = response.choices[0].message

# 4. Check if tool was requested
if msg.tool_calls:
    messages.append(msg) # Append assistant message with tool_calls
    for tc in msg.tool_calls:
        if tc.function.name == "lookup_weather":
            args = json.loads(tc.function.arguments)
            result = lookup_weather(args["location"])
            
            # Append tool response
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": result
            })

    # 5. Second API call for final answer
    final_response = client.chat.completions.create(
        model="claude-sonnet-5.5",
        messages=messages,
        tools=tools
    )
    print(final_response.choices[0].message.content)
else:
    print(msg.content)

Need an Unlocked AI Key or Credits?

Generate secret API keys backed by non-expiring pay-as-you-go credits starting at $5. No monthly minimums.