Tool Calling
Several local models support OpenAI-compatible tool calling, allowing the model to invoke functions you define.
Supported models
Section titled “Supported models”Loading model list …
Example
Section titled “Example”The model can’t check the weather on its own — but with a tool, it can call a real API. This example uses Open-Meteo (free, no API key needed):
import jsonimport requestsfrom openai import OpenAI
client = OpenAI( base_url="https://maki.uni-mannheim.de/v1", api_key="your-api-key",)
tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "City name, e.g. 'Mannheim'", } }, "required": ["city"], }, }, }]
def get_weather(city: str) -> str: """Call the Open-Meteo API to get current weather.""" geo = requests.get( "https://geocoding-api.open-meteo.com/v1/search", params={"name": city, "count": 1}, ).json() loc = geo["results"][0]
weather = requests.get( "https://api.open-meteo.com/v1/forecast", params={ "latitude": loc["latitude"], "longitude": loc["longitude"], "current": "temperature_2m,wind_speed_10m", }, ).json()["current"]
return json.dumps({ "city": loc["name"], "temperature_c": weather["temperature_2m"], "wind_speed_kmh": weather["wind_speed_10m"], })
messages = [ {"role": "user", "content": "What's the weather like in Mannheim?"}]
# Step 1: The model decides it needs the weather toolresponse = client.chat.completions.create( model="gemma4-26b", messages=messages, tools=tools,)
message = response.choices[0].message
# Step 2: Execute the tool call and send the result backif message.tool_calls: call = message.tool_calls[0] args = json.loads(call.function.arguments)
# Call the real API result = get_weather(args["city"])
# Send tool result back to the model messages.append(message) messages.append({ "role": "tool", "tool_call_id": call.id, "content": result, })
# Step 3: The model uses the result to answer final = client.chat.completions.create( model="gemma4-26b", messages=messages, tools=tools, ) print(final.choices[0].message.content) # "It's currently 18.3 °C in Mannheim with a wind speed of 12.5 km/h."With PydanticAI
Section titled “With PydanticAI”PydanticAI handles the tool-calling loop for you — you just decorate a function and the framework takes care of the rest:
import jsonimport requestsfrom pydantic_ai import Agentfrom pydantic_ai.models.openai import OpenAIChatModelfrom pydantic_ai.providers.openai import OpenAIProvider
model = OpenAIChatModel( "gemma4-26b", provider=OpenAIProvider( base_url="https://maki.uni-mannheim.de/v1", api_key="your-api-key", ),)
agent = Agent(model)
@agent.tool_plaindef get_weather(city: str) -> str: """Get the current weather for a city.
Args: city: City name, e.g. 'Mannheim'. """ geo = requests.get( "https://geocoding-api.open-meteo.com/v1/search", params={"name": city, "count": 1}, ).json() loc = geo["results"][0]
weather = requests.get( "https://api.open-meteo.com/v1/forecast", params={ "latitude": loc["latitude"], "longitude": loc["longitude"], "current": "temperature_2m,wind_speed_10m", }, ).json()["current"]
return json.dumps({ "city": loc["name"], "temperature_c": weather["temperature_2m"], "wind_speed_kmh": weather["wind_speed_10m"], })
result = agent.run_sync("What's the weather like in Mannheim?")print(result.output)# "It's currently 18.3 °C in Mannheim with a wind speed of 12.5 km/h."