Skip to content

Commit 9c69581

Browse files
authored
Update to Azure OpenAI version 2024-02-01 (#565)
1 parent 225fbc8 commit 9c69581

File tree

14 files changed

+617
-154
lines changed

14 files changed

+617
-154
lines changed

.devcontainer/devcontainer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
"settings": {
3636
"azureFunctions.projectSubpath": "code/backend/batch",
3737
"python.defaultInterpreterPath": "/usr/local/bin/python",
38-
"python.pythonPath": "/usr/local/bin/python"
38+
"python.pythonPath": "/usr/local/bin/python",
39+
"files.insertFinalNewline": true,
40+
"files.trimFinalNewlines": true,
41+
"files.trimTrailingWhitespace": true
3942
}
4043
}
4144
}

.env.sample

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ AZURE_OPENAI_TOP_P=1.0
2727
AZURE_OPENAI_MAX_TOKENS=1000
2828
AZURE_OPENAI_STOP_SEQUENCE=
2929
AZURE_OPENAI_SYSTEM_MESSAGE=You are an AI assistant that helps people find information.
30-
AZURE_OPENAI_API_VERSION=2023-12-01-preview
30+
AZURE_OPENAI_API_VERSION=2024-02-01
3131
AZURE_OPENAI_STREAM=True
3232
# Backend for processing the documents and application logging in the app
3333
AzureWebJobsStorage=
@@ -44,8 +44,8 @@ AZURE_FORM_RECOGNIZER_KEY=
4444
# Azure AI Content Safety for filtering out the inappropriate questions or answers
4545
AZURE_CONTENT_SAFETY_ENDPOINT=
4646
AZURE_CONTENT_SAFETY_KEY=
47-
# Orchestration strategy. Use Azure OpenAI Functions (openai_functions) or LangChain (langchain) for messages orchestration. If you are using a new model version 0613 select "openai_functions" (or "langchain"), if you are using a 0314 model version select "langchain"
48-
ORCHESTRATION_STRATEGY=openai_functions
47+
# Orchestration strategy. Use Azure OpenAI Functions (openai_function) or LangChain (langchain) for messages orchestration. If you are using a new model version 0613 select "openai_function" (or "langchain"), if you are using a 0314 model version select "langchain"
48+
ORCHESTRATION_STRATEGY=openai_function
4949
#Speech-to-text feature
5050
AZURE_SPEECH_SERVICE_KEY=
5151
AZURE_SPEECH_SERVICE_REGION=
@@ -54,4 +54,4 @@ AZURE_SPEECH_SERVICE_REGION=
5454
# When USE_KEY_VAULT=true, please make sure to set AZURE_KEY_VAULT_ENDPOINT
5555
AZURE_AUTH_TYPE=keys
5656
USE_KEY_VAULT=true
57-
AZURE_KEY_VAULT_ENDPOINT=
57+
AZURE_KEY_VAULT_ENDPOINT=

code/app.py

Lines changed: 114 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -47,77 +47,70 @@ def prepare_body_headers_with_data(request):
4747

4848
body = {
4949
"messages": request_messages,
50-
"temperature": env_helper.AZURE_OPENAI_TEMPERATURE,
51-
"max_tokens": env_helper.AZURE_OPENAI_MAX_TOKENS,
52-
"top_p": env_helper.AZURE_OPENAI_TOP_P,
50+
"temperature": float(env_helper.AZURE_OPENAI_TEMPERATURE),
51+
"max_tokens": int(env_helper.AZURE_OPENAI_MAX_TOKENS),
52+
"top_p": float(env_helper.AZURE_OPENAI_TOP_P),
5353
"stop": (
5454
env_helper.AZURE_OPENAI_STOP_SEQUENCE.split("|")
5555
if env_helper.AZURE_OPENAI_STOP_SEQUENCE
5656
else None
5757
),
5858
"stream": env_helper.SHOULD_STREAM,
59-
"dataSources": [
59+
"data_sources": [
6060
{
61-
"type": "AzureCognitiveSearch",
61+
"type": "azure_search",
6262
"parameters": {
63+
# authentication is set below
6364
"endpoint": env_helper.AZURE_SEARCH_SERVICE,
64-
"key": env_helper.AZURE_SEARCH_KEY,
65-
"indexName": env_helper.AZURE_SEARCH_INDEX,
66-
"fieldsMapping": {
67-
"contentField": (
65+
"index_name": env_helper.AZURE_SEARCH_INDEX,
66+
"fields_mapping": {
67+
"content_fields": (
6868
env_helper.AZURE_SEARCH_CONTENT_COLUMNS.split("|")
6969
if env_helper.AZURE_SEARCH_CONTENT_COLUMNS
7070
else []
7171
),
72-
"titleField": (
73-
env_helper.AZURE_SEARCH_TITLE_COLUMN
74-
if env_helper.AZURE_SEARCH_TITLE_COLUMN
75-
else None
76-
),
77-
"urlField": (
78-
env_helper.AZURE_SEARCH_URL_COLUMN
79-
if env_helper.AZURE_SEARCH_URL_COLUMN
80-
else None
81-
),
82-
"filepathField": (
83-
env_helper.AZURE_SEARCH_FILENAME_COLUMN
84-
if env_helper.AZURE_SEARCH_FILENAME_COLUMN
85-
else None
72+
"title_field": env_helper.AZURE_SEARCH_TITLE_COLUMN or None,
73+
"url_field": env_helper.AZURE_SEARCH_URL_COLUMN or None,
74+
"filepath_field": (
75+
env_helper.AZURE_SEARCH_FILENAME_COLUMN or None
8676
),
8777
},
88-
"inScope": env_helper.AZURE_SEARCH_ENABLE_IN_DOMAIN,
89-
"topNDocuments": env_helper.AZURE_SEARCH_TOP_K,
90-
"queryType": (
78+
"in_scope": env_helper.AZURE_SEARCH_ENABLE_IN_DOMAIN,
79+
"top_n_documents": env_helper.AZURE_SEARCH_TOP_K,
80+
"query_type": (
9181
"semantic"
9282
if env_helper.AZURE_SEARCH_USE_SEMANTIC_SEARCH
9383
else "simple"
9484
),
95-
"semanticConfiguration": (
85+
"semantic_configuration": (
9686
env_helper.AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG
9787
if env_helper.AZURE_SEARCH_USE_SEMANTIC_SEARCH
9888
and env_helper.AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG
9989
else ""
10090
),
101-
"roleInformation": env_helper.AZURE_OPENAI_SYSTEM_MESSAGE,
91+
"role_information": env_helper.AZURE_OPENAI_SYSTEM_MESSAGE,
10292
},
10393
}
10494
],
10595
}
10696

107-
chatgpt_url = f"{env_helper.AZURE_OPENAI_ENDPOINT}openai/deployments/{env_helper.AZURE_OPENAI_MODEL}"
108-
if env_helper.is_chat_model():
109-
chatgpt_url += "/chat/completions?api-version=2023-12-01-preview"
110-
else:
111-
chatgpt_url += "/completions?api-version=2023-12-01-preview"
112-
11397
headers = {
11498
"Content-Type": "application/json",
115-
"api-key": env_helper.AZURE_OPENAI_API_KEY,
116-
"chatgpt_url": chatgpt_url,
117-
"chatgpt_key": env_helper.AZURE_OPENAI_API_KEY,
11899
"x-ms-useragent": "GitHubSampleWebApp/PublicAPI/1.0.0",
119100
}
120101

102+
if env_helper.AZURE_AUTH_TYPE == "rbac":
103+
body["data_sources"][0]["parameters"]["authentication"] = {
104+
"type": "system_assigned_managed_identity"
105+
}
106+
headers["Authorization"] = f"Bearer {env_helper.AZURE_TOKEN_PROVIDER()}"
107+
else:
108+
body["data_sources"][0]["parameters"]["authentication"] = {
109+
"type": "api_key",
110+
"key": env_helper.AZURE_SEARCH_KEY,
111+
}
112+
headers["api-key"] = env_helper.AZURE_OPENAI_API_KEY
113+
121114
return body, headers
122115

123116

@@ -128,37 +121,54 @@ def stream_with_data(body, headers, endpoint):
128121
"model": "",
129122
"created": 0,
130123
"object": "",
131-
"choices": [{"messages": []}],
124+
"choices": [
125+
{
126+
"messages": [
127+
{
128+
"content": "",
129+
"end_turn": False,
130+
"role": "tool",
131+
},
132+
{
133+
"content": "",
134+
"end_turn": False,
135+
"role": "assistant",
136+
},
137+
]
138+
}
139+
],
132140
}
133141
try:
134142
with s.post(endpoint, json=body, headers=headers, stream=True) as r:
135143
for line in r.iter_lines(chunk_size=10):
136144
if line:
137-
lineJson = json.loads(line.lstrip(b"data:").decode("utf-8"))
145+
lineJson = json.loads(line.lstrip(b"data: ").decode("utf-8"))
138146
if "error" in lineJson:
139147
yield json.dumps(lineJson, ensure_ascii=False) + "\n"
148+
return
149+
150+
if lineJson["choices"][0]["end_turn"]:
151+
response["choices"][0]["messages"][1]["end_turn"] = True
152+
yield json.dumps(response, ensure_ascii=False) + "\n"
153+
return
154+
140155
response["id"] = lineJson["id"]
141156
response["model"] = lineJson["model"]
142157
response["created"] = lineJson["created"]
143158
response["object"] = lineJson["object"]
144159

145-
role = lineJson["choices"][0]["messages"][0]["delta"].get("role")
146-
if role == "tool":
147-
response["choices"][0]["messages"].append(
148-
lineJson["choices"][0]["messages"][0]["delta"]
149-
)
150-
elif role == "assistant":
151-
response["choices"][0]["messages"].append(
152-
{"role": "assistant", "content": ""}
160+
delta = lineJson["choices"][0]["delta"]
161+
role = delta.get("role")
162+
163+
if role == "assistant":
164+
response["choices"][0]["messages"][0]["content"] = json.dumps(
165+
delta["context"],
166+
ensure_ascii=False,
153167
)
154168
else:
155-
deltaText = lineJson["choices"][0]["messages"][0]["delta"][
169+
response["choices"][0]["messages"][1]["content"] += delta[
156170
"content"
157171
]
158-
if deltaText != "[DONE]":
159-
response["choices"][0]["messages"][1][
160-
"content"
161-
] += deltaText
162172

163173
yield json.dumps(response, ensure_ascii=False) + "\n"
164174
except Exception as e:
@@ -167,39 +177,68 @@ def stream_with_data(body, headers, endpoint):
167177

168178
def conversation_with_data(request):
169179
body, headers = prepare_body_headers_with_data(request)
170-
endpoint = f"{env_helper.AZURE_OPENAI_ENDPOINT}openai/deployments/{env_helper.AZURE_OPENAI_MODEL}/extensions/chat/completions?api-version={env_helper.AZURE_OPENAI_API_VERSION}"
180+
endpoint = f"{env_helper.AZURE_OPENAI_ENDPOINT}openai/deployments/{env_helper.AZURE_OPENAI_MODEL}/chat/completions?api-version={env_helper.AZURE_OPENAI_API_VERSION}"
171181

172182
if not env_helper.SHOULD_STREAM:
173183
r = requests.post(endpoint, headers=headers, json=body)
174184
status_code = r.status_code
175185
r = r.json()
176186

177-
return Response(json.dumps(r, ensure_ascii=False), status=status_code)
187+
response = {
188+
"id": r["id"],
189+
"model": r["model"],
190+
"created": r["created"],
191+
"object": r["object"],
192+
"choices": [
193+
{
194+
"messages": [
195+
{
196+
"content": json.dumps(
197+
r["choices"][0]["message"]["context"],
198+
ensure_ascii=False,
199+
),
200+
"end_turn": False,
201+
"role": "tool",
202+
},
203+
{
204+
"content": r["choices"][0]["message"]["content"],
205+
"end_turn": True,
206+
"role": "assistant",
207+
},
208+
]
209+
}
210+
],
211+
}
212+
213+
return Response(json.dumps(response, ensure_ascii=False), status=status_code)
178214
else:
179-
if request.method == "POST":
180-
return Response(
181-
stream_with_data(body, headers, endpoint),
182-
mimetype="application/json-lines",
183-
)
184-
else:
185-
return Response(None, mimetype="application/json-lines")
215+
return Response(
216+
stream_with_data(body, headers, endpoint),
217+
mimetype="application/json-lines",
218+
)
186219

187220

188221
def stream_without_data(response):
189222
responseText = ""
190223
for line in response:
191-
deltaText = line["choices"][0]["delta"].get("content")
192-
if deltaText and deltaText != "[DONE]":
193-
responseText += deltaText
224+
if not line.choices:
225+
continue
226+
227+
deltaText = line.choices[0].delta.content
228+
229+
if deltaText is None:
230+
return
231+
232+
responseText += deltaText
194233

195234
response_obj = {
196-
"id": line["id"],
197-
"model": line["model"],
198-
"created": line["created"],
199-
"object": line["object"],
235+
"id": line.id,
236+
"model": line.model,
237+
"created": line.created,
238+
"object": line.object,
200239
"choices": [{"messages": [{"role": "assistant", "content": responseText}]}],
201240
}
202-
yield json.dumps(response_obj).replace("\n", "\\n") + "\n"
241+
yield json.dumps(response_obj, ensure_ascii=False) + "\n"
203242

204243

205244
def conversation_without_data(request):
@@ -239,7 +278,7 @@ def conversation_without_data(request):
239278

240279
if not env_helper.SHOULD_STREAM:
241280
response_obj = {
242-
"id": response,
281+
"id": response.id,
243282
"model": response.model,
244283
"created": response.created,
245284
"object": response.object,
@@ -257,15 +296,12 @@ def conversation_without_data(request):
257296

258297
return jsonify(response_obj), 200
259298
else:
260-
if request.method == "POST":
261-
return Response(
262-
stream_without_data(response), mimetype="application/json-lines"
263-
)
264-
else:
265-
return Response(None, mimetype="application/json-lines")
299+
return Response(
300+
stream_without_data(response), mimetype="application/json-lines"
301+
)
266302

267303

268-
@app.route("/api/conversation/azure_byod", methods=["GET", "POST"])
304+
@app.route("/api/conversation/azure_byod", methods=["POST"])
269305
def conversation_azure_byod():
270306
try:
271307
if env_helper.should_use_data():
@@ -297,7 +333,7 @@ def get_orchestrator_config():
297333
return ConfigHelper.get_active_config_or_default().orchestrator
298334

299335

300-
@app.route("/api/conversation/custom", methods=["GET", "POST"])
336+
@app.route("/api/conversation/custom", methods=["POST"])
301337
def conversation_custom():
302338
message_orchestrator = get_message_orchestrator()
303339

code/backend/batch/utilities/helpers/EnvHelper.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,16 @@ def __init__(self, **kwargs) -> None:
5757
self.AZURE_OPENAI_MODEL_NAME = os.getenv(
5858
"AZURE_OPENAI_MODEL_NAME", "gpt-35-turbo"
5959
)
60-
self.AZURE_OPENAI_TEMPERATURE = os.getenv("AZURE_OPENAI_TEMPERATURE", 0)
61-
self.AZURE_OPENAI_TOP_P = os.getenv("AZURE_OPENAI_TOP_P", 1.0)
62-
self.AZURE_OPENAI_MAX_TOKENS = os.getenv("AZURE_OPENAI_MAX_TOKENS", 1000)
60+
self.AZURE_OPENAI_TEMPERATURE = os.getenv("AZURE_OPENAI_TEMPERATURE", "0")
61+
self.AZURE_OPENAI_TOP_P = os.getenv("AZURE_OPENAI_TOP_P", "1.0")
62+
self.AZURE_OPENAI_MAX_TOKENS = os.getenv("AZURE_OPENAI_MAX_TOKENS", "1000")
6363
self.AZURE_OPENAI_STOP_SEQUENCE = os.getenv("AZURE_OPENAI_STOP_SEQUENCE", "")
6464
self.AZURE_OPENAI_SYSTEM_MESSAGE = os.getenv(
6565
"AZURE_OPENAI_SYSTEM_MESSAGE",
6666
"You are an AI assistant that helps people find information.",
6767
)
6868
self.AZURE_OPENAI_API_VERSION = os.getenv(
69-
"AZURE_OPENAI_API_VERSION", "2023-12-01-preview"
69+
"AZURE_OPENAI_API_VERSION", "2024-02-01"
7070
)
7171
self.AZURE_OPENAI_STREAM = os.getenv("AZURE_OPENAI_STREAM", "true")
7272
self.AZURE_OPENAI_EMBEDDING_MODEL = os.getenv(
@@ -160,7 +160,7 @@ def should_use_data(self) -> bool:
160160
if (
161161
self.AZURE_SEARCH_SERVICE
162162
and self.AZURE_SEARCH_INDEX
163-
and self.AZURE_SEARCH_KEY
163+
and (self.AZURE_SEARCH_KEY or self.AZURE_AUTH_TYPE == "rbac")
164164
):
165165
return True
166166
return False

code/tests/functional/backend_api/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def app_port() -> int:
5252

5353

5454
@pytest.fixture(scope="session")
55-
def app_url(app_port: int) -> int:
55+
def app_url(app_port: int) -> str:
5656
return f"http://localhost:{app_port}"
5757

5858

code/tests/functional/backend_api/test_conversation_custom.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def test_post_makes_correct_call_to_openai_embeddings(
7676
"Authorization": f"Bearer {app_config.get('AZURE_OPENAI_API_KEY')}",
7777
"Api-Key": app_config.get("AZURE_OPENAI_API_KEY"),
7878
},
79-
query_string="api-version=2023-12-01-preview",
79+
query_string="api-version=2024-02-01",
8080
times=2,
8181
),
8282
)
@@ -193,7 +193,7 @@ def test_post_makes_correct_call_to_openai_chat_completions(
193193
"Authorization": f"Bearer {app_config.get('AZURE_OPENAI_API_KEY')}",
194194
"Api-Key": app_config.get("AZURE_OPENAI_API_KEY"),
195195
},
196-
query_string="api-version=2023-12-01-preview",
196+
query_string="api-version=2024-02-01",
197197
times=1,
198198
),
199199
)

0 commit comments

Comments
 (0)