1+ import json
2+ import re
3+ from unittest .mock import ANY
14import pytest
25import requests
36from pytest_httpserver import HTTPServer
@@ -68,7 +71,9 @@ def completions_mocking(httpserver: HTTPServer, app_config: AppConfig):
6871 )
6972
7073 httpserver .expect_oneshot_request (
71- f"/openai/deployments/{ app_config .get ('AZURE_OPENAI_MODEL' )} /chat/completions" ,
74+ re .compile (
75+ f"/openai/deployments/({ app_config .get ('AZURE_OPENAI_MODEL' )} |{ app_config .get ('AZURE_OPENAI_VISION_MODEL' )} )/chat/completions"
76+ ),
7277 method = "POST" ,
7378 ).respond_with_json (
7479 {
@@ -112,6 +117,30 @@ def completions_mocking(httpserver: HTTPServer, app_config: AppConfig):
112117 }
113118 )
114119
120+ httpserver .expect_oneshot_request (
121+ f"/indexes('{ app_config .get ('AZURE_SEARCH_INDEX' )} ')/docs/search.post.search" ,
122+ method = "POST" ,
123+ ).respond_with_json (
124+ {
125+ "value" : [
126+ {
127+ "@search.score" : 0.02916666865348816 ,
128+ "id" : "doc_1" ,
129+ "content" : "content" ,
130+ "content_vector" : [
131+ - 0.012909674 ,
132+ 0.00838491 ,
133+ ],
134+ "metadata" : '{"id": "doc_1", "source": "https://source_SAS_TOKEN_PLACEHOLDER_", "title": "/documents/doc.png", "chunk": 95, "offset": 202738, "page_number": null}' ,
135+ "title" : "/documents/doc.png" ,
136+ "source" : "https://source_SAS_TOKEN_PLACEHOLDER_" ,
137+ "chunk" : 95 ,
138+ "offset" : 202738 ,
139+ }
140+ ]
141+ }
142+ )
143+
115144
116145def test_post_responds_successfully (app_url : str , app_config : AppConfig ):
117146 # when
@@ -124,7 +153,7 @@ def test_post_responds_successfully(app_url: str, app_config: AppConfig):
124153 {
125154 "messages" : [
126155 {
127- "content" : r'{"citations": [{"content": "[/documents/doc.pdf](https://source)\n\n\ncontent", "id": "doc_1", "chunk_id": 95, "title": "/documents/doc.pdf", "filepath": "source", "url": "[/documents/doc.pdf](https://source)", "metadata": {"offset": 202738, "source": "https://source", "markdown_url": "[/documents/doc.pdf](https://source)", "title": "/documents/doc.pdf", "original_url": "https://source", "chunk": 95, "key": "doc_1", "filename": "source"}}], "intent": "What is the meaning of life?"}' ,
156+ "content" : ANY , # SAS URL changes each time
128157 "end_turn" : False ,
129158 "role" : "tool" ,
130159 },
@@ -143,6 +172,32 @@ def test_post_responds_successfully(app_url: str, app_config: AppConfig):
143172 }
144173 assert response .headers ["Content-Type" ] == "application/json"
145174
175+ content = json .loads (response .json ()["choices" ][0 ]["messages" ][0 ]["content" ])
176+
177+ assert content == {
178+ "citations" : [
179+ {
180+ "content" : ANY ,
181+ "id" : "doc_1" ,
182+ "chunk_id" : 95 ,
183+ "title" : "/documents/doc.png" ,
184+ "filepath" : "source" ,
185+ "url" : ANY ,
186+ "metadata" : {
187+ "offset" : 202738 ,
188+ "source" : "https://source_SAS_TOKEN_PLACEHOLDER_" ,
189+ "markdown_url" : ANY ,
190+ "title" : "/documents/doc.png" ,
191+ "original_url" : "https://source_SAS_TOKEN_PLACEHOLDER_" ,
192+ "chunk" : 95 ,
193+ "key" : "doc_1" ,
194+ "filename" : "source" ,
195+ },
196+ }
197+ ],
198+ "intent" : "What is the meaning of life?" ,
199+ }
200+
146201
147202def test_text_passed_to_computer_vision_to_generate_text_embeddings (
148203 app_url : str , httpserver : HTTPServer , app_config : AppConfig
@@ -169,3 +224,68 @@ def test_text_passed_to_computer_vision_to_generate_text_embeddings(
169224 times = 1 ,
170225 ),
171226 )
227+
228+
229+ def test_image_urls_included_in_call_to_openai (
230+ app_url : str , app_config : AppConfig , httpserver : HTTPServer
231+ ):
232+ # when
233+ requests .post (f"{ app_url } { path } " , json = body )
234+
235+ # then
236+ request = verify_request_made (
237+ mock_httpserver = httpserver ,
238+ request_matcher = RequestMatcher (
239+ path = f"/openai/deployments/{ app_config .get ('AZURE_OPENAI_VISION_MODEL' )} /chat/completions" ,
240+ method = "POST" ,
241+ json = {
242+ "messages" : [
243+ {
244+ "content" : "system prompt" ,
245+ "role" : "system" ,
246+ },
247+ {
248+ "content" : '## Retrieved Documents\n {"retrieved_documents":[{"[doc1]":{"content":"content"}}]}\n \n ## User Question\n user question' ,
249+ "name" : "example_user" ,
250+ "role" : "system" ,
251+ },
252+ {
253+ "content" : "answer" ,
254+ "name" : "example_assistant" ,
255+ "role" : "system" ,
256+ },
257+ {
258+ "content" : "You are an AI assistant that helps people find information." ,
259+ "role" : "system" ,
260+ },
261+ {"content" : "Hello" , "role" : "user" },
262+ {"content" : "Hi, how can I help?" , "role" : "assistant" },
263+ {
264+ "content" : [
265+ {
266+ "type" : "text" ,
267+ "text" : '## Retrieved Documents\n {"retrieved_documents":[{"[doc1]":{"content":"content"}}]}\n \n ## User Question\n What is the meaning of life?' ,
268+ },
269+ {"type" : "image_url" , "image_url" : ANY },
270+ ],
271+ "role" : "user" ,
272+ },
273+ ],
274+ "model" : app_config .get ("AZURE_OPENAI_VISION_MODEL" ),
275+ "max_tokens" : int (app_config .get ("AZURE_OPENAI_MAX_TOKENS" )),
276+ "temperature" : 0 ,
277+ },
278+ headers = {
279+ "Accept" : "application/json" ,
280+ "Content-Type" : "application/json" ,
281+ "Authorization" : f"Bearer { app_config .get ('AZURE_OPENAI_API_KEY' )} " ,
282+ "Api-Key" : app_config .get ("AZURE_OPENAI_API_KEY" ),
283+ },
284+ query_string = "api-version=2024-02-01" ,
285+ times = 1 ,
286+ ),
287+ )[0 ]
288+
289+ assert request .json ["messages" ][6 ]["content" ][1 ]["image_url" ].startswith (
290+ "https://source"
291+ )
0 commit comments