fix(web): clear stale api-alias credential on provider switch in main-model assignment - #49551
Conversation
…in-model assignment c253b07 added clear_model_endpoint_credentials() to scrub an old endpoint's inline secret (api_key, the legacy `api` alias, api_mode) when the web UI switches the main model to a different provider. But _apply_main_model_assignment gates the key-scrub path on model_cfg["api_key"] being truthy, so when the stale secret lives only under the legacy `api` alias (no api_key), a provider switch never clears it — the secret survives in config.yaml. model.api is a live credential read path (_resolve_openrouter_runtime reads `for k in ("api_key", "api")`), so the old endpoint's key contaminates a later custom resolution — the exact harm clear_model_endpoint_credentials documents. The sibling persistence sites (the gateway model-picker paths and the aux-slot path) call the helper unconditionally on a non-custom switch and already scrub `api`; only this caller had the api_key-only gate. Widen the guard to fire on either field. The same-provider re-pick and explicit-new-key paths are unchanged. Adds the api-alias case to the assignment test (it fails without the fix).
|
Thanks for the focused regression fix. The premise holds on current main: The proposed guard makes the existing cleanup path cover the missing alias-only state without changing same-provider or explicit-key behavior. The added regression case directly exercises that state. Automated hermes-sweeper review. |
|
Merged via PR #68125 with your commit's authorship preserved in git log (rebase-merge). Your fix was picked up in a post-merge sweep of the credential-hygiene cluster (#67806/#67797/#68074) — the premise held exactly as you described: the switch-clears-the-key path never fired when the stale secret lived under the legacy |
Summary
Follow-up to
c253b0738("clear stale endpoint credentials across switches").That commit added
clear_model_endpoint_credentials()to scrub an old endpoint's inline secret —api_key, the legacyapialias, andapi_mode— when the web UI switches the main model to a different provider. But_apply_main_model_assignment(hermes_cli/web_server.py) gates the key-scrub path onmodel_cfg["api_key"]being truthy:So when the stale secret lives only under the legacy
apialias (noapi_key), a provider switch fails thatelif, and the second call usesclear_api_key=False— the secret underapiis never cleared and survives inconfig.yaml.This matters because
model.apiis a live credential read path —_resolve_openrouter_runtime()readsfor k in ("api_key", "api")and feeds it as the api_key candidate on the custom/auto branch. So endpoint A's plaintext secret persists and becomes the active credential the next time resolution flows through a custom endpoint — the exact "secrets in config.yaml… contaminate later custom resolution" harmclear_model_endpoint_credentials's own docstring names.It's also an inconsistency: the sibling persistence sites (the two gateway model-picker paths and the aux-slot path) call the helper unconditionally on a non-custom switch and already scrub
api; only this caller had theapi_key-only gate.Reproduced:
Fix
Widen the guard to fire on either field:
The same-provider re-pick (preserves the key) and explicit-new-key paths are unchanged.
Tests
Added the legacy-
api-alias case totest_apply_main_model_assignment_base_url_and_context_reconcileintests/hermes_cli/test_web_server.py(asserts bothapiandapi_keyare absent after a provider switch). Fails without the fix (theapialias survives); passes with it.