Skip to content

Commit e96516e

Browse files
authored
Merge pull request #17 from singhnitant/nisi-rag
Convert from Key to AAD auth
2 parents 43a5dbe + e09e00a commit e96516e

File tree

13 files changed

+131
-90
lines changed

13 files changed

+131
-90
lines changed

src/copilot_flow/copilot.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,29 @@
1-
# ---------------------------------------------------------
2-
# Copyright (c) Microsoft Corporation. All rights reserved.
3-
# ---------------------------------------------------------
41
import os
52
# set environment variables before importing any other code
63
from dotenv import load_dotenv
74
load_dotenv()
85

96
from pathlib import Path
10-
117
from typing import TypedDict
12-
138
from openai import AzureOpenAI
149

15-
from azure.core.credentials import AzureKeyCredential
10+
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
1611
from azure.search.documents import SearchClient
1712
from azure.search.documents.models import VectorizedQuery
1813

19-
from promptflow.tracing import trace
2014
from promptflow.core import Prompty, AzureOpenAIModelConfiguration
15+
from promptflow.tracing import trace
2116

2217
class ChatResponse(TypedDict):
2318
context: dict
2419
reply: str
2520

2621
@trace
2722
def get_chat_response(chat_input: str, chat_history: list = []) -> ChatResponse:
28-
2923
model_config = AzureOpenAIModelConfiguration(
3024
azure_deployment=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT"],
3125
api_version=os.environ["AZURE_OPENAI_API_VERSION"],
32-
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
33-
api_key=os.environ["AZURE_OPENAI_API_KEY"]
26+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"]
3427
)
3528

3629
searchQuery = chat_input
@@ -48,7 +41,6 @@ def get_chat_response(chat_input: str, chat_history: list = []) -> ChatResponse:
4841
searchQuery = intentPrompty(query=chat_input, chat_history=chat_history)
4942

5043
# retrieve relevant documents and context given chat_history and current user query (chat_input)
51-
print(os.environ["AZURE_OPENAI_API_KEY"])
5244
documents = get_documents(searchQuery, 3)
5345

5446
# send query + document context to chat completion for a response
@@ -74,21 +66,24 @@ def get_documents(search_query: str, num_docs=3):
7466

7567
index_name = os.environ["AZUREAI_SEARCH_INDEX_NAME"]
7668

77-
# retrieve documents relevant to the user's query from Azure AI Search index
69+
# retrieve documents relevant to the user's question from Cognitive Search
7870
search_client = SearchClient(
7971
endpoint=os.environ["AZURE_SEARCH_ENDPOINT"],
80-
credential=AzureKeyCredential(os.environ["AZURE_SEARCH_KEY"]),
81-
index_name=index_name)
72+
credential=DefaultAzureCredential(),
73+
index_name=index_name
74+
)
8275

8376
aoai_client = AzureOpenAI(
8477
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
85-
api_key=os.environ["AZURE_OPENAI_API_KEY"],
78+
azure_ad_token_provider=get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"),
8679
api_version=os.environ["AZURE_OPENAI_API_VERSION"]
8780
)
8881

8982
# generate a vector embedding of the user's question
90-
embedding = aoai_client.embeddings.create(input=search_query,
91-
model=os.environ["AZURE_OPENAI_EMBEDDING_DEPLOYMENT"])
83+
embedding = aoai_client.embeddings.create(
84+
input=search_query,
85+
model=os.environ["AZURE_OPENAI_EMBEDDING_DEPLOYMENT"]
86+
)
9287
embedding_to_query = embedding.data[0].embedding
9388

9489
context = ""
@@ -102,4 +97,4 @@ def get_documents(search_query: str, num_docs=3):
10297
for result in results:
10398
context += f"\n>>> From: {result['id']}\n{result['content']}"
10499

105-
return context
100+
return context

src/custom_evaluators/completeness_cot.prompty

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ model:
55
api: chat
66
configuration:
77
type: azure_openai
8-
azure_deployment: ${env:AZURE_DEPLOYMENT}
9-
api_key: ${env:AZURE_OPENAI_API_KEY}
8+
azure_deployment: ${env:AZURE_OPENAI_EVALUATION_DEPLOYMENT}
9+
api_version: ${env:AZURE_OPENAI_API_VERSION}
1010
azure_endpoint: ${env:AZURE_OPENAI_ENDPOINT}
1111
parameters:
1212
temperature: 0.0

src/custom_evaluators/friendliness.prompty

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ model:
55
api: chat
66
configuration:
77
type: azure_openai
8-
azure_deployment: gpt-4
9-
api_key: ${env:AZURE_OPENAI_API_KEY}
8+
azure_deployment: ${env:AZURE_OPENAI_EVALUATION_DEPLOYMENT}
9+
api_version: ${env:AZURE_OPENAI_API_VERSION}
1010
azure_endpoint: ${env:AZURE_OPENAI_ENDPOINT}
1111
parameters:
1212
temperature: 0.1

src/deployment/deploy.py

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
from azure.ai.ml.entities import ManagedOnlineEndpoint, ManagedOnlineDeployment, Model, Environment, BuildContext
2-
3-
import os
1+
import os, uuid
2+
# set environment variables before importing any other code
43
from dotenv import load_dotenv
54
load_dotenv()
65

6+
from azure.ai.ml.entities import ManagedOnlineEndpoint, ManagedOnlineDeployment, Model, Environment, BuildContext
7+
from azure.identity import DefaultAzureCredential
8+
from azure.mgmt.authorization import AuthorizationManagementClient
9+
from azure.mgmt.authorization.models import RoleAssignmentCreateParameters
710

811
from helper_functions import get_client, get_ai_studio_url_for_deploy
912

@@ -20,10 +23,11 @@ def deploy_flow(endpoint_name, deployment_name):
2023
name=endpoint_name,
2124
properties={
2225
"enforce_access_to_default_secret_stores": "enabled" # if you want secret injection support
23-
}
26+
},
27+
auth_mode="aad_token" # using aad auth instead of key-based auth
2428
)
2529

26-
deployment = ManagedOnlineDeployment( # defaults to key auth_mode
30+
deployment = ManagedOnlineDeployment(
2731
name=deployment_name,
2832
endpoint_name=endpoint_name,
2933
model=Model(
@@ -64,29 +68,79 @@ def deploy_flow(endpoint_name, deployment_name):
6468
"PRT_CONFIG_OVERRIDE": f"deployment.subscription_id={client.subscription_id},deployment.resource_group={client.resource_group_name},deployment.workspace_name={client.workspace_name},deployment.endpoint_name={endpoint_name},deployment.deployment_name={deployment_name}",
6569
# the following is enabled by secret injection
6670
# make sure your environment variables here match the environment variables your code depends on
67-
'AZURE_OPENAI_ENDPOINT': os.getenv('AZURE_OPENAI_ENDPOINT'),
68-
'AZURE_OPENAI_API_KEY': os.getenv('AZURE_OPENAI_API_KEY'),
69-
'AZURE_SEARCH_ENDPOINT': os.getenv('AZURE_SEARCH_ENDPOINT'),
70-
'AZURE_SEARCH_KEY': os.getenv('AZURE_SEARCH_KEY'),
71-
'AZURE_OPENAI_API_VERSION': os.getenv('AZURE_OPENAI_API_VERSION'),
72-
'AZURE_OPENAI_CHAT_DEPLOYMENT': os.getenv('AZURE_OPENAI_CHAT_DEPLOYMENT'),
73-
'AZURE_OPENAI_EVALUATION_DEPLOYMENT': os.getenv('AZURE_OPENAI_EVALUATION_DEPLOYMENT'),
74-
'AZURE_OPENAI_EMBEDDING_DEPLOYMENT': os.getenv('AZURE_OPENAI_EMBEDDING_DEPLOYMENT'),
75-
'AZUREAI_SEARCH_INDEX_NAME': os.getenv('AZUREAI_SEARCH_INDEX_NAME')
71+
'AZURE_OPENAI_ENDPOINT': os.environ['AZURE_OPENAI_ENDPOINT'],
72+
'AZURE_SEARCH_ENDPOINT': os.environ['AZURE_SEARCH_ENDPOINT'],
73+
'AZURE_OPENAI_API_VERSION': os.environ['AZURE_OPENAI_API_VERSION'],
74+
'AZURE_OPENAI_CHAT_DEPLOYMENT': os.environ['AZURE_OPENAI_CHAT_DEPLOYMENT'],
75+
'AZURE_OPENAI_EVALUATION_DEPLOYMENT': os.environ['AZURE_OPENAI_EVALUATION_DEPLOYMENT'],
76+
'AZURE_OPENAI_EMBEDDING_DEPLOYMENT': os.environ['AZURE_OPENAI_EMBEDDING_DEPLOYMENT'],
77+
'AZUREAI_SEARCH_INDEX_NAME': os.environ['AZUREAI_SEARCH_INDEX_NAME']
7678
}
7779
)
7880

7981
# 1. create endpoint
80-
client.begin_create_or_update(endpoint).result() # result() means we wait on this to complete
82+
endpoint = client.begin_create_or_update(endpoint).result() # result() means we wait on this to complete
8183

8284
# 2. create deployment
83-
client.begin_create_or_update(deployment).result()
85+
deployment = client.begin_create_or_update(deployment).result()
8486

8587
# 3. update endpoint traffic for the deployment
8688
endpoint.traffic = {deployment_name: 100} # 100% of traffic
87-
client.begin_create_or_update(endpoint).result()
88-
89-
output_deployment_details(client, endpoint_name, deployment_name)
89+
endpoint = client.begin_create_or_update(endpoint).result()
90+
91+
# 4. provide endpoint access to Azure Open AI resource
92+
create_role_assignment(
93+
scope=f"/subscriptions/{os.environ["AZURE_SUBSCRIPTION_ID"]}/resourceGroups/{os.environ["AZURE_RESOURCE_GROUP"]}/providers/Microsoft.CognitiveServices/accounts/{os.environ["AZURE_OPENAI_CONNECTION_NAME"]}",
94+
role_name="Cognitive Services OpenAI User",
95+
principal_id=endpoint.identity.principal_id
96+
)
97+
98+
# 5. provide endpoint access to Azure AI Search resource
99+
create_role_assignment(
100+
scope=f"/subscriptions/{os.environ["AZURE_SUBSCRIPTION_ID"]}/resourceGroups/{os.environ["AZURE_RESOURCE_GROUP"]}/providers/Microsoft.Search/searchServices/{os.environ["AZURE_SEARCH_CONNECTION_NAME"]}",
101+
role_name="Search Index Data Contributor",
102+
principal_id=endpoint.identity.principal_id
103+
)
104+
105+
output_deployment_details(
106+
client=client,
107+
endpoint_name=endpoint_name,
108+
deployment_name=deployment_name
109+
)
110+
111+
def create_role_assignment(scope, role_name, principal_id):
112+
113+
# Get credential
114+
credential = DefaultAzureCredential()
115+
116+
# Instantiate the authorization management client
117+
auth_client = AuthorizationManagementClient(
118+
credential=credential,
119+
subscription_id=os.environ["AZURE_SUBSCRIPTION_ID"]
120+
)
121+
122+
roles = list(auth_client.role_definitions.list(
123+
scope,
124+
filter="roleName eq '{}'".format(role_name)))
125+
126+
assert len(roles) == 1
127+
role = roles[0]
128+
129+
# Create role assignment properties
130+
parameters = RoleAssignmentCreateParameters(
131+
role_definition_id=role.id,
132+
principal_id=principal_id,
133+
principal_type="ServicePrincipal"
134+
)
135+
136+
# Create role assignment
137+
role_assignment = auth_client.role_assignments.create(
138+
scope=scope,
139+
role_assignment_name=uuid.uuid4(),
140+
parameters=parameters
141+
)
142+
143+
return role_assignment
90144

91145
def output_deployment_details(client, endpoint_name, deployment_name) -> str:
92146
print("\n ~~~Deployment details~~~")
@@ -107,4 +161,4 @@ def output_deployment_details(client, endpoint_name, deployment_name) -> str:
107161
endpoint_name = args.endpoint_name if args.endpoint_name else f"rag-copilot-endpoint"
108162
deployment_name = args.deployment_name if args.deployment_name else f"rag-copilot-deployment"
109163

110-
deploy_flow(endpoint_name, deployment_name)
164+
deploy_flow(endpoint_name, deployment_name)

src/deployment/invoke.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1+
import requests
12
from helper_functions import get_client
23

34
def invoke_deployment(endpoint_name: str, query: str, stream: bool = False):
45
client = get_client()
56

6-
import requests
7-
87
if stream:
98
accept_header = "text/event-stream"
109
else:
1110
accept_header = "application/json"
1211

1312
scoring_url = client.online_endpoints.get(endpoint_name).scoring_uri
14-
primary_key = client.online_endpoints.get_keys(endpoint_name).primary_key
1513

1614
headers = {
1715
"Content-Type": "application/json",
18-
"Authorization": f"Bearer {primary_key}",
16+
"Authorization": f"Bearer {client._credential.get_token('https://ml.azure.com').token}",
1917
"Accept": accept_header
2018
}
2119

src/evaluation/evaluate.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
21
import json
32
import pathlib
4-
3+
import os
54
# set environment variables before importing any other code
65
from dotenv import load_dotenv
76
load_dotenv()
87

9-
import os
108
import pandas as pd
119
from pprint import pprint
1210

@@ -33,12 +31,12 @@ def copilot_qna(*, chat_input, **kwargs):
3331
}
3432
return parsedResult
3533

36-
def run_evaluation(name, dataset_path):
34+
def run_evaluation(eval_name, dataset_path):
3735

3836
model_config = AzureOpenAIModelConfiguration(
39-
azure_endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"),
40-
api_key=os.environ.get("AZURE_OPENAI_API_KEY"),
41-
azure_deployment=os.environ.get("AZURE_OPENAI_EVALUATION_DEPLOYMENT"),
37+
azure_deployment=os.environ["AZURE_OPENAI_EVALUATION_DEPLOYMENT"],
38+
api_version=os.environ["AZURE_OPENAI_API_VERSION"],
39+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"]
4240
)
4341

4442
# Initializing Evaluators
@@ -59,7 +57,7 @@ def run_evaluation(name, dataset_path):
5957

6058
result = evaluate(
6159
target=copilot_qna,
62-
evaluation_name=name,
60+
evaluation_name=eval_name,
6361
data=path,
6462
evaluators={
6563
"groundedness": groundedness_eval,
@@ -68,7 +66,6 @@ def run_evaluation(name, dataset_path):
6866
"coherence": coherence_eval,
6967
"friendliness":friendliness_eval,
7068
#"completeness": completeness_eval
71-
7269
},
7370
evaluator_config={
7471
"relevance": {"question": "${data.chat_input}"},
@@ -96,7 +93,7 @@ def run_evaluation(name, dataset_path):
9693
evaluation_name = args.evaluation_name if args.evaluation_name else "test-sdk-copilot"
9794
dataset_path = args.dataset_path if args.dataset_path else "./evaluation/evaluation_dataset_small.jsonl"
9895

99-
result, tabular_result = run_evaluation(name=evaluation_name,
96+
result, tabular_result = run_evaluation(eval_name=evaluation_name,
10097
dataset_path=dataset_path)
10198

10299
pprint("-----Summarized Metrics-----")

src/evaluation/evaluate_completeness.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ def copilot_qna(*, chat_input, **kwargs):
3838
def run_evaluation(name, dataset_path, prompty_filename: str):
3939

4040
model_config = AzureOpenAIModelConfiguration(
41-
azure_endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"),
42-
api_key=os.environ.get("AZURE_OPENAI_API_KEY"),
43-
azure_deployment=os.environ.get("AZURE_OPENAI_EVALUATION_DEPLOYMENT"),
44-
)
41+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
42+
api_version=os.environ["AZURE_OPENAI_API_VERSION"],
43+
azure_deployment=os.environ["AZURE_OPENAI_EVALUATION_DEPLOYMENT"]
44+
)
4545

4646
# Initializing Evaluators
4747
# relevance_eval = RelevanceEvaluator(model_config)

src/helper_functions.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from azure.ai.ml import MLClient
2-
from azure.identity import DefaultAzureCredential
3-
41
import os
2+
# set environment variables before importing any other code
53
from dotenv import load_dotenv
64
load_dotenv()
75

6+
from azure.ai.ml import MLClient
7+
from azure.identity import DefaultAzureCredential
8+
89
def get_client() -> MLClient:
910
# check if env variables are set and initialize client from those
1011
client = MLClient(DefaultAzureCredential(), os.environ["AZURE_SUBSCRIPTION_ID"], os.environ["AZURE_RESOURCE_GROUP"], os.environ["AZUREAI_PROJECT_NAME"])

0 commit comments

Comments
 (0)