feat: opt-in exact-match LLM response cache (stage 1/5)#404
Draft
zhongxuanwang-nv wants to merge 1 commit into
Draft
feat: opt-in exact-match LLM response cache (stage 1/5)#404zhongxuanwang-nv wants to merge 1 commit into
zhongxuanwang-nv wants to merge 1 commit into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
License DiffCompared against Lockfile license changesLockfile License ChangesRustAdded
Removed
Updated/Changed
NodeAdded
Removed
Updated/Changed
PythonAdded
Removed
Updated/Changed
Status output |
4b575ad to
22b5fdb
Compare
2 tasks
22b5fdb to
17520f9
Compare
An opt-in feature of the adaptive plugin (a response_cache config section, not a new plugin kind): managed LLM calls are keyed by a SHA-256 fingerprint of the normalized request and repeats are served from the store — instant, free, reproducible. Buffered and streaming calls share one keyspace; a streamed miss is teed and stored as its aggregated response, a hit replays provider-native chunks. Keying auto-detects the provider surface from the request shape and trusts the decode only where it is faithful: known-lossy shapes and decodes that fail to round-trip fall back to raw-body fingerprinting, which can only cost a miss. Stateful calls (Responses persistence, conversations, containers) and nondeterministic calls under the safety toggle bypass entirely. Stored answers must be complete: non-null errors, non-final statuses, truncated or lossily-collected streams, and upstream failures are never cached — the CLI gateway relays failed upstream replies to the client verbatim while keeping them invisible to the execution chain. The in-memory store is bounded by an honest resident-size budget with oldest-first eviction; the Redis backend runs under hard deadlines and re-checks entry expiry. Everything fails open: any cache error falls through to a live call. Same config surface in Rust, Python, Node, and Go; doctor reports configuration and backend reachability; hit/miss/ bypass marks carry fingerprints and savings, never bodies. Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
17520f9 to
043afc6
Compare
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.
Overview
First stage of a five-stage series (exact match → logical keys → tool-result cache → semantic → streaming semantic), each stage a single reviewable commit with a matching docs PR. This stage is fully standalone.
Adds an opt-in, exact-match response cache for managed LLM calls as a
response_cachesection of the adaptive plugin config (no new plugin kind, no call-site changes). Repeated identical calls are served from an in-memory or Redis store instead of re-running the provider — instant repeats in dev loops and evals, and reproducible runs via thebypass_ratetrust dial. Off by default; every cache error fails open to a live call.Details
crates/adaptive/src/response_cache/key.rs): requests are keyed by a SHA-256 fingerprint of the RFC 8785-canonicalized, normalized request. The provider surface is auto-detected from the request shape (nothing to configure); the decode is trusted only where faithful — known-lossy shapes and decodes that fail a structural round-trip (tools, conversation, tool_choice, generation params) fall back to raw-body fingerprinting, which can only cost a miss, never a wrong reuse. Tool-call IDs are normalized; only allowlisted headers are keyed; namespace/provider/schema-version are folded in.response_cache/mod.rs): only complete, replayable answers are stored — non-nullerrorbodies, non-finalstatus, truncated streams (no protocol-terminal event), and lossily-collected aggregates (thinking blocks, refusal-only choices) are never cached. Stateful calls (Responses persist-by-default withoutstore: false,conversation,container) and nondeterministic calls undercache_nondeterministic: falsebypass entirely.response_cache/store.rs): in-memory backend bounded by an honest resident-size budget with oldest-first eviction; Redis backend (feature-gated) with hard per-op deadlines, native expiry plus an embedded-expiry re-check.crates/cli/src/gateway.rs): non-2xx / non-JSON upstream replies flow back asErrso nothing downstream can cache them, while the client still receives the upstream response verbatim; short-circuited hits carry correct content types.skip_keys, and auth headers inheader_allowlist. Same typed config in Rust, Python, Node, and Go;nemo-relay doctorreports configuration and backend reachability.response_cachehit/miss/bypass marks carry key fingerprints and savings, never bodies. Entries are stored unredacted (PII sanitize guardrails rewrite telemetry only), so shared Redis deployments must be trusted and namespaced — documented.Validation run on this branch:
cargo test -p nemo-relay-adaptive --lib --tests --features nemo-relay/openinference(all targets green, ~530 tests incl. integration + benchmark suites), gateway HTTP cache tests (server::tests::gateway_*, real mock upstream),cargo fmt --all --check,cargo clippy -- -D warningson both crates. Python/Go surfaces are config mirrors validated by the shared Rust validator; their suites cover round-trip + validation parity. Known pre-existing sandbox failures (plugin-shim sidecar tests) are unrelated.Where should the reviewer start?
crates/adaptive/src/response_cache/key.rs— the key-derivation contract and the lossy-decode guards (the core correctness decisions: when to trust the normalized decode vs fall back to raw keying).crates/adaptive/tests/integration/response_cache_tests.rs— the behavioral contract as tests: exact repeats hit, guards force misses instead of wrong reuses, streams store only on terminal events.crates/cli/src/gateway.rs— the upstream-failure boundary (design decision: failures relayed verbatim but invisible to the execution chain).Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
codec::resolve; the local surface-name helpers inresponse_cache/key.rsare slated to migrate onto the provider codec factory once it lands.