Merge branch 'simp/r3-21-B' into simp/integration3

This commit is contained in:
Teknium
2026-09-02 23:11:06 -07:00
2 changed files with 55 additions and 86 deletions
+46 -75
View File
@@ -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
@@ -901,14 +888,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*."""
@@ -1001,59 +980,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:
@@ -1105,6 +1078,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
@@ -1299,11 +1280,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:
@@ -1311,8 +1290,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
@@ -1346,8 +1324,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
@@ -1369,13 +1346,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
+9 -11
View File
@@ -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 (
@@ -602,12 +598,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))