mirror of
https://github.com/open-webui/open-webui.git
synced 2026-11-05 07:57:06 +00:00
fix(config): tolerate a non string default models value
default_models and default_pinned_models are stored as JSON and read as comma separated strings. Nothing coerces them on the way out, so a value that is not a string reaches the browser unchanged and every consumer that calls split on it throws. In v0.10.2 the only call site ran later in the chat flow, so a malformed value sat there unnoticed. v0.11.0 added a second call site that runs while the chat component initialises, which turned the same stored value into an uncaught TypeError on New Chat: the route changed but the previous chat stayed on screen and no new chat was created. Rolling back appeared to fix it because the new call site was gone, not because the value had changed. Both keys are now normalised where they are read. A list is joined back into the comma separated form the readers expect, a string passes through, and anything else becomes None. This covers the app config the browser consumes, the admin models config endpoints, and the custom model fallback lookup, which called split on the raw value as well. The admin UI and the models config endpoint already write these keys as strings, so this only concerns values that reached the database another way, such as a config import, which upserts its payload without per key validation.
This commit is contained in:
parent
8faaf2cd1e
commit
5eb684fbc9
3 changed files with 23 additions and 6 deletions
|
|
@ -1640,6 +1640,15 @@ DEFAULT_MODELS = os.getenv('DEFAULT_MODELS', None)
|
|||
|
||||
DEFAULT_PINNED_MODELS = os.getenv('DEFAULT_PINNED_MODELS', None)
|
||||
|
||||
|
||||
def normalize_model_id_csv(value) -> Optional[str]:
|
||||
if value is None or isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, (list, tuple)):
|
||||
return ','.join(str(model_id).strip() for model_id in value if model_id)
|
||||
return None
|
||||
|
||||
|
||||
try:
|
||||
default_prompt_suggestions = JSONCodec.loads(os.getenv('DEFAULT_PROMPT_SUGGESTIONS', '[]'))
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ from open_webui.config import (
|
|||
WEBUI_NAME,
|
||||
async_reset_config,
|
||||
import_legacy_config_json,
|
||||
normalize_model_id_csv,
|
||||
seed_registered_defaults,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES, TASKS
|
||||
|
|
@ -1113,7 +1114,7 @@ async def chat_completion(
|
|||
base_model_id = model_info.base_model_id
|
||||
if base_model_id not in request.app.state.MODELS:
|
||||
if ENABLE_CUSTOM_MODEL_FALLBACK:
|
||||
default_models = ((await Config.get('ui.default_models')) or '').split(',')
|
||||
default_models = (normalize_model_id_csv(await Config.get('ui.default_models')) or '').split(',')
|
||||
|
||||
fallback_model_id = default_models[0].strip() if default_models[0] else None
|
||||
|
||||
|
|
@ -2234,8 +2235,8 @@ async def get_app_config(request: Request):
|
|||
},
|
||||
**(
|
||||
{
|
||||
'default_models': config.get('ui.default_models'),
|
||||
'default_pinned_models': config.get('ui.default_pinned_models'),
|
||||
'default_models': normalize_model_id_csv(config.get('ui.default_models')),
|
||||
'default_pinned_models': normalize_model_id_csv(config.get('ui.default_pinned_models')),
|
||||
'default_prompt_suggestions': config.get('ui.prompt_suggestions'),
|
||||
**({'user_count': user_count} if user_count is not None else {}),
|
||||
'code': {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from typing import Optional
|
|||
import aiohttp
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from mcp.shared.auth import OAuthMetadata
|
||||
from open_webui.config import BannerModel
|
||||
from open_webui.config import BannerModel, normalize_model_id_csv
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL, AIOHTTP_CLIENT_TIMEOUT
|
||||
from open_webui.events import EVENTS, publish_event
|
||||
from open_webui.models.config import Config
|
||||
|
|
@ -740,15 +740,22 @@ async def get_models_defaults(request: Request, user=Depends(get_verified_user))
|
|||
}
|
||||
|
||||
|
||||
def normalize_models_config_values(values: dict) -> dict:
|
||||
for field in ('DEFAULT_MODELS', 'DEFAULT_PINNED_MODELS'):
|
||||
if field in values:
|
||||
values[field] = normalize_model_id_csv(values[field])
|
||||
return values
|
||||
|
||||
|
||||
@router.get('/models', response_model=ModelsConfigForm)
|
||||
async def get_models_config(request: Request, user=Depends(get_admin_user)):
|
||||
return await get_config_values(MODELS_CONFIG_KEYS)
|
||||
return normalize_models_config_values(await get_config_values(MODELS_CONFIG_KEYS))
|
||||
|
||||
|
||||
@router.post('/models', response_model=ModelsConfigForm)
|
||||
async def set_models_config(request: Request, form_data: ModelsConfigForm, user=Depends(get_admin_user)):
|
||||
await Config.upsert(config_updates(form_data.model_dump(), MODELS_CONFIG_KEYS))
|
||||
values = await get_config_values(MODELS_CONFIG_KEYS)
|
||||
values = normalize_models_config_values(await get_config_values(MODELS_CONFIG_KEYS))
|
||||
await publish_event(
|
||||
request,
|
||||
EVENTS.CONFIG_MODELS_UPDATED,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue