How to set up a Python API to call an Azure OpenAI instance that is behind Azure API Management and requires both a subscription key & an OAuth2 access token using Semantic Kernel

As a follow up to my previous post, here is the same system access via a Python script that uses Semantic Kernel.

Raw REST API call to Azure OpenAI through APIM

As a reminder, here is what a properly formatted request to the Azure OpenAI Service through APIM would look like. The important part is the Ocp-Apim-Subscription-Key & Authorization headers. I’m using the REST Client in VS Code to easily test this API call.

# @name openai_request
POST https://{{apiManagementName}}.azure-api.net/openai/deployments/{{chatDeploymentName}}/chat/completions?api-version=2024-06-01
Content-Type: application/json
Ocp-Apim-Subscription-Key: {{subscriptionKey}}
Authorization: Bearer {{token}}

{
    "model":"gpt-4o",
    "messages":[
        {
            "role":"user",
            "content":"Hello"
        }
    ]
}

The REST Client lets you specify variables (either local variables or secrets stored in the settings.json file).

Python Semantic Kernel code

Review the Entra ID Client App Service Principal setup here to find the tenant_id, client_id, client_secret & redirect_uri.

from openai import AzureOpenAI
from azure.identity import DefaultAzureCredential, OnBehalfOfCredential, InteractiveBrowserCredential, get_bearer_token_provider
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatHistory
from semantic_kernel.functions import KernelArguments
from semantic_kernel import Kernel

#this example uses an app registration with the "Mobile and desktop applications" redirect URI set to "http://localhost:8000"
credential = InteractiveBrowserCredential(
   tenant_id=tenant_id,
   client_id=client_id,
   redirect_uri=redirect_uri)

token_provider = get_bearer_token_provider(credential, client_id_scope)

In the code above, I use the InteractiveBrowserCredential class to prompt the user to sign in.

obo_credential = OnBehalfOfCredential(
    tenant_id=tenant_id,
    client_id=client_id,
    client_secret=client_secret,
    user_assertion=token_provider(),
)

obo_token_provider = get_bearer_token_provider(obo_credential, openai_id_scope)

Then, I instantiate the OnBehalfOfCredential class that will be used to exchange the user’s original access token for an access token to the OpenAI service.

service_id = "chat"

kernel = Kernel()

chat_service = AzureChatCompletion(
    service_id = service_id,
    deployment_name = chat_deployment_name,
    endpoint = "https://" + apim_name + ".azure-api.net",
    api_version = openai_api_version,
    ad_token_provider = obo_token_provider,
    default_headers = {"Ocp-Apim-Subscription-Key": apim_subscription_key}
)

kernel.add_service(chat_service)

Now, I can set up the Semantic Kernel Kernel class with my token provider & the APIM subscription header that I need associated with each request.

Finally, I can use Semantic Kernel to call the LLM.

settings = kernel.get_prompt_execution_settings_from_service_id(service_id)
settings.max_tokens = 2000
settings.temperature = 0.7
settings.top_p = 0.8

chat_function = kernel.add_function(
    plugin_name="ChatBot",
    function_name="Chat",
    prompt="{{$chat_history}}{{$user_input}}",
    template_format="semantic-kernel",
    prompt_execution_settings=settings,
)

chat_history = ChatHistory(system_message = "Hello! I'm a chatbot. How can I help you today?")

answer = await kernel.invoke(
    chat_function,
    KernelArguments(
        user_input = "Hello!",
        chat_history = chat_history,
    )
)

print(answer)

Related Posts

One thought on “How to set up a Python API to call an Azure OpenAI instance that is behind Azure API Management and requires both a subscription key & an OAuth2 access token using Semantic Kernel”

  1. This is a typical use case for enterprise applications. Thanks for providing both .NET and Python implementations!

Leave a Reply

Your email address will not be published. Required fields are marked *