Conversation
…on memo
resolve_runtime_provider() calls load_config() (a full defensive deepcopy,
~265us warm) 5-7x per call via the resolve tree — measured 74% of the
~2.35 ms/call cost — and the gateway resolver calls it 5-8x per inbound
message. Two layers fix this:
Layer A (hermes_cli/runtime_provider.py): every config read in the resolve
tree is read-only, so switch all 6 sites to load_config_readonly()
(mtime-cached, no deepcopy). _get_model_config() deep-copies only the small
model section to preserve its mutation-safety contract. Measured:
resolve_runtime_provider 2.35ms -> 0.94ms/call; _get_model_config 0.38ms ->
0.023ms/call.
Layer B (gateway/run.py): the MoA path already caches
resolve_runtime_provider behind a 300s TTL (merged MoA runtime cache,
agent/moa_loop.py); the gateway path now gets the same treatment via
_memoized_resolve_runtime, keyed on the files the resolution reads
(config.yaml + profile/global auth.json) with a TTL backstop for
env-var-only changes. Vertex (per-call OAuth token) and AuthError/fallback
results are never cached. The memo stores a copy and hands out fresh dicts
so caller mutation (pop('model')) can never corrupt it. Per-message:
~12-19ms -> ~1.5-2ms (first call resolves, the rest hit the memo).
Tests: new tests/gateway/test_runtime_resolve_fastpath.py pins the
measured-work (no deepcopy from the resolve tree, one resolve per message),
TTL/config/auth invalidation, vertex bypass, and mutation safety. Existing
tests that patched rp.load_config as their config-injection seam now patch
rp.load_config_readonly (the seam moved with the loader).
The per-message memo in _memoized_resolve_runtime() keyed on config/auth file (mtime_ns, size) signatures only. A multiplex gateway resolves multiple profiles' agents in the same OS process (the desktop tui_gateway switches profiles per request via set_hermes_home_override), and 'hermes profile create --clone-all' copies the profile tree with mtime-preserving shutil.copy2 — so a cloned profile's config.yaml/auth.json carry the source's identical (mtime_ns, size) and compute the same memo signature, letting one profile receive the other's cached api_key/base_url for up to the 300s TTL. Add str(get_hermes_home()) to the memo key, the same profile-boundary fix open PR NousResearch#78185 applies to agent/moa_loop.py's sibling _runtime_cache. Tests: new tests/gateway/test_runtime_resolve_fastpath_profile_isolation.py drives _memoized_resolve_runtime under two profile overrides and asserts each profile gets its own credentials; both tests fail without the fix and pass with it. Same-profile memo hit preserved (2 invocations, 1 resolve).
Contributor
Author
|
this is the same profile-boundary gap #78185 closes for What changed: Verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
resolve_runtime_provider() deep-copies the whole config 5-7x per call (measured 74% of its 2.35ms/call cost) and the gateway resolver calls it 5-8x per inbound message (~12-19ms of CPU before every LLM call). The MoA path already caches it behind a 300s TTL (#66793); the gateway path never got that. Fix: (A) convert all 6 read-only config reads in the resolve tree to load_config_readonly() (mtime-cached, no deepcopy), deep-copying only the small model section in _get_model_config to preserve its mutation contract — measured 2.35ms->0.94ms/call; (B) add _memoized_resolve_runtime at the gateway per-message entry, keyed on config+auth mtimes with a 300s TTL backstop, never caching the vertex OAuth path or fallback results — measured 0.94ms->0.149ms on memo hit. Per-message cost drops ~10x. 12 new measured-work/behavior-parity tests; sabotage PASS.
Related Issue
No GitHub issue — discovered via code review and reproduced live (see below). Happy to file one first if preferred.
Changes Made
fix/provider-runtime-fastpath— 9 file(s) changed vs base:gateway/run.pyhermes_cli/runtime_provider.pytests/agent/test_bedrock_adapter.pytests/agent/test_nous_portal_anthropic_wire.pytests/gateway/test_runtime_resolve_fastpath.pytests/hermes_cli/test_canonical_custom_identity.pytests/hermes_cli/test_custom_provider_identity.pytests/hermes_cli/test_runtime_provider_resolution.pytests/tui_gateway/test_custom_provider_session_persistence.pyHow to Test
Validation completed:
tests/gateway/test_runtime_resolve_fastpath.py.<full suite>: branch 27401 passed / 6 failed vs baseline 27387 passed / 8 failed — zero branch-only failures.Logs
Sabotage verification output: