Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
AZURE_OPENAI_API_VERSION=2024-02-15-preview
AZURE_OPENAI_ENDPOINT=https://YOUR-ENDPOINT-HERE.openai.azure.com/
# Name of the Azure OpenAI GPT deployment (different from the model name)
AZURE_OPENAI_CHAT_DEPLOYMENT=gpt4o-mini
AZURE_TENANT_ID=YOUR-TENANT-ID
AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o-mini
AZURE_TENANT_ID=YOUR-TENANT-ID
411 changes: 411 additions & 0 deletions .github/prompts/migrate-responses.prompt.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion .github/workflows/azure-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ jobs:
AZURE_OPENAI_DEPLOYMENT_CAPACITY: ${{ vars.AZURE_OPENAI_DEPLOYMENT_CAPACITY }}
AZURE_OPENAI_DEPLOYMENT_SKU_NAME: ${{ vars.AZURE_OPENAI_DEPLOYMENT_SKU_NAME }}
AZURE_OPENAI_SKU_NAME: ${{ vars.AZURE_OPENAI_SKU_NAME }}
AZURE_OPENAI_API_VERSION: ${{ vars.AZURE_OPENAI_API_VERSION }}
CREATE_AZURE_OPENAI: ${{ vars.CREATE_AZURE_OPENAI }}
AZURE_OPENAI_ENDPOINT: ${{ vars.AZURE_OPENAI_ENDPOINT }}
SERVICE_ACA_RESOURCE_EXISTS: ${{ vars.SERVICE_ACA_RESOURCE_EXISTS }}
Expand Down
5 changes: 0 additions & 5 deletions infra/aca.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ param serviceName string = 'aca'
param exists bool
param openAiDeploymentName string
param openAiEndpoint string
param openAiApiVersion string

resource acaIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
Expand All @@ -25,10 +24,6 @@ var env = [
name: 'AZURE_OPENAI_ENDPOINT'
value: openAiEndpoint
}
{
name: 'AZURE_OPENAI_API_VERSION'
value: openAiApiVersion
}
{
name: 'RUNNING_IN_PRODUCTION'
value: 'true'
Expand Down
4 changes: 0 additions & 4 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ param openAiResourceGroupName string = ''
})
param openAiResourceLocation string
param openAiSkuName string = ''
param openAiApiVersion string = '' // Used by the SDK in the app code
param disableKeyBasedAuth bool = true

// Parameters for the specific Azure OpenAI deployment:
Expand Down Expand Up @@ -152,7 +151,6 @@ module aca 'aca.bicep' = {
containerRegistryName: containerApps.outputs.registryName
openAiDeploymentName: openAiDeploymentName
openAiEndpoint: createAzureOpenAi ? openAi.outputs.endpoint : openAiEndpoint
openAiApiVersion: openAiApiVersion
exists: acaExists
}
}
Expand Down Expand Up @@ -185,7 +183,6 @@ output AZURE_TENANT_ID string = tenant().tenantId
output AZURE_OPENAI_RESOURCE_GROUP string = openAiResourceGroup.name
output AZURE_OPENAI_RESOURCE_NAME string = openAi.outputs.name
output AZURE_OPENAI_CHAT_DEPLOYMENT string = openAiDeploymentName
output AZURE_OPENAI_API_VERSION string = openAiApiVersion
output AZURE_OPENAI_ENDPOINT string = createAzureOpenAi ? openAi.outputs.endpoint : openAiEndpoint

output SERVICE_ACA_IDENTITY_PRINCIPAL_ID string = aca.outputs.SERVICE_ACA_IDENTITY_PRINCIPAL_ID
Expand All @@ -196,4 +193,3 @@ output SERVICE_ACA_IMAGE_NAME string = aca.outputs.SERVICE_ACA_IMAGE_NAME
output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerApps.outputs.registryLoginServer
output AZURE_CONTAINER_REGISTRY_NAME string = containerApps.outputs.registryName

3 changes: 0 additions & 3 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@
"openAiSkuName": {
"value": "${AZURE_OPENAI_SKU_NAME}"
},
"openAiApiVersion": {
"value": "${AZURE_OPENAI_API_VERSION}"
},
"createAzureOpenAi": {
"value": "${CREATE_AZURE_OPENAI=true}"
},
Expand Down
2 changes: 1 addition & 1 deletion src/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies = [
"httptools",
# Used by uvicorn for reload functionality
"watchfiles",
"openai",
"openai>=1.108.1",
"azure-identity",
"aiohttp>=3.11.0",
"python-dotenv",
Expand Down
46 changes: 28 additions & 18 deletions src/quartapp/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
ManagedIdentityCredential,
get_bearer_token_provider,
)
from openai import AsyncAzureOpenAI
from openai import AsyncOpenAI
from quart import (
Blueprint,
Response,
Expand All @@ -22,7 +22,6 @@

@bp.before_app_serving
async def configure_openai():

# Use ManagedIdentityCredential with the client_id for user-assigned managed identities
user_assigned_managed_identity_credential = ManagedIdentityCredential(client_id=os.getenv("AZURE_CLIENT_ID"))

Expand All @@ -47,11 +46,11 @@ async def configure_openai():
if not os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT"):
raise ValueError("AZURE_OPENAI_CHAT_DEPLOYMENT is required for Azure OpenAI")

# Create the Asynchronous Azure OpenAI client
bp.openai_client = AsyncAzureOpenAI(
api_version=os.getenv("AZURE_OPENAI_API_VERSION") or "2024-02-15-preview",
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
azure_ad_token_provider=token_provider,
# Create the Asynchronous OpenAI client with Azure OpenAI endpoint
azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT").rstrip("/")
bp.openai_client = AsyncOpenAI(
base_url=f"{azure_endpoint}/openai/v1",
api_key=token_provider,
)
# Set the model name to the Azure OpenAI model deployment name
bp.openai_model = os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT")
Expand All @@ -73,22 +72,33 @@ async def chat_handler():

@stream_with_context
async def response_stream():
# This sends all messages, so API request may exceed token limits
all_messages = [
{"role": "system", "content": "You are a helpful assistant."},
] + request_messages

chat_coroutine = bp.openai_client.chat.completions.create(
# Build input items for Responses API
input_items = [
{"role": "system", "content": [{"type": "input_text", "text": "You are a helpful assistant."}]},
]
for msg in request_messages:
content_type = "output_text" if msg["role"] == "assistant" else "input_text"
input_items.append(
{
"role": msg["role"],
"content": [{"type": content_type, "text": msg["content"]}],
}
)

response_stream = await bp.openai_client.responses.create(
# Azure OpenAI takes the deployment name as the model name
model=bp.openai_model,
messages=all_messages,
input=input_items,
stream=True,
store=False,
)
try:
async for event in await chat_coroutine:
event_dict = event.model_dump()
if event_dict["choices"]:
yield json.dumps(event_dict["choices"][0], ensure_ascii=False) + "\n"
async for event in response_stream:
if event.type == "response.output_text.delta":
yield json.dumps({"delta": {"content": event.delta, "role": None}}, ensure_ascii=False) + "\n"
elif event.type == "response.completed":
finish_event = {"delta": {"content": None, "role": None}, "finish_reason": "stop"}
yield json.dumps(finish_event, ensure_ascii=False) + "\n"
except Exception as e:
current_app.logger.error(e)
yield json.dumps({"error": str(e)}, ensure_ascii=False) + "\n"
Expand Down
36 changes: 18 additions & 18 deletions src/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#
# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
# pip-compile pyproject.toml
#
# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
# pip-compile --output-file=requirements.txt pyproject.toml
#
aiofiles==24.1.0
# via quart
aiohappyeyeballs==2.6.1
# via aiohttp
aiohttp==3.12.15
aiohttp==3.12.15
# via quartapp (pyproject.toml)
aiosignal==1.4.0
# via aiohttp
Expand Down Expand Up @@ -64,19 +64,19 @@ h11==0.16.0
# hypercorn
# uvicorn
# wsproto
h2==4.3.0
# via hypercorn
hpack==4.1.0
h2==4.3.0
# via hypercorn
hpack==4.1.0
# via h2
httpcore==1.0.9
# via httpx
httptools==0.6.4
# via quartapp (pyproject.toml)
httpx==0.28.1
httpx==0.28.1
# via openai
hypercorn==0.17.3
# via quart
hyperframe==6.1.0
hyperframe==6.1.0
# via h2
idna==3.10
# via
Expand Down Expand Up @@ -110,13 +110,13 @@ multidict==6.6.4
# via
# aiohttp
# yarl
openai==1.107.2
openai==2.16.0
# via quartapp (pyproject.toml)
packaging==25.0
# via gunicorn
priority==2.0.0
# via hypercorn
propcache==0.3.2
propcache==0.3.2
# via
# aiohttp
# yarl
Expand All @@ -126,11 +126,11 @@ pydantic==2.11.9
# via openai
pydantic-core==2.33.2
# via pydantic
pyjwt[crypto]==2.10.1
# via
# msal
# pyjwt
python-dotenv==1.1.1
pyjwt[crypto]==2.10.1
# via
# msal
# pyjwt
python-dotenv==1.1.1
# via quartapp (pyproject.toml)
pyyaml==6.0.2
# via quartapp (pyproject.toml)
Expand Down
Loading
Loading