From 9f8807208d3a890307bb199e5d4dae9abfce5774 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:45:27 -0700 Subject: [PATCH 1/3] refactor(hermes_cli): _apply_direct_alias_endpoint mutates _Switch directly --- hermes_cli/model_switch.py | 54 +++++++++++++++----------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index feecdeccb7..08988d5380 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -1001,59 +1001,53 @@ def _config_declares_model( return False -def _apply_direct_alias_endpoint( - da: DirectAlias, target_provider: str, new_model: str, api_key: str, base_url: str, -) -> tuple[str, str, dict | None, bool]: - """Route a direct alias to its own base_url and decide its credential. +def _apply_direct_alias_endpoint(st: "_Switch", da: DirectAlias) -> None: + """Route a direct alias to its own base_url and decide its credential (mutates ``st``). - Returns ``(api_key, base_url, validation_headers_override, suppress_ollama_headers)``; a - ``None`` headers override means "leave as is". Credentials were resolved against the DEFAULT - provider; carrying that key onto the alias endpoint both 401s and ships the default provider's - secret to an unrelated host. The alias's own endpoint decides: its declared key; else the - session key only for the SAME ORIGIN; else a fresh resolution against the alias base_url - (env-key fallbacks are host-gated: OLLAMA_API_KEY resolves for ollama.com, OPENROUTER_API_KEY - never reaches an unrelated host). + Credentials were resolved against the DEFAULT provider; carrying that key onto the alias + endpoint both 401s and ships the default provider's secret to an unrelated host. The alias's + own endpoint decides: its declared key; else the session key only for the SAME ORIGIN; else a + fresh resolution against the alias base_url (env-key fallbacks are host-gated: OLLAMA_API_KEY + resolves for ollama.com, OPENROUTER_API_KEY never reaches an unrelated host). """ from hermes_cli.models import _same_ollama_native_root from hermes_cli.runtime_provider import resolve_runtime_provider alias_key = direct_alias_api_key(da) + same_host = _may_reuse_session_credential(st.base_url, da.base_url) if alias_key: - base_url, api_key = da.base_url, alias_key - elif api_key and api_key != "no-key-required" and _may_reuse_session_credential(base_url, da.base_url): + st.base_url, st.api_key = da.base_url, alias_key + elif st.api_key and st.api_key != "no-key-required" and same_host: # Same origin: the key is host-appropriate and re-resolving would only repeat the work. - base_url = da.base_url + st.base_url = da.base_url else: try: req, explicit = direct_alias_runtime_request(da) alias_runtime = resolve_runtime_provider( - requested=req, explicit_api_key=explicit, explicit_base_url=da.base_url, target_model=new_model, - ) + requested=req, explicit_api_key=explicit, explicit_base_url=da.base_url, target_model=st.new_model) except Exception: alias_runtime = {} - same_host = _may_reuse_session_credential(base_url, da.base_url) - base_url = alias_runtime.get("base_url", "") or da.base_url + st.base_url = alias_runtime.get("base_url", "") or da.base_url # The resolver reports "no key found" as the `no-key-required` placeholder; normalise so # a same-host credential still outranks it. resolved_key = alias_runtime.get("api_key", "") if resolved_key == "no-key-required": resolved_key = "" - api_key = resolved_key or (api_key if same_host else "") or "no-key-required" + st.api_key = resolved_key or (st.api_key if same_host else "") or "no-key-required" - headers_override = None - suppress = False # providers.ollama refinement: pick up the configured key only for the configured native # root; drop key and provider-level headers for any other origin. Skipped when the alias # declared its own credential (explicit api_key/key_env outranks a provider-level config key). - if not alias_key and target_provider.strip().lower() == "ollama": + if not alias_key and st.target_provider.strip().lower() == "ollama": ollama_cfg, ollama_cfg_base = _ollama_configured_base() - if ollama_cfg_base and _same_ollama_native_root(base_url, ollama_cfg_base): + if ollama_cfg_base and _same_ollama_native_root(st.base_url, ollama_cfg_base): configured_key = _entry_configured_key(ollama_cfg, lambda n: os.environ.get(n, "").strip()) if configured_key: - api_key = configured_key + st.api_key = configured_key else: # Different origin, or no configured root to safely associate the headers with. - headers_override, suppress, api_key = {}, True, "no-key-required" - return api_key or "no-key-required", base_url, headers_override, suppress + st.validation_headers, st.suppress_ollama_headers, st.api_key = {}, True, "no-key-required" + st.api_key = st.api_key or "no-key-required" + st.api_mode = "" # clear so determine_api_mode re-detects from URL def _moa_default_preset() -> str: @@ -1369,13 +1363,7 @@ def _resolve_switch_credentials(st: _Switch) -> Optional[ModelSwitchResult]: _ensure_direct_aliases() da = DIRECT_ALIASES.get(st.resolved_alias) if da is not None and da.base_url: - st.api_key, st.base_url, headers_override, suppress = _apply_direct_alias_endpoint( - da, st.target_provider, st.new_model, st.api_key, st.base_url) - st.api_mode = "" # clear so determine_api_mode re-detects from URL - if headers_override is not None: - st.validation_headers = headers_override - if suppress: - st.suppress_ollama_headers = True + _apply_direct_alias_endpoint(st, da) # Fills an empty mode (alias cleared it) and overrides a STALE mode carried from previous # session state when the host mandates one wire protocol (e.g. gpt-5.x on api.openai.com From 97ff1f5ad485d7e23b6e8076cf3e2dcb11113f5f Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:56:54 -0700 Subject: [PATCH 2/3] refactor(hermes_cli): _Switch.resolve_runtime; _PickerBuild norm properties --- hermes_cli/model_switch.py | 30 ++++++++++++---------------- hermes_cli/model_switch_providers.py | 12 ++++++----- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index 08988d5380..1c69e24dfc 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -901,14 +901,6 @@ def _resolve_named_custom_model_id(model_name: str, target_provider: str, custom # --- Core model-switching pipeline -def _runtime_creds(fallback_headers: dict, **kwargs) -> tuple[str, str, str, dict]: - """``resolve_runtime_provider`` unpacked as ``(api_key, base_url, api_mode, extra_headers)``; - ``extra_headers`` falls back to *fallback_headers*.""" - from hermes_cli.runtime_provider import resolve_runtime_provider - rt = resolve_runtime_provider(**kwargs) - return rt.get("api_key", ""), rt.get("base_url", ""), rt.get("api_mode", ""), rt.get("extra_headers") or fallback_headers - - def _entry_configured_key(cfg: dict, read_env) -> str: """Inline ``api_key`` (a ``${VAR}`` template resolves via *read_env*), else ``key_env``/``api_key_env`` via *read_env*.""" @@ -1099,6 +1091,14 @@ class _Switch: def provider_changed(self) -> bool: return self.target_provider != self.current_provider + def resolve_runtime(self, **kwargs) -> None: + """Fill api_key / base_url / api_mode / validation_headers from ``resolve_runtime_provider`` + for ``new_model``; headers keep their current value when the resolver returns none.""" + from hermes_cli.runtime_provider import resolve_runtime_provider + rt = resolve_runtime_provider(target_model=self.new_model, **kwargs) + self.api_key, self.base_url, self.api_mode = rt.get("api_key", ""), rt.get("base_url", ""), rt.get("api_mode", "") + self.validation_headers = rt.get("extra_headers") or self.validation_headers + def _route_explicit_provider(st: _Switch) -> Optional[ModelSwitchResult]: """PATH A (``--provider`` given): resolve the provider, auto-detect a model from a local @@ -1293,11 +1293,9 @@ def _creds_for_switched_provider(st: _Switch) -> Optional[ModelSwitchResult]: ukey = _entry_configured_key(ucfg, _scoped_key_env) st.validation_headers = _extra_headers_from_config(ucfg) try: - api_key, base_url, st.api_mode, st.validation_headers = _runtime_creds( - st.validation_headers, requested=st.target_provider, explicit_api_key=ukey or None, - explicit_base_url=user_pdef.base_url, target_model=st.new_model) - st.api_key = api_key or ukey - st.base_url = base_url or user_pdef.base_url + st.resolve_runtime( + requested=st.target_provider, explicit_api_key=ukey or None, explicit_base_url=user_pdef.base_url) + st.api_key, st.base_url = st.api_key or ukey, st.base_url or user_pdef.base_url except Exception: st.api_key, st.base_url, st.api_mode = ukey, user_pdef.base_url, "" elif st.target_provider == "custom" and st.current_base_url: @@ -1305,8 +1303,7 @@ def _creds_for_switched_provider(st: _Switch) -> Optional[ModelSwitchResult]: st.api_mode = determine_api_mode(st.target_provider, st.base_url) else: try: - st.api_key, st.base_url, st.api_mode, st.validation_headers = _runtime_creds( - st.validation_headers, requested=st.target_provider, target_model=st.new_model) + st.resolve_runtime(requested=st.target_provider) except Exception as e: return st.fail_on_target(f"Could not resolve credentials for provider '{st.provider_label}': {e}") return None @@ -1340,8 +1337,7 @@ def _creds_for_current_provider(st: _Switch) -> None: st.validation_headers = ollama_headers else: try: - st.api_key, st.base_url, st.api_mode, st.validation_headers = _runtime_creds( - st.validation_headers, requested=st.current_provider, target_model=st.new_model) + st.resolve_runtime(requested=st.current_provider) except Exception: pass diff --git a/hermes_cli/model_switch_providers.py b/hermes_cli/model_switch_providers.py index 5a40877eff..9acbf726f2 100644 --- a/hermes_cli/model_switch_providers.py +++ b/hermes_cli/model_switch_providers.py @@ -602,12 +602,14 @@ class _PickerBuild: builtin_endpoints: set = field(default_factory=set) # (display_name, base_url) pairs from section 3 so section 4 skips overlapping rows. section3_pairs: set = field(default_factory=set) - current_provider_norm: str = field(init=False) - current_base_url_norm: str = field(init=False) - def __post_init__(self): - self.current_provider_norm = self.current_provider.lower() - self.current_base_url_norm = self.current_base_url.rstrip("/").lower() + @property + def current_provider_norm(self) -> str: + return self.current_provider.lower() + + @property + def current_base_url_norm(self) -> str: + return self.current_base_url.rstrip("/").lower() def can_probe_custom(self, *, row_is_current: bool) -> bool: return bool(self.probe_custom_providers or (self.probe_current_custom_provider and row_is_current)) From aeabe4374fa72391bc782879367ff7b4a6196e71 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:09:45 -0700 Subject: [PATCH 3/3] refactor(hermes_cli): flag tokenizer via iterator; signature packing --- hermes_cli/model_switch.py | 37 +++++++++------------------- hermes_cli/model_switch_providers.py | 8 ++---- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index 1c69e24dfc..4bddc3891a 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -356,10 +356,7 @@ class StartupModelRoute(NamedTuple): def resolve_startup_model_route( - raw_model: str, - *, - explicit_provider: str = "", - current_provider: str = "", + raw_model: str, *, explicit_provider: str = "", current_provider: str = "", user_providers: Optional[dict] = None, custom_providers: Optional[list] = None) -> Optional[StartupModelRoute]: """Resolve aliases and configured ``provider/model`` input at startup. @@ -477,18 +474,15 @@ def parse_model_flags_detailed(raw_args: str) -> ModelFlagParseResult: # require shell quoting. flags = dict.fromkeys(_BOOL_FLAGS.values(), False) explicit_provider = "" - parts = raw_args.split() - i = 0 filtered: list[str] = [] - while i < len(parts): - if parts[i] in _BOOL_FLAGS: - flags[_BOOL_FLAGS[parts[i]]] = True - elif parts[i] == "--provider" and i + 1 < len(parts): - explicit_provider = parts[i + 1] - i += 1 + tokens = iter(raw_args.split()) + for tok in tokens: + if tok in _BOOL_FLAGS: + flags[_BOOL_FLAGS[tok]] = True + elif tok == "--provider" and (value := next(tokens, None)) is not None: + explicit_provider = value else: - filtered.append(parts[i]) - i += 1 + filtered.append(tok) # a trailing bare ``--provider`` stays part of the model text return ModelFlagParseResult(model_input=" ".join(filtered).strip(), explicit_provider=explicit_provider, **flags) @@ -796,14 +790,9 @@ def _resolve_alias_fallback( def resolve_display_context_length( - model: str, - provider: str, - base_url: str = "", - api_key: str = "", - model_info: Optional[ModelInfo] = None, - custom_providers: list | None = None, - config_context_length: int | None = None, - configured_model: str | None = None, + model: str, provider: str, base_url: str = "", api_key: str = "", + model_info: Optional[ModelInfo] = None, custom_providers: list | None = None, + config_context_length: int | None = None, configured_model: str | None = None, configured_provider: str | None = None, configured_base_url: str | None = None) -> Optional[int]: """Context length to show in /model output. @@ -867,12 +856,10 @@ def _configured_provider_matches( matches: dict[str, str] = {} for slug, cfg in candidates: - if slug in matches: - continue hit = next((mid for key in ("models", "model", "default_model") for mid in _declared_model_ids(cfg.get(key)) if mid.lower() == target), None) if hit: - matches[slug] = hit + matches.setdefault(slug, hit) # first declaration wins return matches diff --git a/hermes_cli/model_switch_providers.py b/hermes_cli/model_switch_providers.py index 9acbf726f2..3caad9d2ec 100644 --- a/hermes_cli/model_switch_providers.py +++ b/hermes_cli/model_switch_providers.py @@ -93,12 +93,8 @@ class _NativePickerModelList(list[str]): def _fetch_picker_live_models( - api_key: str, - api_url: str, - native_catalog_provider: str, - preserve_native_models: bool, - headers: dict[str, str] | None = None, - timeout: float = 5.0, + api_key: str, api_url: str, native_catalog_provider: str, preserve_native_models: bool, + headers: dict[str, str] | None = None, timeout: float = 5.0, api_mode: str | None = None) -> list[str] | None: """Fetch picker models with native Ollama and cached generic discovery.""" from hermes_cli.models import (