5
5
import mimetypes
6
6
import os
7
7
from pathlib import Path
8
- from typing import AsyncGenerator , cast
8
+ from typing import Any , AsyncGenerator , Dict , cast
9
9
10
10
from azure .core .exceptions import ResourceNotFoundError
11
11
from azure .identity .aio import DefaultAzureCredential , get_bearer_token_provider
14
14
from azure .search .documents .aio import SearchClient
15
15
from azure .search .documents .indexes .aio import SearchIndexClient
16
16
from azure .storage .blob .aio import BlobServiceClient
17
- from openai import APIError , AsyncAzureOpenAI , AsyncOpenAI
17
+ from openai import AsyncAzureOpenAI , AsyncOpenAI
18
18
from opentelemetry .instrumentation .aiohttp_client import AioHttpClientInstrumentor
19
19
from opentelemetry .instrumentation .asgi import OpenTelemetryMiddleware
20
20
from opentelemetry .instrumentation .httpx import HTTPXClientInstrumentor
36
36
from approaches .chatreadretrievereadvision import ChatReadRetrieveReadVisionApproach
37
37
from approaches .retrievethenread import RetrieveThenReadApproach
38
38
from approaches .retrievethenreadvision import RetrieveThenReadVisionApproach
39
+ from config import (
40
+ CONFIG_ASK_APPROACH ,
41
+ CONFIG_ASK_VISION_APPROACH ,
42
+ CONFIG_AUTH_CLIENT ,
43
+ CONFIG_BLOB_CONTAINER_CLIENT ,
44
+ CONFIG_CHAT_APPROACH ,
45
+ CONFIG_CHAT_VISION_APPROACH ,
46
+ CONFIG_GPT4V_DEPLOYED ,
47
+ CONFIG_OPENAI_CLIENT ,
48
+ CONFIG_SEARCH_CLIENT ,
49
+ )
39
50
from core .authentication import AuthenticationHelper
40
-
41
- CONFIG_OPENAI_TOKEN = "openai_token"
42
- CONFIG_CREDENTIAL = "azure_credential"
43
- CONFIG_ASK_APPROACH = "ask_approach"
44
- CONFIG_ASK_VISION_APPROACH = "ask_vision_approach"
45
- CONFIG_CHAT_VISION_APPROACH = "chat_vision_approach"
46
- CONFIG_CHAT_APPROACH = "chat_approach"
47
- CONFIG_BLOB_CONTAINER_CLIENT = "blob_container_client"
48
- CONFIG_AUTH_CLIENT = "auth_client"
49
- CONFIG_GPT4V_DEPLOYED = "gpt4v_deployed"
50
- CONFIG_SEARCH_CLIENT = "search_client"
51
- CONFIG_OPENAI_CLIENT = "openai_client"
52
- ERROR_MESSAGE = """The app encountered an error processing your request.
53
- If you are an administrator of the app, view the full error in the logs. See aka.ms/appservice-logs for more information.
54
- Error type: {error_type}
55
- """
56
- ERROR_MESSAGE_FILTER = """Your message contains content that was flagged by the OpenAI content filter."""
51
+ from decorators import authenticated , authenticated_path
52
+ from error import error_dict , error_response
57
53
58
54
bp = Blueprint ("routes" , __name__ , static_folder = "static" )
59
55
# Fix Windows registry issue with mimetypes
@@ -83,11 +79,16 @@ async def assets(path):
83
79
return await send_from_directory (Path (__file__ ).resolve ().parent / "static" / "assets" , path )
84
80
85
81
86
- # Serve content files from blob storage from within the app to keep the example self-contained.
87
- # *** NOTE *** this assumes that the content files are public, or at least that all users of the app
88
- # can access all the files. This is also slow and memory hungry.
89
82
@bp .route ("/content/<path>" )
83
+ @authenticated_path
90
84
async def content_file (path : str ):
85
+ """
86
+ Serve content files from blob storage from within the app to keep the example self-contained.
87
+ *** NOTE *** if you are using app services authentication, this route will return unauthorized to all users that are not logged in
88
+ if AZURE_ENFORCE_ACCESS_CONTROL is not set or false, logged in users can access all files regardless of access control
89
+ if AZURE_ENFORCE_ACCESS_CONTROL is set to true, logged in users can only access files they have access to
90
+ This is also slow and memory hungry.
91
+ """
91
92
# Remove page number from path, filename-1.txt -> filename.txt
92
93
if path .find ("#page=" ) > 0 :
93
94
path_parts = path .rsplit ("#page=" , 1 )
@@ -110,28 +111,15 @@ async def content_file(path: str):
110
111
return await send_file (blob_file , mimetype = mime_type , as_attachment = False , attachment_filename = path )
111
112
112
113
113
- def error_dict (error : Exception ) -> dict :
114
- if isinstance (error , APIError ) and error .code == "content_filter" :
115
- return {"error" : ERROR_MESSAGE_FILTER }
116
- return {"error" : ERROR_MESSAGE .format (error_type = type (error ))}
117
-
118
-
119
- def error_response (error : Exception , route : str , status_code : int = 500 ):
120
- logging .exception ("Exception in %s: %s" , route , error )
121
- if isinstance (error , APIError ) and error .code == "content_filter" :
122
- status_code = 400
123
- return jsonify (error_dict (error )), status_code
124
-
125
-
126
114
@bp .route ("/ask" , methods = ["POST" ])
127
- async def ask ():
115
+ @authenticated
116
+ async def ask (auth_claims : Dict [str , Any ]):
128
117
if not request .is_json :
129
118
return jsonify ({"error" : "request must be json" }), 415
130
119
request_json = await request .get_json ()
131
120
context = request_json .get ("context" , {})
132
- auth_helper = current_app . config [ CONFIG_AUTH_CLIENT ]
121
+ context [ "auth_claims" ] = auth_claims
133
122
try :
134
- context ["auth_claims" ] = await auth_helper .get_auth_claims_if_enabled (request .headers )
135
123
use_gpt4v = context .get ("overrides" , {}).get ("use_gpt4v" , False )
136
124
approach : Approach
137
125
if use_gpt4v and CONFIG_ASK_VISION_APPROACH in current_app .config :
@@ -163,14 +151,14 @@ async def format_as_ndjson(r: AsyncGenerator[dict, None]) -> AsyncGenerator[str,
163
151
164
152
165
153
@bp .route ("/chat" , methods = ["POST" ])
166
- async def chat ():
154
+ @authenticated
155
+ async def chat (auth_claims : Dict [str , Any ]):
167
156
if not request .is_json :
168
157
return jsonify ({"error" : "request must be json" }), 415
169
158
request_json = await request .get_json ()
170
159
context = request_json .get ("context" , {})
171
- auth_helper = current_app . config [ CONFIG_AUTH_CLIENT ]
160
+ context [ "auth_claims" ] = auth_claims
172
161
try :
173
- context ["auth_claims" ] = await auth_helper .get_auth_claims_if_enabled (request .headers )
174
162
use_gpt4v = context .get ("overrides" , {}).get ("use_gpt4v" , False )
175
163
approach : Approach
176
164
if use_gpt4v and CONFIG_CHAT_VISION_APPROACH in current_app .config :
0 commit comments