fix: drop cached base models on every instance when a connection changes

With ENABLE_BASE_MODELS_CACHE on and more than one instance behind the load balancer, editing an OpenAI or Ollama connection only refreshes the instance that served the admin request. Every other instance keeps handing out the model list it cached before the edit, so a newly added model appears for some users and not others, and a removed one lingers. With MODELS_CACHE_TTL left empty the per-provider cache never expires either, so those instances stay wrong until they are restarted.

A provider config change now bumps an epoch stored in the config table, and each instance drops its cached base models when the epoch it last stamped no longer matches. The database is the only thing every deployment shares, and the key rides along in the config query get_all_models already makes, so this costs no extra round trip.

The direct /openai/models and /ollama/api/tags endpoints still answer from their own provider cache without checking the epoch, a window of MODELS_CACHE_TTL and one second by default. The model list users actually see, /api/models, is covered.
This commit is contained in:
Classic298 2026-08-27 23:28:57 +02:00
parent 87bed3f0b3
commit 99f2549289
5 changed files with 15 additions and 10 deletions

View file

@ -2841,6 +2841,7 @@ DEFAULT_CONFIG = {
'openai.api_base_urls': OPENAI_API_BASE_URLS,
'openai.api_configs': OPENAI_API_CONFIGS,
'models.base_models_cache': ENABLE_BASE_MODELS_CACHE,
'models.base_models_cache_epoch': '',
'tool_server.connections': TOOL_SERVER_CONNECTIONS,
'oauth.client.timeout': OAUTH_CLIENT_TIMEOUT,
'terminal_server.connections': TERMINAL_SERVER_CONNECTIONS,

View file

@ -587,6 +587,7 @@ app.state.SCIM_TOKEN = SCIM_TOKEN
########################################
app.state.BASE_MODELS = []
app.state.BASE_MODELS_EPOCH = None
########################################
#

View file

@ -9,6 +9,7 @@ import time
from datetime import datetime
from typing import Optional, Union
from urllib.parse import urlparse
from uuid import uuid4
import aiofiles
import aiohttp
@ -315,6 +316,7 @@ async def update_config(
'ollama.enable': form_data.ENABLE_OLLAMA_API,
'ollama.base_urls': form_data.OLLAMA_BASE_URLS,
'ollama.api_configs': api_configs,
'models.base_models_cache_epoch': str(uuid4()),
}
)

View file

@ -6,6 +6,7 @@ import logging
import re
from typing import Optional
from urllib.parse import quote, urlparse
from uuid import uuid4
import aiofiles
import aiohttp
@ -344,6 +345,7 @@ async def get_openai_connection(idx: int) -> tuple[str, str, dict]:
async def clear_openai_model_cache(request: Request):
await Config.upsert({'models.base_models_cache_epoch': str(uuid4())})
await get_all_models.cache.clear()
request.app.state.BASE_MODELS = []
request.app.state.OPENAI_MODELS = {}
@ -571,14 +573,7 @@ async def update_config(request: Request, form_data: OpenAIConfigForm, user=Depe
}
)
await get_all_models.cache.clear()
request.app.state.BASE_MODELS = []
request.app.state.OPENAI_MODELS = {}
models = getattr(request.app.state, 'MODELS', None)
if hasattr(models, 'clear'):
models.clear()
else:
request.app.state.MODELS = {}
await clear_openai_model_cache(request)
await publish_event(
request,

View file

@ -67,22 +67,28 @@ async def get_all_base_models(request: Request, user: UserModel = None):
async def get_all_models(request, refresh: bool = False, user: UserModel = None):
config = await Config.get_many(
'models.base_models_cache',
'models.base_models_cache_epoch',
'evaluation.arena.enable',
'evaluation.arena.models',
'models.default_metadata',
)
if refresh:
# A changed epoch means another instance edited a provider, so our cached models are stale.
cache_epoch = config.get('models.base_models_cache_epoch')
stale = request.app.state.BASE_MODELS_EPOCH != cache_epoch
if refresh or stale:
await openai.get_all_models.cache.clear()
await ollama.get_all_models.cache.clear()
if (
request.app.state.MODELS
and request.app.state.BASE_MODELS
and (config.get('models.base_models_cache') and not refresh)
and (config.get('models.base_models_cache') and not (refresh or stale))
):
base_models = request.app.state.BASE_MODELS
else:
base_models = await get_all_base_models(request, user=user)
request.app.state.BASE_MODELS_EPOCH = cache_epoch
if base_models:
request.app.state.BASE_MODELS = base_models
else: