|
|
@@ -8,7 +8,7 @@ from typing import Literal, Optional, overload
|
|
|
import aiohttp
|
|
|
from aiocache import cached
|
|
|
import requests
|
|
|
-
|
|
|
+from urllib.parse import quote
|
|
|
|
|
|
from fastapi import Depends, FastAPI, HTTPException, Request, APIRouter
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
@@ -21,6 +21,7 @@ from open_webui.config import (
|
|
|
CACHE_DIR,
|
|
|
)
|
|
|
from open_webui.env import (
|
|
|
+ MODELS_CACHE_TTL,
|
|
|
AIOHTTP_CLIENT_SESSION_SSL,
|
|
|
AIOHTTP_CLIENT_TIMEOUT,
|
|
|
AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
|
|
|
@@ -66,10 +67,10 @@ async def send_get_request(url, key=None, user: UserModel = None):
|
|
|
**({"Authorization": f"Bearer {key}"} if key else {}),
|
|
|
**(
|
|
|
{
|
|
|
- "X-OpenWebUI-User-Name": user.name,
|
|
|
- "X-OpenWebUI-User-Id": user.id,
|
|
|
- "X-OpenWebUI-User-Email": user.email,
|
|
|
- "X-OpenWebUI-User-Role": user.role,
|
|
|
+ "X-OpenWebUI-User-Name": quote(user.name),
|
|
|
+ "X-OpenWebUI-User-Id": quote(user.id),
|
|
|
+ "X-OpenWebUI-User-Email": quote(user.email),
|
|
|
+ "X-OpenWebUI-User-Role": quote(user.role),
|
|
|
}
|
|
|
if ENABLE_FORWARD_USER_INFO_HEADERS and user
|
|
|
else {}
|
|
|
@@ -225,10 +226,10 @@ async def speech(request: Request, user=Depends(get_verified_user)):
|
|
|
),
|
|
|
**(
|
|
|
{
|
|
|
- "X-OpenWebUI-User-Name": user.name,
|
|
|
- "X-OpenWebUI-User-Id": user.id,
|
|
|
- "X-OpenWebUI-User-Email": user.email,
|
|
|
- "X-OpenWebUI-User-Role": user.role,
|
|
|
+ "X-OpenWebUI-User-Name": quote(user.name),
|
|
|
+ "X-OpenWebUI-User-Id": quote(user.id),
|
|
|
+ "X-OpenWebUI-User-Email": quote(user.email),
|
|
|
+ "X-OpenWebUI-User-Role": quote(user.role),
|
|
|
}
|
|
|
if ENABLE_FORWARD_USER_INFO_HEADERS
|
|
|
else {}
|
|
|
@@ -386,7 +387,7 @@ async def get_filtered_models(models, user):
|
|
|
return filtered_models
|
|
|
|
|
|
|
|
|
-@cached(ttl=1)
|
|
|
+@cached(ttl=MODELS_CACHE_TTL)
|
|
|
async def get_all_models(request: Request, user: UserModel) -> dict[str, list]:
|
|
|
log.info("get_all_models()")
|
|
|
|
|
|
@@ -478,10 +479,10 @@ async def get_models(
|
|
|
"Content-Type": "application/json",
|
|
|
**(
|
|
|
{
|
|
|
- "X-OpenWebUI-User-Name": user.name,
|
|
|
- "X-OpenWebUI-User-Id": user.id,
|
|
|
- "X-OpenWebUI-User-Email": user.email,
|
|
|
- "X-OpenWebUI-User-Role": user.role,
|
|
|
+ "X-OpenWebUI-User-Name": quote(user.name),
|
|
|
+ "X-OpenWebUI-User-Id": quote(user.id),
|
|
|
+ "X-OpenWebUI-User-Email": quote(user.email),
|
|
|
+ "X-OpenWebUI-User-Role": quote(user.role),
|
|
|
}
|
|
|
if ENABLE_FORWARD_USER_INFO_HEADERS
|
|
|
else {}
|
|
|
@@ -573,10 +574,10 @@ async def verify_connection(
|
|
|
"Content-Type": "application/json",
|
|
|
**(
|
|
|
{
|
|
|
- "X-OpenWebUI-User-Name": user.name,
|
|
|
- "X-OpenWebUI-User-Id": user.id,
|
|
|
- "X-OpenWebUI-User-Email": user.email,
|
|
|
- "X-OpenWebUI-User-Role": user.role,
|
|
|
+ "X-OpenWebUI-User-Name": quote(user.name),
|
|
|
+ "X-OpenWebUI-User-Id": quote(user.id),
|
|
|
+ "X-OpenWebUI-User-Email": quote(user.email),
|
|
|
+ "X-OpenWebUI-User-Role": quote(user.role),
|
|
|
}
|
|
|
if ENABLE_FORWARD_USER_INFO_HEADERS
|
|
|
else {}
|
|
|
@@ -633,13 +634,7 @@ async def verify_connection(
|
|
|
raise HTTPException(status_code=500, detail=error_detail)
|
|
|
|
|
|
|
|
|
-def convert_to_azure_payload(
|
|
|
- url,
|
|
|
- payload: dict,
|
|
|
-):
|
|
|
- model = payload.get("model", "")
|
|
|
-
|
|
|
- # Filter allowed parameters based on Azure OpenAI API
|
|
|
+def get_azure_allowed_params(api_version: str) -> set[str]:
|
|
|
allowed_params = {
|
|
|
"messages",
|
|
|
"temperature",
|
|
|
@@ -669,6 +664,23 @@ def convert_to_azure_payload(
|
|
|
"max_completion_tokens",
|
|
|
}
|
|
|
|
|
|
+ try:
|
|
|
+ if api_version >= "2024-09-01-preview":
|
|
|
+ allowed_params.add("stream_options")
|
|
|
+ except ValueError:
|
|
|
+ log.debug(
|
|
|
+ f"Invalid API version {api_version} for Azure OpenAI. Defaulting to allowed parameters."
|
|
|
+ )
|
|
|
+
|
|
|
+ return allowed_params
|
|
|
+
|
|
|
+
|
|
|
+def convert_to_azure_payload(url, payload: dict, api_version: str):
|
|
|
+ model = payload.get("model", "")
|
|
|
+
|
|
|
+ # Filter allowed parameters based on Azure OpenAI API
|
|
|
+ allowed_params = get_azure_allowed_params(api_version)
|
|
|
+
|
|
|
# Special handling for o-series models
|
|
|
if model.startswith("o") and model.endswith("-mini"):
|
|
|
# Convert max_tokens to max_completion_tokens for o-series models
|
|
|
@@ -806,10 +818,10 @@ async def generate_chat_completion(
|
|
|
),
|
|
|
**(
|
|
|
{
|
|
|
- "X-OpenWebUI-User-Name": user.name,
|
|
|
- "X-OpenWebUI-User-Id": user.id,
|
|
|
- "X-OpenWebUI-User-Email": user.email,
|
|
|
- "X-OpenWebUI-User-Role": user.role,
|
|
|
+ "X-OpenWebUI-User-Name": quote(user.name),
|
|
|
+ "X-OpenWebUI-User-Id": quote(user.id),
|
|
|
+ "X-OpenWebUI-User-Email": quote(user.email),
|
|
|
+ "X-OpenWebUI-User-Role": quote(user.role),
|
|
|
}
|
|
|
if ENABLE_FORWARD_USER_INFO_HEADERS
|
|
|
else {}
|
|
|
@@ -817,8 +829,8 @@ async def generate_chat_completion(
|
|
|
}
|
|
|
|
|
|
if api_config.get("azure", False):
|
|
|
- request_url, payload = convert_to_azure_payload(url, payload)
|
|
|
- api_version = api_config.get("api_version", "") or "2023-03-15-preview"
|
|
|
+ api_version = api_config.get("api_version", "2023-03-15-preview")
|
|
|
+ request_url, payload = convert_to_azure_payload(url, payload, api_version)
|
|
|
headers["api-key"] = key
|
|
|
headers["api-version"] = api_version
|
|
|
request_url = f"{request_url}/chat/completions?api-version={api_version}"
|
|
|
@@ -924,10 +936,10 @@ async def embeddings(request: Request, form_data: dict, user):
|
|
|
"Content-Type": "application/json",
|
|
|
**(
|
|
|
{
|
|
|
- "X-OpenWebUI-User-Name": user.name,
|
|
|
- "X-OpenWebUI-User-Id": user.id,
|
|
|
- "X-OpenWebUI-User-Email": user.email,
|
|
|
- "X-OpenWebUI-User-Role": user.role,
|
|
|
+ "X-OpenWebUI-User-Name": quote(user.name),
|
|
|
+ "X-OpenWebUI-User-Id": quote(user.id),
|
|
|
+ "X-OpenWebUI-User-Email": quote(user.email),
|
|
|
+ "X-OpenWebUI-User-Role": quote(user.role),
|
|
|
}
|
|
|
if ENABLE_FORWARD_USER_INFO_HEADERS and user
|
|
|
else {}
|
|
|
@@ -996,10 +1008,10 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
|
|
"Content-Type": "application/json",
|
|
|
**(
|
|
|
{
|
|
|
- "X-OpenWebUI-User-Name": user.name,
|
|
|
- "X-OpenWebUI-User-Id": user.id,
|
|
|
- "X-OpenWebUI-User-Email": user.email,
|
|
|
- "X-OpenWebUI-User-Role": user.role,
|
|
|
+ "X-OpenWebUI-User-Name": quote(user.name),
|
|
|
+ "X-OpenWebUI-User-Id": quote(user.id),
|
|
|
+ "X-OpenWebUI-User-Email": quote(user.email),
|
|
|
+ "X-OpenWebUI-User-Role": quote(user.role),
|
|
|
}
|
|
|
if ENABLE_FORWARD_USER_INFO_HEADERS
|
|
|
else {}
|
|
|
@@ -1007,16 +1019,15 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
|
|
}
|
|
|
|
|
|
if api_config.get("azure", False):
|
|
|
+ api_version = api_config.get("api_version", "2023-03-15-preview")
|
|
|
headers["api-key"] = key
|
|
|
- headers["api-version"] = (
|
|
|
- api_config.get("api_version", "") or "2023-03-15-preview"
|
|
|
- )
|
|
|
+ headers["api-version"] = api_version
|
|
|
|
|
|
payload = json.loads(body)
|
|
|
- url, payload = convert_to_azure_payload(url, payload)
|
|
|
+ url, payload = convert_to_azure_payload(url, payload, api_version)
|
|
|
body = json.dumps(payload).encode()
|
|
|
|
|
|
- request_url = f"{url}/{path}?api-version={api_config.get('api_version', '2023-03-15-preview')}"
|
|
|
+ request_url = f"{url}/{path}?api-version={api_version}"
|
|
|
else:
|
|
|
headers["Authorization"] = f"Bearer {key}"
|
|
|
request_url = f"{url}/{path}"
|