Inference API

Function calling

Let the model ask your code to run a function, then continue with its result, the primitive behind every agent.

You describe your functions in the tools parameter; the model never runs anything itself. When it decides a tool would help, it replies with tool_calls, structured requests naming the function and its arguments, and pauses. Your code executes them and sends the results back, and the model writes its answer with those results in hand. Every model in the catalog supports tools.

What a tool call looks like

A response that requests tools has finish_reason: "tool_calls" and an assistant message shaped like this, note arguments is a JSON string to parse, not an object:

{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_ab12cd34",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"location\": \"Paris\"}"
      }
    }
  ]
}

The full round trip

Three legs: request with tools → execute what the model asked for → send results back as role: "tool" messages (one per call, echoing the tool_call_id):

import json
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://inference.pearlresearch.ai/v1",
    api_key=os.environ.get("PEARL_API_KEY"),
)

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

def get_weather(location: str) -> dict:
    return {"location": location, "temp_c": 21, "conditions": "partly cloudy"}

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

# 1) The model decides it needs a tool and returns tool_calls.
response = client.chat.completions.create(
    model="deepseek/deepseek-v4-flash-0731",
    messages=messages,
    tools=TOOLS,
)
msg = response.choices[0].message

# 2) Run each requested tool and append the results.
if msg.tool_calls:
    messages.append(msg)  # the assistant turn with tool_calls stays in history
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_weather(**args)
        messages.append(
            {
                "role": "tool",
                "tool_call_id": call.id,
                "content": json.dumps(result),
            }
        )

    # 3) Send the results back; the model writes the final answer.
    final = client.chat.completions.create(
        model="deepseek/deepseek-v4-flash-0731",
        messages=messages,
        tools=TOOLS,
    )
    print(final.choices[0].message.content)
else:
    print(msg.content)  # no tool needed

Keep the assistant message containing tool_calls in the history when you send the results, the tool messages answer it. Dropping it is the most common cause of confused follow-ups.

From round trip to agent

An agent is this round trip in a while loop: keep executing tool_calls and appending results until the model responds with plain content (or you hit your own step budget). The model may chain different tools across iterations to decompose a task, put the loop bound and any dangerous-action confirmation in your code, not in the prompt.

Best practices

  • Write description fields like documentation for a new teammate, what the tool does, when to use it, what it returns. Tool choice quality tracks description quality.
  • Constrain arguments with JSON Schema: enum for closed sets, required for mandatory fields, reject-and-retry beats guessing.
  • Return machine-readable results (JSON), and return errors as results too, a {"error": "city not found"} tool message lets the model recover or ask the user, instead of the loop dying.
  • For latency-sensitive agents, start with DeepSeek V4 Flash and move to V4 Pro when tasks need deeper reasoning between calls.
Function calling, Pearl Inference Docs