Supercharging LLMs: A Guide to Tool Use with the Cerebras SDK
Build a financial assistant that can calculate the Simple Moving Average and more

Introduction
Large Language Models (LLMs) are incredibly powerful, but their knowledge is often limited to the data they were trained on. What if you could give them access to real-time information or allow them to interact with other systems? That's where "tools" come in. The Cerebras SDK makes it straightforward to equip your LLM applications with the ability to use custom functions, effectively extending their capabilities.
This improves upon the official guide by using a working demo API and fixing some code bugs. Thanks to the generous free API by Cerebras which lets you try out the (current) latest Qwen 3 model.
In this post, we'll walk through a practical example: building a financial assistant that can calculate the Simple Moving Average (SMA) for stock data. We'll use the Cerebras SDK to enable an LLM to call our custom Python function for this calculation.
A Stock-Savvy Assistant Version 1
Our objective is to create a system where a user can ask a question like, "What's the 10-day moving average for company A over the last 50 days?" and the LLM, instead of just guessing, can use a specific tool (our Python function) to get the precise answer.
Step 1: Gathering the Data
First, we need a way to get stock data. Our demo uses the Alpha Vantage API. While the original guide uses mock data, here we implement get_stocks_data function to fetch daily time series data for a given stock symbol using Alpha Vantage APIs. We need to convert the dict to a list with date as a key in each item.
import requests
# URLs for fetching stock data from Alpha Vantage API
urls = [
f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=APPL&outputsize=full&apikey={api_key}",
f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=GOOG&outputsize=full&apikey={api_key}",
]
def get_stocks_data(url: str) -> list[dict]:
"""
Fetches and processes daily time series stock data from a given Alpha Vantage URL.
"""
r = requests.get(url)
r.raise_for_status() # Good practice to check for request errors
data = r.json()
# Extract the 'Time Series (Daily)' data from the response
# Add error handling for unexpected API response structure
time_series_data = data.get("Time Series (Daily)")
if not time_series_data:
raise ValueError("Could not find 'Time Series (Daily)' in API response.")
stocks_data = []
# Reformat the data into a list of dictionaries
for s_date, s_data in time_series_data.items():
n = {"date": s_date}
n.update(s_data)
stocks_data.append(n)
return stocks_data
# Fetch stock data for two predefined companies
company_a_data = get_stocks_data(urls[0])
company_b_data = get_stocks_data(urls[1])
# Store the fetched company data in a dictionary for easy access
available_data = {
"company_a": company_a_data,
"company_b": company_b_data,
}
This function fetches data for "IBM" and "RELIANCE.BSE" and stores it in available_data.
Step 2: The Core Logic - Calculating Moving Average
Next, we define the function that will act as our "tool". The calculate_moving_average function takes a data reference (e.g., "company_a"), the number of days to consider, and the window size for the moving average
def calculate_moving_average(
data_reference: str, num_days: int, window_size: int
) -> list[dict[str, float]]:
"""
Calculates the moving average for a specified stock.
"""
if data_reference not in available_data:
raise ValueError(
f"Invalid data reference. Available options: {list(available_data.keys())}"
)
stock_data = available_data[data_reference]
if num_days < window_size:
raise ValueError("num_days must be greater than or equal to window_size")
if len(stock_data) < num_days:
raise ValueError("Insufficient data for the specified number of days")
# Data is typically sorted newest to oldest from Alpha Vantage.
# For SMA, we usually want to process oldest to newest or ensure correct slicing.
# Assuming data is sorted: stock_data[0] is newest, stock_data[-1] is oldest.
# To get the most recent 'num_days', we might need to reverse or slice carefully.
# For this example, let's assume the API returns data oldest to newest,
# or that get_stocks_data sorts it that way. If not, this needs adjustment.
# If data is newest to oldest:
# recent_data_points = stock_data[:num_days] # Get the N most recent
# recent_data_points.reverse() # Process oldest to newest for SMA calculation
# Let's stick to the original logic of taking the last 'num_days' from the list
# This implies the list is already sorted oldest to newest.
recent_data_points: list[dict] = stock_data[num_days:]
moving_averages: list[dict[str, float]] = []
if not recent_data_points or len(recent_data_points) < window_size:
return [] # Not enough data to calculate even one SMA
# Initialize the price window with the closing prices of the first 'window_size' days
price_window: list[float] = [
float(item["4. close"]) for item in recent_data_points[:window_size]
]
# Add the first SMA if we have exactly window_size data points
if len(price_window) == window_size:
moving_averages.append({
"date": recent_data_points[window_size -1]["date"], # Date of the last day in the window
"movingAverage": round(sum(price_window) / window_size, 2)
})
# Calculate moving average for the remaining days
for i in range(window_size, len(recent_data_points)):
current_data = recent_data_points[i]
current_price = float(current_data["4. close"])
price_window.pop(0) # Remove the oldest price
price_window.append(current_price) # Add the newest price
average = round(sum(price_window) / window_size, 2)
moving_averages.append({"date": current_data["date"], "movingAverage": average})
return moving_averages
This function performs the actual SMA calculation. Note the input validation and the logic for sliding the window.
Step 3: Describing the Tool to the LLM
For the LLM to understand how to use our function, we need to provide a schema for its parameters. We'll use Pydantic which is excellent for this:
from pydantic import BaseModel, Field
from typing import Literal
class CalculateMovingAverageArgs(BaseModel):
"""
Pydantic model defining the arguments for the calculate_moving_average function.
Used for schema validation and generation for AI tool usage.
"""
data_reference: Literal["company_a", "company_b"] = Field(
...,
description="The key to access specific stock data in the stock_data dictionary.",
)
num_days: int = Field(
..., description="The number of recent days to consider for calculation."
)
window_size: int = Field(..., description="The size of the moving average window.")
This CalculateMovingAverageArgs model clearly defines the expected inputs, their types, and descriptions.
Step 4: Registering the Tool with Cerebras SDK
Now, we tell the Cerebras SDK about our tool:
# Define the tool (function) that the AI model can use
tools = [
{
"type": "function",
"function": {
"name": "calculate_moving_average",
"description": "Calculate the moving average of stock data for a specified number of recent days.",
"parameters": CalculateMovingAverageArgs.model_json_schema(),
},
}
]
We create a list of tool definitions. Each tool has a type ("function"), a name (matching our Python function name), a description (for the LLM to understand its purpose), and parameters (using the JSON schema from our Pydantic model).
Step 5: Making the API Call
With everything set up, we can now interact with the LLM:
import os
from cerebras.cloud.sdk import Cerebras
# Define the messages for the AI model, including system prompt and user query
messages = [
{
"role": "system",
"content": "You are a helpful financial analyst. Use the supplied tools to assist the user.",
},
{
"role": "user",
"content": "What's the 10-day moving average for company A over the last 50 days?",
},
]
# Initialize the Cerebras SDK client using API key from environment variable
client = Cerebras(
api_key=os.environ.get("CEREBRAS_API_KEY"),
)
# Make a chat completion request to the Cerebras API
response = client.chat.completions.create(
model="qwen-3-32b", # Specify the AI model to use
messages=messages,
tools=tools, # Provide the available tools to the model
)
We initialize the Cerebras client, define our conversation messages, and importantly, pass our tools definition to the create method.
Step 6: Handling the LLM's Response
The LLM might respond directly, or it might decide to use our tool. We need to handle both cases
# Process the AI model's response
content = response.choices[0].message.content
if content:
print("AI Response Content:")
print(content)
# Check if the AI decided to call a function
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0] # Get the first tool call
function_call = tool_call.function
if function_call.name == "calculate_moving_average":
# Parse the arguments provided by the AI for the function call
try:
arguments = json.loads(function_call.arguments)
# Call the local function with the AI-provided arguments
result = calculate_moving_average(**arguments)
print("\\nResult of function call (calculate_moving_average):")
print(result)
except json.JSONDecodeError:
print(f"Error: Could not decode arguments: {function_call.arguments}")
except Exception as e:
print(f"Error executing function {function_call.name}: {e}")
If response.choices[0].message.tool_calls is present, it means the LLM wants to use one of our tools. We extract the function name and arguments, then execute our local calculate_moving_average function.
The commented-out section shows how you could then send this result back to the LLM. This allows the LLM to formulate a natural language response based on the data retrieved by the tool, making the interaction more conversational.
Here's a sample output:
[{'date': '2025-06-04', 'movingAverage': 200.0}, {'date': '2025-06-03', 'movingAverage': 200.76}, {'date': '2025-06-02', 'movingAverage': 201.09}, {'date': '2025-05-30', 'movingAverage': 201.53}, {'date': '2025-05-29', 'movingAverage': 201.6}, {'date': '2025-05-28', 'movingAverage': 201.77}, {'date': '2025-05-27', 'movingAverage': 201.52}, {'date': '2025-05-23', 'movingAverage': 200.9}, {'date': '2025-05-22', 'movingAverage': 200.65}, {'date': '2025-05-21', 'movingAverage': 200.79}, {'date': '2025-05-20', 'movingAverage': 201.2}, {'date': '2025-05-19', 'movingAverage': 201.75}, {'date': '2025-05-16', 'movingAverage': 202.7}, {'date': '2025-05-15', 'movingAverage': 203.77}, {'date': '2025-05-14', 'movingAverage': 205.0}, {'date': '2025-05-13', 'movingAverage': 206.25}, {'date': '2025-05-12', 'movingAverage': 207.31}, {'date': '2025-05-09', 'movingAverage': 207.64}, {'date': '2025-05-08', 'movingAverage': 207.25}, {'date': '2025-05-07', 'movingAverage': 206.67}, {'date': '2025-05-06', 'movingAverage': 205.83}, {'date': '2025-05-05', 'movingAverage': 204.84}, {'date': '2025-05-02', 'movingAverage': 204.25}, {'date': '2025-05-01', 'movingAverage': 204.44}, {'date': '2025-04-30', 'movingAverage': 204.46}, {'date': '2025-04-29', 'movingAverage': 204.28}, {'date': '2025-04-28', 'movingAverage': 204.22}, {'date': '2025-04-25', 'movingAverage': 205.29}, {'date': '2025-04-24', 'movingAverage': 206.38}, {'date': '2025-04-23', 'movingAverage': 207.22}, {'date': '2025-04-22', 'movingAverage': 207.34}, {'date': '2025-04-21', 'movingAverage': 206.77}, {'date': '2025-04-17', 'movingAverage': 205.93}, {'date': '2025-04-16', 'movingAverage': 204.03}, {'date': '2025-04-15', 'movingAverage': 202.99}, {'date': '2025-04-14', 'movingAverage': 202.12}, {'date': '2025-04-11', 'movingAverage': 200.92}, {'date': '2025-04-10', 'movingAverage': 199.03}, {'date': '2025-04-09', 'movingAverage': 198.08}, {'date': '2025-04-08', 'movingAverage': 194.87}]
Process finished with exit code 0
What happens if there is no suitable tool is available? Let us modify the message and try again to see the result:
messages = [
{
"role": "system",
"content": "You are a helpful financial analyst. Use the supplied tools to assist the user.",
},
{
"role": "user",
"content": "Give an overview of the company A",
},
]
# Make a chat completion request to the Cerebras API
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
tools=tools,
)
# Process the AI model's response
content = response.choices[0].message.content
if content:
print("AI Response Content:")
print(content)
This will print the message following:
I don't have access to a function/tool that provides general company overviews. The available tool only supports calculating moving averages for stock data. Would you like me to help with a moving average calculation for Company A's stock instead?
This is where multi tool use comes into picture. However, we are not implementing multi tool in this demo.
Stock-Savvy Assistant Version 2
If you noticed, we did not use the data from company B at all.
Let us fix that by comparing the SMA for both companies. However, there’s a challenge:
calculate_moving_averageandCalculateMovingAverageArgsacceptdata_referencenotavailable_data
We can either fix the signatures or use Python partial
No points for guessing, that we will use partial instead of repeating the same arg every where.
Here’s the updated version, with the following changes:
Use dotenv to read API Keys
Calculate SMA of both company A and B
Implement partials to use the same tool function and args with
available_data
import json
import os
from collections import deque
from functools import partial
from typing import Literal, List, Dict
import requests
from cerebras.cloud.sdk import Cerebras
from dotenv import load_dotenv
from pydantic import BaseModel, Field
load_dotenv()
v_api_key = os.environ.get("STOCK_API_KEY")
if not v_api_key:
print("Error: STOCK_API_KEY environment variable not set.")
STOCK_URLS = {
"company_a": f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=AAPL&outputsize=full&apikey={v_api_key}",
"company_b": f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=GOOG&outputsize=full&apikey={v_api_key}",
}
def get_stocks_data(url: str) -> List[Dict]:
"""
Fetches and processes daily time series stock data from a given Alpha Vantage URL.
... (docstring remains the same) ...
"""
response = requests.get(url)
response.raise_for_status()
data = response.json()
time_series_data = data.get("Time Series (Daily)")
if not time_series_data:
# Add the API response note to the error for better debugging
note = data.get("Note")
if note:
raise ValueError(f"API response missing 'Time Series (Daily)' data. Note: {note}")
raise ValueError("API response missing 'Time Series (Daily)' data.")
stocks_data = []
for date_str, daily_data in time_series_data.items():
record = {"date": date_str}
record.update(daily_data)
stocks_data.append(record)
stocks_data.reverse()
return stocks_data
def calculate_moving_average(
available_data: Dict, # <-- Dependency is now an explicit argument
data_reference: str,
num_days: int,
window_size: int,
) -> List[Dict[str, float]]:
"""
Calculates the moving average for a specified stock over a given number of days
and window size.
Args:
available_data: A dictionary containing the pre-loaded stock data.
... (other args remain the same) ...
"""
if data_reference not in available_data:
raise ValueError(
f"Invalid data reference. Available options: {list(available_data.keys())}"
)
stock_data = available_data[data_reference]
if num_days < window_size:
raise ValueError("num_days must be greater than or equal to window_size")
if len(stock_data) < num_days:
raise ValueError(
f"Insufficient data for the specified number of days. "
f"Requested: {num_days}, Available: {len(stock_data)}"
)
recent_data = stock_data[-num_days:]
moving_averages = []
price_window = deque(maxlen=window_size)
for i in range(len(recent_data)):
current_data = recent_data[i]
price_window.append(float(current_data["4. close"]))
if len(price_window) == window_size:
average = round(sum(price_window) / window_size, 2)
moving_averages.append(
{"date": current_data["date"], "movingAverage": average}
)
return moving_averages
def compare_moving_averages(
available_data: Dict, num_days: int, window_size: int
) -> Dict:
"""
Calculates and compares the latest moving average of two companies.
Args:
available_data: A dictionary containing the pre-loaded stock data.
... (other args remain the same) ...
"""
try:
# Pass the dependency down to the next function
ma_a_list = calculate_moving_average(
available_data, "company_a", num_days, window_size
)
ma_b_list = calculate_moving_average(
available_data, "company_b", num_days, window_size
)
latest_ma_a = ma_a_list[-1] if ma_a_list else None
latest_ma_b = ma_b_list[-1] if ma_b_list else None
if not latest_ma_a or not latest_ma_b:
return {
"error": "Could not calculate moving average for one or both companies."
}
result = {
"company_a": {
"latest_date": latest_ma_a["date"],
"moving_average": latest_ma_a["movingAverage"],
},
"company_b": {
"latest_date": latest_ma_b["date"],
"moving_average": latest_ma_b["movingAverage"],
},
"summary": (
f"As of their latest data points, Company A's {window_size}-day moving average is "
f"{latest_ma_a['movingAverage']} ({latest_ma_a['date']}), while Company B's is "
f"{latest_ma_b['movingAverage']} ({latest_ma_b['date']})."
),
}
return result
except ValueError as e:
return {"error": str(e)}
class CalculateMovingAverageArgs(BaseModel):
"""Defines arguments for the calculate_moving_average function."""
data_reference: Literal["company_a", "company_b"] = Field(..., description="The key for the stock data.")
num_days: int = Field(..., description="The number of recent days for calculation.")
window_size: int = Field(..., description="The size of the moving average window.")
class CompareMovingAveragesArgs(BaseModel):
"""Defines arguments for the compare_moving_averages function."""
num_days: int = Field(..., description="The number of recent days for calculation.")
window_size: int = Field(..., description="The size of the moving average window.")
def main():
"""
Main function to run the financial analyst assistant.
"""
print("Fetching stock data...")
loaded_data = {key: get_stocks_data(url) for key, url in STOCK_URLS.items()}
print("Data loaded successfully.")
calculate_moving_average_tool = partial(
calculate_moving_average, available_data=loaded_data
)
compare_moving_averages_tool = partial(
compare_moving_averages, available_data=loaded_data
)
tools = [
{
"type": "function",
"function": {
"name": "calculate_moving_average",
"description": "Calculate the moving average of a single company's stock data.",
"parameters": CalculateMovingAverageArgs.model_json_schema(),
},
},
{
"type": "function",
"function": {
"name": "compare_moving_averages",
"description": "Compare the moving averages of company A and company B.",
"parameters": CompareMovingAveragesArgs.model_json_schema(),
},
},
]
available_functions = {
"calculate_moving_average": calculate_moving_average_tool,
"compare_moving_averages": compare_moving_averages_tool,
}
messages = [
{
"role": "system",
"content": "You are a helpful financial analyst. Use the supplied tools to assist the user. "
"When comparing, state which company has the higher moving average and by how much.",
},
{
"role": "user",
"content": "How do the 10-day moving averages for company A and company B compare over the last 50 days?",
},
]
ai_api_key = os.environ.get("CEREBRAS_API_KEY")
if not ai_api_key:
print("Error: CEREBRAS_API_KEY environment variable not set.")
return
client = Cerebras(api_key=ai_api_key)
print("\n--- Sending request to LLM ---")
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
tools=tools,
tool_choice="auto",
)
response_message = response.choices[0].message
if response_message.tool_calls:
print("LLM decided to use a tool.")
tool_call = response_message.tool_calls[0]
function_name = tool_call.function.name
if function_name not in available_functions:
print(f"Error: LLM tried to call an unknown function: {function_name}")
return
print(f"Calling function: {function_name}")
function_to_call = available_functions[function_name]
try:
arguments = json.loads(tool_call.function.arguments)
tool_result = function_to_call(**arguments)
print("\n--- Tool Result ---")
print(json.dumps(tool_result, indent=2))
print("\n--- Sending tool result back to LLM for final answer ---")
messages.append(response_message)
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": json.dumps(tool_result),
}
)
final_response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
)
final_answer = final_response.choices[0].message.content
print("\n--- Final AI Analyst Answer ---")
print(final_answer)
except Exception as e:
print(f"An error occurred during tool execution: {e}")
else:
print("\n--- Direct LLM Response ---")
print(response_message.content)
if __name__ == "__main__":
main()
Here’s a sample response:
Data loaded successfully.
--- Sending request to LLM ---
LLM decided to use a tool.
Calling function: compare_moving_averages
--- Tool Result ---
{
"company_a": {
"latest_date": "2025-06-20",
"moving_average": 199.41
},
"company_b": {
"latest_date": "2025-06-20",
"moving_average": 176.11
},
"summary": "As of their latest data points, Company A's 10-day moving average is 199.41 (2025-06-20), while Company B's is 176.11 (2025-06-20)."
}
--- Sending tool result back to LLM for final answer ---
--- Final AI Analyst Answer ---
<think>
Okay, let me start by understanding the user's question. They want to compare the 10-day moving averages of company A and company B over the last 50 days. The tool response provided data for both companies as of June 20, 2025.
First, I need to verify if the data matches the query. The user asked for a 50-day comparison, but the tool's summary only gives the moving average as of the latest date. The answer should confirm whether the data spans the last 50 days correctly. Since the latest date is 2025-06-20 and the moving average is based on the current data up to that point, it's likely that the moving average reflects the closing prices over a period that includes the last 50 days. However, the exact comparison over the 50-day window isn't detailed in the tool's response beyond the current snapshot.
The key points here are the latest moving averages. The user asked for a comparison over the last 50 days, but the tool's output only shows the most recent numbers. This might mean that the user is interested in the current state of the moving averages, perhaps to assess recent performance trends.
Next, I should calculate the difference between the two moving averages. Company A's is 199.41 and Company B's is 176.11. Subtracting these gives 23.3 points. To make it more meaningful, I can express this difference as a percentage of the lower moving average (Company B's). The percentage difference is (23.3 / 176.11) * 100 ≈ 13.23%. That shows how much higher Company A's moving average is in relative terms.
I should also consider if there's any missing information. The user might expect a time series comparison or a trend analysis over the 50 days, but since the tool's response only provides the latest values, I need to clarify that my answer is based on a single point in time. It's possible that the 10-day moving averages fluctuated over the past 50 days, but without historical data, I can only state the current difference.
Therefore, the response should highlight the latest moving averages, the absolute difference, and the percentage difference. I should also mention that the data is as of June 20, 2025, and note that a more detailed 50-day trend would require additional historical data, which isn't provided here.
</think>
**Comparison of 10-day Moving Averages:**
- **Company A**: 199.41 (as of 2025-06-20)
- **Company B**: 176.11 (as of 2025-06-20)
**Conclusion**:
Company A's 10-day moving average is **$23.30 higher** than Company B's, representing a **13.23% increase** relative to Company B's value. This comparison reflects the latest data point for both companies, but historical trends over the 50-day period would require a time-series analysis (not included in the provided data).
Process finished with exit code 0
Why use tools with AI?
Access to Real-Time Data: LLMs can query live APIs for up-to-the-minute information.
Interacting with External Systems: Control databases, send emails, or interact with any system that has an API.
Performing Complex Calculations: Offload specialized computations to dedicated functions, like our SMA calculator.
Improved Accuracy and Reliability: Ground LLM responses in factual data retrieved by tools, rather than relying solely on its training.
By integrating tools with the Cerebras SDK, you can build more dynamic, capable, and reliable AI applications. This example is just the tip of the iceberg – the possibilities are vast!






