fix(desktop): let explicit model switches escape broken config providers (#42241) (#46796)

When a desktop/dashboard session had no agent built yet and the user explicitly
picked a provider in the model picker, config.set('model', ...) would first try
to initialize the agent from the (possibly broken) config default provider —
failing before the user's explicit switch could take effect, trapping them on a
misconfigured default.

config.set now pre-parses the model flags: if an explicit --provider is present
and no agent exists yet, it skips the default-provider agent build and routes
straight through _apply_model_switch with the explicit provider. _apply_model_switch
gained a parsed_flags passthrough (avoids double-parsing) and only falls back to
resolve_runtime_provider(requested=None) when no explicit provider was given.

The desktop hook now sends config.set instead of slash.exec for active-session
model changes, so errors from the selected provider surface to the user instead
of being swallowed.

Co-authored-by: rodboev <rod.boev@gmail.com>
This commit is contained in:
Austin Pickett
2026-06-15 15:36:51 -04:00
committed by GitHub
co-authored by rodboev
parent 2a08b8c86f
commit ed20f5ed06
4 changed files with 212 additions and 20 deletions
+27 -15
View File
@@ -1961,11 +1961,14 @@ def _apply_model_switch(
*,
confirm_expensive_model: bool = False,
pin_session_override: bool = True,
parsed_flags: tuple[str, str, bool, bool] | None = None,
) -> dict:
from hermes_cli.model_switch import parse_model_flags, switch_model
from hermes_cli.runtime_provider import resolve_runtime_provider
model_input, explicit_provider, persist_global, _force_refresh = parse_model_flags(raw_input)
if parsed_flags is None:
parsed_flags = parse_model_flags(raw_input)
model_input, explicit_provider, persist_global, _force_refresh = parsed_flags
if not model_input:
raise ValueError("model value required")
@@ -1976,20 +1979,24 @@ def _apply_model_switch(
current_base_url = getattr(agent, "base_url", "") or ""
current_api_key = getattr(agent, "api_key", "") or ""
else:
runtime = resolve_runtime_provider(requested=None)
current_provider = str(runtime.get("provider", "") or "")
current_model = _resolve_model()
current_base_url = str(runtime.get("base_url", "") or "")
# Preserve a callable api_key (Azure Foundry Entra ID bearer
# provider) unchanged — ``str(...)`` would produce
# ``"<function ...>"`` and poison downstream switch_model
# validation. Match the agent-present branch's behavior at the
# top of this block.
_runtime_key = runtime.get("api_key", "")
if callable(_runtime_key) and not isinstance(_runtime_key, str):
current_api_key = _runtime_key
else:
current_api_key = str(_runtime_key or "")
current_provider = explicit_provider.strip()
current_base_url = ""
current_api_key = ""
if not explicit_provider:
runtime = resolve_runtime_provider(requested=None)
current_provider = str(runtime.get("provider", "") or "")
current_base_url = str(runtime.get("base_url", "") or "")
# Preserve a callable api_key (Azure Foundry Entra ID bearer
# provider) unchanged — ``str(...)`` would produce
# ``"<function ...>"`` and poison downstream switch_model
# validation. Match the agent-present branch's behavior at the
# top of this block.
_runtime_key = runtime.get("api_key", "")
if callable(_runtime_key) and not isinstance(_runtime_key, str):
current_api_key = _runtime_key
else:
current_api_key = str(_runtime_key or "")
# Load user-defined providers so switch_model can resolve named custom
# endpoints (e.g. "ollama-launch") and validate against saved model lists.
@@ -6996,7 +7003,11 @@ def _(rid, params: dict) -> dict:
4009,
"session busy — /interrupt the current turn before switching models",
)
if session.get("agent") is None:
from hermes_cli.model_switch import parse_model_flags
parsed_flags = parse_model_flags(value)
_model_input, explicit_provider, _persist_global, _force_refresh = parsed_flags
if session.get("agent") is None and not explicit_provider.strip():
session_id = params.get("session_id", "")
_start_agent_build(session_id, session)
init_err = _wait_agent(session, rid)
@@ -7011,6 +7022,7 @@ def _(rid, params: dict) -> dict:
confirm_expensive_model=bool(
params.get("confirm_expensive_model", False)
),
parsed_flags=parsed_flags,
)
else:
result = _apply_model_switch(