> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overcontrolgroup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Function calling

> Connect Lume to functions your application executes.

Function calling lets a Lume model request actions or data from your application. Describe a function, receive its name and arguments, run your code, and send the result back so the model can answer. All four Lume models support function calling, strict function tools, and parallel tool calls on both APIs.

Your application executes function tools. OverControl executes the separate [web search](https://docs.overcontrolgroup.com/tools/web-search.md) and [web fetch](https://docs.overcontrolgroup.com/tools/web-fetch.md) tools during a request.

## Before you start

Create an API key in the [dashboard](https://app.overcontrolgroup.com/dashboard) with `chat:completions` for Chat Completions or `messages:create` for Messages. If your key has a model allowlist, include `lume-3.5`. Keep the key on your server and set it in the shell where you run the examples:

```bash theme={null}
export OVERCONTROL_API_KEY="your_api_key"
```

Send one authentication header per request: `Authorization: Bearer` or `x-api-key`. Supplying both with values returns HTTP 401.

For SDK examples, install the client you use. cURL examples do not need an SDK.

```bash Python: Chat Completions theme={null}
python -m pip install openai
```

```bash Python: Messages theme={null}
python -m pip install anthropic
```

Run Python examples from a `.py` file.

Messages accepts an optional `anthropic-version` header. If supplied, it must be one valid calendar date in `YYYY-MM-DD` form. It is a compatibility hint and does not change the supported features. The SDK supplies its version header automatically.

## Chat Completions

Call `POST /v1/chat/completions` with `chat:completions`. Declare each function as a `tools` entry with `type: "function"`. Its parameters are a JSON Schema object. `strict: true` requests strict argument handling.

The example below completes a two-request round trip. `get_temperature` uses fixed demonstration data so you can run the example without another API key. Replace that function with your application's data source.

```python theme={null}
import json
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["OVERCONTROL_API_KEY"],
    base_url="https://api.overcontrolgroup.com/v1",
)

def get_temperature(city):
    if not isinstance(city, str) or city.casefold() != "lisbon":
        raise ValueError("This demonstration only supports Lisbon.")
    return {"city": "Lisbon", "celsius": 18, "source": "demo"}

tools = [{
    "type": "function",
    "function": {
        "name": "get_temperature",
        "description": "Look up demonstration temperature data for Lisbon.",
        "strict": True,
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
            "additionalProperties": False,
        },
    },
}]
messages = [{"role": "user", "content": "Look up the demonstration temperature for Lisbon."}]

first = client.chat.completions.create(
    model="lume-3.5",
    max_tokens=256,
    messages=messages,
    tools=tools,
    tool_choice={"type": "function", "function": {"name": "get_temperature"}},
    parallel_tool_calls=False,
)
choice = first.choices[0]
if choice.finish_reason != "tool_calls" or not choice.message.tool_calls:
    raise RuntimeError("No complete function call was returned.")

assistant = choice.message
messages.append({
    "role": "assistant",
    "content": assistant.content,
    "tool_calls": [call.model_dump(exclude_none=True) for call in assistant.tool_calls],
})
for call in assistant.tool_calls:
    if call.function.name != "get_temperature":
        raise ValueError("Unknown function.")
    arguments = json.loads(call.function.arguments)
    if not isinstance(arguments, dict) or set(arguments) != {"city"}:
        raise ValueError("Unexpected function arguments.")
    result = get_temperature(arguments["city"])
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps(result),
    })

final = client.chat.completions.create(
    model="lume-3.5",
    max_tokens=256,
    messages=messages,
    tools=tools,
    tool_choice="none",
)
if final.choices[0].finish_reason != "stop":
    raise RuntimeError("The final answer did not complete.")
print(final.choices[0].message.content)
```

`function.arguments` is a JSON string. Parse it before calling your function. Keep the assistant's tool calls in the conversation, then add a `role: "tool"` message for each result using the matching `tool_call_id`. The second request above disables further calls to make the demonstration finite.

| Chat `tool_choice` | Behavior |
| - | - |
| Omitted or `"auto"` | The model can answer directly or request a function. |
| `"none"` | Do not call a tool. |
| `"required"` | Require a tool call. |
| `{"type":"function","function":{"name":"get_temperature"}}` | Select a declared function. |

Set `parallel_tool_calls: false` when your application should receive one call at a time. If parallel calls are allowed, handle every returned call and pair each with its own result. Assistant text can accompany tool calls; use `finish_reason` and `tool_calls` to determine what work is needed.

Older clients can send `functions` and `function_call`. Those responses use `message.function_call` and `finish_reason: "function_call"`. Use `tools` for new integrations.

## Messages

Call `POST /v1/messages` with `messages:create`. A function tool has `name`, `description`, and `input_schema`; omit `type` or use `"custom"`. `strict: true` is supported.

This complete example uses the same fixed demonstration lookup and returns the result in the next user message.

```python theme={null}
import json
import os
from anthropic import Anthropic

client = Anthropic(
    api_key=os.environ["OVERCONTROL_API_KEY"],
    base_url="https://api.overcontrolgroup.com",
)

def get_temperature(city):
    if not isinstance(city, str) or city.casefold() != "lisbon":
        raise ValueError("This demonstration only supports Lisbon.")
    return {"city": "Lisbon", "celsius": 18, "source": "demo"}

tools = [{
    "name": "get_temperature",
    "description": "Look up demonstration temperature data for Lisbon.",
    "strict": True,
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
        "additionalProperties": False,
    },
}]
messages = [{"role": "user", "content": "Look up the demonstration temperature for Lisbon."}]

first = client.messages.create(
    model="lume-3.5",
    max_tokens=256,
    messages=messages,
    tools=tools,
    tool_choice={"type": "tool", "name": "get_temperature", "disable_parallel_tool_use": True},
)
calls = [block for block in first.content if block.type == "tool_use"]
if first.stop_reason != "tool_use" or not calls:
    raise RuntimeError("No complete function call was returned.")

messages.append({
    "role": "assistant",
    "content": [block.model_dump(exclude_none=True) for block in first.content],
})
results = []
for call in calls:
    if call.name != "get_temperature":
        raise ValueError("Unknown function.")
    if not isinstance(call.input, dict) or set(call.input) != {"city"}:
        raise ValueError("Unexpected function arguments.")
    result = get_temperature(call.input["city"])
    results.append({
        "type": "tool_result",
        "tool_use_id": call.id,
        "content": json.dumps(result),
    })
messages.append({"role": "user", "content": results})

final = client.messages.create(
    model="lume-3.5",
    max_tokens=256,
    messages=messages,
    tools=tools,
    tool_choice={"type": "none"},
)
if final.stop_reason != "end_turn":
    raise RuntimeError("The final answer did not complete.")
for block in final.content:
    if block.type == "text":
        print(block.text)
```

Messages returns `tool_use` blocks with an `input` object and `stop_reason: "tool_use"`. Put each `tool_result` in a user message, preserving the matching `tool_use_id`. A result must refer to a preceding assistant call. For a function failure, return a tool result with `is_error: true` and a useful error description.

| Messages `tool_choice` | Behavior |
| - | - |
| Omitted or `{"type":"auto"}` | Let the model decide. |
| `{"type":"none"}` | Do not call a tool. |
| `{"type":"any"}` | Require a tool call. |
| `{"type":"tool","name":"get_temperature"}` | Select a declared function. |

Use `disable_parallel_tool_use: true` inside an `auto`, `any`, or named `tool` choice to disable parallel calls. That option is not valid with a `none` choice.

### Compatible tool history

On a Messages `tool_use`, `caller` can be absent or exactly `{"type":"direct"}`. This describes the normal function-calling flow; your application still executes the function. Other caller types, including programmatic code-execution callers, are unsupported.

On `tool_use` and `tool_result`, `toolset_name` can be absent or `null`. A named toolset is unsupported. An empty string or malformed value is invalid. Accepting `null` does not enable named toolsets.

A tool result can contain a string or supported text/image blocks. Images need Lume Omni and must fit its media limits. PDF documents and arbitrary nested block types are not supported inside a client `tool_result`.

## Limits and execution

You can declare up to **64 tools**. Function names must match `^[A-Za-z0-9_-]{1,64}$`. Each function schema can be at most **16,384 bytes**. Messages also limits combined schemas to 512 KiB, individual descriptions to 64 KiB, and combined descriptions to 512 KiB.

Lume 2 allows up to 16,384 output tokens; the other models allow up to 32,768. Chat accepts one of `max_tokens` and `max_completion_tokens`, defaulting to the model maximum; Messages requires a positive `max_tokens`. Both APIs reduce the budget to the model maximum and remaining context space. Tool definitions and results consume context too.

Strict tools shape the requested arguments. Validate names, arguments, and application permissions before execution. A function call does not grant access to another user's data or permission to perform an action. For a general schema, use a JSON Schema validator rather than relying only on the small checks in these examples.

For a longer workflow, repeat the call/result cycle until the model finishes, with an application limit on the number of rounds. Check for Chat `length` or Messages `max_tokens` before executing an incomplete call. When streaming, accumulate Chat `delta.tool_calls` or Messages `input_json_delta` fragments before parsing arguments.

## Usage and common errors

Both requests in a function round trip consume model tokens, including the function definitions and results you send. Your function's own data-source costs are separate. Customer-executed functions have no OverControl server-side web-tool operation charge. [Pricing](https://docs.overcontrolgroup.com/pricing.md) explains model usage and the separate web-tool charges.

Requests reserve estimated usage before execution and release unused reserved usage afterward. HTTP 400 can indicate an undeclared function, malformed arguments or history, an unsupported caller, or a schema limit. HTTP 401 indicates invalid authentication; HTTP 403 can indicate missing scope; Messages uses HTTP 404 for unknown or disallowed models. HTTP 429 with `Insufficient credits` means available usage cannot cover the reservation. Keep `X-Request-Id` for support; Messages also returns `request-id`.

## Next

> [card] [Web search](https://docs.overcontrolgroup.com/tools/web-search.md)
>
> Let OverControl find web sources during a request.

> [card] [Structured outputs](https://docs.overcontrolgroup.com/text/structured-outputs.md)
>
> Return JSON text instead of requesting a function call.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.