feat: add database-backed verified models for dynamic model managemen… (#12833)

Co-authored-by: statxc <statxc@user.noreply.github.com>
Co-authored-by: bittoby <bittoby@users.noreply.github.com>
This commit is contained in:
BitToby
2026-02-26 15:17:18 +02:00
committed by GitHub
parent f93e3254d3
commit a92bfe6cc0
9 changed files with 701 additions and 37 deletions

View File

@@ -24,7 +24,8 @@ async def get_litellm_models() -> list[str]:
"""Get all models supported by LiteLLM.
This function combines models from litellm and Bedrock, removing any
error-prone Bedrock models.
error-prone Bedrock models. In SaaS mode, it uses database-backed
verified models for dynamic updates without code deployments.
To get the models:
```sh
@@ -34,7 +35,29 @@ async def get_litellm_models() -> list[str]:
Returns:
list[str]: A sorted list of unique model names.
"""
return get_supported_llm_models(config)
verified_models = _load_verified_models_from_db()
return get_supported_llm_models(config, verified_models)
def _load_verified_models_from_db() -> list[str] | None:
"""Try to load verified models from the database (SaaS mode only).
Returns:
List of model strings like 'provider/model_name' if available, None otherwise.
"""
try:
from storage.verified_model_store import VerifiedModelStore
except ImportError:
return None
try:
db_models = VerifiedModelStore.get_enabled_models()
return [f'{m.provider}/{m.model_name}' for m in db_models]
except Exception:
from openhands.core.logger import openhands_logger as logger
logger.exception('Failed to load verified models from database')
return None
@app.get('/agents', response_model=list[str])

View File

@@ -11,6 +11,38 @@ from openhands.core.config import LLMConfig, OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.llm import bedrock
# Hardcoded OpenHands provider models used in self-hosted mode.
# In SaaS mode these are loaded from the database instead.
OPENHANDS_MODELS = [
'openhands/claude-opus-4-5-20251101',
'openhands/claude-sonnet-4-5-20250929',
'openhands/gpt-5.2-codex',
'openhands/gpt-5.2',
'openhands/minimax-m2.5',
'openhands/gemini-3-pro-preview',
'openhands/gemini-3-flash-preview',
'openhands/deepseek-chat',
'openhands/devstral-medium-2512',
'openhands/kimi-k2-0711-preview',
'openhands/qwen3-coder-480b',
]
CLARIFAI_MODELS = [
'clarifai/openai.chat-completion.gpt-oss-120b',
'clarifai/openai.chat-completion.gpt-oss-20b',
'clarifai/openai.chat-completion.gpt-5',
'clarifai/openai.chat-completion.gpt-5-mini',
'clarifai/qwen.qwen3.qwen3-next-80B-A3B-Thinking',
'clarifai/qwen.qwenLM.Qwen3-30B-A3B-Instruct-2507',
'clarifai/qwen.qwenLM.Qwen3-30B-A3B-Thinking-2507',
'clarifai/qwen.qwenLM.Qwen3-14B',
'clarifai/qwen.qwenCoder.Qwen3-Coder-30B-A3B-Instruct',
'clarifai/deepseek-ai.deepseek-chat.DeepSeek-R1-0528-Qwen3-8B',
'clarifai/deepseek-ai.deepseek-chat.DeepSeek-V3_1',
'clarifai/zai.completion.GLM_4_5',
'clarifai/moonshotai.kimi.Kimi-K2-Instruct',
]
def is_openhands_model(model: str | None) -> bool:
"""Check if the model uses the OpenHands provider.
@@ -67,12 +99,20 @@ def get_provider_api_base(model: str) -> str | None:
return None
def get_supported_llm_models(config: OpenHandsConfig) -> list[str]:
def get_supported_llm_models(
config: OpenHandsConfig,
verified_models: list[str] | None = None,
) -> list[str]:
"""Get all models supported by LiteLLM.
This function combines models from litellm and Bedrock, removing any
error-prone Bedrock models.
Args:
config: The OpenHands configuration.
verified_models: Optional list of verified model strings from the database
(SaaS mode). When provided, these replace the hardcoded OPENHANDS_MODELS.
Returns:
list[str]: A sorted list of unique model names.
"""
@@ -109,39 +149,8 @@ def get_supported_llm_models(config: OpenHandsConfig) -> list[str]:
except httpx.HTTPError as e:
logger.error(f'Error getting OLLAMA models: {e}')
# Add OpenHands provider models
openhands_models = [
'openhands/claude-opus-4-5-20251101',
'openhands/claude-sonnet-4-5-20250929',
'openhands/gpt-5.2-codex',
'openhands/gpt-5.2',
'openhands/minimax-m2.5',
'openhands/gemini-3-pro-preview',
'openhands/gemini-3-flash-preview',
'openhands/deepseek-chat',
'openhands/devstral-medium-2512',
'openhands/kimi-k2-0711-preview',
'openhands/qwen3-coder-480b',
]
model_list = openhands_models + model_list
# Add Clarifai provider models (via OpenAI-compatible endpoint)
clarifai_models = [
# clarifai featured models
'clarifai/openai.chat-completion.gpt-oss-120b',
'clarifai/openai.chat-completion.gpt-oss-20b',
'clarifai/openai.chat-completion.gpt-5',
'clarifai/openai.chat-completion.gpt-5-mini',
'clarifai/qwen.qwen3.qwen3-next-80B-A3B-Thinking',
'clarifai/qwen.qwenLM.Qwen3-30B-A3B-Instruct-2507',
'clarifai/qwen.qwenLM.Qwen3-30B-A3B-Thinking-2507',
'clarifai/qwen.qwenLM.Qwen3-14B',
'clarifai/qwen.qwenCoder.Qwen3-Coder-30B-A3B-Instruct',
'clarifai/deepseek-ai.deepseek-chat.DeepSeek-R1-0528-Qwen3-8B',
'clarifai/deepseek-ai.deepseek-chat.DeepSeek-V3_1',
'clarifai/zai.completion.GLM_4_5',
'clarifai/moonshotai.kimi.Kimi-K2-Instruct',
]
model_list = clarifai_models + model_list
# Use database-backed models if provided (SaaS), otherwise use hardcoded list
openhands_models = verified_models if verified_models else OPENHANDS_MODELS
model_list = openhands_models + CLARIFAI_MODELS + model_list
return sorted(set(model_list))