Skip to content

Commit f180e49

Browse files
devin-ai-integration[bot]DanielSLewTylerBarnes
authored
feat(core): add ResponseCache input processor (#16283)
## Description Adds opt-in response caching as a `ResponseCache` **input processor**. Identical LLM steps skip the model call and replay a previously cached response, dropping latency to single-digit milliseconds and avoiding duplicate spend. Following the [Slack thread discussion](https://kepler-bej6556.slack.com/archives/C06D49JB83B/p1777579909130359?thread_ts=1777579909.130359&cid=C06D49JB83B) and Tyler's review, this is **processor-only opt-in** — there is no agent-level option. Per-call overrides flow through `RequestContext`. We can widen the API later based on user feedback; for now we keep the surface small. ```ts import { Agent } from '@mastra/core/agent'; import { InMemoryServerCache, createMastraCacheFromServerCache } from '@mastra/core/cache'; import { ResponseCache } from '@mastra/core/processors'; const cache = createMastraCacheFromServerCache(new InMemoryServerCache()); const agent = new Agent({ name: 'Search Agent', instructions: 'You answer questions concisely.', model: 'openai/gpt-5', inputProcessors: [new ResponseCache({ cache, ttl: 600 })], // 10 minutes }); // First call: cache miss → LLM call await agent.generate('What is the capital of France?'); // Second identical call: cache hit → no LLM call await agent.generate('What is the capital of France?'); ``` Per-call overrides via `RequestContext`: ```ts import { ResponseCache } from '@mastra/core/processors'; import { RequestContext } from '@mastra/core/request-context'; // Force a fresh call but still update the cache. await agent.stream(prompt, { requestContext: ResponseCache.context({ bust: true }), }); // Or merge into an existing context. const ctx = new RequestContext(); ctx.set('caller-meta', { userId: 'u-123' }); ResponseCache.applyContext(ctx, { key: 'custom-key' }); await agent.stream(prompt, { requestContext: ctx }); ``` `key`, `scope`, `bust` are overridable per call. `cache`, `ttl`, `agentId` stay on the constructor. A `key` function receives `{ agentId, scope, model, prompt, stepNumber }` and returns a string (or `Promise<string>`). If it throws, the processor falls back to the deterministic hash so the call still benefits from caching. ```ts import { ResponseCache, buildResponseCacheKey } from '@mastra/core/processors'; await agent.stream(input, { requestContext: ResponseCache.context({ key: ({ model, prompt }) => `qa:${model.modelId}:${JSON.stringify(prompt).slice(-200)}`, }), }); // Or override individual fields while preserving the rest of the standard hash: await agent.stream(input, { requestContext: ResponseCache.context({ key: inputs => buildResponseCacheKey({ ...inputs, scope: 'global' }), }), }); ``` ### Architecture Caching happens **inside** the agentic execution loop on the `processLLMRequest` hook (added in #16176; extended here to allow early-return responses), not at the top of `agent.stream` / `agent.generate`. This addresses the safety concern Tyler raised earlier in the thread: keying off the resolved `LanguageModelV2Prompt` (post memory + post input processors) rather than raw user input means cached entries can't leak context across users with shared prompts but different memory state. Each step in an agentic tool loop is independently cached. **New public API:** - `ProcessLLMRequestResult.response?: CachedLLMStepResponse` — optional early-return for cache hits. When set, the loop synthesizes the model stream from the cached chunks instead of calling `execute()`. - `Processor.processLLMResponse(args)` — symmetric post-step hook. Receives `chunks`, `model`, `stepNumber`, `steps`, `state`, `warnings`, `request`, `rawResponse`, `fromCache`. Pairs with `processLLMRequest` via shared `state` for cache writes. - `ResponseCache` exported from `@mastra/core/processors`, plus `ResponseCache.context()` / `ResponseCache.applyContext()` static helpers. - `MastraCache` interface, `InMemoryServerCache`, and `createMastraCacheFromServerCache` adapter (in `@mastra/core/cache`). **Cache key (default):** `agentId`, `stepNumber`, `scope`, model identity (`provider`, `modelId`, `specificationVersion`), and the resolved `prompt`. Mastra's internal `providerOptions.mastra.*` metadata (e.g. per-message `createdAt`) is stripped before hashing. **Default scope** falls back to `MASTRA_RESOURCE_ID_KEY` from the request context, so agents that already populate the resource id (e.g. via memory) get per-user isolation automatically. Pass `scope: null` to opt out for known-public content. **Cache hits / misses:** - **Hit**: `processLLMRequest` returns `{ response }`. The loop replays the cached chunks through the same pipeline. `agent.generate()` collects them into a `FullOutput`; `agent.stream()` returns a `MastraModelOutput` whose `fullStream` / `text` / `usage` / `finishReason` come from the cached buffer. - **Miss**: `processLLMRequest` stashes the cache key on `args.state` for `processLLMResponse` to use later. The model is called as normal. After the step completes, `processLLMResponse` writes the captured chunks to the cache. Failed runs (`error` / `tripwire` chunk, or non-success `finishReason`) are not cached. **Backend:** Bring any `MastraCache` implementation. The `MastraCache` interface is intentionally narrow (`get<T>` / `set<T>(value, ttlSeconds?)`) so the same primitive can back a future filesystem-based recorder for tests — discussed but explicitly **out of scope** for this PR. **Per-key TTL on the server cache:** `MastraServerCache.set()` now accepts an optional `ttlMs` argument that overrides the cache's default TTL for a single entry. `@mastra/redis` already supported per-call TTL natively; this PR threads it through the `MastraServerCache` interface so other backends can opt in. ### What changed since the last review (Tyler's `CHANGES_REQUESTED`) - Removed `responseCache` from `AgentConfig` and per-call `generate()` / `stream()` options. - Removed agent auto-registration of `ResponseCache` and the `getResponseCacheProcessors` helper. - Deleted `agent/response-cache.ts` (`AgentResponseCacheOption`, `resolveResponseCacheConfig`, `buildAgentResponseCacheKey`). - Moved helpers + types into the processor file and dropped the `Agent` prefix: `ResponseCacheKeyFn`, `ResponseCacheKeyInputs`, `buildResponseCacheKey`. - Added `ResponseCache.context()` / `ResponseCache.applyContext()` for per-call overrides via `RequestContext`. - `ResponseCache.processLLMRequest` reads per-call overrides from the context and merges with constructor options. - Default scope falls back to `MASTRA_RESOURCE_ID_KEY` from the request context (Tyler's #2). - Stale pending state is cleared at the start of each `processLLMRequest` (Tyler's #3). - `ProcessorRunner.runProcessLLMRequest` uses `Object.prototype.hasOwnProperty.call(result, 'prompt')` instead of truthiness, so processors can intentionally pass an empty prompt without it being silently ignored (Tyler's #4). - Rewrote tests for the processor-only API + context-based per-call overrides. - Reworked docs (`/docs/agents/response-caching.mdx`) and added `/reference/processors/response-cache.mdx`. Removed the `responseCache` rows from the agent / generate / stream reference pages. - Updated the changeset. ### Out of scope - LLM recorder unification — covered in the thread; same primitive (filesystem-backed `MastraCache`), but reusing the recorder's MSW interception layer is a separate PR with fixture regeneration churn. - Widening to an agent-level option / per-call `responseCache` argument. We'll revisit based on user feedback on the processor-first API. ## Related Issue(s) - #16176 (provider-boundary `processLLMRequest` hook this PR builds on) - #15968 (processor-level cache for the LLM-based guardrail processors — orthogonal; can be merged or superseded independently) ## Type of Change - [x] New feature (non-breaking change that adds functionality) - [x] Documentation update ## Checklist - [x] I have made corresponding changes to the documentation (if applicable) - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have addressed all Coderabbit comments on this PR Link to Devin session: https://app.devin.ai/sessions/e48f195413104d83997d80d939a166b9 Requested by: @DanielSLew <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## ELI5 This PR adds a new feature that lets the AI system remember and reuse answers it's already given. When the same question is asked again, instead of asking the AI model to think of an answer from scratch, it just plays back the answer it already saved—kind of like having a notebook to check before asking the model to do the work again. This saves time and money. --- ## Overview Added an opt-in `ResponseCache` input processor that caches identical LLM step responses, enabling identical steps to replay cached chunks instead of calling the model. The feature is processor-only (no agent-level option); per-call overrides flow through `RequestContext`. ## Key Features **Constructor Configuration:** - `cache`: A `MastraCache` instance for storing responses - `ttl`: TTL in seconds (default: 300) - `agentId`: Optional agent identifier - `scope`: Cache scope key (defaults to `MASTRA_RESOURCE_ID_KEY` for per-user isolation; `null` opts out) - `key`: Custom key function returning a string or throwing to fall back to default **Per-Call Overrides (via RequestContext):** - `key`: Custom cache key override - `scope`: Override cache scope - `bust`: Skip cache reads but still write on completion - `responseCache`: Disable caching per call **Cache Key Derivation:** The default cache key is deterministic, derived from: - Agent ID and step number - Cache scope - Model identity - Resolved `LanguageModelV2Prompt` (after memory loading and earlier input processors) - Mastra provider metadata is stripped before hashing **Hook Integration:** - `processLLMRequest`: Returns cached response on cache hit, short-circuiting the LLM call - `processLLMResponse`: Writes successful responses to cache (failed runs are not cached) ## Implementation Details **Core Changes:** - New `MastraCache<T>` interface defining typed `get` and `set` methods - `InMemoryServerCache` and `createMastraCacheFromServerCache` adapter for server-cache compatibility - `ResponseCache` processor implementing both `processLLMRequest` and `processLLMResponse` - Updated `MastraServerCache.set()` to accept optional per-key `ttlMs` - New processor types: `CachedLLMStepChunk`, `CachedLLMStepResponse`, `ProcessLLMResponseArgs`, `ProcessLLMResponseResult` **Agent Integration:** - Agent resolves auto-registered `ResponseCache` processor when enabled - Supports both custom cache and Mastra server-cache fallback - Per-call caching can be disabled even when configured globally **LLM Execution Flow:** - Cache hits in `processLLMRequest` return early with cached response, bypassing model call - Cache replay streams cached chunks and skips `processLLMResponse` and output processors - Cache writes occur only after successful step completion with success `finishReason` ## Files Modified **Documentation:** - New guide: `docs/src/content/en/docs/agents/response-caching.mdx` (169 lines) - New API reference: `docs/src/content/en/reference/processors/response-cache.mdx` (202 lines) - Updated sidebars to include new documentation entries **Changesets:** - `@mastra/core` (minor): ResponseCache processor and caching infrastructure - `@mastra/redis` (patch): Per-key TTL support for `MastraServerCache.set()` **Core Implementation:** - `packages/core/src/processors/processors/response-cache.ts`: ResponseCache processor (252 lines) - `packages/core/src/cache/cache.ts`: MastraCache interface and adapter (59 lines) - `packages/core/src/cache/base.ts`: Updated MastraServerCache signature - `packages/core/src/cache/inmemory.ts`: InMemoryServerCache TTL support - `packages/core/src/processors/index.ts`: New processor types and hooks (122 lines) - `packages/core/src/processors/runner.ts`: ProcessLLMResponse hook execution (85 lines) - `packages/core/src/loop/workflows/agentic-execution/llm-execution-step.ts`: Cache hit handling (124 lines) - `packages/core/src/agent/agent.ts`: Agent-level caching integration (131 lines) - `stores/redis/src/cache.ts`: RedisServerCache TTL support **Tests:** - `packages/core/src/agent/__tests__/agent-response-cache.test.ts`: Comprehensive test suite (341 lines) covering cache hits/misses, TTL overrides, scope isolation, key function behaviors, async key functions, streaming, and server-cache fallback ## Testing Coverage - Cache hits on repeated prompts - Cache opt-out and per-call disabling - Prompt-dependent cache entries - `bust` behavior (skip reads, write on completion) - Per-call cache key overrides and shared keys - Cache key functions with structured inputs and async support - Fallback when key function throws - Failed runs are not cached - Stream caching preserving chunks, `finishReason`, and `usage` - Agent defaults vs per-call overrides - Mastra server cache fallback when no custom cache provided <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: daniel <danielshlomolew@gmail.com> Co-authored-by: Tyler Barnes <tylerdbarnes@gmail.com>
1 parent c50ebc3 commit f180e49

16 files changed

Lines changed: 1750 additions & 62 deletions

File tree

.changeset/legal-snakes-rescue.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@mastra/redis': patch
3+
---
4+
5+
**Per-key TTL support in `RedisCache`**
6+
7+
`RedisCache.set()` now accepts an optional `ttlMs` argument that overrides the configured default TTL for a single entry. Sub-second values are rounded up to one second (Redis `EXPIRE` granularity); a non-positive value persists the entry without expiry.
8+
9+
```ts
10+
const cache = new RedisCache({ url: 'redis://...' });
11+
await cache.set('weather:nyc', payload, 60_000); // expires in 60s
12+
await cache.set('manifest', payload, 0); // persists indefinitely
13+
```

.changeset/loud-dogs-repair.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
'@mastra/core': minor
3+
---
4+
5+
**Added `ResponseCache` input processor**
6+
7+
Cache identical LLM steps to skip the model call and replay a previously cached response. Useful for prompt templates, suggested-prompt buttons, agentic search re-asks, or guardrail LLMs that classify the same input over and over.
8+
9+
Caching is opt-in: register `ResponseCache` explicitly on `inputProcessors`. There is no agent-level option — this keeps the surface small while we collect feedback on the processor API. Per-call overrides flow through `RequestContext`.
10+
11+
```ts
12+
import { Agent } from '@mastra/core/agent';
13+
import { InMemoryServerCache } from '@mastra/core/cache';
14+
import { ResponseCache } from '@mastra/core/processors';
15+
16+
const cache = new InMemoryServerCache();
17+
18+
const agent = new Agent({
19+
name: 'Search Agent',
20+
instructions: 'You answer questions concisely.',
21+
model: 'openai/gpt-5',
22+
inputProcessors: [new ResponseCache({ cache, ttl: 600 })],
23+
});
24+
25+
// First call: cache miss → LLM call
26+
await agent.generate('What is the capital of France?');
27+
28+
// Second identical call: cache hit → no LLM call
29+
await agent.generate('What is the capital of France?');
30+
```
31+
32+
Per-call overrides via `RequestContext`:
33+
34+
```ts
35+
import { ResponseCache } from '@mastra/core/processors';
36+
import { RequestContext } from '@mastra/core/request-context';
37+
38+
// Force a fresh call but still update the cache.
39+
await agent.stream(prompt, {
40+
requestContext: ResponseCache.context({ bust: true }),
41+
});
42+
43+
// Or merge into an existing context.
44+
const ctx = new RequestContext();
45+
ResponseCache.applyContext(ctx, { key: 'custom-key' });
46+
await agent.stream(prompt, { requestContext: ctx });
47+
```
48+
49+
Three fields are overridable per call: `key`, `scope`, `bust`. `cache`, `ttl`, and `agentId` stay on the constructor.
50+
51+
A `key` function receives `{ agentId, scope, model, prompt, stepNumber }` and returns a string (or `Promise<string>`):
52+
53+
```ts
54+
await agent.stream(prompt, {
55+
requestContext: ResponseCache.context({
56+
key: ({ model, prompt }) =>
57+
`qa:${model.modelId}:${JSON.stringify(prompt).slice(-200)}`,
58+
}),
59+
});
60+
```
61+
62+
The cache key is derived from the resolved prompt Mastra is about to send to the model — i.e. _after_ memory loading and earlier input processors have run — so cached entries are tenant-isolated and don't leak context across users with shared prompts but different memory state. Each step in an agentic tool loop is independently cached. By default, the cache scope falls back to `MASTRA_RESOURCE_ID_KEY` from the request context for automatic per-user isolation. Failed runs (errors, tripwire activations) are not cached. See [Response caching](https://mastra.ai/en/docs/agents/response-caching) for details.
63+
64+
Also adds:
65+
66+
- `InMemoryServerCache` (in `@mastra/core/cache`) for local development. `ResponseCache` accepts any `MastraServerCache` directly — use `RedisCache` from `@mastra/redis` for production.
67+
- `MastraServerCache.set()` now accepts an optional `ttlMs` argument so implementations can override the configured default TTL on a per-entry basis. `InMemoryServerCache` and `RedisCache` (in `@mastra/redis`) both honor this.
68+
- New paired processor hooks `processLLMRequest` and `processLLMResponse`. `ProcessLLMRequestResult` may return `{ response }` to short-circuit the LLM call with a cached payload.
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
title: "Response caching | Agents"
3+
description: "Cache identical agent calls to skip the LLM, return responses instantly, and avoid duplicate spend."
4+
packages:
5+
- "@mastra/core"
6+
---
7+
8+
# Response caching
9+
10+
Response caching skips the LLM call and replays a previously cached response when an agent receives an identical request. Use it to drop latency to single-digit milliseconds and avoid paying for repeated calls.
11+
12+
Caching is implemented as the [`ResponseCache`](/reference/processors/response-cache) input processor. There is no agent-level option — to enable caching, register the processor explicitly. This keeps the API surface small while we collect feedback; per-call overrides flow through `RequestContext`.
13+
14+
## When to use response caching
15+
16+
Reach for it when the same request shape repeats across users or sessions, for example prompt templates, suggested-prompt buttons, agentic search re-asks, or guardrail LLMs that classify the same input over and over. Skip it when calls trigger external side effects through tools, since cache hits replay tool calls without re-executing them.
17+
18+
## Quickstart
19+
20+
Add a `ResponseCache` to the agent's `inputProcessors` and pass any `MastraServerCache` as the backend. For development, `InMemoryServerCache` works out of the box:
21+
22+
```typescript title="src/mastra/agents/search-agent.ts"
23+
import { Agent } from '@mastra/core/agent'
24+
import { InMemoryServerCache } from '@mastra/core/cache'
25+
import { ResponseCache } from '@mastra/core/processors'
26+
27+
const cache = new InMemoryServerCache()
28+
29+
export const searchAgent = new Agent({
30+
name: 'Search Agent',
31+
instructions: 'You answer questions concisely.',
32+
model: 'openai/gpt-5',
33+
inputProcessors: [new ResponseCache({ cache, ttl: 600 })], // 10 minutes
34+
})
35+
```
36+
37+
The first call runs the LLM normally and writes the response to the cache. Subsequent calls with an identical resolved prompt return the cached response without invoking the LLM.
38+
39+
## Per-call overrides via RequestContext
40+
41+
Per-call config flows through `RequestContext`. Use `ResponseCache.context()` to build a fresh context, or `ResponseCache.applyContext()` to merge into one you already have:
42+
43+
```typescript title="src/example.ts"
44+
import { ResponseCache } from '@mastra/core/processors'
45+
import { RequestContext } from '@mastra/core/request-context'
46+
47+
// Fresh context with the override
48+
await agent.stream('hello', {
49+
requestContext: ResponseCache.context({ key: 'custom-key', bust: true }),
50+
})
51+
52+
// Or merge into an existing context
53+
const ctx = new RequestContext()
54+
ctx.set('caller-meta', { userId: 'u-123' })
55+
ResponseCache.applyContext(ctx, { bust: true })
56+
await agent.stream('hello', { requestContext: ctx })
57+
```
58+
59+
Three fields are overridable per call:
60+
61+
- `key` — string or function. Overrides the auto-derived cache key for this request only.
62+
- `scope` — string or `null`. Overrides the tenant/user scope for this request only. `null` opts out of scoping.
63+
- `bust` — boolean. Skips the cache read but still writes on completion (useful for "force refresh" buttons).
64+
65+
`cache`, `ttl`, and `agentId` stay on the constructor — they are instance-level concerns and not safe to vary per call.
66+
67+
## Tenant scoping
68+
69+
By default, `ResponseCache` looks up `MASTRA_RESOURCE_ID_KEY` on the request context and uses it as the cache scope. This means an agent that already populates the resource id (e.g. via memory) gets per-user isolation automatically — two users never see each other's cached responses.
70+
71+
Override explicitly when you need a different scope:
72+
73+
```typescript title="src/mastra/agents/scoped-agent.ts"
74+
new Agent({
75+
// ...
76+
inputProcessors: [
77+
new ResponseCache({
78+
cache,
79+
scope: 'org-123', // explicit tenant scope
80+
}),
81+
],
82+
})
83+
```
84+
85+
Pass `scope: null` to deliberately share entries across all callers — only use this for known-public, non-personalized content.
86+
87+
## Custom cache backend
88+
89+
`ResponseCache` accepts any `MastraServerCache`. For production, use `RedisCache` from `@mastra/redis`:
90+
91+
```typescript title="src/mastra/agents/cached-agent.ts"
92+
import { Agent } from '@mastra/core/agent'
93+
import { ResponseCache } from '@mastra/core/processors'
94+
import { RedisCache } from '@mastra/redis'
95+
96+
const cache = new RedisCache({ url: process.env.REDIS_URL })
97+
98+
export const agent = new Agent({
99+
name: 'Cached Agent',
100+
instructions: '...',
101+
model: 'openai/gpt-5',
102+
inputProcessors: [new ResponseCache({ cache })],
103+
})
104+
```
105+
106+
For a custom backend, extend `MastraServerCache` and implement its abstract methods (the processor only calls `get` and `set`).
107+
108+
## How caching is implemented
109+
110+
`ResponseCache` hooks into `processLLMRequest` (cache lookup, short-circuits on hit) and `processLLMResponse` (cache write on completion). Both run inside the agentic loop _after_ memory has loaded and earlier input processors have transformed the prompt.
111+
112+
This means the cache key is derived from the resolved `LanguageModelV2Prompt` Mastra is about to send to the model — i.e. _after_ memory has loaded and earlier input processors have run — and each step in an agentic tool loop is independently cached.
113+
114+
## What's in the cache key
115+
116+
When you don't supply `key`, the processor derives one deterministically from the inputs that change the LLM's response at this step: `agentId`, `stepNumber` (so each step in a tool loop has its own cache entry), `scope`, model identity (`provider`, `modelId`, spec version), and the resolved `prompt` (post-memory + post-processors). Any change to these inputs automatically invalidates the cache.
117+
118+
### Customize the cache key
119+
120+
Pass `key` as a function on the constructor or per-call to derive your own cache key from any subset of those inputs. The function receives the same inputs the deterministic hash would have consumed and returns a string (or a `Promise<string>`):
121+
122+
```typescript title="src/example.ts"
123+
import { ResponseCache, buildResponseCacheKey } from '@mastra/core/processors'
124+
125+
await agent.stream(input, {
126+
requestContext: ResponseCache.context({
127+
// Cache only on the model id and the resolved prompt tail — ignore
128+
// step number, scope, etc.
129+
key: ({ model, prompt }) => `qa:${model.modelId}:${JSON.stringify(prompt).slice(-200)}`,
130+
}),
131+
})
132+
133+
// Or reuse the deterministic helper while overriding individual fields:
134+
await agent.stream(input, {
135+
requestContext: ResponseCache.context({
136+
key: inputs => buildResponseCacheKey({ ...inputs, scope: 'global' }),
137+
}),
138+
})
139+
```
140+
141+
If the function throws, the processor falls back to the default key derivation so the call still benefits from caching.
142+
143+
## How cache hits work
144+
145+
When the processor finds a cache hit, it short-circuits the LLM call by returning the cached chunks from `processLLMRequest`. The agentic loop synthesizes a stream from those chunks instead of calling the model. `agent.generate()` collects them into a `FullOutput`; `agent.stream()` returns a `MastraModelOutput` whose chunks come from the cached buffer, so consumers iterating `fullStream` or awaiting `text`, `usage`, and `finishReason` see the cached values.
146+
147+
Cache writes happen after the response completes. Failed runs (errors, tripwire activations) are not cached, so the next call retries cleanly.
148+
149+
## Related
150+
151+
- [`ResponseCache` reference](/reference/processors/response-cache)
152+
- [Processors](/docs/agents/processors)
153+
- [Guardrails](/docs/agents/guardrails)
154+
- [Agent.stream()](/reference/streaming/agents/stream)
155+
- [Agent.generate()](/reference/agents/generate)

docs/src/content/en/docs/sidebars.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ const sidebars = {
117117
id: 'agents/guardrails',
118118
label: 'Guardrails',
119119
},
120+
{
121+
type: 'doc',
122+
id: 'agents/response-caching',
123+
label: 'Response caching',
124+
},
120125
{
121126
type: 'doc',
122127
id: 'agents/agent-approval',

0 commit comments

Comments
 (0)