Function calling lets models go beyond generating text. Instead of just answering questions, the model can decide to call functions you define, fill in the required arguments, and hand the results back to you for execution. This is the foundation of agentic workflows, where the model acts as a decision-maker that coordinates between user input and your application's capabilities.
JSON Mode and JSON Schema complement function calling by giving you control over the structure of the model's output. Whether you need a simple JSON object or a response that conforms to a specific schema, these features ensure you get machine-readable output you can parse reliably.
How function calling works
The flow has three stages:
- You define tools: Describe your functions as JSON schemas, specifying the name, description, and expected parameters. Include these in the
toolsparameter of your API request. - The model decides: Based on the user's input, the model either responds conversationally or suggests a function call with filled-in arguments.
- You execute and return: If the model suggests a function call, you run the function with the provided arguments, then pass the result back to the model so it can incorporate it into its response.
Supported models
| Model | Notes |
|---|---|
MiniMax-M2.5 | Recommended for applications combining conversation and tool calling. |
DeepSeek-V3.2 | |
DeepSeek-V3.1-Terminus | |
gpt-oss-120B | For best results, set reasoning_effort to high. |
Important: Smaller models (8B-class) cannot reliably maintain conversations alongside tool-calling definitions. Use 70B+ models for production function calling.
Function calling walkthrough
The example below uses a simple weather lookup function to demonstrate the full flow.
Step 1: Define your tools and make the initial request
python
from openai import OpenAI
import json
import random
client = OpenAI(
base_url="https://bczfskny6zqw.poweredby.snova.ai/v1",
api_key="your-mara-api-key",
)
MODEL = "MiniMax-M2.5"
# A simple mock function (replace with a real API in production)
def get_weather(city: str) -> dict:
temp = random.randint(20, 50)
return {"city": city, "temperature_celsius": temp}
# Define the function schema
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a given city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name, e.g. San Francisco, CA",
}
},
"required": ["city"],
},
},
}
]
messages = [{"role": "user", "content": "What's the weather like in Paris today?"}]
# Send the request with tools
completion = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
)Step 2: Handle the tool call and execute the function
If the model decides to call a function, extract the call details and run it.
python
tool_call = completion.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
result = get_weather(args["city"])Step 3: Return the result to the model
Pass the function output back so the model can generate a natural language response.
python
messages.append(completion.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(
model=MODEL,
messages=messages,
)
print(final.choices[0].message.content)Expected output:
text
The current weather in Paris is 25 degrees Celsius.Controlling tool choice
The
tool_choice parameter controls how the model uses your tools:| Value | Behavior |
|---|---|
"auto" | The model decides whether to call a function or respond directly. This is the default. |
"required" | Forces the model to call at least one function. |
{"type": "function", "function": {"name": "get_weather"}} | Forces the model to call a specific function. |
JSON Mode
JSON Mode ensures the model's response is a valid JSON object. This is useful when you need structured output but don't require a specific schema.
Set
response_format to {"type": "json_object"} and include formatting instructions in your system prompt.python
response = client.chat.completions.create(
model="MiniMax-M2.5",
messages=[
{
"role": "system",
"content": 'Always respond in this JSON format: {"country": "name", "capital": "name"}',
},
{"role": "user", "content": "What is the capital of Austria?"},
],
response_format={"type": "json_object"},
)
print(response.choices[0].message.content)Expected output:
json
{"country": "Austria", "capital": "Vienna"}Note: If the model fails to produce valid JSON, the API returns an error: "Model did not output valid JSON."
JSON Schema
For more control over the output structure, use JSON Schema mode. Instead of just requesting "any valid JSON," you define the exact shape of the response.
python
response_format = {
"type": "json_schema",
"json_schema": {
"name": "data_extraction",
"schema": {
"type": "object",
"properties": {
"section": {"type": "string"},
"products": {
"type": "array",
"items": {"type": "string"},
},
},
"required": ["section", "products"],
"additionalProperties": False,
},
"strict": False,
},
}
response = client.chat.completions.create(
model="MiniMax-M2.5",
messages=[
{
"role": "system",
"content": "You are an expert at structured data extraction. Convert unstructured text into the given structure.",
},
{
"role": "user",
"content": "Section 24 has appliances and video games.",
},
],
response_format=response_format,
)
print(response.choices[0].message.content)Expected output:
json
{"section": "24", "products": ["appliances", "video games"]}Important: Set
strict to false. Strict mode (true) is not yet supported. When available, it will enforce exact schema compliance rather than best-effort matching.Next steps
- Error Codes - Understand API error responses and how to handle them.