Function Calls (Tool Calls) 101
Author: Zhaohan Dong
Date: Jan 17, 2025
You might be familiar with basic Grok API operations such as building a chatbot, or letting LLM solve questions based on your conversation input.
With Function Calls (aka Tool Calls), you can expand Grok's capability by interacting with your local system, so Grok can ask your local system perform tasks such as updating a database, call another API to allocate resources, or find information on the website, etc.
Objectives
By the end of this tutorial, you should be able to set up a basic Function Call for retrieving data and using it with Grok.
Example Scenario
Imagine it's winter time and you are sitting snugly at your place. You are trying to put together a ski trip with family and friends in 3 days. You are not sure what the weather would be and what's the best way to prepare.
Of course you can Google the weather, but you also want Grok to give you some personalized advices. You set out to build a chatbot with function call that retrieves live weather forecast.
Overview of Function Call (Tool Call)
Suggested reading: Function calling on xAI API Documentation
Letting Grok use function call involves:
- Defining a function to perform desired actions on your system.
- Make the function parameter signature available to Grok in your API request, so Grok knows that these functions are available for use on your local system.
- When Grok determines that your request requires additional information/action available through those functions, it will send a
tool_callobject in the API response message. - Your system handles the
tool_callasked by Grok, and return the result to Grok. (You can also add optional request for Grok) - Grok generates response using those results, and might ask for more
tool_callsbased on the specific case.

In a nutshell — Grok calls our local function in xAI API response, and our local function returns result to Grok via xAI API request.
Building Blocks
Now with the basic ideas of function calling, let's set up to build the following:
- Functions that Grok can use
- Function call handler to handle when Grok asks for a function call in response
- Set up xAI API chat request and response pipeline
First let's install some dependencies:
Python (OpenAI)
!pip install openai pydantic --quiet
Python (OpenAI)
from openai import OpenAI
XAI_API_KEY = "" # paste your API key here
client = OpenAI(
base_url="https://api.x.ai/v1",
api_key=XAI_API_KEY
)
1. Creating Functions locally that Grok can use
To fetch the weather forecast, we will use the NOAA API Web Service.
We can get a 7-day weather forecast on a 2.5km grid area in the following format, by retrieving data from
https://api.weather.gov/gridpoints/{wfo}/{x},{y}/forecast. The wfo and {x},{y} can be obtained from https://api.weather.gov/points/{latitude},{longitude}
For example, the following is a weather forecast for Boston, MA, retrieved from endpoint https://api.weather.gov/gridpoints/BOX/72,90/forecast
JSON
{
"@context": [
"https://geojson.org/geojson-ld/geojson-context.jsonld",
{
"@version": "1.1",
"wx": "https://api.weather.gov/ontology#",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#"
}
],
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-71.029600000000002, 42.345599999999997],
[-71.0244, 42.366999999999997],
[-71.053399999999996, 42.370799999999996],
[-71.058599999999998, 42.349399999999996],
[-71.029600000000002, 42.345599999999997]
]
]
},
"properties": {
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2025-01-17T18:07:01+00:00",
"updateTime": "2025-01-17T15:18:01+00:00",
"validTimes": "2025-01-17T09:00:00+00:00/P8DT6H",
"elevation": {
"unitCode": "wmoUnit:m",
"value": 0.91439999999999999
},
"periods": [
{
"number": 1,
"name": "This Afternoon",
"startTime": "2025-01-17T13:00:00-05:00",
"endTime": "2025-01-17T18:00:00-05:00",
"isDaytime": true,
"temperature": 34,
"temperatureUnit": "F",
"temperatureTrend": "",
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": null
},
"windSpeed": "5 to 8 mph",
"windDirection": "NW",
"icon": "https://api.weather.gov/icons/land/day/few?size=medium",
"shortForecast": "Sunny",
"detailedForecast": "Sunny, with a high near 34. Northwest wind 5 to 8 mph."
},
{
"number": 2,
"name": "Tonight",
"startTime": "2025-01-17T18:00:00-05:00",
"endTime": "2025-01-18T06:00:00-05:00",
"isDaytime": false,
"temperature": 28,
"temperatureUnit": "F",
"temperatureTrend": "",
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": null
},
"windSpeed": "5 to 10 mph",
"windDirection": "S",
"icon": "https://api.weather.gov/icons/land/night/sct?size=medium",
"shortForecast": "Partly Cloudy",
"detailedForecast": "Partly cloudy, with a low around 28. South wind 5 to 10 mph."
},
{
"number": 3,
"name": "Saturday",
"startTime": "2025-01-18T06:00:00-05:00",
"endTime": "2025-01-18T18:00:00-05:00",
"isDaytime": true,
"temperature": 44,
"temperatureUnit": "F",
"temperatureTrend": "",
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 70
},
"windSpeed": "10 to 14 mph",
"windDirection": "S",
"icon": "https://api.weather.gov/icons/land/day/rain,30/rain,70?size=medium",
"shortForecast": "Light Rain Likely",
"detailedForecast": "Rain likely after 10am. Cloudy, with a high near 44. South wind 10 to 14 mph. Chance of precipitation is 70%. New rainfall amounts less than a tenth of an inch possible."
},
{
"number": 4,
"name": "Saturday Night",
"startTime": "2025-01-18T18:00:00-05:00",
"endTime": "2025-01-19T06:00:00-05:00",
"isDaytime": false,
"temperature": 33,
"temperatureUnit": "F",
"temperatureTrend": "",
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 60
},
"windSpeed": "7 to 10 mph",
"windDirection": "SW",
"icon": "https://api.weather.gov/icons/land/night/rain,60/bkn?size=medium",
"shortForecast": "Light Rain Likely then Mostly Cloudy",
"detailedForecast": "Rain likely and patchy fog before 11pm. Mostly cloudy, with a low around 33. Southwest wind 7 to 10 mph. Chance of precipitation is 60%. New rainfall amounts less than a tenth of an inch possible."
}
// ...
]
}
}
For our use case, we will limit the forecast area to a few popular ski areas, and return the properties.periods from the API response from NOAA to Grok.
Here, we will define the function inputs and outputs using Pydantic:
Python (OpenAI)
from enum import Enum
from typing import Literal
from pydantic import BaseModel, Field
import requests
# Available ski resorts
class SkiResort(str, Enum):
aspen = 'aspen'
breckenridge = 'breckenridge'
jackson_hole = 'jackson_hole'
vali = 'vali'
# Tool call request available to Grok
class ForecastRequest(BaseModel):
location: SkiResort = Field(description="Ski resort location name in snake case")
# Probability of precipitation used in response body definition
class ProbabilityOfPrecipitation(BaseModel):
unitCode: str = Field(description="Unit code of precipitation")
value: int | None = Field(description="Probability of precipitation in unitCode")
# Response format to send back to Grok
class ForecastResponse(BaseModel):
number: int = Field(description="Index of the forecast in the sequence")
name: str = Field(description="Name of the report period, relative to today")
startTime: str = Field(description="ISO8601 format of forecasting period start with timezone")
endTime: str = Field(description="ISO8601 format of forecasting period end with timezone")
isDaytime: bool = Field(description="Whether forecasting period is daytime. True if it is daytime")
temperature: int = Field(description="Temperature in temperatureUnit unit")
temperatureUnit: Literal["C", "F"] = Field(description="Temperature Unit")
temperatureTrend: str = Field(description="Description of temperature trend")
probabilityOfPrecipitation: ProbabilityOfPrecipitation = Field(description="Probability of Precipitation")
windSpeed: str = Field(description="Description of Wind Speed")
windDirection: str = Field(description="Wind direction")
shortForecast: str = Field(description="A short summary of forecast condition")
detailedForecast: str = Field(description="Detailed description of the forecast")
# URLs of the forecast locations
skiResortForecastUrl: dict[SkiResort, str] = {
SkiResort.aspen : "https://api.weather.gov/gridpoints/GJT/156,102/forecast",
SkiResort.breckenridge : "https://api.weather.gov/gridpoints/BOU/25,53/forecast",
SkiResort.jackson_hole : "https://api.weather.gov/gridpoints/RIW/42,139/forecast",
SkiResort.vali : "https://api.weather.gov/gridpoints/GJT/173,121/forecast"
}
# Local function that will be executed when Grok asks for
def get_weather_forecast(**kwargs) -> list[ForecastResponse]:
req = ForecastRequest(**kwargs) # Validate and parse the keyword parameters that Grok sends to us
forecast_url = skiResortForecastUrl[req.location] # Get request url for a given ski resort location
forecast = requests.get(url=forecast_url).json() # Retrieve forecast
res: list[ForecastResponse] = []
for item in forecast["properties"]["periods"]:
item.pop("icon") # Remove unnecessary weather icon url
res.append(item)
return res
You can preview the data we send to Grok, when Grok asks for the weather forecast at Aspen, CO:
Python (OpenAI)
get_weather_forecast(location='aspen')
Text
[{'number': 1,
'name': 'This Afternoon',
'startTime': '2025-01-17T12:00:00-07:00',
'endTime': '2025-01-17T18:00:00-07:00',
'isDaytime': True,
'temperature': 33,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': 40},
'windSpeed': '5 to 10 mph',
'windDirection': 'WNW',
'shortForecast': 'Chance Snow Showers',
'detailedForecast': 'A chance of snow showers after 3pm. Partly sunny, with a high near 33. West northwest wind 5 to 10 mph. Chance of precipitation is 40%. New snow accumulation of less than half an inch possible.'},
{'number': 2,
'name': 'Tonight',
'startTime': '2025-01-17T18:00:00-07:00',
'endTime': '2025-01-18T06:00:00-07:00',
'isDaytime': False,
'temperature': 10,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': 70},
'windSpeed': '5 to 10 mph',
'windDirection': 'W',
'shortForecast': 'Snow Showers Likely',
'detailedForecast': 'Snow showers likely. Mostly cloudy, with a low around 10. West wind 5 to 10 mph. Chance of precipitation is 70%. New snow accumulation of 1 to 2 inches possible.'},
{'number': 3,
'name': 'Saturday',
'startTime': '2025-01-18T06:00:00-07:00',
'endTime': '2025-01-18T18:00:00-07:00',
'isDaytime': True,
'temperature': 18,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': 70},
'windSpeed': '5 to 10 mph',
'windDirection': 'NW',
'shortForecast': 'Snow Showers Likely',
'detailedForecast': 'Snow showers likely before 5pm. Mostly cloudy, with a high near 18. Northwest wind 5 to 10 mph. Chance of precipitation is 70%. New snow accumulation of less than one inch possible.'},
{'number': 4,
'name': 'Saturday Night',
'startTime': '2025-01-18T18:00:00-07:00',
'endTime': '2025-01-19T06:00:00-07:00',
'isDaytime': False,
'temperature': -7,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '5 to 10 mph',
'windDirection': 'W',
'shortForecast': 'Partly Cloudy',
'detailedForecast': 'Partly cloudy, with a low around -7. Wind chill values as low as -14. West wind 5 to 10 mph.'},
{'number': 5,
'name': 'Sunday',
'startTime': '2025-01-19T06:00:00-07:00',
'endTime': '2025-01-19T18:00:00-07:00',
'isDaytime': True,
'temperature': 16,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '0 to 5 mph',
'windDirection': 'WSW',
'shortForecast': 'Partly Sunny',
'detailedForecast': 'Partly sunny, with a high near 16. West southwest wind 0 to 5 mph.'},
{'number': 6,
'name': 'Sunday Night',
'startTime': '2025-01-19T18:00:00-07:00',
'endTime': '2025-01-20T06:00:00-07:00',
'isDaytime': False,
'temperature': 0,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': 50},
'windSpeed': '0 to 5 mph',
'windDirection': 'WSW',
'shortForecast': 'Chance Snow Showers',
'detailedForecast': 'A chance of snow showers after 11pm. Mostly cloudy, with a low around 0. Chance of precipitation is 50%. New snow accumulation of less than half an inch possible.'},
{'number': 7,
'name': 'M.L. King Jr. Day',
'startTime': '2025-01-20T06:00:00-07:00',
'endTime': '2025-01-20T18:00:00-07:00',
'isDaytime': True,
'temperature': 14,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': 50},
'windSpeed': '0 to 5 mph',
'windDirection': 'WNW',
'shortForecast': 'Chance Snow Showers',
'detailedForecast': 'A chance of snow showers before 5pm. Mostly cloudy, with a high near 14. Chance of precipitation is 50%. New snow accumulation of less than half an inch possible.'},
{'number': 8,
'name': 'Monday Night',
'startTime': '2025-01-20T18:00:00-07:00',
'endTime': '2025-01-21T06:00:00-07:00',
'isDaytime': False,
'temperature': -9,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '5 mph',
'windDirection': 'SW',
'shortForecast': 'Partly Cloudy',
'detailedForecast': 'Partly cloudy, with a low around -9.'},
{'number': 9,
'name': 'Tuesday',
'startTime': '2025-01-21T06:00:00-07:00',
'endTime': '2025-01-21T18:00:00-07:00',
'isDaytime': True,
'temperature': 28,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '5 mph',
'windDirection': 'WSW',
'shortForecast': 'Sunny',
'detailedForecast': 'Sunny, with a high near 28.'},
{'number': 10,
'name': 'Tuesday Night',
'startTime': '2025-01-21T18:00:00-07:00',
'endTime': '2025-01-22T06:00:00-07:00',
'isDaytime': False,
'temperature': 7,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '5 mph',
'windDirection': 'SSW',
'shortForecast': 'Partly Cloudy',
'detailedForecast': 'Partly cloudy, with a low around 7.'},
{'number': 11,
'name': 'Wednesday',
'startTime': '2025-01-22T06:00:00-07:00',
'endTime': '2025-01-22T18:00:00-07:00',
'isDaytime': True,
'temperature': 31,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '0 to 5 mph',
'windDirection': 'WSW',
'shortForecast': 'Partly Sunny',
'detailedForecast': 'Partly sunny, with a high near 31.'},
{'number': 12,
'name': 'Wednesday Night',
'startTime': '2025-01-22T18:00:00-07:00',
'endTime': '2025-01-23T06:00:00-07:00',
'isDaytime': False,
'temperature': 7,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '5 mph',
'windDirection': 'WNW',
'shortForecast': 'Mostly Cloudy then Slight Chance Snow Showers',
'detailedForecast': 'A slight chance of snow showers after 5am. Mostly cloudy, with a low around 7.'},
{'number': 13,
'name': 'Thursday',
'startTime': '2025-01-23T06:00:00-07:00',
'endTime': '2025-01-23T18:00:00-07:00',
'isDaytime': True,
'temperature': 26,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '5 mph',
'windDirection': 'NW',
'shortForecast': 'Slight Chance Snow Showers then Mostly Sunny',
'detailedForecast': 'A slight chance of snow showers before 11am. Mostly sunny, with a high near 26.'},
{'number': 14,
'name': 'Thursday Night',
'startTime': '2025-01-23T18:00:00-07:00',
'endTime': '2025-01-24T06:00:00-07:00',
'isDaytime': False,
'temperature': 8,
'temperatureUnit': 'F',
'temperatureTrend': '',
'probabilityOfPrecipitation': {'unitCode': 'wmoUnit:percent', 'value': None},
'windSpeed': '5 mph',
'windDirection': 'SW',
'shortForecast': 'Partly Cloudy',
'detailedForecast': 'Partly cloudy, with a low around 8.'}]
The function to call is defined! Hurray! Now we need to send the function name and parameters signature so that Grok knows how to call the function.
Python (OpenAI)
# Definition of parameters with Pydantic JSON schema
tools_definition = [
{
"type": "function",
"function": {
"name": "get_weather_forecast", # the function name that we defined
"description": "Get the weather forecast at a given location", # Description of the function, so that Grok knows whether the function would be useful to solving the problem
"parameters": ForecastRequest.model_json_schema() # Generate the request parameter schema from Pydantic
},
},
]
2. Function handler to invoke the function we defined and add result to conversation history
With our previous definition of the function, we can send a request to Grok.
Let's see how Grok will respond:
Python (OpenAI)
chat_history = [{"role": "user", "content": "What should I prepare for a ski trip on Wednesday according to the weather in Vali, CO?"}]
response = client.chat.completions.create(
model="grok-4",
messages=chat_history,
tools=tools_definition, # The dictionary of our functions and their parameters
tool_choice="auto",
)
# You can inspect the response which contains a tool call
response.choices[0].message
Text
ChatCompletionMessage(content='I am checking the weather forecast for Vali, CO on Wednesday to help you prepare for your ski trip.', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_24298422', function=Function(arguments='{"location":"vali"}', name='get_weather_forecast'), type='function')])
You can see in the response ChatCompletionMessage, Grok included the following:
tool_calls=[ChatCompletionMessageToolCall(id='call_67298806', function=Function(arguments='{"location":"vali"}', name='get_weather_forecast'), type='function').
We need to design a handler to handle the tool_calls, by:
- Add Grok's response to chat history, in case we want to continue the conversation.
- Decide if Grok's response has a
tool_call. If not:- Print Grok's response message to end user.
- Skip the following steps.
- Calling function named
get_weather_forecast. - Add the function result to chat history like:
JSON
[ // ... Previous conversation // Add the following: { "role": "tool", "tool_call_id": "call_67298806", // The id we received from Grok for the tool call, so Grok can identify which tool call the result belongs to "content": {} // The JSON object of our get_weather_forecast() function return } ]
Python (OpenAI)
import json
from typing import Callable
# Define a mapping between function name and the function's callable object
tools_map: dict[str, Callable] = {
"get_weather_forecast": get_weather_forecast
}
def function_calls_handler(chat_completion_message, chat_history):
# Add Grok's response to chat history
chat_history.append(chat_completion_message.to_dict())
# Check if there is any tool calls in response body
if chat_completion_message.tool_calls:
# There is a tool call, run get_weather_forecast or iterate through all of
for tool_call in chat_completion_message.tool_calls:
# Get the tool function name and arguments Grok wants to call
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
# Call one of the tool function defined earlier in tools_map with arguments
result = tools_map[function_name](**function_args)
# Append the result from tool function call to the chat message history,
# with "role": "tool" - append per tool_call inside the loop
chat_history.append(
{
"role": "tool",
"content": json.dumps(result),
"tool_call_id": tool_call.id # tool_call.id supplied in Grok's response
}
)
else:
# No tool call, print the message content
print(chat_completion_message.content)
Now we call the handler on our earlier message from Grok and see what happens to our chat_history
Python (OpenAI)
# Chat history before handler
chat_history
Text
[{'role': 'user',
'content': 'What should I prepare for a ski trip on Wednesday according to the weather in Vali, CO?'}]
Python (OpenAI)
# Call handler
function_calls_handler(response.choices[0].message, chat_history)
# Chat history after handler
chat_history
Text
[{'role': 'user',
'content': 'What should I prepare for a ski trip on Wednesday according to the weather in Vali, CO?'},
{'content': 'I am checking the weather forecast for Vali, CO on Wednesday to help you prepare for your ski trip.',
'refusal': None,
'role': 'assistant',
'tool_calls': [{'id': 'call_24298422',
'function': {'arguments': '{"location":"vali"}',
'name': 'get_weather_forecast'},
'type': 'function'}]},
{'role': 'tool',
'content': '[{"number": 1, "name": "This Afternoon", "startTime": "2025-01-17T12:00:00-07:00", "endTime": "2025-01-17T18:00:00-07:00", "isDaytime": true, "temperature": 33, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": 70}, "windSpeed": "5 to 10 mph", "windDirection": "W", "shortForecast": "Snow Showers Likely", "detailedForecast": "Snow showers likely. Mostly cloudy, with a high near 33. West wind 5 to 10 mph. Chance of precipitation is 70%. New snow accumulation of 1 to 3 inches possible."}, {"number": 2, "name": "Tonight", "startTime": "2025-01-17T18:00:00-07:00", "endTime": "2025-01-18T06:00:00-07:00", "isDaytime": false, "temperature": 7, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": 90}, "windSpeed": "5 to 10 mph", "windDirection": "W", "shortForecast": "Snow Showers", "detailedForecast": "Snow showers. Cloudy, with a low around 7. West wind 5 to 10 mph. Chance of precipitation is 90%. New snow accumulation of 3 to 7 inches possible."}, {"number": 3, "name": "Saturday", "startTime": "2025-01-18T06:00:00-07:00", "endTime": "2025-01-18T18:00:00-07:00", "isDaytime": true, "temperature": 16, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": 80}, "windSpeed": "10 mph", "windDirection": "NW", "shortForecast": "Snow Showers", "detailedForecast": "Snow showers. Cloudy, with a high near 16. Northwest wind around 10 mph. Chance of precipitation is 80%. New snow accumulation of 1 to 2 inches possible."}, {"number": 4, "name": "Saturday Night", "startTime": "2025-01-18T18:00:00-07:00", "endTime": "2025-01-19T06:00:00-07:00", "isDaytime": false, "temperature": -9, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": 20}, "windSpeed": "0 to 10 mph", "windDirection": "W", "shortForecast": "Slight Chance Snow Showers then Mostly Cloudy", "detailedForecast": "A slight chance of snow showers before 11pm. Mostly cloudy, with a low around -9. Wind chill values as low as -16. West wind 0 to 10 mph. Chance of precipitation is 20%. New snow accumulation of less than half an inch possible."}, {"number": 5, "name": "Sunday", "startTime": "2025-01-19T06:00:00-07:00", "endTime": "2025-01-19T18:00:00-07:00", "isDaytime": true, "temperature": 15, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": 20}, "windSpeed": "5 mph", "windDirection": "W", "shortForecast": "Slight Chance Snow Showers", "detailedForecast": "A slight chance of snow showers after 11am. Partly sunny, with a high near 15. West wind around 5 mph. Chance of precipitation is 20%. New snow accumulation of less than half an inch possible."}, {"number": 6, "name": "Sunday Night", "startTime": "2025-01-19T18:00:00-07:00", "endTime": "2025-01-20T06:00:00-07:00", "isDaytime": false, "temperature": -2, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": 60}, "windSpeed": "0 to 5 mph", "windDirection": "W", "shortForecast": "Snow Showers Likely", "detailedForecast": "Snow showers likely. Mostly cloudy, with a low around -2. Chance of precipitation is 60%. New snow accumulation of around one inch possible."}, {"number": 7, "name": "M.L. King Jr. Day", "startTime": "2025-01-20T06:00:00-07:00", "endTime": "2025-01-20T18:00:00-07:00", "isDaytime": true, "temperature": 11, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": 60}, "windSpeed": "0 to 10 mph", "windDirection": "WNW", "shortForecast": "Snow Showers Likely", "detailedForecast": "Snow showers likely before 5pm. Mostly cloudy, with a high near 11. Chance of precipitation is 60%. New snow accumulation of less than half an inch possible."}, {"number": 8, "name": "Monday Night", "startTime": "2025-01-20T18:00:00-07:00", "endTime": "2025-01-21T06:00:00-07:00", "isDaytime": false, "temperature": -14, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": null}, "windSpeed": "0 to 5 mph", "windDirection": "WSW", "shortForecast": "Partly Cloudy", "detailedForecast": "Partly cloudy, with a low around -14."}, {"number": 9, "name": "Tuesday", "startTime": "2025-01-21T06:00:00-07:00", "endTime": "2025-01-21T18:00:00-07:00", "isDaytime": true, "temperature": 26, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": null}, "windSpeed": "5 mph", "windDirection": "WSW", "shortForecast": "Sunny", "detailedForecast": "Sunny, with a high near 26."}, {"number": 10, "name": "Tuesday Night", "startTime": "2025-01-21T18:00:00-07:00", "endTime": "2025-01-22T06:00:00-07:00", "isDaytime": false, "temperature": 2, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": null}, "windSpeed": "5 mph", "windDirection": "SSW", "shortForecast": "Partly Cloudy", "detailedForecast": "Partly cloudy, with a low around 2."}, {"number": 11, "name": "Wednesday", "startTime": "2025-01-22T06:00:00-07:00", "endTime": "2025-01-22T18:00:00-07:00", "isDaytime": true, "temperature": 29, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": null}, "windSpeed": "5 mph", "windDirection": "WSW", "shortForecast": "Slight Chance Snow Showers", "detailedForecast": "A slight chance of snow showers between 11am and 5pm. Partly sunny, with a high near 29."}, {"number": 12, "name": "Wednesday Night", "startTime": "2025-01-22T18:00:00-07:00", "endTime": "2025-01-23T06:00:00-07:00", "isDaytime": false, "temperature": 4, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": null}, "windSpeed": "0 to 5 mph", "windDirection": "W", "shortForecast": "Slight Chance Snow Showers", "detailedForecast": "A slight chance of snow showers after 11pm. Mostly cloudy, with a low around 4."}, {"number": 13, "name": "Thursday", "startTime": "2025-01-23T06:00:00-07:00", "endTime": "2025-01-23T18:00:00-07:00", "isDaytime": true, "temperature": 25, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": null}, "windSpeed": "5 mph", "windDirection": "WNW", "shortForecast": "Slight Chance Snow Showers", "detailedForecast": "A slight chance of snow showers before 5pm. Partly sunny, with a high near 25."}, {"number": 14, "name": "Thursday Night", "startTime": "2025-01-23T18:00:00-07:00", "endTime": "2025-01-24T06:00:00-07:00", "isDaytime": false, "temperature": 3, "temperatureUnit": "F", "temperatureTrend": "", "probabilityOfPrecipitation": {"unitCode": "wmoUnit:percent", "value": null}, "windSpeed": "5 mph", "windDirection": "SW", "shortForecast": "Partly Cloudy", "detailedForecast": "Partly cloudy, with a low around 3."}]',
'tool_call_id': 'call_24298422'}]
Now we can finally send this back to Grok to get the recommendation!
3. Setting up the whole pipeline to generate recommendation
As a recap:
- We have defined our local function
get_weather_forecast()that retrieves NOAA weather forecast for Grok to run. - We have defined a
function_calls_handler()to append Grok's response to chat history, and run the function if Grok asks for it.- As part of this, we also defined a
tools_mapto map function name given by Grok -> function Callable object.
- As part of this, we also defined a
We will run our chat request/response up to this point:
Python (OpenAI)
# 1. Send initial request to Grok, Grok responds with a tool_call request
chat_history = [{"role": "user", "content": "What should I prepare for a ski trip on Wednesday according to the weather in Vali, CO?"}]
response = client.chat.completions.create(
model="grok-4",
messages=chat_history,
tools=tools_definition, # The dictionary of our functions and their parameters
tool_choice="auto",
)
# 2. We handle the tool_call, and append Grok's response + the function result to chat_history
function_calls_handler(response.choices[0].message, chat_history)
# 3. We send the chat_history back to Grok to get our final recommendation
response = client.chat.completions.create(
model="grok-4",
messages=chat_history,
tools=tools_definition, # The dictionary of our functions and their parameters
tool_choice="auto",
)
# 4. Print the response
function_calls_handler(response.choices[0].message, chat_history)
Text
For your ski trip on Wednesday in Vali, CO, you should prepare for a slight chance of snow showers between 11am and 5pm. The high temperature will be around 29°F, so dress warmly. It's recommended to bring waterproof clothing and possibly extra layers for the cold. Also, consider bringing sunglasses for the partly sunny conditions.
You can wrap a user input, the call to Grok, and function_call_handler in a loop. This way, Grok will continuously answer your questions!