forked from microsoft/everyonecancode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.py
189 lines (156 loc) · 6.24 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
"""
This is a simple FastAPI app developed for AnyoneCanCode that allows you to upload images to Azure Blob Storage.
This app is designed to be deployed to Azure App Service as a Linux container. It uses the Azure Storage SDK for Python to
stream images from Azure Blob Storage to the browser.
"""
import os
from datetime import datetime
from typing import List
from urllib.parse import quote
import uvicorn
from azure.core.exceptions import ResourceNotFoundError
from azure.storage.blob import BlobServiceClient
from fastapi import Depends, FastAPI, File, HTTPException, Request, UploadFile
from fastapi.responses import RedirectResponse, StreamingResponse, JSONResponse
from pydantic import BaseModel
import openai
app = FastAPI()
cache_header = {"Cache-Control": "max-age=31556952"}
shared_container_client = None
shared_openai_client = None
async def get_container_client():
"""Get a client to interact with the blob storage container."""
global shared_container_client
if not shared_container_client:
connection_string = os.environ["CUSTOMCONNSTR_STORAGE"]
service = BlobServiceClient.from_connection_string(conn_str=connection_string)
# Get a client to interact with a specific container - though it may not yet exist
shared_container_client = service.get_container_client("images")
return shared_container_client
async def get_openai_client():
"""Get a client to interact with the Azure OpenAI chat API."""
global shared_openai_client
if not shared_openai_client:
chat_api_key = os.environ["CHAT_API_KEY"]
chat_azure_endpoint = os.environ["CHAT_API_ENDPOINT"]
chat_azure_deployment = os.environ["AZURE_OPENAI_MODEL_NAME"]
shared_openai_client = openai.AzureOpenAI(
api_key=chat_api_key,
api_version="2023-12-01-preview",
azure_endpoint=chat_azure_endpoint,
azure_deployment=chat_azure_deployment
)
return shared_openai_client
@app.exception_handler(KeyError)
async def unicorn_exception_handler(request: Request, exc: KeyError):
"""Handle missing environment variables."""
match exc.args[0]:
case "CUSTOMCONNSTR_STORAGE":
return JSONResponse(
status_code=500,
content={
"message": f"Oops! You forgot to set the STORAGE connection string for your Azure Storage Account. You can test locally by setting CUSTOMCONNSTR_STORAGE. 🤓"
},
)
case "CHAT_API_KEY":
return JSONResponse(
status_code=500,
content={
"message": f"Oops! You forgot to set the CHAT_API_KEY environment variable. 🤓"
},
)
case "CHAT_API_ENDPOINT":
return JSONResponse(
status_code=500,
content={
"message": f"Oops! You forgot to set the CHAT_API_ENDPOINT environment variable. 🤓"
},
)
case "AZURE_OPENAI_MODEL_NAME":
return JSONResponse(
status_code=500,
content={
"message": f"Oops! You forgot to set the AZURE_OPENAI_MODEL_NAME environment variable. 🤓"
},
)
raise exc
@app.exception_handler(ValueError)
async def unicorn_exception_handler(request: Request, exc: KeyError):
if exc.args[0] == "Connection string is either blank or malformed.":
return JSONResponse(
status_code=500,
content={
"message": f"Oops! Your connection string is either blank or malformed. 🤓"
},
)
raise exc
class Image(BaseModel):
created_at: datetime = None
image_url: str
class Prompt(BaseModel):
message: str
@app.get("/")
async def redirect_to_docs():
"""Redirect to the docs page."""
return RedirectResponse("/docs")
@app.get("/images", response_model=List[Image])
async def list_images(container_client=Depends(get_container_client)):
"""List all images from a blob storage container."""
try:
blobs = [
Image(created_at=b.last_modified, image_url=f"/images/{quote(b.name)}")
for b in container_client.list_blobs()
]
blobs.sort(key=lambda a: a.created_at, reverse=True)
except ResourceNotFoundError:
return JSONResponse(
status_code=500,
content={
"message": f"Oops! You forgot to create a blob storage container named 'images'. 🤓"
},
)
return blobs
@app.get("/images/{image_name}")
async def images(image_name, container_client=Depends(get_container_client)):
"""Stream an image from the blob storage container."""
try:
blob_container_client = container_client.get_blob_client(image_name)
blob = blob_container_client.download_blob()
except ResourceNotFoundError:
raise HTTPException(
status_code=404,
detail="Your picture was not found, just try to upload again. 🤓",
)
return StreamingResponse(blob.chunks(), headers=cache_header)
@app.delete("/images/{image_name}")
async def delete(image_name, container_client=Depends(get_container_client)):
"""Delete an image from the blob storage container."""
try:
blob_container_client = container_client.get_blob_client(image_name)
blob_container_client.delete_blob()
except ResourceNotFoundError:
raise HTTPException(status_code=404, detail="Your picture was not found. 🤓")
return {}
@app.post("/upload/")
async def upload(
file: UploadFile = File(...), container_client=Depends(get_container_client)
):
"""Upload an image to the blob storage container."""
container_client.upload_blob(
file.filename, file.file, blob_type="BlockBlob", overwrite=True
)
return {"filename": file.filename}
@app.post("/chat")
async def chat(prompt: Prompt, azOpenAIClient=Depends(get_openai_client)):
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt.message}
]
response = azOpenAIClient.chat.completions.create(
model="gpt-35-turbo",
messages=messages,
)
return response.choices[0].message.content
if __name__ == "__main__":
"""Run the app locally for testing."""
uvicorn.run(app, port=8000)