Skip to content

Instantly share code, notes, and snippets.

@peterroelants
Last active October 4, 2023 07:38
Show Gist options
  • Select an option

  • Save peterroelants/f3508b85b5574576b5f85cd90c721a9d to your computer and use it in GitHub Desktop.

Select an option

Save peterroelants/f3508b85b5574576b5f85cd90c721a9d to your computer and use it in GitHub Desktop.

Revisions

  1. peterroelants revised this gist Oct 4, 2023. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions openai_function_calling_react_loop.py
    Original file line number Diff line number Diff line change
    @@ -14,8 +14,6 @@
    - ReAct
    - https://arxiv.org/abs/2210.03629
    - https://peterroelants.github.io/posts/react-repl-agent/
    """
    import inspect
    import json
  2. peterroelants revised this gist Oct 4, 2023. 1 changed file with 21 additions and 6 deletions.
    27 changes: 21 additions & 6 deletions openai_function_calling_react_loop.py
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,21 @@
    """
    Simple example of OpenAI Function Calling in ReAct Loop.
    Functions are described in JSON with each parameter the function accepts described as a JSON Schema object.
    More info:
    - https://openai.com/blog/function-calling-and-other-api-updates
    - https://platform.openai.com/docs/guides/gpt/function-calling
    - https://arxiv.org/abs/2210.03629
    - https://peterroelants.github.io/posts/react-repl-agent/
    - OpenAI Function Calling
    - https://openai.com/blog/function-calling-and-other-api-updates
    - https://platform.openai.com/docs/guides/gpt/function-calling
    - https://platform.openai.com/docs/api-reference/chat/create#chat/create-functions
    - JSON Schema
    - https://json-schema.org/
    - https://json-schema.org/specification-links#2020-12
    - ReAct
    - https://arxiv.org/abs/2210.03629
    - https://peterroelants.github.io/posts/react-repl-agent/
    """
    import inspect
    import json
    @@ -15,6 +25,10 @@

    # Functions ########################################################
    class StopException(Exception):
    """
    Stop Execution (Task is Finished)
    """

    ...


    @@ -59,6 +73,7 @@ def calculate(formula):
    }


    # The parameters the functions accepts, described as a JSON Schema object.
    functions = [
    {
    "name": get_current_location.__name__,
    @@ -122,7 +137,7 @@ def calculate(formula):
    {
    "role": "user",
    "content": "What's the current weather for my location? Give me the temperature in degrees Celsius.\n\nReason step by step which actions to take to get to the answer.",
    }
    },
    ]


    @@ -250,4 +265,4 @@ def calculate(formula):
    # "arguments": "{\n \"answer\": \"The current weather for your location is sunny with a temperature of 22.22 degrees Celsius.\"\n}"
    # }
    # }
    # ]
    # ]
  3. peterroelants revised this gist Sep 22, 2023. 1 changed file with 46 additions and 47 deletions.
    93 changes: 46 additions & 47 deletions openai_function_calling_react_loop.py
    Original file line number Diff line number Diff line change
    @@ -192,63 +192,62 @@ def calculate(formula):
    print(f"messages: {json.dumps(messages, indent=2)}")
    # [
    # {
    # "role": "system",
    # "content": "You are a helpful assistant that can answer multi-step questions by sequentially calling functions. Follow a pattern of THOUGHT (reason step-by-step about which function to call next), ACTION (call a function to get closer to the answer), OBSERVATION (output of the function)."
    # },
    # {
    # "role": "user",
    # "content": "What's the current weather for my location? Give me the temperature in degrees Celsius.\n\nReason step by step which actions to take to get to the answer."
    # },
    # {
    # "role": "assistant",
    # "content": "To get the current weather for the user's location, we need to follow these steps:\n\n1. Get the user's current location.\n2. Retrieve the latitude and longitude coordinates from the user's location.\n3. Use the coordinates to get the current weather.\n4. Extract the temperature from the weather data.\n5. Convert the temperature to degrees Celsius.\n\nNow, let's take these steps one by one and call the appropriate functions to get the answer.",
    # "function_call": {
    # "name": "get_current_location",
    # "arguments": "{}"
    # }
    # },
    # {
    # "role": "function",
    # "name": "get_current_location",
    # "description": "Get the current location of the user",
    # "parameters": {
    # "type": "object",
    # "properties": {},
    # "required": []
    # "content": "{\"latitude\": 50.9326, \"longitude\": 5.3426}"
    # },
    # {
    # "role": "assistant",
    # "content": null,
    # "function_call": {
    # "name": "get_current_weather",
    # "arguments": "{\n \"latitude\": 50.9326,\n \"longitude\": 5.3426\n}"
    # }
    # },
    # {
    # "role": "function",
    # "name": "get_current_weather",
    # "description": "Get the current weather in a given location",
    # "parameters": {
    # "type": "object",
    # "properties": {
    # "latitude": {
    # "type": "number"
    # },
    # "longitude": {
    # "type": "number"
    # }
    # },
    # "required": [
    # "latitude",
    # "longitude"
    # ]
    # "content": "{\"location\": [50.9326, 5.3426], \"temperature\": \"72\", \"unit\": \"fahrenheit\", \"forecast\": \"sunny\"}"
    # },
    # {
    # "role": "assistant",
    # "content": "The current weather for your location is sunny with a temperature of 72 degrees Fahrenheit. \n\nTo convert the temperature to Celsius, we can use the formula: \n\nCelsius = (Fahrenheit - 32) * 5/9\n\nLet's calculate it.",
    # "function_call": {
    # "name": "calculate",
    # "arguments": "{\n \"formula\": \"(72 - 32) * 5/9\"\n}"
    # }
    # },
    # {
    # "role": "function",
    # "name": "calculate",
    # "description": "Calculate the result of a given formula",
    # "parameters": {
    # "type": "object",
    # "properties": {
    # "formula": {
    # "type": "string",
    # "description": "Formula to compute the result of, in Python syntax."
    # }
    # },
    # "required": [
    # "formula"
    # ]
    # }
    # "content": "22.22222222222222"
    # },
    # {
    # "role": "assistant",
    # "content": "The current temperature in your location is approximately 22.22 degrees Celsius."
    # },
    # {
    # "name": "finish",
    # "description": "Answer the user's question, and finish the conversation.",
    # "parameters": {
    # "type": "object",
    # "properties": {
    # "answer": {
    # "type": "string",
    # "description": "Text to answer the user with."
    # }
    # },
    # "required": [
    # "answer"
    # ]
    # "role": "assistant",
    # "content": null,
    # "function_call": {
    # "name": "finish",
    # "arguments": "{\n \"answer\": \"The current weather for your location is sunny with a temperature of 22.22 degrees Celsius.\"\n}"
    # }
    # }
    # ]
    # ]
  4. peterroelants revised this gist Sep 22, 2023. 1 changed file with 62 additions and 0 deletions.
    62 changes: 62 additions & 0 deletions openai_function_calling_react_loop.py
    Original file line number Diff line number Diff line change
    @@ -190,3 +190,65 @@ def calculate(formula):


    print(f"messages: {json.dumps(messages, indent=2)}")
    # [
    # {
    # "name": "get_current_location",
    # "description": "Get the current location of the user",
    # "parameters": {
    # "type": "object",
    # "properties": {},
    # "required": []
    # }
    # },
    # {
    # "name": "get_current_weather",
    # "description": "Get the current weather in a given location",
    # "parameters": {
    # "type": "object",
    # "properties": {
    # "latitude": {
    # "type": "number"
    # },
    # "longitude": {
    # "type": "number"
    # }
    # },
    # "required": [
    # "latitude",
    # "longitude"
    # ]
    # }
    # },
    # {
    # "name": "calculate",
    # "description": "Calculate the result of a given formula",
    # "parameters": {
    # "type": "object",
    # "properties": {
    # "formula": {
    # "type": "string",
    # "description": "Formula to compute the result of, in Python syntax."
    # }
    # },
    # "required": [
    # "formula"
    # ]
    # }
    # },
    # {
    # "name": "finish",
    # "description": "Answer the user's question, and finish the conversation.",
    # "parameters": {
    # "type": "object",
    # "properties": {
    # "answer": {
    # "type": "string",
    # "description": "Text to answer the user with."
    # }
    # },
    # "required": [
    # "answer"
    # ]
    # }
    # }
    # ]
  5. peterroelants revised this gist Sep 22, 2023. 1 changed file with 0 additions and 61 deletions.
    61 changes: 0 additions & 61 deletions example_output.json
    Original file line number Diff line number Diff line change
    @@ -1,61 +0,0 @@
    [
    {
    "role": "system",
    "content": "You are a helpful assistant that can answer multi-step questions by sequentially calling functions. Follow a pattern of THOUGHT (reason step-by-step about which function to call next), ACTION (call a function to get closer to the answer), OBSERVATION (output of the function)."
    },
    {
    "role": "user",
    "content": "What's the current weather for my location? Give me the temperature in degrees Celsius.\n\nReason step by step which actions to take to get to the answer."
    },
    {
    "role": "assistant",
    "content": "To get the current weather for the user's location, we need to follow these steps:\n\n1. Get the user's current location.\n2. Retrieve the latitude and longitude coordinates from the user's location.\n3. Use the coordinates to get the current weather.\n4. Extract the temperature from the weather data.\n5. Convert the temperature to degrees Celsius.\n\nNow, let's take these steps one by one and call the appropriate functions to get the answer.",
    "function_call": {
    "name": "get_current_location",
    "arguments": "{}"
    }
    },
    {
    "role": "function",
    "name": "get_current_location",
    "content": "{\"latitude\": 50.9326, \"longitude\": 5.3426}"
    },
    {
    "role": "assistant",
    "content": null,
    "function_call": {
    "name": "get_current_weather",
    "arguments": "{\n \"latitude\": 50.9326,\n \"longitude\": 5.3426\n}"
    }
    },
    {
    "role": "function",
    "name": "get_current_weather",
    "content": "{\"location\": [50.9326, 5.3426], \"temperature\": \"72\", \"unit\": \"fahrenheit\", \"forecast\": \"sunny\"}"
    },
    {
    "role": "assistant",
    "content": "The current weather for your location is sunny with a temperature of 72 degrees Fahrenheit. \n\nTo convert the temperature to Celsius, we can use the formula: \n\nCelsius = (Fahrenheit - 32) * 5/9\n\nLet's calculate it.",
    "function_call": {
    "name": "calculate",
    "arguments": "{\n \"formula\": \"(72 - 32) * 5/9\"\n}"
    }
    },
    {
    "role": "function",
    "name": "calculate",
    "content": "22.22222222222222"
    },
    {
    "role": "assistant",
    "content": "The current temperature in your location is approximately 22.22 degrees Celsius."
    },
    {
    "role": "assistant",
    "content": null,
    "function_call": {
    "name": "finish",
    "arguments": "{\n \"answer\": \"The current weather for your location is sunny with a temperature of 22.22 degrees Celsius.\"\n}"
    }
    }
    ]
  6. peterroelants revised this gist Sep 22, 2023. No changes.
  7. peterroelants revised this gist Sep 22, 2023. No changes.
  8. peterroelants revised this gist Sep 22, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion example_output.json
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    messages: [
    [
    {
    "role": "system",
    "content": "You are a helpful assistant that can answer multi-step questions by sequentially calling functions. Follow a pattern of THOUGHT (reason step-by-step about which function to call next), ACTION (call a function to get closer to the answer), OBSERVATION (output of the function)."
  9. peterroelants created this gist Sep 22, 2023.
    61 changes: 61 additions & 0 deletions example_output.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    messages: [
    {
    "role": "system",
    "content": "You are a helpful assistant that can answer multi-step questions by sequentially calling functions. Follow a pattern of THOUGHT (reason step-by-step about which function to call next), ACTION (call a function to get closer to the answer), OBSERVATION (output of the function)."
    },
    {
    "role": "user",
    "content": "What's the current weather for my location? Give me the temperature in degrees Celsius.\n\nReason step by step which actions to take to get to the answer."
    },
    {
    "role": "assistant",
    "content": "To get the current weather for the user's location, we need to follow these steps:\n\n1. Get the user's current location.\n2. Retrieve the latitude and longitude coordinates from the user's location.\n3. Use the coordinates to get the current weather.\n4. Extract the temperature from the weather data.\n5. Convert the temperature to degrees Celsius.\n\nNow, let's take these steps one by one and call the appropriate functions to get the answer.",
    "function_call": {
    "name": "get_current_location",
    "arguments": "{}"
    }
    },
    {
    "role": "function",
    "name": "get_current_location",
    "content": "{\"latitude\": 50.9326, \"longitude\": 5.3426}"
    },
    {
    "role": "assistant",
    "content": null,
    "function_call": {
    "name": "get_current_weather",
    "arguments": "{\n \"latitude\": 50.9326,\n \"longitude\": 5.3426\n}"
    }
    },
    {
    "role": "function",
    "name": "get_current_weather",
    "content": "{\"location\": [50.9326, 5.3426], \"temperature\": \"72\", \"unit\": \"fahrenheit\", \"forecast\": \"sunny\"}"
    },
    {
    "role": "assistant",
    "content": "The current weather for your location is sunny with a temperature of 72 degrees Fahrenheit. \n\nTo convert the temperature to Celsius, we can use the formula: \n\nCelsius = (Fahrenheit - 32) * 5/9\n\nLet's calculate it.",
    "function_call": {
    "name": "calculate",
    "arguments": "{\n \"formula\": \"(72 - 32) * 5/9\"\n}"
    }
    },
    {
    "role": "function",
    "name": "calculate",
    "content": "22.22222222222222"
    },
    {
    "role": "assistant",
    "content": "The current temperature in your location is approximately 22.22 degrees Celsius."
    },
    {
    "role": "assistant",
    "content": null,
    "function_call": {
    "name": "finish",
    "arguments": "{\n \"answer\": \"The current weather for your location is sunny with a temperature of 22.22 degrees Celsius.\"\n}"
    }
    }
    ]
    192 changes: 192 additions & 0 deletions openai_function_calling_react_loop.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,192 @@
    """
    Simple example of OpenAI Function Calling in ReAct Loop.
    More info:
    - https://openai.com/blog/function-calling-and-other-api-updates
    - https://platform.openai.com/docs/guides/gpt/function-calling
    - https://arxiv.org/abs/2210.03629
    - https://peterroelants.github.io/posts/react-repl-agent/
    """
    import inspect
    import json

    import openai


    # Functions ########################################################
    class StopException(Exception):
    ...


    def finish(answer):
    """Answer the user's question, and finish the conversation."""
    raise StopException(answer)


    def get_current_location():
    """Get the current location of the user"""
    # Mocked for the sake of the example
    return json.dumps(
    {
    "latitude": 50.9326,
    "longitude": 5.34260,
    }
    )


    def get_current_weather(latitude, longitude):
    """Get the current weather in a given location"""
    # Mocked for the sake of the example
    weather_info = {
    "location": (latitude, longitude),
    "temperature": "72",
    "unit": "fahrenheit",
    "forecast": "sunny",
    }
    return json.dumps(weather_info)


    def calculate(formula):
    """Calculate the result of a given formula"""
    return str(eval(formula))


    name_to_function_map = {
    get_current_location.__name__: get_current_location,
    get_current_weather.__name__: get_current_weather,
    calculate.__name__: calculate,
    finish.__name__: finish,
    }


    functions = [
    {
    "name": get_current_location.__name__,
    "description": inspect.getdoc(get_current_location).strip(),
    "parameters": {
    "type": "object",
    "properties": {},
    "required": [],
    },
    },
    {
    "name": get_current_weather.__name__,
    "description": inspect.getdoc(get_current_weather).strip(),
    "parameters": {
    "type": "object",
    "properties": {
    "latitude": {"type": "number"},
    "longitude": {"type": "number"},
    },
    "required": ["latitude", "longitude"],
    },
    },
    {
    "name": calculate.__name__,
    "description": inspect.getdoc(calculate).strip(),
    "parameters": {
    "type": "object",
    "properties": {
    "formula": {
    "type": "string",
    "description": "Formula to compute the result of, in Python syntax.",
    },
    },
    "required": ["formula"],
    },
    },
    {
    "name": finish.__name__,
    "description": inspect.getdoc(finish).strip(),
    "parameters": {
    "type": "object",
    "properties": {
    "answer": {
    "type": "string",
    "description": "Text to answer the user with.",
    },
    },
    "required": ["answer"],
    },
    },
    ]


    # Function Calling in loop #########################################
    # Initial "chat" messages
    messages = [
    {
    "role": "system",
    "content": "You are a helpful assistant that can answer multi-step questions by sequentially calling functions. Follow a pattern of THOUGHT (reason step-by-step about which function to call next), ACTION (call a function to get closer to the answer), OBSERVATION (output of the function).",
    },
    {
    "role": "user",
    "content": "What's the current weather for my location? Give me the temperature in degrees Celsius.\n\nReason step by step which actions to take to get to the answer.",
    }
    ]


    # Run in loop
    max_iterations = 20
    for i in range(max_iterations):
    print(f"\n\n# Iteration {i}:")
    # Call OpenAI's API to get the next message
    response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=messages,
    functions=functions,
    function_call="auto",
    )
    response_message = response["choices"][0]["message"]
    # Extend conversation with assistant's reply
    messages.append(response_message.to_dict())
    # Check if GPT wanted to call a function
    if response_message.get("function_call"):
    function_call_message = response_message["function_call"]
    print(f"function_call_message={json.dumps(function_call_message, indent=2)}")
    # Call the function
    function_name = function_call_message["name"]
    function_to_call = name_to_function_map[function_name]
    function_args = function_call_message["arguments"]
    try:
    function_args_dict = json.loads(function_args)
    except json.JSONDecodeError as exc:
    # JSON decoding failed
    print(f"Error decoding function arguments: {exc}")
    messages.append(
    {
    "role": "function",
    "name": function_name,
    "content": f"Error decoding function arguments {function_args!r}! Error: {exc}",
    }
    )
    continue
    try:
    function_response = function_to_call(**function_args_dict)
    except StopException as exc:
    # Agent wants to stop the conversation (Expected)
    print(f"Stopping conversation: {exc}")
    break
    except Exception as exc:
    # Unexpected error calling function
    print(f"Error calling function {function_name}: {exc}")
    messages.append(
    {
    "role": "function",
    "name": function_name,
    "content": f"Error calling function {function_name}: {exc}!",
    }
    )
    continue
    print(f"{function_response=}")
    # Extend conversation with function response
    messages.append(
    {
    "role": "function",
    "name": function_name,
    "content": function_response,
    }
    )


    print(f"messages: {json.dumps(messages, indent=2)}")