Skip to content

feat: add opt-in exact-match caching for LLM responses - #404

Merged
rapids-bot[bot] merged 21 commits into
NVIDIA:mainfrom
zhongxuanwang-nv:feat/response-cache-stage1
Jul 31, 2026
Merged

feat: add opt-in exact-match caching for LLM responses#404
rapids-bot[bot] merged 21 commits into
NVIDIA:mainfrom
zhongxuanwang-nv:feat/response-cache-stage1

Conversation

@zhongxuanwang-nv

@zhongxuanwang-nv zhongxuanwang-nv commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Overview

First implementation stage of a five-stage response-cache series (exact match → logical keys → tool-result cache → semantic → streaming semantic), with the standalone documentation in #405. This stage is independently reviewable.

Adds an opt-in, exact-match response cache for managed LLM calls as a response_cache section of the Adaptive plugin config. Repeated eligible calls can be served from an in-memory or Redis store instead of re-running the provider. The feature is off until the section is present; by default, only explicitly deterministic requests (temperature = 0) are cacheable. Runtime backend failures fail open to a live call, while invalid configuration is rejected.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

  • Keying (crates/adaptive/src/response_cache/key.rs): keys use a SHA-256 fingerprint of the RFC 8785-canonicalized, normalized request. Provider surface detection and guarded raw-body fallback avoid incorrect reuse when a decode would be lossy. Provider, namespace, and schema version are folded into the key; only allowlisted headers participate.
  • Eligibility and storage (crates/adaptive/src/response_cache/intercept.rs): incomplete, error, stateful, unparseable, or otherwise unsafe calls bypass or remain unstored. Nondeterministic requests bypass unless cache_nondeterministic = true is set explicitly.
  • Streaming: live misses tee provider-native chunks while assembling a replayable aggregate. Only naturally completed, faithfully replayable streams are stored. Early close and cleanup errors remain idempotent, close upstream exactly once, and prevent cache writes.
  • Store (crates/adaptive/src/response_cache/store.rs): bounded in-memory storage and feature-gated Redis storage with expiry and operation deadlines. Redis initialization/storage failures disable or bypass the optional cache rather than blocking managed calls.
  • Gateway integration (crates/cli/src/gateway): upstream failures remain verbatim for the client but are not cacheable; post-success runtime rejections still surface as errors. Buffered and streaming cache hits use the correct content type.
  • Config and bindings: Rust, Python, Node.js, and Go expose the same safe default (cache_nondeterministic = false) and validation behavior. nemo-relay doctor reports configuration and backend reachability.
  • Observability: response_cache hit/miss/bypass marks include fingerprints, reasons, and savings without request or response bodies.
  • Review follow-up: restored the close-aware streaming regressions dropped by an earlier branch replacement, added subscriber-visible nondeterministic bypass assertions, added Redis initialization fail-open coverage, and strengthened the benchmark determinism assertion.

Validation completed on the final branch head:

  • RUST_TEST_THREADS=1 just test-rust
  • cargo clippy --workspace --all-targets -- -D warnings
  • just test-python — 542 passed
  • just test-node — 286 passed
  • just test-go
  • uv run pre-commit run --all-files — all hooks pass on the final tree

Where should the reviewer start?

  1. crates/adaptive/src/response_cache/key.rs — key derivation and guarded normalization.
  2. crates/adaptive/tests/integration/response_cache_tests.rs — end-to-end eligibility, replay, and stream-lifecycle behavior.
  3. crates/cli/src/gateway — the upstream-failure and cached-response boundary.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

Summary by CodeRabbit

  • New Features
    • Added opt-in exact-match LLM response caching (buffered + streaming) with TTL, namespace/priority, bypass controls, header allowlisting, and in-memory/Redis backends.
    • Extended configuration and editor/schema support across Rust, Go, JavaScript/TypeScript, and Python, including cache telemetry (hit/miss/bypass) and savings metadata.
    • Added CLI response-cache health/reachability checks with safe fail-open when initialization can’t complete.
  • Bug Fixes
    • Improved gateway replay by capturing upstream body bytes, enforcing strict JSON parsing rules, and preserving correct headers and SSE content type.
  • Tests
    • Expanded unit/integration/coverage/benchmark coverage for keying, replay safety, marks, gateway behavior, and storage backends.

@github-actions github-actions Bot added size:XXL PR is very large Feature a new feature labels Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an opt-in exact-match LLM response cache with canonical keying, TTL-backed in-memory or Redis storage, buffered and streaming interception, provider-native replay, telemetry, configuration validation, gateway handling, and Rust, Go, Python, and Node APIs.

Changes

Response cache feature

Layer / File(s) Summary
Configuration and public contracts
crates/adaptive/src/config.rs, crates/adaptive/src/response_cache/*, crates/adaptive/src/lib.rs, crates/node/*, go/nemo_relay/*, python/nemo_relay/*, crates/adaptive/Cargo.toml
Adds response-cache configuration, backend specifications, editor metadata, public exports, language bindings, factory helpers, and integration-test targets.
Validation, storage, and runtime registration
crates/adaptive/src/plugin_component.rs, crates/adaptive/src/runtime/validation.rs, crates/adaptive/src/response_cache/store.rs, crates/adaptive/src/runtime/features.rs, crates/adaptive/src/redis.rs
Validates cache settings, implements bounded in-memory and optional Redis stores, exposes backend health checks, and registers shared buffered and streaming intercepts.
Keying, interception, and streaming replay
crates/adaptive/src/response_cache/key.rs, crates/adaptive/src/response_cache/intercept.rs, crates/adaptive/src/response_cache/replay.rs
Builds canonical SHA-256 keys with cacheability safeguards, handles buffered and streaming hits or misses, aggregates eligible streams, and replays supported provider-native chunk formats.
Savings, diagnostics, and gateway responses
crates/adaptive/src/response_cache/mark.rs, crates/cli/src/diagnostics/mod.rs, crates/cli/src/gateway/mod.rs, crates/cli/tests/coverage/shared/*
Reports cache decisions and savings, checks backend reachability, preserves captured upstream responses, serializes short-circuited JSON, and supplies gateway and doctor coverage.
Adaptive integration coverage
crates/adaptive/tests/integration/*, crates/adaptive/tests/unit/config_tests.rs, crates/adaptive/tests/unit/response_cache/*
Tests exact-match reuse, bypass rules, validation, telemetry, streaming replay exclusions, cross-mode reuse, Redis sharing, cache isolation, storage behavior, key safety, and latency behavior.
Cross-language configuration validation
go/nemo_relay/*, python/tests/test_adaptive_config.py
Tests typed configuration round trips, default factories, serialization, and invalid response-cache diagnostics across Go and Python surfaces.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Gateway
  participant ResponseCacheIntercept
  participant CacheStore
  participant Provider
  Client->>Gateway: submit LLM request
  Gateway->>ResponseCacheIntercept: execute buffered or streaming request
  ResponseCacheIntercept->>CacheStore: lookup canonical key
  alt cache hit
    CacheStore-->>ResponseCacheIntercept: cached response aggregate
    ResponseCacheIntercept-->>Gateway: return response or replay chunks
  else cache miss
    ResponseCacheIntercept->>Provider: execute upstream request
    Provider-->>ResponseCacheIntercept: response or stream
    ResponseCacheIntercept->>CacheStore: store eligible result
    ResponseCacheIntercept-->>Gateway: return live response or stream
  end
  Gateway-->>Client: response with appropriate content type
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the PR's exact-match response-cache feature.
Description check ✅ Passed The description includes the required Overview, Details, reviewer-start, and related-issues sections with substantial implementation detail.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added lang:go PR changes/introduces Go code lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code labels Jul 10, 2026
@zhongxuanwang-nv zhongxuanwang-nv changed the title feat: opt-in exact-match LLM response cache feat: opt-in exact-match LLM response cache (stage 1/5) Jul 10, 2026
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

License Diff

Compared against origin/main.

Lockfile license changes

Lockfile License Changes

Rust

Added

  • None

Removed

  • None

Updated/Changed

  • None

Node

Added

  • None

Removed

  • None

Updated/Changed

  • None

Python

Added

  • None

Removed

  • None

Updated/Changed

  • None
Status output
[license-diff] selected languages: rust, node, python
[license-diff] generating current inventory
[license-diff] current: generating Rust inventory
[license-diff] current: Rust inventory complete (449 packages)
[license-diff] current: generating Node inventory
[license-diff] current: Node inventory complete (367 packages)
[license-diff] current: generating Python inventory
[license-diff] current: Python inventory complete (105 packages)
[license-diff] current inventory complete
[license-diff] checking out base ref origin/main into a temporary worktree
[license-diff] base: generating Rust inventory
[license-diff] base: Rust inventory complete (449 packages)
[license-diff] base: generating Node inventory
[license-diff] base: Node inventory complete (367 packages)
[license-diff] base: generating Python inventory
[license-diff] base: Python inventory complete (105 packages)
[license-diff] base inventory complete
[license-diff] removing temporary base worktree
[license-diff] comparing inventories
[license-diff] rendering Markdown output
[license-diff] done

@zhongxuanwang-nv
zhongxuanwang-nv force-pushed the feat/response-cache-stage1 branch from 4b575ad to 22b5fdb Compare July 10, 2026 23:57
@zhongxuanwang-nv
zhongxuanwang-nv force-pushed the feat/response-cache-stage1 branch from 22b5fdb to 17520f9 Compare July 11, 2026 00:55
@zhongxuanwang-nv
zhongxuanwang-nv force-pushed the feat/response-cache-stage1 branch from 17520f9 to 043afc6 Compare July 11, 2026 01:09
@zhongxuanwang-nv
zhongxuanwang-nv force-pushed the feat/response-cache-stage1 branch from 043afc6 to b149846 Compare July 13, 2026 15:47
@zhongxuanwang-nv zhongxuanwang-nv self-assigned this Jul 13, 2026
@zhongxuanwang-nv
zhongxuanwang-nv force-pushed the feat/response-cache-stage1 branch from b149846 to 7253a68 Compare July 13, 2026 23:57
@zhongxuanwang-nv
zhongxuanwang-nv force-pushed the feat/response-cache-stage1 branch from 7253a68 to 4363be4 Compare July 14, 2026 01:21
@zhongxuanwang-nv
zhongxuanwang-nv force-pushed the feat/response-cache-stage1 branch from 4363be4 to 83db5c1 Compare July 14, 2026 04:57
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>

@willkill07 willkill07 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue found that should be addressed.

Comment thread crates/cli/src/gateway/mod.rs Outdated
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
@zhongxuanwang-nv
zhongxuanwang-nv removed the request for review from a team July 30, 2026 23:27
@zhongxuanwang-nv

Copy link
Copy Markdown
Contributor Author

/ok to test 5975aa6

@zhongxuanwang-nv

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit 8abc32a into NVIDIA:main Jul 31, 2026
80 checks passed
rapids-bot Bot pushed a commit that referenced this pull request Jul 31, 2026
#### Overview

> **Documents the feature in #404.** This branch is synced with current `main`, so the diff is documentation-only and can be reviewed independently. It should merge after #404 so the documentation does not land before the feature.

Documents the opt-in exact-match response cache, its safety boundaries, binding APIs, streaming publication model, and managed-gateway integration.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Adds the Adaptive response-cache guide with `plugins.toml`, Python, Node.js, and Rust examples plus manual lifecycle APIs.
- Documents the mandatory trust-domain namespace, fixed audited noise fields, response-affecting key partitions, header restrictions, and canonicalization safety bypass.
- Documents eligibility, replay fidelity, TTL and eviction semantics, streaming write-behind behavior, lack of single-flight coordination, fail-open behavior, observability, and unredacted storage.
- Updates the Adaptive entry points and crate README.
- Calls out the intentional Rust source break for exhaustive `AdaptiveConfig` struct literals in the 0.7 migration guide.
- Validated with `just docs`, `just docs-linkcheck`, targeted pre-commit, and the full pre-commit hook set. Docs passed with zero errors; the authenticated redirects check was skipped because `FERN_TOKEN` is not present.

#### Where should the reviewer start?

`docs/configure-plugins/adaptive/response-cache.mdx` — the complete behavior, safety, and configuration contract.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to #404


## Summary by CodeRabbit

* **Documentation**
  * Added guidance for enabling and configuring optional response caching for repeated LLM requests.
  * Documented cache behavior, including TTLs, request matching, streaming replay, bypass conditions, and failure handling.
  * Added details on cache keys, observability signals, configurable settings, backend requirements, and validation warnings.
  * Updated Adaptive configuration and navigation guidance to include the new Response Cache documentation.
  * Documented in-memory and Redis-backed storage options, including Redis setup for shared persistence.
  * Clarified gateway handling for provider payloads, lifecycle events, streaming responses, errors, and retry behavior.

Authors:
  - Zhongxuan (Daniel) Wang (https://github.com/zhongxuanwang-nv)
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: #405
@coderabbitai coderabbitai Bot mentioned this pull request Aug 2, 2026
2 tasks
rapids-bot Bot pushed a commit that referenced this pull request Aug 10, 2026
#### Overview

ACG includes the first non-system message in its learning key. A workflow whose
system prompt, tool schemas, and output contract never change is therefore split
into a separate profile for every distinct first user task, so observations never
accumulate and the stable scaffold is never recognized as a reusable prefix.

This change buckets learning by the stable system/tool/structured-output
scaffold, preserves the prior task seed for requests without one, and gates reuse
on a fingerprint bound to the exact scaffold key.

Scope is the three items assessed as meaningful in the [#323 review](#323 (comment)):
scaffold-aware keying, a stable-prefix fingerprint guard, and the
structured-output contract in `PromptIR`. No governor, drift detector,
convergence machinery, topology state, configuration surface, or binding change.
Nothing is added to Python, Node, Go, WebAssembly, or C FFI.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

5 source files (+432/-51), 18 test files (+938/-35).

- The learning key derives from the stable scaffold. Requests carrying a system
  prompt, tool schemas, or a structured-output contract bucket under
  `seed=stable-scaffold`; requests without one keep the prior task-seed behavior.
- `structured_output_schema_id` is populated and the canonicalized
  `response_format` becomes a scaffold block ahead of the first non-system
  message, so an unchanging output contract sits inside the stable prefix.
- Fingerprints bind to the exact scaffold key. A prefix stopping inside the
  scaffold binds to that scaffold; one extending past it binds to the complete
  source request, because the normalized IR retains no lossless provider-prefix
  representation.
- The gate fails closed on legacy, missing, shorter, mismatched, and
  beyond-scaffold-without-a-source-request fingerprints.
- `find_stable_prefix_length` is removed. This change replaces its only caller
  and nothing else in the workspace used it.

#### Learning-key fragmentation on `main`

Control: `main` at `2ab7a070`, no source change. Two requests sharing a system
prompt and tool schema, differing only in the first user task, key as:

```text
agent-a::model=gpt-4o::seed=user:sha256:d006::system=sha256:1a842e58b::tools=sha256:69848c057
agent-a::model=gpt-4o::seed=user:sha256:5607::system=sha256:1a842e58b::tools=sha256:69848c057
```

`system` and `tools` are identical; only `seed` differs, so the two runs land in
separate profiles and neither accumulates enough observations to produce a
reusable prefix. On this head the pair produces one key with
`seed=stable-scaffold`.

#### Reuse determinism

A profile persisted by one process is read back by another, so the analysis must
not depend on the per-process seed Rust draws for `HashMap` iteration. Over `N`
processes producing outcomes with multiplicities `c_1..c_k`, the cross-process
agreement rate is

```text
A = sum_i c_i (c_i - 1) / (N (N - 1))
```

the probability two independent processes agree, and equivalently the probability
that a persisted profile satisfies the reuse gate elsewhere. `A = 1` exactly when
the analysis is seed-independent.

Measured over `N = 20` processes on one 20-observation window whose first turn
varies in 1 of 20 runs, with four tool spans sharing a sequence index:

| Rule | Span order `A` | Prefix length `A` | Fingerprint `A` | Prefix |
|---|---|---|---|---|
| Per-span run on `sequence_index` (`main`) | 0.000 | 0.479 | 0.479 | `1` ×9, `2` ×11 |
| Per-span run on `(index, rank, span id)` | 1.000 | 1.000 | 1.000 | `1` |
| Exact prefix mass | 1.000 | 1.000 | 1.000 | `6` |

`main` derives the prefix as the leading run of per-span scores, then
re-sequences those scores by first-seen index. That index does not order the span
set: span ids carry the role and tool suffix, so `assistant-1-search` and
`assistant-1-fetch` are distinct spans at index 1, and the index is a minimum
across observations. At `A = 0.479`, better than half of cross-process profile
reads miss.

Two changes follow. Score ordering extends to `(index, stability rank, span id)`,
injective because span ids are unique within one analysis. Prefix economics walks
that vector and stops pricing at the first non-stable span, so ranking the least
stable first ends the priced prefix at a contested position instead of pricing
across it.

The prefix length no longer reads that vector. It descends the prefix tree of
block sequences while one child holds the configured share, which is the rule
named in the #323 review: the same exact prefix observed for N samples. Descent
also requires a strict majority, which two disjoint children cannot both hold, so
the dominant child is unique where it exists and no tie-break is reachable; where
none dominates, the prefix stops. Equivalently, under `d(x, y) = 2^-lcp(x, y)`
the window is ultrametric, and every point of a closed ball is a center, so the
ball is fixed by its members rather than by traversal order.

That also recovers reuse. On this window 19 of 20 observations share an exact
6-block prefix; the per-span rule reports 1 because the minority span at index 1
stops the run. The outlier still fails the fingerprint gate, so the wider prefix
stays exactly validated.

#### Test coverage

Variable first-user tasks under one scaffold; changed system prompts, tool
schemas, and output contracts; canonical structured output; whitespace changes
hidden by normalized IR; prefixes beyond the scaffold; legacy state with no
fingerprint; same-run aggregation; fresh-cache rehydration; Redis restart;
serialization compatibility.

Adversarial cases: an interleaving storage backend that yields between the
learner's load and store halves shows concurrent runs never pair a fingerprint
with a foreign observation window; two workflows sharing a system prompt and
anchor turn collapse to one key and then fail closed once the prefix extends past
the scaffold; every rotation of one span set produces a single canonical ordering
without depending on a hash seed.

The Redis restart test previously read its learning key out of the hot cache, so
it passed for whichever key `process_run` wrote — and `process_run` also persists
under the plain agent id for rehydration, so a mis-keyed scaffold record could
have satisfied it. It now asserts the key is not the bare agent id and carries the
stable-scaffold marker before reloading.

#### Validation

PR head `25c771e7` on `main` at `2ab7a070`, Windows 11, rustc 1.97.1, go 1.26.1:

| Suite | Result |
|---|---|
| `cargo test -p nemo-relay-adaptive` | 538/538 lib, 8/8 surface, 36/36 response cache, 4/4 response-cache bench, 12/12 runtime integration, 1 doctest passed and 3 ignored |
| `NEMO_RELAY_RUN_REDIS_TESTS=1 … --features redis-backend --test redis_integration` | 11/11 against Redis 8.0.5 on `127.0.0.1:6379` |
| `just test-python` | 609 passed, 5 skipped |
| `just test-node` | 342 passed, 2 skipped, 0 failed of 344 |
| `cargo clippy -p nemo-relay-adaptive -p nemo-relay-python --all-targets -- -D warnings` | clean |
| `cargo fmt --all -- --check` | clean |

The 36 response-cache and 4 response-cache benchmark tests cover the opt-in
exact-match LLM response cache from #404 and pass unchanged on this head.

#### Limits

No provider-cache, cost, or latency improvement is claimed. The analysis-only
trace report, the three-arm interleaved experiment, and the provider
cache-read/cache-write, billed-cost, parity, and p50/p95/p99 evidence requested in
points 1 through 3 of the #323 review are not supplied here; this diff is the
internal keying and validation change those points would measure. An earlier
interleaved run showed the fragmentation directly (144 keys and 0 hints versus 1
key and 120 hints) but used an older `main` and a benchmark overlay, so it is not
offered as evidence for this diff.

The exact-prefix-mass rule widens prefixes relative to `main`, which is a
behavior change and not only a determinism fix; reuse remains gated on the exact
fingerprint.

Two suites did not run on this host: `just test-go` requires `clang`/`lld`, and
full-workspace `just test-rust` reaches unrelated CLI filesystem failures on
Windows. CI provides the authoritative Linux matrix.

Persisted JSON stays backward-compatible through an optional fingerprint field,
which is source-visible to downstream Rust constructing `StabilityAnalysisResult`
with a struct literal.

#### Where should the reviewer start?

`crates/adaptive/src/acg_profile.rs` for the learning-key boundary, then
`crates/adaptive/src/acg/stability.rs` for the prefix and ordering rules and
`crates/adaptive/src/acg_component.rs` for the gate. Focused tests are in
`crates/adaptive/tests/unit/acg_profile_tests.rs`,
`crates/adaptive/tests/unit/acg/stability_internal_tests.rs`, and
`crates/adaptive/tests/unit/acg_component_tests.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to #323.
- Replaces the scaffold-keying and exact-validation core of #322. The
  convergence, governor, drift, and binding scope rejected there is absent.

Authors:
  - Teerth Sharma (https://github.com/teerthsharma)
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: #481
@zhongxuanwang-nv
zhongxuanwang-nv deleted the feat/response-cache-stage1 branch August 18, 2026 03:17
@zhongxuanwang-nv
zhongxuanwang-nv restored the feat/response-cache-stage1 branch August 18, 2026 03:20
@zhongxuanwang-nv
zhongxuanwang-nv deleted the feat/response-cache-stage1 branch August 18, 2026 03:25
rapids-bot Bot pushed a commit that referenced this pull request Aug 20, 2026
#### Overview

Adds opt-in tool-result caching to Adaptive response caching. A cache hit suppresses the real tool call, so caching remains disabled by default and only explicitly classified read-only, TTL-stable tools are eligible.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Adds default, class, and per-tool override policies with exact/wildcard resolution, TTL, bypass rate, argument skips, and tool-version keying.
- Shares the existing cache store while keeping tool and LLM keys disjoint; key and store failures fail open to a live tool call.
- Adds Rust, Python, Node.js, and Go configuration parity, validation, doctor diagnostics, saved-invocation marks, and focused integration/benchmark coverage.
- Keeps the feature diff focused: user-facing tool-cache documentation remains outside this PR, and only essential API/safety comments are retained.
- Is based directly on NVIDIA/NeMo-Relay `main` at `b467deae`; this branch reconciles with the merged canonical tool-execution-result API while preserving annotations on cache hits. Earlier logical-key and staged documentation changes are not part of this diff.
- Breaking changes: none.
- Validation: `just ci=true test-rust` (3,717 passed), response-cache unit tests (61 passed), response-cache integration tests (50 passed), Python (613 passed), Node.js (342 passed), Go, workspace clippy with warnings denied, and all repository pre-commit hooks pass. The canonical non-CI Rust run also completed all unit/integration tests but encounters the existing upstream `scope_stack.rs` doctest reference to unavailable `nemo_relay::Result`.

#### Where should the reviewer start?

Start with `crates/adaptive/src/response_cache/tool.rs` for policy resolution and fail-open execution, then `crates/adaptive/tests/integration/response_cache_tests.rs` for the behavioral contract.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to #404


## Summary by CodeRabbit

* **New Features**
  * Added opt-in caching for tool results alongside exact-match LLM response caching.
  * Added configurable tool policies for classes, overrides, TTLs, argument exclusions, bypass rates, priorities, and optional error caching.
  * Exposed tool-cache configuration across Python, Node.js, and Go APIs.
  * Added diagnostics for tool-cache status and configuration issues.

* **Bug Fixes**
  * Improved cache-key normalization and validation for tool arguments, headers, wildcard rules, and expired entries.
  * Improved handling of cache failures and malformed streaming responses.

Authors:
  - Zhongxuan (Daniel) Wang (https://github.com/zhongxuanwang-nv)

Approvers:
  - Will Killian (https://github.com/willkill07)
  - Bryan Bednarski (https://github.com/bbednarski9)

URL: #597
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature a new feature lang:go PR changes/introduces Go code lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code size:XXL PR is very large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants