feat: forward upstream OpenAI errors

This commit is contained in:
Juan Calderon-Perez 2025-08-08 09:38:34 -04:00
parent 9af64f0db1
commit 2ae7584686

View file

@ -2,17 +2,15 @@ import asyncio
import hashlib import hashlib
import json import json
import logging import logging
from pathlib import Path from typing import Optional
from typing import Literal, Optional, overload
import aiohttp import aiohttp
from aiocache import cached from aiocache import cached
import requests import requests
from urllib.parse import quote from urllib.parse import quote
from fastapi import Depends, FastAPI, HTTPException, Request, APIRouter from fastapi import Depends, HTTPException, Request, APIRouter
from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, StreamingResponse, JSONResponse, PlainTextResponse
from fastapi.responses import FileResponse, StreamingResponse
from pydantic import BaseModel from pydantic import BaseModel
from starlette.background import BackgroundTask from starlette.background import BackgroundTask
@ -31,7 +29,7 @@ from open_webui.env import (
from open_webui.models.users import UserModel from open_webui.models.users import UserModel
from open_webui.constants import ERROR_MESSAGES from open_webui.constants import ERROR_MESSAGES
from open_webui.env import ENV, SRC_LOG_LEVELS from open_webui.env import SRC_LOG_LEVELS
from open_webui.utils.payload import ( from open_webui.utils.payload import (
@ -595,15 +593,17 @@ async def verify_connection(
headers=headers, headers=headers,
ssl=AIOHTTP_CLIENT_SESSION_SSL, ssl=AIOHTTP_CLIENT_SESSION_SSL,
) as r: ) as r:
if r.status != 200: try:
# Extract response error details if available response_data = await r.json()
error_detail = f"HTTP Error: {r.status}" except Exception:
res = await r.json() response_data = await r.text()
if "error" in res:
error_detail = f"External Error: {res['error']}" if r.status != 200:
raise Exception(error_detail) if isinstance(response_data, (dict, list)):
return JSONResponse(status_code=r.status, content=response_data)
else:
return PlainTextResponse(status_code=r.status, content=response_data)
response_data = await r.json()
return response_data return response_data
else: else:
headers["Authorization"] = f"Bearer {key}" headers["Authorization"] = f"Bearer {key}"
@ -613,15 +613,17 @@ async def verify_connection(
headers=headers, headers=headers,
ssl=AIOHTTP_CLIENT_SESSION_SSL, ssl=AIOHTTP_CLIENT_SESSION_SSL,
) as r: ) as r:
if r.status != 200: try:
# Extract response error details if available response_data = await r.json()
error_detail = f"HTTP Error: {r.status}" except Exception:
res = await r.json() response_data = await r.text()
if "error" in res:
error_detail = f"External Error: {res['error']}" if r.status != 200:
raise Exception(error_detail) if isinstance(response_data, (dict, list)):
return JSONResponse(status_code=r.status, content=response_data)
else:
return PlainTextResponse(status_code=r.status, content=response_data)
response_data = await r.json()
return response_data return response_data
except aiohttp.ClientError as e: except aiohttp.ClientError as e:
@ -632,8 +634,9 @@ async def verify_connection(
) )
except Exception as e: except Exception as e:
log.exception(f"Unexpected error: {e}") log.exception(f"Unexpected error: {e}")
error_detail = f"Unexpected error: {str(e)}" raise HTTPException(
raise HTTPException(status_code=500, detail=error_detail) status_code=500, detail="Open WebUI: Server Connection Error"
)
def get_azure_allowed_params(api_version: str) -> set[str]: def get_azure_allowed_params(api_version: str) -> set[str]:
@ -883,21 +886,19 @@ async def generate_chat_completion(
log.error(e) log.error(e)
response = await r.text() response = await r.text()
r.raise_for_status() if r.status >= 400:
if isinstance(response, (dict, list)):
return JSONResponse(status_code=r.status, content=response)
else:
return PlainTextResponse(status_code=r.status, content=response)
return response return response
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
detail = None
if isinstance(response, dict):
if "error" in response:
detail = f"{response['error']['message'] if 'message' in response['error'] else response['error']}"
elif isinstance(response, str):
detail = response
raise HTTPException( raise HTTPException(
status_code=r.status if r else 500, status_code=r.status if r else 500,
detail=detail if detail else "Open WebUI: Server Connection Error", detail="Open WebUI: Server Connection Error",
) )
finally: finally:
if not streaming: if not streaming:
@ -951,7 +952,7 @@ async def embeddings(request: Request, form_data: dict, user):
), ),
}, },
) )
r.raise_for_status()
if "text/event-stream" in r.headers.get("Content-Type", ""): if "text/event-stream" in r.headers.get("Content-Type", ""):
streaming = True streaming = True
return StreamingResponse( return StreamingResponse(
@ -963,21 +964,23 @@ async def embeddings(request: Request, form_data: dict, user):
), ),
) )
else: else:
response_data = await r.json() try:
response_data = await r.json()
except Exception:
response_data = await r.text()
if r.status >= 400:
if isinstance(response_data, (dict, list)):
return JSONResponse(status_code=r.status, content=response_data)
else:
return PlainTextResponse(status_code=r.status, content=response_data)
return response_data return response_data
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
detail = None
if r is not None:
try:
res = await r.json()
if "error" in res:
detail = f"External: {res['error']['message'] if 'message' in res['error'] else res['error']}"
except Exception:
detail = f"External: {e}"
raise HTTPException( raise HTTPException(
status_code=r.status if r else 500, status_code=r.status if r else 500,
detail=detail if detail else "Open WebUI: Server Connection Error", detail="Open WebUI: Server Connection Error",
) )
finally: finally:
if not streaming: if not streaming:
@ -1043,7 +1046,6 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
headers=headers, headers=headers,
ssl=AIOHTTP_CLIENT_SESSION_SSL, ssl=AIOHTTP_CLIENT_SESSION_SSL,
) )
r.raise_for_status()
# Check if response is SSE # Check if response is SSE
if "text/event-stream" in r.headers.get("Content-Type", ""): if "text/event-stream" in r.headers.get("Content-Type", ""):
@ -1057,24 +1059,24 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
), ),
) )
else: else:
response_data = await r.json() try:
response_data = await r.json()
except Exception:
response_data = await r.text()
if r.status >= 400:
if isinstance(response_data, (dict, list)):
return JSONResponse(status_code=r.status, content=response_data)
else:
return PlainTextResponse(status_code=r.status, content=response_data)
return response_data return response_data
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
detail = None
if r is not None:
try:
res = await r.json()
log.error(res)
if "error" in res:
detail = f"External: {res['error']['message'] if 'message' in res['error'] else res['error']}"
except Exception:
detail = f"External: {e}"
raise HTTPException( raise HTTPException(
status_code=r.status if r else 500, status_code=r.status if r else 500,
detail=detail if detail else "Open WebUI: Server Connection Error", detail="Open WebUI: Server Connection Error",
) )
finally: finally:
if not streaming: if not streaming: