Skip to content

Commit 185aa7b

Browse files
refactor: Replace OpenAI API call with Foundry SDK
2 parents 9bd4885 + 4e14e12 commit 185aa7b

File tree

11 files changed

+113
-148
lines changed

11 files changed

+113
-148
lines changed

infra/main.bicep

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ param gptModelVersion string = '2024-07-18'
3333
@description('Optional. Version of the GPT model to deploy.')
3434
param embeddingModelVersion string = '2'
3535

36-
@description('Optional. API version for the Azure OpenAI service.')
37-
param azureOpenaiAPIVersion string = '2025-04-01-preview'
36+
@description('Optional. API version for the Azure AI Services.')
37+
param azureAIServicesAPIVersion string = '2025-04-01-preview'
3838

3939
@minValue(10)
4040
@description('Optional. Capacity of the GPT deployment:')
@@ -494,7 +494,6 @@ module jumpboxVM 'br/public:avm/res/compute/virtual-machine:0.15.0' = if (enable
494494
// ========== Private DNS Zones ========== //
495495
var privateDnsZones = [
496496
'privatelink.cognitiveservices.azure.com'
497-
'privatelink.openai.azure.com'
498497
'privatelink.services.ai.azure.com'
499498
'privatelink.azurewebsites.net'
500499
'privatelink.blob.${environment().suffixes.storage}'
@@ -509,22 +508,20 @@ var privateDnsZones = [
509508
// DNS Zone Index Constants
510509
var dnsZoneIndex = {
511510
cognitiveServices: 0
512-
openAI: 1
513-
aiServices: 2
514-
appService: 3
515-
storageBlob: 4
516-
storageQueue: 5
517-
storageFile: 6
518-
cosmosDB: 7
519-
keyVault: 8
520-
sqlServer: 9
521-
searchService: 10
511+
aiServices: 1
512+
appService: 2
513+
storageBlob: 3
514+
storageQueue: 4
515+
storageFile: 5
516+
cosmosDB: 6
517+
keyVault: 7
518+
sqlServer: 8
519+
searchService: 9
522520
}
523521

524522
// List of DNS zone indices that correspond to AI-related services.
525523
var aiRelatedDnsZoneIndices = [
526524
dnsZoneIndex.cognitiveServices
527-
dnsZoneIndex.openAI
528525
dnsZoneIndex.aiServices
529526
]
530527

@@ -613,11 +610,11 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
613610
}
614611
{
615612
name: 'AZURE-OPENAI-PREVIEW-API-VERSION'
616-
value: azureOpenaiAPIVersion
613+
value: azureAIServicesAPIVersion
617614
}
618615
{
619616
name: 'AZURE-OPENAI-ENDPOINT'
620-
value: aiFoundryAiServices.outputs.endpoints['OpenAI Language Model Instance API']
617+
value: aiFoundryAiServices.outputs.endpoint
621618
}
622619
{
623620
name: 'AZURE-OPENAI-EMBEDDING-MODEL'
@@ -631,6 +628,10 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
631628
name: 'AZURE-SEARCH-ENDPOINT'
632629
value: 'https://${aiSearchName}.search.windows.net'
633630
}
631+
{
632+
name: 'AZURE-AI-AGENT-ENDPOINT'
633+
value: aiFoundryAiServices.outputs.aiProjectInfo.apiEndpoint
634+
}
634635
]
635636
enableTelemetry: enableTelemetry
636637
}
@@ -733,10 +734,6 @@ module aiFoundryAiServices 'modules/ai-services.bicep' = if (aiFoundryAIservices
733734
name: 'ai-services-dns-zone-cognitiveservices'
734735
privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId
735736
}
736-
{
737-
name: 'ai-services-dns-zone-openai'
738-
privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId
739-
}
740737
{
741738
name: 'ai-services-dns-zone-aiservices'
742739
privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId
@@ -1111,20 +1108,20 @@ module webSite 'modules/web-sites.bicep' = {
11111108
AZURE_SEARCH_URL_COLUMN: azureSearchUrlColumn
11121109
AZURE_OPENAI_RESOURCE: aiFoundryAiServices.outputs.name
11131110
AZURE_OPENAI_MODEL: gptModelName
1114-
AZURE_OPENAI_ENDPOINT: aiFoundryAiServices.outputs.endpoints['OpenAI Language Model Instance API']
1111+
AZURE_OPENAI_ENDPOINT: aiFoundryAiServices.outputs.endpoint
11151112
AZURE_OPENAI_TEMPERATURE: azureOpenAITemperature
11161113
AZURE_OPENAI_TOP_P: azureOpenAITopP
11171114
AZURE_OPENAI_MAX_TOKENS: azureOpenAIMaxTokens
11181115
AZURE_OPENAI_STOP_SEQUENCE: azureOpenAIStopSequence
11191116
AZURE_OPENAI_SYSTEM_MESSAGE: azureOpenAISystemMessage
1120-
AZURE_OPENAI_PREVIEW_API_VERSION: azureOpenaiAPIVersion
1117+
AZURE_OPENAI_PREVIEW_API_VERSION: azureAIServicesAPIVersion
11211118
AZURE_OPENAI_STREAM: azureOpenAIStream
11221119
AZURE_SEARCH_QUERY_TYPE: azureSearchQueryType
11231120
AZURE_SEARCH_VECTOR_COLUMNS: azureSearchVectorFields
11241121
AZURE_SEARCH_PERMITTED_GROUPS_COLUMN: azureSearchPermittedGroupsField
11251122
AZURE_SEARCH_STRICTNESS: azureSearchStrictness
11261123
AZURE_OPENAI_EMBEDDING_NAME: embeddingModel
1127-
AZURE_OPENAI_EMBEDDING_ENDPOINT : aiFoundryAiServices.outputs.endpoints['OpenAI Language Model Instance API']
1124+
AZURE_OPENAI_EMBEDDING_ENDPOINT : aiFoundryAiServices.outputs.endpoint
11281125
SQLDB_SERVER: sqlServerFqdn
11291126
SQLDB_DATABASE: sqlDbName
11301127
USE_INTERNAL_STREAM: useInternalStream
@@ -1140,7 +1137,7 @@ module webSite 'modules/web-sites.bicep' = {
11401137
USE_AI_PROJECT_CLIENT: useAIProjectClientFlag
11411138
AZURE_AI_AGENT_ENDPOINT: aiFoundryAiServices.outputs.aiProjectInfo.apiEndpoint
11421139
AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME: gptModelName
1143-
AZURE_AI_AGENT_API_VERSION: azureOpenaiAPIVersion
1140+
AZURE_AI_AGENT_API_VERSION: azureAIServicesAPIVersion
11441141
AZURE_SEARCH_CONNECTION_NAME: aiSearchName
11451142
AZURE_CLIENT_ID: userAssignedIdentity.outputs.clientId
11461143
}
@@ -1345,8 +1342,8 @@ output APPLICATIONINSIGHTS_CONNECTION_STRING string = enableMonitoring
13451342
? applicationInsights!.outputs.connectionString
13461343
: ''
13471344

1348-
@description('The API version used for the Azure AI Agent service.')
1349-
output AZURE_AI_AGENT_API_VERSION string = azureOpenaiAPIVersion
1345+
@description('The API version used for the Azure AI Agent service.')
1346+
output AZURE_AI_AGENT_API_VERSION string = azureAIServicesAPIVersion
13501347

13511348
@description('The endpoint URL of the Azure AI Agent project.')
13521349
output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiServices.outputs.aiProjectInfo.apiEndpoint
@@ -1373,13 +1370,13 @@ output AZURE_COSMOSDB_DATABASE string = cosmosDbDatabaseName
13731370
output AZURE_COSMOSDB_ENABLE_FEEDBACK string = azureCosmosDbEnableFeedback
13741371

13751372
@description('The endpoint URL for the Azure OpenAI Embedding model.')
1376-
output AZURE_OPENAI_EMBEDDING_ENDPOINT string = aiFoundryAiServices.outputs.endpoints['OpenAI Language Model Instance API']
1373+
output AZURE_OPENAI_EMBEDDING_ENDPOINT string = aiFoundryAiServices.outputs.endpoint
13771374

13781375
@description('The name of the Azure OpenAI Embedding model.')
13791376
output AZURE_OPENAI_EMBEDDING_NAME string = embeddingModel
13801377

13811378
@description('The endpoint URL for the Azure OpenAI service.')
1382-
output AZURE_OPENAI_ENDPOINT string = aiFoundryAiServices.outputs.endpoints['OpenAI Language Model Instance API']
1379+
output AZURE_OPENAI_ENDPOINT string = aiFoundryAiServices.outputs.endpoint
13831380

13841381
@description('The maximum number of tokens for Azure OpenAI responses.')
13851382
output AZURE_OPENAI_MAX_TOKENS string = azureOpenAIMaxTokens
@@ -1388,7 +1385,7 @@ output AZURE_OPENAI_MAX_TOKENS string = azureOpenAIMaxTokens
13881385
output AZURE_OPENAI_MODEL string = gptModelName
13891386

13901387
@description('The preview API version for Azure OpenAI.')
1391-
output AZURE_OPENAI_PREVIEW_API_VERSION string = azureOpenaiAPIVersion
1388+
output AZURE_OPENAI_PREVIEW_API_VERSION string = azureAIServicesAPIVersion
13921389

13931390
@description('The Azure OpenAI resource name.')
13941391
output AZURE_OPENAI_RESOURCE string = aiFoundryAiServices.outputs.name

infra/main.parameters.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"gptModelName": {
1515
"value": "${AZURE_ENV_MODEL_NAME}"
1616
},
17-
"azureOpenaiAPIVersion": {
17+
"azureAIServicesAPIVersion": {
1818
"value": "${AZURE_ENV_MODEL_VERSION}"
1919
},
2020
"gptDeploymentCapacity": {

infra/main.waf.parameters.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"gptModelName": {
1515
"value": "${AZURE_ENV_MODEL_NAME}"
1616
},
17-
"azureOpenaiAPIVersion": {
17+
"azureAIServicesAPIVersion": {
1818
"value": "${AZURE_ENV_MODEL_VERSION}"
1919
},
2020
"gptDeploymentCapacity": {

infra/modules/dependencies.bicep

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,6 @@ var builtInRoleNames = {
129129
'Microsoft.Authorization/roleDefinitions',
130130
'3b20f47b-3825-43cb-8114-4bd2201156a8'
131131
)
132-
'Cognitive Services OpenAI Contributor': subscriptionResourceId(
133-
'Microsoft.Authorization/roleDefinitions',
134-
'a001fd3d-188f-4b5d-821b-7da978bf7442'
135-
)
136-
'Cognitive Services OpenAI User': subscriptionResourceId(
137-
'Microsoft.Authorization/roleDefinitions',
138-
'5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
139-
)
140132
'Cognitive Services QnA Maker Editor': subscriptionResourceId(
141133
'Microsoft.Authorization/roleDefinitions',
142134
'f4cc2bf9-21be-47a1-bdf1-5c5804381025'

infra/scripts/index_scripts/create_search_index.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
DataLakeServiceClient,
3232
FileSystemClient,
3333
)
34-
from openai import AzureOpenAI
34+
from azure.ai.projects import AIProjectClient
3535

3636
# Get Azure Key Vault Client
3737
key_vault_name = "kv_to-be-replaced" #'nc6262-kv-2fpeafsylfd2e'
@@ -61,6 +61,7 @@
6161
openai_api_version = secret_client.get_secret("AZURE-OPENAI-PREVIEW-API-VERSION").value
6262
openai_embedding_model = secret_client.get_secret("AZURE-OPENAI-EMBEDDING-MODEL").value
6363
account_name = secret_client.get_secret("ADLS-ACCOUNT-NAME").value
64+
ai_project_endpoint = secret_client.get_secret("AZURE-AI-AGENT-ENDPOINT").value
6465

6566
# Create a search index
6667
index_client = SearchIndexClient(endpoint=search_endpoint, credential=credential)
@@ -132,15 +133,22 @@
132133

133134

134135
# Function: Get Embeddings
135-
def get_embeddings(text: str, openai_api_base, openai_api_version, azure_token_provider):
136+
def get_embeddings(text: str, ai_project_endpoint, openai_api_version, credential):
136137
model_id = openai_embedding_model or "text-embedding-ada-002"
137-
client = AzureOpenAI(
138+
139+
# Create AI Projects client
140+
project_client = AIProjectClient(
141+
endpoint=ai_project_endpoint,
142+
credential=credential,
138143
api_version=openai_api_version,
139-
azure_endpoint=openai_api_base,
140-
azure_ad_token_provider=azure_token_provider,
144+
)
145+
146+
# Get the OpenAI client from the AI Projects client
147+
openai_client = project_client.get_openai_client(
148+
api_version=openai_api_version
141149
)
142150

143-
embedding = client.embeddings.create(input=text, model=model_id).data[0].embedding
151+
embedding = openai_client.embeddings.create(input=text, model=model_id).data[0].embedding
144152

145153
return embedding
146154

@@ -260,12 +268,12 @@ def chunk_data(text):
260268

261269
try:
262270
v_contentVector = get_embeddings(
263-
d["content"], openai_api_base, openai_api_version, token_provider
271+
d["content"], ai_project_endpoint, openai_api_version, credential
264272
)
265273
except:
266274
time.sleep(30)
267275
v_contentVector = get_embeddings(
268-
d["content"], openai_api_base, openai_api_version, token_provider
276+
d["content"], ai_project_endpoint, openai_api_version, credential
269277
)
270278

271279
docs.append(

src/App/app.py

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66
import uuid
77
from types import SimpleNamespace
88

9-
from azure.identity import get_bearer_token_provider
109
from backend.helpers.azure_credential_utils import get_azure_credential
1110
from azure.monitor.opentelemetry import configure_azure_monitor
11+
from azure.ai.projects import AIProjectClient
1212

1313
# from quart.sessions import SecureCookieSessionInterface
14-
from openai import AsyncAzureOpenAI
1514
from opentelemetry import trace
1615
from opentelemetry.trace import Status, StatusCode
1716
from quart import (
@@ -129,7 +128,7 @@ async def assets(path):
129128
if DEBUG.lower() == "true":
130129
logging.basicConfig(level=logging.DEBUG)
131130

132-
USER_AGENT = "GitHubSampleWebApp/AsyncAzureOpenAI/1.0.0"
131+
USER_AGENT = "GitHubSampleWebApp/AzureAIProjects/1.0.0"
133132

134133
frontend_settings = {
135134
"auth_enabled": config.AUTH_ENABLED,
@@ -163,9 +162,9 @@ def should_use_data():
163162
SHOULD_USE_DATA = should_use_data()
164163

165164

166-
# Initialize Azure OpenAI Client
167-
def init_openai_client(use_data=SHOULD_USE_DATA):
168-
azure_openai_client = None
165+
# Initialize Azure AI Projects Client
166+
def init_ai_projects_client(use_data=SHOULD_USE_DATA):
167+
ai_projects_client = None
169168
try:
170169
# API version check
171170
if (
@@ -177,59 +176,42 @@ def init_openai_client(use_data=SHOULD_USE_DATA):
177176
)
178177

179178
# Endpoint
180-
if not config.AZURE_OPENAI_ENDPOINT and not config.AZURE_OPENAI_RESOURCE:
179+
if not config.AI_PROJECT_ENDPOINT:
181180
raise Exception(
182-
"AZURE_OPENAI_ENDPOINT or AZURE_OPENAI_RESOURCE is required"
181+
"AI_PROJECT_ENDPOINT is required for Azure AI Projects client"
183182
)
184183

185-
endpoint = (
186-
config.AZURE_OPENAI_ENDPOINT
187-
if config.AZURE_OPENAI_ENDPOINT
188-
else f"https://{config.AZURE_OPENAI_RESOURCE}.openai.azure.com/"
184+
# Authentication using managed identity
185+
credential = get_azure_credential(config.MID_ID)
186+
# Create AI Projects client
187+
ai_projects_client = AIProjectClient(
188+
endpoint=config.AI_PROJECT_ENDPOINT,
189+
credential=credential,
190+
api_version=config.AZURE_OPENAI_PREVIEW_API_VERSION,
189191
)
190192

191-
# Authentication
192-
aoai_api_key = config.AZURE_OPENAI_KEY
193-
ad_token_provider = None
194-
if not aoai_api_key:
195-
logging.debug("No AZURE_OPENAI_KEY found, using Azure AD auth")
196-
ad_token_provider = get_bearer_token_provider(
197-
get_azure_credential(config.MID_ID), "https://cognitiveservices.azure.com/.default"
198-
)
199-
200-
# Deployment
201-
deployment = config.AZURE_OPENAI_MODEL
202-
if not deployment:
203-
raise Exception("AZURE_OPENAI_MODEL is required")
204-
205-
# Default Headers
206-
default_headers = {"x-ms-useragent": USER_AGENT}
207-
208-
azure_openai_client = AsyncAzureOpenAI(
209-
api_version=config.AZURE_OPENAI_PREVIEW_API_VERSION,
210-
api_key=aoai_api_key,
211-
azure_ad_token_provider=ad_token_provider,
212-
default_headers=default_headers,
213-
azure_endpoint=endpoint,
193+
# Get the OpenAI client from the AI Projects client
194+
openai_client = ai_projects_client.get_openai_client(
195+
api_version=config.AZURE_OPENAI_PREVIEW_API_VERSION
214196
)
215197

216198
track_event_if_configured(
217-
"AzureOpenAIClientInitialized",
199+
"AzureAIProjectsClientInitialized",
218200
{
219201
"status": "success",
220-
"endpoint": endpoint,
221-
"use_api_key": bool(aoai_api_key),
202+
"endpoint": config.AI_PROJECT_ENDPOINT,
203+
"use_managed_identity": True,
222204
},
223205
)
224206

225-
return azure_openai_client
207+
return openai_client
226208
except Exception as e:
227-
logging.exception("Exception in Azure OpenAI initialization", e)
209+
logging.exception("Exception in Azure AI Projects initialization")
228210
span = trace.get_current_span()
229211
if span is not None:
230212
span.record_exception(e)
231213
span.set_status(Status(StatusCode.ERROR, str(e)))
232-
azure_openai_client = None
214+
ai_projects_client = None
233215
raise e
234216

235217

@@ -265,7 +247,7 @@ def init_cosmosdb_client():
265247
},
266248
)
267249
except Exception as e:
268-
logging.exception("Exception in CosmosDB initialization", e)
250+
logging.exception("Exception in CosmosDB initialization")
269251
span = trace.get_current_span()
270252
if span is not None:
271253
span.record_exception(e)
@@ -521,7 +503,7 @@ async def send_chat_request(request_body, request_headers):
521503
model_args = prepare_model_args(request_body, request_headers)
522504

523505
try:
524-
azure_openai_client = init_openai_client()
506+
azure_openai_client = init_ai_projects_client()
525507
raw_response = (
526508
await azure_openai_client.chat.completions.with_raw_response.create(
527509
**model_args
@@ -1324,7 +1306,7 @@ async def generate_title(conversation_messages):
13241306
messages.append({"role": "user", "content": title_prompt})
13251307

13261308
try:
1327-
azure_openai_client = init_openai_client(use_data=False)
1309+
azure_openai_client = init_ai_projects_client(use_data=False)
13281310
response = await azure_openai_client.chat.completions.create(
13291311
model=config.AZURE_OPENAI_MODEL,
13301312
messages=messages,
@@ -1333,6 +1315,7 @@ async def generate_title(conversation_messages):
13331315
)
13341316

13351317
title = json.loads(response.choices[0].message.content)["title"]
1318+
13361319
return title
13371320
except Exception:
13381321
return messages[-2]["content"]

0 commit comments

Comments
 (0)