feat: opt-in exact-match result cache for search and extract (zero-cost repeat calls) - #89
Open
brainsparker wants to merge 1 commit into
Open
feat: opt-in exact-match result cache for search and extract (zero-cost repeat calls)#89brainsparker wants to merge 1 commit into
brainsparker wants to merge 1 commit into
Conversation
Repeated identical frugal__search / frugal__extract calls inside the TTL are answered from process memory at zero cost, marked cached: true with cache_age_ms. frugal__execute shares entries with the direct tools; explicit cheap/premium priority bypasses the cache; browse is never cached. New internal/cache package (bounded TTL + LRU, nil-safe), cache: config section, docs, and tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
frugal | e2e64e4 | Commit Preview URL Branch Preview URL |
Aug 29 2026, 01:14 PM |
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 this does
Adds an opt-in, exact-match result cache to the routed read tools. When enabled, a repeated identical call inside the TTL is answered from process memory instead of a provider, at zero cost:
Behavior:
cached: true,cache_age_ms,cost_usd: 0, withprovider_usedstill naming the provider that produced the original result.frugal__executeshares entries with the direct tools.frugal__execute("search python docs")is a hit afterfrugal__search("python docs")and vice versa. An explicitpriority: cheaporpremiumbypasses the cache (read and write): the caller asked for a specific routing outcome, and a memoized answer from a different chain would misreport it.frugal__browseis never cached. Rendering a page is exactly the case where the caller wants the live DOM.Why this, why now
Agents repeat themselves. Retry loops, sibling subagents issuing the same query, and follow-up turns re-fetching the same page are the documented waste pattern of 2026 agent stacks:
frugal already owns the interception point (every search / extract / browse call routes through it), already prices every call, and already ships the receipt that proves savings. A cache that returns
cost_usd: 0on hits is the most on-brand feature this repo can ship. The roadmap lists a Phase 3 semantic cache; this lands the deterministic exact-match layer that work builds on, and the package comment ininternal/mcp/tools/search.gohas reservedinternal/cachefor this since the tool layer landed.Runner-up candidates considered today: an MCP 2026-07-28 spec migration for frugal (timely after the stateless-core release, but a larger lift gated on go-sdk support, and better done deliberately), and further PromptLens / you.md features (both repos have multiple unreviewed daily PRs stacked up, including near-duplicates in PromptLens; adding more before those are triaged creates review debt, not value).
Implementation notes
internal/cachepackage: bounded TTL + LRU store with a nil-safe API (mirrors therouting.Guardconvention, so call sites need no conditionals), an injectable clock for tests, and hit / miss / saved-USD counters exposed viaSnapshot()for a futurefrugal statsline.cache.SearchKeyandcache.ExtractKeynormalize pins, trim queries, and treat formats as a case-insensitive set. Query case is preserved: exact match means exact.ToolOption(WithResultCache) consumed by search, extract, and execute registration. Execute stores and reads through the same keys insidedispatchIntent, and only on the clean single-capability paths: the extract-to-browse fall-forward result is never cached.cache:section with validation (negativemax_entriesfails the load; invalid TTLs warn at wiring time and fall back to defaults, same asrouting.cooldown).config/models.yaml.Test status
internal/cache(TTL expiry, LRU eviction, age reporting, nil-safety, key canonicalization), 5 integration tests ininternal/mcp/toolsdriving real in-memory MCP client sessions (second call hits, distinct args miss, disabled cache never labels, execute shares entries with search in both directions, premium priority bypasses), 3 config parse tests.go build ./...,go vet ./..., andgo test ./...all green locally on Go 1.25.