fix: update MiniMax/Z.AI model lists, pass base_url to AIAgent

- Update _PROVIDER_MODELS['minimax'] from stale ABAB 6.5 models to
  current MiniMax-M2.7/M2.5/M2.1 lineup (matching hermes-agent upstream)
- Update _PROVIDER_MODELS['zai'] from GLM-4 to current GLM-5/4.7/4.5
  lineup (matching hermes-agent upstream)
- Extend resolve_model_provider() to also return base_url from config.yaml,
  so providers with custom endpoints (MiniMax, Z.AI) are routed correctly
- Pass base_url to AIAgent in both streaming and sync chat paths

Fixes #6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nathan Esquenazi
2026-04-01 23:15:04 -07:00
parent f9f56da484
commit ceb091d6b1
3 changed files with 27 additions and 14 deletions

View File

@@ -295,9 +295,11 @@ _PROVIDER_MODELS = {
{'id': 'gemini-2.5-pro', 'label': 'Gemini 2.5 Pro (via Nous)'}, {'id': 'gemini-2.5-pro', 'label': 'Gemini 2.5 Pro (via Nous)'},
], ],
'zai': [ 'zai': [
{'id': 'glm-4-plus', 'label': 'GLM-4 Plus'}, {'id': 'glm-5', 'label': 'GLM-5'},
{'id': 'glm-4-air', 'label': 'GLM-4 Air'}, {'id': 'glm-5-turbo', 'label': 'GLM-5 Turbo'},
{'id': 'glm-z1-flash', 'label': 'GLM-Z1 Flash'}, {'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': [ 'kimi-coding': [
{'id': 'moonshot-v1-8k', 'label': 'Moonshot v1 8k'}, {'id': 'moonshot-v1-8k', 'label': 'Moonshot v1 8k'},
@@ -306,40 +308,49 @@ _PROVIDER_MODELS = {
{'id': 'kimi-latest', 'label': 'Kimi Latest'}, {'id': 'kimi-latest', 'label': 'Kimi Latest'},
], ],
'minimax': [ 'minimax': [
{'id': 'abab6.5s-chat', 'label': 'MiniMax ABAB 6.5S'}, {'id': 'MiniMax-M2.7', 'label': 'MiniMax M2.7'},
{'id': 'abab6.5g-chat', 'label': 'MiniMax ABAB 6.5G'}, {'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): 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 Model IDs from the dropdown may include a provider prefix
(e.g. 'anthropic/claude-sonnet-4.6'). Direct-API providers expect (e.g. 'anthropic/claude-sonnet-4.6'). Direct-API providers expect
bare model names, while OpenRouter expects the full provider/model path. 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_provider = None
config_base_url = None
model_cfg = cfg.get('model', {}) model_cfg = cfg.get('model', {})
if isinstance(model_cfg, dict): if isinstance(model_cfg, dict):
config_provider = model_cfg.get('provider') config_provider = model_cfg.get('provider')
config_base_url = model_cfg.get('base_url')
model_id = (model_id or '').strip() model_id = (model_id or '').strip()
if not model_id: if not model_id:
return model_id, config_provider return model_id, config_provider, config_base_url
if '/' in model_id: if '/' in model_id:
prefix, bare = model_id.split('/', 1) prefix, bare = model_id.split('/', 1)
# If prefix matches config provider, strip it # If prefix matches config provider, strip it
if config_provider and prefix == config_provider: 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 # If prefix is a known direct-API provider, use it
# (base_url only applies when matching config provider)
if prefix in _PROVIDER_MODELS: 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: def get_available_models() -> dict:

View File

@@ -630,8 +630,9 @@ def _handle_chat_sync(handler, body):
from run_agent import AIAgent from run_agent import AIAgent
with CHAT_LOCK: with CHAT_LOCK:
from api.config import resolve_model_provider from api.config import resolve_model_provider
_model, _provider = resolve_model_provider(s.model) _model, _provider, _base_url = resolve_model_provider(s.model)
agent = AIAgent(model=_model, provider=_provider, platform='cli', quiet_mode=True, agent = AIAgent(model=_model, provider=_provider, base_url=_base_url,
platform='cli', quiet_mode=True,
enabled_toolsets=CLI_TOOLSETS, session_id=s.session_id) enabled_toolsets=CLI_TOOLSETS, session_id=s.session_id)
workspace_ctx = f"[Workspace: {s.workspace}]\n" workspace_ctx = f"[Workspace: {s.workspace}]\n"
workspace_system_msg = ( workspace_system_msg = (

View File

@@ -100,10 +100,11 @@ def _run_agent_streaming(session_id, msg_text, model, workspace, stream_id, atta
if AIAgent is None: if AIAgent is None:
raise ImportError("AIAgent not available -- check that hermes-agent is on sys.path") 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( agent = AIAgent(
model=resolved_model, model=resolved_model,
provider=resolved_provider, provider=resolved_provider,
base_url=resolved_base_url,
platform='cli', platform='cli',
quiet_mode=True, quiet_mode=True,
enabled_toolsets=CLI_TOOLSETS, enabled_toolsets=CLI_TOOLSETS,