Skip to content

Tool Calling

Before You Run Examples

All examples in this page assume environment variables are available. For convenience, create a .env file in the repository root (you can copy from .env.example) with:

TUDELFT_TULIP_BASE_URL=https://api.tulip.tudelft.nl
TUDELFT_TULIP_API_KEY=YOUR_API_KEY
TUDELFT_TULIP_MODEL_ALIAS=code
TUDELFT_TULIP_CAPABILITY=code

Set TUDELFT_TULIP_BASE_URL to host root only (for example https://api.tulip.tudelft.nl). The Python helper normalizes it to a capability-specific /v1 endpoint automatically.

What these examples assume

  • The Python snippets on this page assume a local helper file common.py that loads environment variables and exports shared constants.
  • If you cloned this repository, examples/common.py is already present and the scripts run as shown below.
  • If you have not cloned the repo and are working with Python, expand the helper snippet below and place common.py next to the example you want to run.
  • Python run commands use pixi run python ...; dependencies come from pixi.toml.
  • Bash examples pretty-print JSON using jq, so you may need to install it.
  • TUDELFT_TULIP_CAPABILITY chooses the path segment (for example code or chat).
  • TUDELFT_TULIP_MODEL_ALIAS chooses the deployed model alias.
  • For strict OpenAI-style tool_calls output, use the currently verified code alias.
If you have not cloned the repo and are working with Python, create common.py next to the example
common.py
import os

from dotenv import load_dotenv

load_dotenv()  # loads .env if present

TUDELFT_TULIP_BASE_URL = os.getenv(
    "TUDELFT_TULIP_BASE_URL", "https://api.tulip.tudelft.nl"
)
TUDELFT_TULIP_CAPABILITY = os.getenv("TUDELFT_TULIP_CAPABILITY", "chat")
TUDELFT_TULIP_API_KEY = os.getenv("TUDELFT_TULIP_API_KEY", "DUMMY_KEY")
TUDELFT_TULIP_MODEL_ALIAS = os.getenv("TUDELFT_TULIP_MODEL_ALIAS", "chat")

# Normalize TUDELFT_TULIP_BASE_URL to capability/v1. Docs recommend host root only,
# but we also accept URLs that already include capability or capability/v1.
_base_url = TUDELFT_TULIP_BASE_URL.rstrip("/")
if _base_url.endswith("/v1"):
    TUDELFT_TULIP_BASE_URL = _base_url
elif _base_url.endswith(f"/{TUDELFT_TULIP_CAPABILITY}"):
    TUDELFT_TULIP_BASE_URL = f"{_base_url}/v1"
else:
    TUDELFT_TULIP_BASE_URL = f"{_base_url}/{TUDELFT_TULIP_CAPABILITY}/v1"

Endpoint

Tool calling is available at /v1/chat/completions via the tools parameter.

Endpoint And Format Limitations

  • These examples use Chat Completions endpoint (client.chat.completions.create, messages=[...]).
  • Although OpenAI also documents a Responses API variant (client.responses.create, input=[...]), tool calling via Responses currently does not work in this environment due to a backend serving limitation.
  • Tool-calling output shape is model/capability dependent; some configurations emit raw tool markup in message.content instead of structured message.tool_calls.
  • For strict OpenAI-style checks in this tutorial, treat a non-empty message.tool_calls array as the pass condition.
  • For more, seeOpenAI Function Calling Guide.

Supported Models

For available aliases and current backing models, see Model Alias Mapping.

Emission Test

curl-tool-call-emission.sh
#!/bin/bash                                                                     

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "${SCRIPT_DIR}/../.env" ]; then
  source "${SCRIPT_DIR}/../.env"
fi

curl -s --fail --show-error "${TUDELFT_TULIP_BASE_URL}/${TUDELFT_TULIP_CAPABILITY}/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TUDELFT_TULIP_API_KEY}" \
  -d '{
    "model": "'"${TUDELFT_TULIP_MODEL_ALIAS}"'",
    "messages": [
      {
        "role": "user",
        "content": "Call the get_system_health tool now. Do not answer in natural language."
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_system_health",
          "description": "Returns current health status.",
          "parameters": {
            "type": "object",
            "properties": {}
          }
        }
      }
    ],
    "tool_choice": "required",
    "temperature": 0,
    "max_tokens": 64
  }' | jq .
Run:
bash examples/curl-tool-call-emission.sh

SDK: OpenAI Python client (OpenAI)

python_tool_call_emission.py
from common import (
    TUDELFT_TULIP_API_KEY,
    TUDELFT_TULIP_BASE_URL,
    TUDELFT_TULIP_MODEL_ALIAS,
)
from openai import OpenAI

client = OpenAI(base_url=TUDELFT_TULIP_BASE_URL, api_key=TUDELFT_TULIP_API_KEY)
model = TUDELFT_TULIP_MODEL_ALIAS

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_system_health",
            "description": "Returns current health status.",
            "parameters": {
                "type": "object",
                "properties": {},
            },
        },
    }
]

completion = client.chat.completions.create(
    model=model,
    messages=[
        {
            "role": "user",
            "content": (
                "Call the get_system_health tool now. "
                "Do not answer in natural language."
            ),
        }
    ],
    tools=tools,
    tool_choice="required",
    temperature=0,
    max_tokens=64,
)

print(completion.model_dump_json(indent=2))
Run:
pixi run python examples/python_tool_call_emission.py

Success looks like

  • Response includes a tool_calls array.
  • The requested tool name and arguments are present in output.

Loop Test

curl-tool-call-loop.sh
#!/bin/bash                                                                     

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "${SCRIPT_DIR}/../.env" ]; then
  source "${SCRIPT_DIR}/../.env"
fi

curl -s --fail --show-error "${TUDELFT_TULIP_BASE_URL}/${TUDELFT_TULIP_CAPABILITY}/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TUDELFT_TULIP_API_KEY}" \
  -d '{
    "model": "'"${TUDELFT_TULIP_MODEL_ALIAS}"'",
    "messages": [
      {
        "role": "user",
        "content": "Hey, quick check: is everything up and running?"
      },
      {
        "role": "assistant",
        "tool_calls": [
          {
            "id": "call-1",
            "type": "function",
            "function": {
              "name": "get_system_health",
              "arguments": "{}"
            }
          }
        ]
      },
      {
        "role": "tool",
        "tool_call_id": "call-1",
        "content": "{\"status\":\"ok\",\"uptime_seconds\":372045}"
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_system_health",
          "description": "Returns current health status.",
          "parameters": {
            "type": "object",
            "properties": {}
          }
        }
      }
    ],
    "temperature": 0,
    "max_tokens": 128
  }' | jq .
Run:
bash examples/curl-tool-call-loop.sh

SDK: OpenAI Python client (OpenAI)

python_tool_call_loop.py
from common import (
    TUDELFT_TULIP_API_KEY,
    TUDELFT_TULIP_BASE_URL,
    TUDELFT_TULIP_MODEL_ALIAS,
)
from openai import OpenAI

client = OpenAI(base_url=TUDELFT_TULIP_BASE_URL, api_key=TUDELFT_TULIP_API_KEY)
model = TUDELFT_TULIP_MODEL_ALIAS

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_system_health",
            "description": "Returns current health status.",
            "parameters": {
                "type": "object",
                "properties": {},
            },
        },
    }
]

completion = client.chat.completions.create(
    model=model,
    messages=[
        {
            "role": "user",
            "content": "Hey, quick check: is everything up and running?",
        },
        {
            "role": "assistant",
            "tool_calls": [
                {
                    "id": "call-1",
                    "type": "function",
                    "function": {
                        "name": "get_system_health",
                        "arguments": "{}",
                    },
                }
            ],
        },
        {
            "role": "tool",
            "tool_call_id": "call-1",
            "content": '{"status":"ok","uptime_seconds":372045}',
        },
    ],
    tools=tools,
    temperature=0,
    max_tokens=128,
)

print(completion.model_dump_json(indent=2))
Run:
pixi run python examples/python_tool_call_loop.py

Success looks like

  • The model consumes the provided tool message.
  • Final assistant response is coherent and consistent with tool output.