Second function call under the hood


I was wondering, when using function calls, we have to essentially make two requests to chat-gpt. First to pass in the list of available tools, second to pass the output text from the tools back to the api. Example from the docs:

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": "72", "unit": unit})
    elif "paris" in location.lower():
        return json.dumps({"location": "Paris", "temperature": "22", "unit": unit})
        return json.dumps({"location": location, "temperature": "unknown"})

def run_conversation():
    # 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 =
        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 =
            function_to_call = available_functions[function_name]
            function_args = json.loads(tool_call.function.arguments)
            function_response = function_to_call(
                    "role": "tool",
                    "name": function_name,
                    "content": function_response,
            )  # extend conversation with function response
        second_response =
        )  # get a new response from the model where it can see the function response
        return second_response

What actually happens under the hood in step 4? How is the text from both function calls actually formatted and sent to the model?

I’m asking this because I wonder if, for the second request, what is the difference between:

  1. Sending responses from multiple tool calls and separately appending each output to messages, like the above example.

  2. Sending responses from multiple tool calls, but concatenating all the text together so we only need to append once to messages?