diff --git a/api/config.py b/api/config.py index 2bc126e..c593ee9 100644 --- a/api/config.py +++ b/api/config.py @@ -295,9 +295,11 @@ _PROVIDER_MODELS = { {'id': 'gemini-2.5-pro', 'label': 'Gemini 2.5 Pro (via Nous)'}, ], 'zai': [ - {'id': 'glm-4-plus', 'label': 'GLM-4 Plus'}, - {'id': 'glm-4-air', 'label': 'GLM-4 Air'}, - {'id': 'glm-z1-flash', 'label': 'GLM-Z1 Flash'}, + {'id': 'glm-5', 'label': 'GLM-5'}, + {'id': 'glm-5-turbo', 'label': 'GLM-5 Turbo'}, + {'id': 'glm-4.7', 'label': 'GLM-4.7'}, + {'id': 'glm-4.5', 'label': 'GLM-4.5'}, + {'id': 'glm-4.5-flash', 'label': 'GLM-4.5 Flash'}, ], 'kimi-coding': [ {'id': 'moonshot-v1-8k', 'label': 'Moonshot v1 8k'}, @@ -306,40 +308,49 @@ _PROVIDER_MODELS = { {'id': 'kimi-latest', 'label': 'Kimi Latest'}, ], 'minimax': [ - {'id': 'abab6.5s-chat', 'label': 'MiniMax ABAB 6.5S'}, - {'id': 'abab6.5g-chat', 'label': 'MiniMax ABAB 6.5G'}, + {'id': 'MiniMax-M2.7', 'label': 'MiniMax M2.7'}, + {'id': 'MiniMax-M2.7-highspeed', 'label': 'MiniMax M2.7 Highspeed'}, + {'id': 'MiniMax-M2.5', 'label': 'MiniMax M2.5'}, + {'id': 'MiniMax-M2.5-highspeed', 'label': 'MiniMax M2.5 Highspeed'}, + {'id': 'MiniMax-M2.1', 'label': 'MiniMax M2.1'}, ], } def resolve_model_provider(model_id: str): - """Resolve bare model name and provider for AIAgent. + """Resolve bare model name, provider, and base_url for AIAgent. Model IDs from the dropdown may include a provider prefix (e.g. 'anthropic/claude-sonnet-4.6'). Direct-API providers expect bare model names, while OpenRouter expects the full provider/model path. - Returns (model, provider) where provider may be None. + Also reads base_url from config.yaml so providers with custom endpoints + (e.g. MiniMax, Z.AI) are routed correctly. + + Returns (model, provider, base_url) where provider and base_url may be None. """ config_provider = None + config_base_url = None model_cfg = cfg.get('model', {}) if isinstance(model_cfg, dict): config_provider = model_cfg.get('provider') + config_base_url = model_cfg.get('base_url') model_id = (model_id or '').strip() if not model_id: - return model_id, config_provider + return model_id, config_provider, config_base_url if '/' in model_id: prefix, bare = model_id.split('/', 1) # If prefix matches config provider, strip it if config_provider and prefix == config_provider: - return bare, config_provider + return bare, config_provider, config_base_url # If prefix is a known direct-API provider, use it + # (base_url only applies when matching config provider) if prefix in _PROVIDER_MODELS: - return bare, prefix + return bare, prefix, None - return model_id, config_provider + return model_id, config_provider, config_base_url def get_available_models() -> dict: diff --git a/api/routes.py b/api/routes.py index 3c55f4e..aa3b68f 100644 --- a/api/routes.py +++ b/api/routes.py @@ -630,8 +630,9 @@ def _handle_chat_sync(handler, body): from run_agent import AIAgent with CHAT_LOCK: from api.config import resolve_model_provider - _model, _provider = resolve_model_provider(s.model) - agent = AIAgent(model=_model, provider=_provider, platform='cli', quiet_mode=True, + _model, _provider, _base_url = resolve_model_provider(s.model) + agent = AIAgent(model=_model, provider=_provider, base_url=_base_url, + platform='cli', quiet_mode=True, enabled_toolsets=CLI_TOOLSETS, session_id=s.session_id) workspace_ctx = f"[Workspace: {s.workspace}]\n" workspace_system_msg = ( diff --git a/api/streaming.py b/api/streaming.py index ec0ea5f..3bf8bd9 100644 --- a/api/streaming.py +++ b/api/streaming.py @@ -100,10 +100,11 @@ def _run_agent_streaming(session_id, msg_text, model, workspace, stream_id, atta if AIAgent is None: raise ImportError("AIAgent not available -- check that hermes-agent is on sys.path") - resolved_model, resolved_provider = resolve_model_provider(model) + resolved_model, resolved_provider, resolved_base_url = resolve_model_provider(model) agent = AIAgent( model=resolved_model, provider=resolved_provider, + base_url=resolved_base_url, platform='cli', quiet_mode=True, enabled_toolsets=CLI_TOOLSETS,