fix: coerce dict-valued model/default config back to string across resolution paths
A dict-valued model.default (e.g. {provider:..., model:...}) in config.yaml
was leaking into agent.model and crashing the agent at init:
AttributeError: 'dict' object has no attribute 'lower'
agent/agent_runtime_helpers.py: anthropic_prompt_cache_policy
This manifested on the Telegram gateway as an infinite reset loop: every
turn built an agent with model=dict, crashed during init, the gateway
treated the failed turn as a session needing reset, and /reset rebuilt the
agent and crashed again.
Coerce dict -> string at every model-resolution entry point so the value
is normalized once and never reaches a .lower() call as a dict:
- agent/agent_runtime_helpers.py: anthropic_prompt_cache_policy (the crash site)
- agent/agent_init.py: configured default model resolution
- cli.py: CLI config model + _normalize_model_for_provider
- hermes_cli/main.py: _has_any_provider_configured
- hermes_cli/oneshot.py: _run_agent model resolution
- hermes_cli/runtime_provider.py: _get_model_config default handling
- model_tools.py: _resolve_active_context_length
This commit is contained in:
committed by
kshitij
parent
aa5a960675
commit
cb4daf23f3
+4
-1
@@ -2272,7 +2272,10 @@ def init_agent(
|
||||
# overrides consistent with them and let provider metadata resolve the
|
||||
# active model's window instead.
|
||||
if _config_context_length is not None and isinstance(_model_cfg, dict):
|
||||
_configured_default_model = str(_model_cfg.get("default") or "").strip()
|
||||
_default = _model_cfg.get("default")
|
||||
if isinstance(_default, dict):
|
||||
_default = str(_default.get("model") or _default.get("default") or "")
|
||||
_configured_default_model = str(_default or "").strip()
|
||||
_configured_default_runtime_model = _configured_default_model
|
||||
_active_runtime_model = agent.model
|
||||
if _configured_default_model:
|
||||
|
||||
@@ -2235,6 +2235,9 @@ def anthropic_prompt_cache_policy(
|
||||
logger.debug("MoA aggregator cache-policy resolution failed: %s", _moa_exc)
|
||||
return False, False
|
||||
|
||||
if isinstance(eff_model, dict):
|
||||
eff_model = eff_model.get('default') or eff_model.get('model') 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()
|
||||
is_claude = "claude" in model_lower
|
||||
|
||||
@@ -4565,7 +4565,11 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
# authoritative. This avoids conflicts in multi-agent setups where
|
||||
# env vars would stomp each other.
|
||||
_model_config = CLI_CONFIG.get("model", {})
|
||||
_config_model = (_model_config.get("default") or _model_config.get("model") or "") if isinstance(_model_config, dict) else (_model_config or "")
|
||||
_raw_default = (_model_config.get("default") or _model_config.get("model") or "") if isinstance(_model_config, dict) else (_model_config or "")
|
||||
if isinstance(_raw_default, dict):
|
||||
_config_model = str(_raw_default.get("model") or _raw_default.get("default") or _raw_default.get("provider") or "")
|
||||
else:
|
||||
_config_model = str(_raw_default or "")
|
||||
_DEFAULT_CONFIG_MODEL = ""
|
||||
# Track whether the user passed -m / --model so resume knows not to
|
||||
# clobber an explicit override with the session's stored model.
|
||||
@@ -6659,7 +6663,10 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
|
||||
def _normalize_model_for_provider(self, resolved_provider: str) -> bool:
|
||||
"""Normalize provider-specific model IDs and routing."""
|
||||
current_model = (self.model or "").strip()
|
||||
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()
|
||||
changed = False
|
||||
|
||||
try:
|
||||
@@ -8973,11 +8980,11 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
CLI_CONFIG["agent"].get("service_tier", "")
|
||||
)
|
||||
_model_config = CLI_CONFIG.get("model", {})
|
||||
_config_model = (
|
||||
(_model_config.get("default") or _model_config.get("model") or "")
|
||||
if isinstance(_model_config, dict)
|
||||
else (_model_config or "")
|
||||
)
|
||||
_raw_default2 = (_model_config.get("default") or _model_config.get("model") or "") if isinstance(_model_config, dict) else (_model_config or "")
|
||||
if isinstance(_raw_default2, dict):
|
||||
_config_model = str(_raw_default2.get("model") or _raw_default2.get("default") or _raw_default2.get("provider") or "")
|
||||
else:
|
||||
_config_model = str(_raw_default2 or "")
|
||||
if _config_model and _config_model != getattr(self, "model", None):
|
||||
_config_provider = (
|
||||
_model_config.get("provider", "")
|
||||
@@ -14794,8 +14801,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
from hermes_cli.config import load_config
|
||||
|
||||
_img_mode = decide_image_input_mode(
|
||||
(self.provider or "").strip(),
|
||||
(self.model or "").strip(),
|
||||
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(),
|
||||
load_config(),
|
||||
requested_provider=(self.requested_provider or "").strip(),
|
||||
)
|
||||
|
||||
+6
-1
@@ -973,7 +973,12 @@ def _has_any_provider_configured() -> bool:
|
||||
cfg = load_config()
|
||||
model_cfg = cfg.get("model")
|
||||
if isinstance(model_cfg, dict):
|
||||
_model_name = (model_cfg.get("default") or "").strip()
|
||||
_default = model_cfg.get("default")
|
||||
if isinstance(_default, dict):
|
||||
_model_name = (_default.get("model") or _default.get("provider") or "")
|
||||
else:
|
||||
_model_name = (_default or "")
|
||||
_model_name = (str(_model_name) if not isinstance(_model_name, str) else _model_name).strip()
|
||||
elif isinstance(model_cfg, str):
|
||||
_model_name = model_cfg.strip()
|
||||
else:
|
||||
|
||||
@@ -343,7 +343,11 @@ def _run_agent(
|
||||
if isinstance(model_cfg, str):
|
||||
cfg_model = model_cfg
|
||||
else:
|
||||
cfg_model = model_cfg.get("default") or model_cfg.get("model") or ""
|
||||
_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 "")
|
||||
else:
|
||||
cfg_model = str(_raw or "")
|
||||
|
||||
env_model = os.getenv("HERMES_INFERENCE_MODEL", "").strip()
|
||||
effective_model = (model or "").strip() or env_model or cfg_model
|
||||
|
||||
@@ -322,7 +322,16 @@ def _get_model_config() -> Dict[str, Any]:
|
||||
# Accept "model" as alias for "default" (users intuitively write model.model)
|
||||
if not cfg.get("default") and cfg.get("model"):
|
||||
cfg["default"] = cfg["model"]
|
||||
default = (cfg.get("default") or "").strip()
|
||||
# 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 "")
|
||||
cfg["default"] = cfg_model
|
||||
if cfg_provider and not cfg.get("provider"):
|
||||
cfg["provider"] = cfg_provider
|
||||
_default = cfg_model
|
||||
default = (str(_default or "")).strip()
|
||||
base_url = (cfg.get("base_url") or "").strip()
|
||||
is_local = base_url_hostname(base_url) in ("localhost", "127.0.0.1")
|
||||
is_fallback = not default
|
||||
|
||||
+4
-1
@@ -664,7 +664,10 @@ def _resolve_active_context_length() -> int:
|
||||
model_cfg = cfg.get("model") if isinstance(cfg.get("model"), dict) else {}
|
||||
if not isinstance(model_cfg, dict):
|
||||
model_cfg = {}
|
||||
model_id = (model_cfg.get("model") or model_cfg.get("default") or "").strip()
|
||||
_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 ""
|
||||
model_id = str(_raw_model_id).strip()
|
||||
if not model_id:
|
||||
return 0
|
||||
from agent.model_metadata import get_model_context_length
|
||||
|
||||
Reference in New Issue
Block a user