Function Calling in OpenAI¶
Introduction¶
In an API call, you can describe functions and have the model intelligently choose to output a JSON object containing arguments to call one or many functions. The Chat Completions API does not call the function; instead, the model generates JSON that you can use to call the function in your code.
The latest models have been trained to both detect when a function should to be called (depending on the input) and to respond with JSON that adheres to the function signature more closely than previous models.
The basic sequence of steps for function calling is as follows:
- Call the model with the user query and a set of functions defined in the functions parameter.
- The model can choose to call one or more functions; if so, the content will be a stringified JSON object adhering to your custom schema (note: the model may hallucinate parameters).
- Parse the string into JSON in your code, and call your function with the provided arguments if they exist.
- Call the model again by appending the function response as a new message, and let the model summarize the results back to the user.
In [35]:
from openai import OpenAI
import json
client = OpenAI()
# Example dummy function hard coded to return the same weather
# In production, this could be your backend API or an external API
def get_current_weather(location, unit="fahrenheit"):
"""Get the current weather in a given location"""
if "tokyo" in location.lower():
return json.dumps({"location": "Tokyo", "temperature": "10", "unit": unit})
elif "san francisco" in location.lower():
return json.dumps({"location": "San Francisco", "temperature": "32", "unit": unit})
elif "paris" in location.lower():
return json.dumps({"location": "Paris", "temperature": "22", "unit": unit})
else:
return json.dumps({"location": location, "temperature": "unknown"})
# Step 1: send the conversation and available functions to the model
messages = [{"role": "user", "content": "What's the weather like in San Francisco, Tokyo, and Paris?"}]
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
},
}
]
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
tool_choice="auto", # auto is default, but we'll be explicit
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
# Step 2: check if the model wanted to call a function
if tool_calls:
# Step 3: call the function
# Note: the JSON response may not always be valid; be sure to handle errors
available_functions = {
"get_current_weather": get_current_weather,
} # only one function in this example, but you can have multiple
messages.append(response_message) # extend conversation with assistant's reply
# Step 4: send the info for each function call and function response to the model
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
function_response = function_to_call(
location=function_args.get("location"),
unit=function_args.get("unit"),
)
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
) # extend conversation with function response
second_response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
) # get a new response from the model where it can see the function response
print(second_response.choices[0].message.content)
# Save the last assistant message into the messages (to use in the next step)
messages.append(second_response.choices[0].message)
Currently, the weather in San Francisco is 32°C, in Tokyo it's 10°C, and in Paris it's 22°C.
You can log all the conversation into a JSONL file, then you can analyze the performance of the model, or you can use this data to generate other samples to train a custom model.
In [36]:
def to_dict(object):
if type(object) == list:
for i, item in enumerate(object):
object[i] = to_dict(item)
if type(object) == dict:
for key, value in object.items():
object[key] = to_dict(value)
if hasattr(object, "__dict__"): return to_dict(object.__dict__)
return object
# Save the whole conversation to JSONL file for training purposes
import json
# File path for the JSONL file
file_path = 'output/messages.jsonl'
# Write messages to the JSONL file
with open(file_path, 'w') as file:
sample = {
"messages": to_dict(messages),
"tools": tools
}
file.write(json.dumps(sample, allow_nan=False) + '\n') # Write the JSON string to the file with a newline