Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Azure Open AI function calling via Azure Functions #1133

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

goventur
Copy link

Motivation and Context

Description

This PR implements OpenAI Function Calling feature.
It uses Azure Functions to retrieve the OpenAI tools metadata JSON, and also to run the functions.
To add new OpenAI Functions, only the code in Azure Functions needs to be modified.

Architecture

image

1, 2 - Get JSON with OpenAI function call metadata
3, 4, 5, 6 - Send user prompt to Open AI model optionally with On Your Data, querying Azure AI Search
7, 8 - If Open AI model returns any function call, execute the function remotely using App Functions
9, 10 - Submit the results from Azure Function call execution to OpenAI to generate the final answer

This enables users to add real-time data to their chatbots, or to implement actions.

Azure Functions

There are two Azure Functions:

  1. /tools: returns JSON metadata with the available OpenAI functions.
  2. /tool: executes the OpenAI function with the given arguments.

Azure Functions code sample

import azure.functions as func
import logging
import json
import random

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

azure_openai_tools_json = """[{
    "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 name, e.g. San Francisco"
                }
            },
            "required": ["location"]
        }
    }
}]"""

azure_openai_available_tools = ["get_current_weather"]

@app.route(route="tools")
def tools(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('tools function processed a request.')

    return func.HttpResponse(
        azure_openai_tools_json,
        status_code=200
    )

@app.route(route="tool")
def tool(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('tool function processed a request.')

    tool_name = req.params.get('tool_name')
    if not tool_name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            tool_name = req_body.get('tool_name')

    tool_arguments = req.params.get('tool_arguments')
    if not tool_arguments:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            tool_arguments = req_body.get('tool_arguments')

    if tool_name and tool_arguments:
        if tool_name in azure_openai_available_tools:
            logging.info('tool function: tool_name and tool_arguments are valid.')
            result = globals()[tool_name](**tool_arguments)
            return func.HttpResponse(
                result,
                status_code = 200
            )

    logging.info('tool function: tool_name or tool_arguments are invalid.')
    return func.HttpResponse(
            "The tool function we executed successfully but the tool name or arguments were invalid. ",
            status_code=400
    )

def get_current_weather(location: str) -> str:
    logging.info('get_current_weather function processed a request.')
    temperature = random.randint(10, 30)
    weather = random.choice(["sunny", "cloudy", "rainy", "windy"])
    return f"The current weather in {location} is {random.randint(10, 30)}°C and {weather}."

Contribution Checklist

  • I have built and tested the code locally and in a deployed app
  • For frontend changes, I have pulled the latest code from main, built the frontend, and committed all static files.
  • This is a change for all users of this app. No code or asset is specific to my use case or my organization.
  • I didn't break any existing functionality 😄

app.py Outdated Show resolved Hide resolved
@goventur goventur changed the title Goventur/add function calling Enable Azure Open AI function calling via Azure Functions Oct 12, 2024
@github-actions github-actions bot added the stale label Dec 13, 2024
@github-actions github-actions bot closed this Dec 28, 2024
@sarah-widder sarah-widder reopened this Jan 6, 2025
@@ -123,6 +123,11 @@ class _AzureOpenAISettings(BaseSettings):
embedding_endpoint: Optional[str] = None
embedding_key: Optional[str] = None
embedding_name: Optional[str] = None
function_call_azure_functions_enabled: bool = False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this Optional[bool] for consistency?

# Remote function calls
if app_settings.azure_openai.function_call_azure_functions_enabled:
azure_functions_tools_url = f"{app_settings.azure_openai.function_call_azure_functions_tools_base_url}?code={app_settings.azure_openai.function_call_azure_functions_tools_key}"
response = requests.get(azure_functions_tools_url)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use an async call here

"tool_name": function_name,
"tool_arguments": json.loads(function_args)
}
response = requests.post(azure_functions_tool_url, data=json.dumps(body), headers=headers)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make this async as well

)
]
}
if messages[-1]["role"] == "user":
Copy link
Contributor

@sarah-widder sarah-widder Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a safety check here that len(messages) > 0 would be good

@github-actions github-actions bot removed the stale label Jan 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants