Skip to content

Commit 2ddcb30

Browse files
committed
refac
1 parent 96265cf commit 2ddcb30

4 files changed

Lines changed: 106 additions & 84 deletions

File tree

backend/open_webui/main.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from aiocache import cached
2222
import aiohttp
2323
import anyio.to_thread
24-
import requests
24+
2525
from redis import Redis
2626

2727

@@ -60,6 +60,7 @@
6060
from open_webui.utils import logger
6161
from open_webui.utils.audit import AuditLevel, AuditLoggingMiddleware
6262
from open_webui.utils.logger import start_logger
63+
from open_webui.utils.session_pool import get_session
6364
from open_webui.socket.main import (
6465
MODELS,
6566
app as socket_app,
@@ -2512,7 +2513,13 @@ async def oauth_backchannel_logout(
25122513
@app.get('/manifest.json')
25132514
async def get_manifest_json():
25142515
if app.state.EXTERNAL_PWA_MANIFEST_URL:
2515-
return requests.get(app.state.EXTERNAL_PWA_MANIFEST_URL).json()
2516+
session = await get_session()
2517+
async with session.get(
2518+
app.state.EXTERNAL_PWA_MANIFEST_URL,
2519+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
2520+
) as r:
2521+
r.raise_for_status()
2522+
return await r.json()
25162523
else:
25172524
return {
25182525
'name': app.state.WEBUI_NAME,

backend/open_webui/routers/images.py

Lines changed: 81 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from typing import Optional
1111

1212
from urllib.parse import quote
13+
import aiohttp
1314
import requests
1415
from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile
1516
from fastapi.responses import FileResponse
@@ -21,7 +22,8 @@
2122
)
2223
from open_webui.constants import ERROR_MESSAGES
2324
from open_webui.retrieval.web.utils import validate_url
24-
from open_webui.env import ENABLE_FORWARD_USER_INFO_HEADERS
25+
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL, ENABLE_FORWARD_USER_INFO_HEADERS
26+
from open_webui.utils.session_pool import get_session
2527

2628
from open_webui.models.chats import Chats
2729
from open_webui.routers.files import upload_file_handler, get_file_content_by_id
@@ -313,12 +315,14 @@ def get_automatic1111_api_auth(request: Request):
313315
async def verify_url(request: Request, user=Depends(get_admin_user)):
314316
if request.app.state.config.IMAGE_GENERATION_ENGINE == 'automatic1111':
315317
try:
316-
r = requests.get(
318+
session = await get_session()
319+
async with session.get(
317320
url=f'{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options',
318321
headers={'authorization': get_automatic1111_api_auth(request)},
319-
)
320-
r.raise_for_status()
321-
return True
322+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
323+
) as r:
324+
r.raise_for_status()
325+
return True
322326
except Exception:
323327
request.app.state.config.ENABLE_IMAGE_GENERATION = False
324328
raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL)
@@ -327,12 +331,14 @@ async def verify_url(request: Request, user=Depends(get_admin_user)):
327331
if request.app.state.config.COMFYUI_API_KEY:
328332
headers = {'Authorization': f'Bearer {request.app.state.config.COMFYUI_API_KEY}'}
329333
try:
330-
r = requests.get(
334+
session = await get_session()
335+
async with session.get(
331336
url=f'{request.app.state.config.COMFYUI_BASE_URL}/object_info',
332337
headers=headers,
333-
)
334-
r.raise_for_status()
335-
return True
338+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
339+
) as r:
340+
r.raise_for_status()
341+
return True
336342
except Exception:
337343
request.app.state.config.ENABLE_IMAGE_GENERATION = False
338344
raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL)
@@ -357,11 +363,13 @@ async def get_models(request: Request, user=Depends(get_verified_user)):
357363
elif request.app.state.config.IMAGE_GENERATION_ENGINE == 'comfyui':
358364
# TODO - get models from comfyui
359365
headers = {'Authorization': f'Bearer {request.app.state.config.COMFYUI_API_KEY}'}
360-
r = requests.get(
366+
session = await get_session()
367+
async with session.get(
361368
url=f'{request.app.state.config.COMFYUI_BASE_URL}/object_info',
362369
headers=headers,
363-
)
364-
info = r.json()
370+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
371+
) as r:
372+
info = await r.json()
365373

366374
workflow = json.loads(request.app.state.config.COMFYUI_WORKFLOW)
367375
model_node_id = None
@@ -399,11 +407,13 @@ async def get_models(request: Request, user=Depends(get_verified_user)):
399407
request.app.state.config.IMAGE_GENERATION_ENGINE == 'automatic1111'
400408
or request.app.state.config.IMAGE_GENERATION_ENGINE == ''
401409
):
402-
r = requests.get(
410+
session = await get_session()
411+
async with session.get(
403412
url=f'{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/sd-models',
404413
headers={'authorization': get_automatic1111_api_auth(request)},
405-
)
406-
models = r.json()
414+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
415+
) as r:
416+
models = await r.json()
407417
return list(
408418
map(
409419
lambda model: {'id': model['title'], 'name': model['model_name']},
@@ -533,7 +543,7 @@ async def image_generations(
533543

534544
model = get_image_model(request)
535545

536-
r = None
546+
537547
try:
538548
if request.app.state.config.IMAGE_GENERATION_ENGINE == 'openai':
539549
headers = {
@@ -568,16 +578,15 @@ async def image_generations(
568578
),
569579
}
570580

571-
# Use asyncio.to_thread for the requests.post call
572-
r = await asyncio.to_thread(
573-
requests.post,
581+
session = await get_session()
582+
async with session.post(
574583
url=url,
575584
json=data,
576585
headers=headers,
577-
)
578-
579-
r.raise_for_status()
580-
res = r.json()
586+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
587+
) as r:
588+
r.raise_for_status()
589+
res = await r.json()
581590

582591
images = []
583592

@@ -619,16 +628,15 @@ async def image_generations(
619628
model = f'{model}:generateContent'
620629
data = {'contents': [{'parts': [{'text': form_data.prompt}]}]}
621630

622-
# Use asyncio.to_thread for the requests.post call
623-
r = await asyncio.to_thread(
624-
requests.post,
631+
session = await get_session()
632+
async with session.post(
625633
url=f'{request.app.state.config.IMAGES_GEMINI_API_BASE_URL}/models/{model}',
626634
json=data,
627635
headers=headers,
628-
)
629-
630-
r.raise_for_status()
631-
res = r.json()
636+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
637+
) as r:
638+
r.raise_for_status()
639+
res = await r.json()
632640

633641
images = []
634642

@@ -727,15 +735,14 @@ async def image_generations(
727735
if request.app.state.config.AUTOMATIC1111_PARAMS:
728736
data = {**data, **request.app.state.config.AUTOMATIC1111_PARAMS}
729737

730-
# Use asyncio.to_thread for the requests.post call
731-
r = await asyncio.to_thread(
732-
requests.post,
738+
session = await get_session()
739+
async with session.post(
733740
url=f'{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/txt2img',
734741
json=data,
735742
headers={'authorization': get_automatic1111_api_auth(request)},
736-
)
737-
738-
res = r.json()
743+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
744+
) as r:
745+
res = await r.json()
739746
log.debug(f'res: {res}')
740747

741748
images = []
@@ -753,10 +760,8 @@ async def image_generations(
753760
return images
754761
except Exception as e:
755762
error = e
756-
if r != None:
757-
data = r.json()
758-
if 'error' in data:
759-
error = data['error']['message']
763+
if isinstance(e, aiohttp.ClientResponseError):
764+
error = e.message
760765
raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(error))
761766

762767

@@ -798,11 +803,12 @@ async def load_url_image(data):
798803
if data.startswith('http://') or data.startswith('https://'):
799804
# Validate URL to prevent SSRF attacks against local/private networks
800805
validate_url(data)
801-
r = await asyncio.to_thread(requests.get, data)
802-
r.raise_for_status()
806+
session = await get_session()
807+
async with session.get(data, ssl=AIOHTTP_CLIENT_SESSION_SSL) as r:
808+
r.raise_for_status()
803809

804-
image_data = base64.b64encode(r.content).decode('utf-8')
805-
return f'data:{r.headers["content-type"]};base64,{image_data}'
810+
image_data = base64.b64encode(await r.read()).decode('utf-8')
811+
return f'data:{r.headers["content-type"]};base64,{image_data}'
806812

807813
else:
808814
file_id = None
@@ -846,7 +852,7 @@ def get_image_file_item(base64_string, param_name='image'):
846852
),
847853
)
848854

849-
r = None
855+
850856
try:
851857
if request.app.state.config.IMAGE_EDIT_ENGINE == 'openai':
852858
headers = {
@@ -883,17 +889,30 @@ def get_image_file_item(base64_string, param_name='image'):
883889
if request.app.state.config.IMAGES_EDIT_OPENAI_API_VERSION:
884890
url_search_params += f'?api-version={request.app.state.config.IMAGES_EDIT_OPENAI_API_VERSION}'
885891

886-
# Use asyncio.to_thread for the requests.post call
887-
r = await asyncio.to_thread(
888-
requests.post,
892+
# Build multipart form data for aiohttp
893+
form = aiohttp.FormData()
894+
for key, value in data.items():
895+
if isinstance(value, dict):
896+
form.add_field(key, json.dumps(value))
897+
else:
898+
form.add_field(key, str(value))
899+
for param_name, (filename, file_obj, content_type_val) in files:
900+
form.add_field(
901+
param_name,
902+
file_obj,
903+
filename=filename,
904+
content_type=content_type_val,
905+
)
906+
907+
session = await get_session()
908+
async with session.post(
889909
url=f'{request.app.state.config.IMAGES_EDIT_OPENAI_API_BASE_URL}/images/edits{url_search_params}',
890910
headers=headers,
891-
files=files,
892-
data=data,
893-
)
894-
895-
r.raise_for_status()
896-
res = r.json()
911+
data=form,
912+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
913+
) as r:
914+
r.raise_for_status()
915+
res = await r.json()
897916

898917
images = []
899918
for image in res['data']:
@@ -940,16 +959,15 @@ def get_image_file_item(base64_string, param_name='image'):
940959
]
941960
)
942961

943-
# Use asyncio.to_thread for the requests.post call
944-
r = await asyncio.to_thread(
945-
requests.post,
962+
session = await get_session()
963+
async with session.post(
946964
url=f'{request.app.state.config.IMAGES_EDIT_GEMINI_API_BASE_URL}/models/{model}',
947965
json=data,
948966
headers=headers,
949-
)
950-
951-
r.raise_for_status()
952-
res = r.json()
967+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
968+
) as r:
969+
r.raise_for_status()
970+
res = await r.json()
953971

954972
images = []
955973
for image in res['candidates']:
@@ -1048,13 +1066,7 @@ def get_image_file_item(base64_string, param_name='image'):
10481066
return images
10491067
except Exception as e:
10501068
error = e
1051-
if r != None:
1052-
data = r.text
1053-
try:
1054-
data = json.loads(data)
1055-
if 'error' in data:
1056-
error = data['error']['message']
1057-
except Exception:
1058-
error = data
1069+
if isinstance(e, aiohttp.ClientResponseError):
1070+
error = e.message
10591071

10601072
raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(error))

backend/open_webui/routers/openai.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import aiohttp
1010
from aiocache import cached
11-
import requests
11+
1212

1313
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
1414

@@ -312,19 +312,20 @@ async def speech(request: Request, user=Depends(get_verified_user)):
312312

313313
r = None
314314
try:
315-
r = requests.post(
315+
session = await get_session()
316+
r = await session.post(
316317
url=f'{url}/audio/speech',
317318
data=body,
318319
headers=headers,
319320
cookies=cookies,
320-
stream=True,
321+
ssl=AIOHTTP_CLIENT_SESSION_SSL,
321322
)
322323

323324
r.raise_for_status()
324325

325326
# Save the streaming content to a file
326327
with open(file_path, 'wb') as f:
327-
for chunk in r.iter_content(chunk_size=8192):
328+
async for chunk in r.content.iter_chunked(8192):
328329
f.write(chunk)
329330

330331
with open(file_body_path, 'w') as f:
@@ -339,14 +340,14 @@ async def speech(request: Request, user=Depends(get_verified_user)):
339340
detail = None
340341
if r is not None:
341342
try:
342-
res = r.json()
343+
res = await r.json()
343344
if 'error' in res:
344345
detail = f'External: {res["error"]}'
345346
except Exception:
346347
detail = f'External: {e}'
347348

348349
raise HTTPException(
349-
status_code=r.status_code if r else 500,
350+
status_code=r.status if r else 500,
350351
detail=detail if detail else 'Open WebUI: Server Connection Error',
351352
)
352353

backend/open_webui/utils/files.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
import io
2626
import re
2727

28-
import requests
28+
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL
29+
from open_webui.utils.session_pool import get_session
2930

3031
BASE64_IMAGE_URL_PREFIX = re.compile(r'data:image/\w+;base64,', re.IGNORECASE)
3132
MARKDOWN_IMAGE_URL_PATTERN = re.compile(r'!\[(.*?)\]\((.+?)\)', re.IGNORECASE)
@@ -37,12 +38,13 @@ async def get_image_base64_from_url(url: str) -> Optional[str]:
3738
# Validate URL to prevent SSRF attacks against local/private networks
3839
validate_url(url)
3940
# Download the image from the URL
40-
response = requests.get(url)
41-
response.raise_for_status()
42-
image_data = response.content
43-
encoded_string = base64.b64encode(image_data).decode('utf-8')
44-
content_type = response.headers.get('Content-Type', 'image/png')
45-
return f'data:{content_type};base64,{encoded_string}'
41+
session = await get_session()
42+
async with session.get(url, ssl=AIOHTTP_CLIENT_SESSION_SSL) as response:
43+
response.raise_for_status()
44+
image_data = await response.read()
45+
encoded_string = base64.b64encode(image_data).decode('utf-8')
46+
content_type = response.headers.get('Content-Type', 'image/png')
47+
return f'data:{content_type};base64,{encoded_string}'
4648
else:
4749
file = await Files.get_file_by_id(url)
4850

0 commit comments

Comments
 (0)