Define Functions in Python
On this page
From Theory to Code
You now understand the five-step process and the JSON Schema structure. It’s time to write real Python code. In this lesson, you’ll create functions, draft their specifications, and prepare everything for the Mistral API—everything that needs to exist before making the first network call.
Install the Mistral SDK
Start by installing the official package:
pip install mistralai
Next, configure your API key. Best practice dictates that you never hardcode it in your source files. Instead, read it from an environment variable so you can safely commit your code to version control.
export MISTRAL_API_KEY="your-api-key"
import os
from mistralai.client import Mistral
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
Create Python Functions
Let’s look at a worked example: a payment tracking system backed by a Pandas DataFrame. This is a classic example from the Mistral documentation, and it perfectly illustrates the concept—here, the DataFrame plays the role a database would in production.
import pandas as pd
import json
# Payment data
data = {
"transaction_id": ["T1001", "T1002", "T1003", "T1004", "T1005"],
"customer_id": ["C001", "C002", "C003", "C002", "C001"],
"payment_amount": [125.50, 89.99, 350.00, 45.00, 210.75],
"payment_date": ["2026-03-15", "2026-03-16", "2026-03-17", "2026-03-17", "2026-03-18"],
"payment_status": ["paid", "paid", "pending", "paid", "failed"]
}
df = pd.DataFrame(data)
Next comes the code that actually accesses this data. These are standard Python functions; nothing in their implementation hints that an LLM will drive them.
def retrieve_payment_status(df: pd.DataFrame, transaction_id: str) -> str:
"""Retrieves the status of a payment by its ID."""
if transaction_id in df["transaction_id"].values:
status = df[df["transaction_id"] == transaction_id]["payment_status"].values[0]
return json.dumps({"status": status})
return json.dumps({"error": f"Transaction {transaction_id} not found"})
def retrieve_payment_date(df: pd.DataFrame, transaction_id: str) -> str:
"""Retrieves the date of a payment by its ID."""
if transaction_id in df["transaction_id"].values:
date = df[df["transaction_id"] == transaction_id]["payment_date"].values[0]
return json.dumps({"date": date})
return json.dumps({"error": f"Transaction {transaction_id} not found"})
Two implementation details deserve your attention. First, these functions always return a JSON string, never a raw Python object. This is the format the model expects as a tool result, so json.dumps() is non-negotiable. Second, they explicitly handle the case where a transaction isn’t found instead of raising an exception. This error, formatted as JSON, returns to the model just like any other result, allowing it to politely inform the user that transaction T9999 doesn’t exist. You’ll see later in the course why this reflex is essential.
Write JSON Specifications
Each function needs a specification so the model can understand it. Here you apply the structure from the previous lesson to the two functions above—note the strict correspondence between the name fields and the actual Python function names.
tools = [
{
"type": "function",
"function": {
"name": "retrieve_payment_status",
"description": "Retrieves the status of a payment (paid, pending, failed) "
"based on the transaction ID.",
"parameters": {
"type": "object",
"properties": {
"transaction_id": {
"type": "string",
"description": "Unique transaction identifier (e.g., T1001)"
}
},
"required": ["transaction_id"]
}
}
},
{
"type": "function",
"function": {
"name": "retrieve_payment_date",
"description": "Retrieves the date a payment was made "
"based on the transaction ID.",
"parameters": {
"type": "object",
"properties": {
"transaction_id": {
"type": "string",
"description": "Unique transaction identifier (e.g., T1001)"
}
},
"required": ["transaction_id"]
}
}
}
]
Organize Functions in a Dictionary
The model returns a function name as a string. You need a way to map that string to the corresponding callable object without resorting to eval or a cascade of if statements. A dispatch dictionary solves this problem in three lines.
from functools import partial
# Bind the DataFrame to each function
available_functions = {
"retrieve_payment_status": partial(retrieve_payment_status, df),
"retrieve_payment_date": partial(retrieve_payment_date, df),
}
functools.partial addresses a specific, real-world difficulty. Your functions expect two arguments—the DataFrame and the ID—but the model provides only one. The model knows nothing about df, nor should it. partial pre-fills that first parameter once and for all. This same mechanism applies to database connections, HTTP clients, or authentication tokens. Dynamic invocation then becomes trivial.
function_name = "retrieve_payment_status"
function_params = {"transaction_id": "T1001"}
result = available_functions[function_name](**function_params)
# → '{"status": "paid"}'
The tool_choice Option
When sending these tools to the API, the tool_choice parameter decides how much freedom you give the model. Three values are possible.
# The model decides whether to use a tool or respond directly (default)
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="auto"
)
# Force the model to use a tool
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="any"
)
# Prevent the model from using tools
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="none"
)
The "auto" mode is most common in production for good reason: a payment tracking assistant should be able to say “hello” without triggering a database query. The model responds directly to general questions and uses functions only when relevant. A full lesson later in the course covers specific use cases for any and none.
Key Takeaways
- Functions must return JSON strings—never raw Python objects
- Use a dispatch dictionary to map function names to their implementations
functools.partialis ideal for pre-filling parameters like DB connections or DataFramestool_choicecontrols whether the model can or must use functions (auto,any,none)- Always handle error cases in your functions (invalid parameters, missing data)