feat(credential-pool): opt-in preemptive key rotation + footer credential field - #57256
feat(credential-pool): opt-in preemptive key rotation + footer credential field#57256caiolea0 wants to merge 1 commit into
Conversation
…tial field
Two related changes for pooled-credential load-spreading and visibility.
1. Preemptive rotation (env HERMES_PREEMPTIVE_KEY_ROTATION, default off):
run_conversation advances the round_robin/least_used credential-pool
cursor before each API call, spreading load across all pooled keys
instead of only rotating reactively after a 429/401. A single long
session no longer keeps hitting one key until it is rate-limited.
Inert by default; only fires for api-key pools using round_robin or
least_used with an available entry. OAuth, single-key, and fill_first
providers are never touched. Errors are swallowed so a rotation hiccup
can never break a turn.
2. Runtime footer: a new optional `credential` field renders the active
pooled credential label, e.g.
display:
runtime_footer:
fields: [model, credential, context_pct, cwd]
so operators can see which pooled key served each turn. Skipped
silently when no pool/credential is present, exactly like the other
footer fields.
Addresses the standing request for proactive load-spreading across a key
pool (NousResearch#22212, NousResearch#22407, NousResearch#22916).
|
Thanks for the focused implementation and for reusing the existing pool-selection primitives. This automated hermes-sweeper review is closing the PR because its user-facing opt-in is a new non-secret
A focused re-scope using an explicit Closed as not-planned per standing maintainer policy ( |
feat(credential-pool): opt-in preemptive key rotation + footer credential field
Problem
The credential pool rotates reactively only —
pool.select()is consultedinside the 429/401 error-recovery path (
recover_with_credential_pool) and onsession restore, never per-request in the mainline loop. With
round_robin/least_usedconfigured, a single long-running session keeps sending everyrequest on the same key until it is rate-limited, then rotates. Operators with
a pool of N keys expect the load to spread across all N proactively.
Long-standing, still-open request: #22212, #22407, #22916.
Changes
1. Preemptive rotation (
HERMES_PREEMPTIVE_KEY_ROTATION, default off)run_conversationcalls a new helper at the top of every loop iteration:agent_runtime_helpers.preemptive_rotate_credential(agent)advances the poolcursor via the existing
pool.select()and swaps the credential in with theexisting
agent._swap_credential(entry)— the same primitives the restore pathalready uses. No new rotation logic.
2. Footer credential field
gateway/runtime_footer.pygains an optionalcredentialfield;gateway/run.pyresolves the active pooled credential label (from the cached/running agent's
pool) and passes it in. Lets operators see which pooled key served each turn:
Safety / scope
only renders when added to
fields. Zero behavior change for existing setups.round_robin/least_usedapi-key pools with an available entry. OAuth, single-key, and
fill_firstproviders are never touched.
fully wrapped; any exception is swallowed (debug-logged).
pool.select()+agent._swap_credential()— no changes tocredential_pool.pysemantics.Testing
round_robinpool.select()returns a distinct entry per call across a20-key pool (20/20 distinct over one cycle, then wraps).
across distinct keys, surfaced in the footer (
🔑 api-key-9→api-key-17→
NIM Key 3).hermes serve) boot cleanly with the flag on;flag off is a verified no-op.
py_compileclean on all four touched files.Notes
Env-flag chosen over a config key to keep the surface minimal and the default
untouched; happy to move rotation behind
credential_pool_strategies/ a configoption if maintainers prefer that shape.