mattpocock / mattpocock/evalite
Bug: `wrapAISDKModel({ caching: false })` is silently ignored when tracing is enabled
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.7k
- Forks
- 102
- PR merge metrics
- No merged PRs in 30d
Description
Description
wrapAISDKModel(model, { caching: false }) does not actually disable caching. When caching: false is set, the wrapper still reads from and writes to the evalite cache server — the flag is silently ignored as long as tracing is enabled (which it is by default).
This is misleading because the API suggests opt-out is supported, but the only way to truly bypass the cache today is to also set tracing: false (which makes the wrapper return the unwrapped model) or to delete node_modules/.evalite/ between runs.
The impact is that users who want fresh, uncached results (e.g. for cross-model baselines, or to measure sampling variability across trialCount > 1) silently get cached responses. Aggregate cost / latency / token-usage stats are also wrong: cached trials report tokens.input = 0 and latency < 100ms, dragging the aggregates down without the user knowing.
Version
evalite@1.0.0-beta.16- Bug also present at the tip of branch
v1(verified by readingpackages/evalite/src/ai-sdk.ts, same code path)
Reproduction
Minimal eval file:
// repro.eval.ts
import { evalite } from "evalite";
import { wrapAISDKModel } from "evalite/ai-sdk";
import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
evalite("caching-false-repro", {
data: () => [{ input: "say hi", expected: "hi" }],
task: async (input) => {
const model = wrapAISDKModel(openai("gpt-4o-mini"), { caching: false });
const { text, usage } = await generateText({ model, prompt: input });
console.log("usage:", usage);
return text;
},
scorers: [() => ({ score: 1, name: "noop" })],
});
Run twice in a row:
npx evalite run repro.eval.ts # first run — real API call
npx evalite run repro.eval.ts # second run — should be a real call with caching:false
Expected vs. Actual
Expected: both runs hit the API. usage.inputTokens > 0 on both, similar latency.
Actual: second run returns the cached response. usage.inputTokens === 0, latency under 100ms, no API call made.
Root cause
In packages/evalite/src/ai-sdk.ts, wrapAISDKModel reads the enableCaching flag at the top:
const enableCaching = options?.caching ?? true;
…but inside wrapGenerate and wrapStream, the cache fetch and store are gated only on the existence of cacheContext, not on enableCaching:
// wrapGenerate
const cacheContext = getCacheContext();
if (cacheContext) { // ← enableCaching not checked
// fetch from cache server
}
// ...
if (!result) {
result = await opts.doGenerate();
if (cacheContext) { // ← enableCaching not checked
// store in cache server
}
}
Same pattern in wrapStream. The flag is parsed but only used by the early-return if (!enableCaching && !enableTracing) return model; — meaning caching: false is only respected when tracing is also disabled.
Suggested fix
Gate the four cache fetch/store blocks on enableCaching in addition to cacheContext:
-if (cacheContext) {
+if (cacheContext && enableCaching) {
Four sites total (2 in wrapGenerate, 2 in wrapStream). Tracing path is unaffected. Diff is ~4 lines. I have a working patch validated locally — happy to send a PR if you'd like.
Additional note (potential follow-up, not part of this issue)
The CLI flag --no-cache and the cache: false config option set cacheEnabled = false in run-evalite.ts, but that flag only short-circuits the reportCacheHit callback in evalite.ts:240. The cacheContext object itself is still posted into cacheContextLocalStorage, so wrapGenerate still fetches/stores. Fixing the local caching: false flag (this issue) doesn't fix --no-cache — that would need either propagating cacheEnabled into the context, or skipping the cacheContextLocalStorage.enterWith() call entirely when caching is disabled. Happy to file a separate issue if you'd prefer to track that work distinctly.
Related
- #354 — similar pattern (cache directory created regardless of
cacheEnabled), fixed by guarding the relevant code path on the resolved flag - #317 — original PR introducing the AI SDK caching layer
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/evalite/src/ai-sdk.ts, focusing on wrapGenerate and wrapStream and the four cache fetch/store blocks identified in the issue. Run the supplied repro.eval.ts twice to confirm the second run uses the cache, then verify that both runs make API calls with caching:false while tracing remains enabled and usage is reported for each run.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- ai, backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100