diff --git a/agent/agent_init.py b/agent/agent_init.py index 466869a0b5..bf90925a06 100644 --- a/agent/agent_init.py +++ b/agent/agent_init.py @@ -2274,7 +2274,8 @@ def init_agent( if _config_context_length is not None and isinstance(_model_cfg, dict): _default = _model_cfg.get("default") if isinstance(_default, dict): - _default = str(_default.get("model") or _default.get("default") or "") + from hermes_cli.config import split_model_config_default + _default, _ = split_model_config_default(_default) _configured_default_model = str(_default or "").strip() _configured_default_runtime_model = _configured_default_model _active_runtime_model = agent.model diff --git a/agent/agent_runtime_helpers.py b/agent/agent_runtime_helpers.py index db25f428d6..3f3404b40b 100644 --- a/agent/agent_runtime_helpers.py +++ b/agent/agent_runtime_helpers.py @@ -2236,7 +2236,7 @@ def anthropic_prompt_cache_policy( return False, False if isinstance(eff_model, dict): - eff_model = eff_model.get('default') or eff_model.get('model') or '' + eff_model = eff_model.get('model') or eff_model.get('default') or '' eff_model = eff_model if isinstance(eff_model, str) else str(eff_model or '') model_lower = eff_model.lower() provider_lower = eff_provider.lower() diff --git a/cli.py b/cli.py index cb2267ebe7..ea222d3b8b 100644 --- a/cli.py +++ b/cli.py @@ -4382,24 +4382,10 @@ def _normalize_moa_model(model: Optional[str]) -> tuple[Optional[str], Optional[ return None, model def _split_model_config_default(raw_default: Any) -> tuple[str, str]: - """Canonicalize a config ``model.default``/``model.model`` value. - - A dict-valued default (``model.default: {provider: ..., model: ...}``) - pairs the model string with the provider it must be routed through. The - dict is flattened here at the shared boundary so both halves stay - together through ``HermesCLI`` construction: the model becomes a plain - string and the provider is returned explicitly instead of being lost to - the outer merged ``model.provider`` default (often ``"auto"``, which - runtime resolution treats as authoritative and would otherwise route the - model through the wrong active provider). - - Returns ``(model, provider)``; both are ``""`` when nothing is usable. - """ - if isinstance(raw_default, dict): - provider = str(raw_default.get("provider") or "").strip() - model = raw_default.get("model") or raw_default.get("default") - return (str(model or "").strip(), provider) - return (str(raw_default or "").strip(), "") + # Thin wrapper around the shared helper in config.py — kept for + # backward compat with existing call sites in this module. + from hermes_cli.config import split_model_config_default + return split_model_config_default(raw_default) class _VoiceInputMessage: @@ -6688,8 +6674,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): """Normalize provider-specific model IDs and routing.""" current_model = str(self.model or "").strip() if isinstance(self.model, dict): - _m_dict = self.model - current_model = str(_m_dict.get("model") or _m_dict.get("default") or _m_dict.get("provider") or "").strip() + _m, _ = _split_model_config_default(self.model) + current_model = _m changed = False try: @@ -14820,9 +14806,18 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): ) from hermes_cli.config import load_config + _img_model, _img_provider = "", "" + if isinstance(self.model, dict): + _img_model, _ = _split_model_config_default(self.model) + else: + _img_model = str(self.model or "") + if isinstance(self.provider, dict): + _, _img_provider = _split_model_config_default(self.provider) + else: + _img_provider = str(self.provider or "") _img_mode = decide_image_input_mode( - str(self.provider or "").strip() if not isinstance(self.provider, dict) else str(self.provider.get("provider") or self.provider.get("default") or "").strip(), - str(self.model or "").strip() if not isinstance(self.model, dict) else str(self.model.get("model") or self.model.get("default") or "").strip(), + _img_provider.strip(), + _img_model.strip(), load_config(), requested_provider=(self.requested_provider or "").strip(), ) diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 75aeeb5b84..bca802d075 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -2847,6 +2847,27 @@ def _strip_default_values( return result +def split_model_config_default(raw_default: Any) -> tuple[str, str]: + """Canonicalize a config ``model.default``/``model.model`` value. + + A dict-valued default (``model.default: {provider: ..., model: ...}``) + pairs the model string with the provider it must be routed through. The + dict is flattened here at the shared boundary so both halves stay + together through ``HermesCLI`` construction: the model becomes a plain + string and the provider is returned explicitly instead of being lost to + the outer merged ``model.provider`` default (often ``"auto"``, which + runtime resolution treats as authoritative and would otherwise route the + model through the wrong active provider). + + Returns ``(model, provider)``; both are ``""`` when nothing is usable. + """ + if isinstance(raw_default, dict): + provider = str(raw_default.get("provider") or "").strip() + model = raw_default.get("model") or raw_default.get("default") + return (str(model or "").strip(), provider) + return (str(raw_default or "").strip(), "") + + def _normalize_root_model_keys(config: Dict[str, Any]) -> Dict[str, Any]: """Move stale root-level provider/base_url/context_length into model section. @@ -2891,6 +2912,7 @@ def _normalize_root_model_keys(config: Dict[str, Any]) -> Dict[str, Any]: _has_nested_default = isinstance(model_in, dict) and ( isinstance(model_in.get("default"), dict) or isinstance(model_in.get("model"), dict) + or isinstance(model_in.get("name"), dict) ) # A model dict needs canonicalization if its id lives under a non-canonical # key (``model``/``name``) — either because ``default`` is empty (we must @@ -2920,7 +2942,7 @@ def _normalize_root_model_keys(config: Dict[str, Any]) -> Dict[str, Any]: # resolution treats as authoritative and would otherwise route the model # through the wrong active provider), but never over an explicitly # configured outer provider. - for _key in ("default", "model"): + for _key in ("default", "model", "name"): _val = model.get(_key) if isinstance(_val, dict): _nested_model = _val.get("model") or _val.get("default") diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 3987ea458e..23a0559ed1 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -975,7 +975,8 @@ def _has_any_provider_configured() -> bool: if isinstance(model_cfg, dict): _default = model_cfg.get("default") if isinstance(_default, dict): - _model_name = (_default.get("model") or _default.get("provider") or "") + from hermes_cli.config import split_model_config_default + _model_name, _ = split_model_config_default(_default) else: _model_name = (_default or "") _model_name = (str(_model_name) if not isinstance(_model_name, str) else _model_name).strip() diff --git a/hermes_cli/oneshot.py b/hermes_cli/oneshot.py index f0fd2ab876..5ec3345b02 100644 --- a/hermes_cli/oneshot.py +++ b/hermes_cli/oneshot.py @@ -345,7 +345,8 @@ def _run_agent( else: _raw = model_cfg.get("default") or model_cfg.get("model") or "" if isinstance(_raw, dict): - cfg_model = str(_raw.get("model") or _raw.get("default") or _raw.get("provider") or "") + from hermes_cli.config import split_model_config_default + cfg_model, _ = split_model_config_default(_raw) else: cfg_model = str(_raw or "") diff --git a/hermes_cli/runtime_provider.py b/hermes_cli/runtime_provider.py index 955216f58d..801ea3d2d9 100644 --- a/hermes_cli/runtime_provider.py +++ b/hermes_cli/runtime_provider.py @@ -325,8 +325,9 @@ def _get_model_config() -> Dict[str, Any]: # Handle model.default being a dict {provider: ..., model: ...} rather than a string _default = cfg.get("default") if isinstance(_default, dict): - cfg_provider = str(_default.get("provider") or model_cfg.get("provider") or "") - cfg_model = str(_default.get("model") or _default.get("default") or "") + from hermes_cli.config import split_model_config_default + cfg_model, cfg_provider = split_model_config_default(_default) + cfg_provider = cfg_provider or str(model_cfg.get("provider") or "") cfg["default"] = cfg_model if cfg_provider and not cfg.get("provider"): cfg["provider"] = cfg_provider diff --git a/model_tools.py b/model_tools.py index 764d0d50d6..241c862fae 100644 --- a/model_tools.py +++ b/model_tools.py @@ -666,7 +666,8 @@ def _resolve_active_context_length() -> int: model_cfg = {} _raw_model_id = model_cfg.get("model") or model_cfg.get("default") or "" if isinstance(_raw_model_id, dict): - _raw_model_id = _raw_model_id.get("model") or _raw_model_id.get("provider") or "" + from hermes_cli.config import split_model_config_default + _raw_model_id, _ = split_model_config_default(_raw_model_id) model_id = str(_raw_model_id).strip() if not model_id: return 0