pi-mem0-cache
Pi extension that caches mem0 reads and falls back to a local memory store when the mem0 API is unavailable or over quota
Package details
Install pi-mem0-cache from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-mem0-cache- Package
pi-mem0-cache- Version
0.4.0- Published
- Aug 31, 2026
- Downloads
- 323/mo · 25/wk
- Author
- the_real_artrix
- License
- MIT
- Types
- extension
- Size
- 35.2 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-mem0-cache
A pi extension that adds a read cache and offline fallback for mem0 — built for the moment your mem0 quota runs out mid-month and mem0_memory starts returning Usage quota exceeded on every call.
What it does
The extension wraps globalThis.fetch inside the pi process and transparently intercepts traffic to api.mem0.ai:
Reads (search, get_all, get, history)
- Successful responses are cached to disk with a 24h TTL. Identical requests within the TTL never touch the network — this alone cuts most repeat-query quota burn, since agents re-run similar memory searches constantly.
- A freshness gate caps remote reads at one per hour (
MEM0_CACHE_REMOTE_READ_INTERVAL_MS, default 1h). Within the window, searches and listings are answered from the cache/local store./mem0-cache refreshclears the gate explicitly. - A 429 breaker: when mem0 answers
429 Usage quota exceeded, theretry-afterhint arms a breaker; while armed, reads skip the network entirely. The error body (which names the exhausted quota, e.g.SEARCH) is included in the fallback log line. - If the API fails (quota exhausted, 4xx/5xx, network down):
- A stale cache entry is served if one exists, otherwise
- A local memory store answers the query (keyword-overlap search over every memory ever observed plus all local writes).
Writes (add, update, delete, delete_all)
- Tried against the real API first. On success, behavior is unchanged.
- On failure, the mutation is applied to the local store (with the original request payload, scope params included) and a synthetic success response is returned, so no memory is lost while mem0 is unavailable.
Auto-sync
- The moment any mem0 API call succeeds again (quota refilled, network back), all pending local memories are uploaded in the background via
/v3/memories/add/— replayed with their original scope payload (user_id,app_id, …), so they land in the right scope. - Uploaded memories are marked
observed; local copies that were deleted before ever syncing are purged. - Auth headers are captured transparently from the mem0 client's own requests — no configuration needed.
- On failure mid-sync, the runner backs off for 1h before retrying. Force an immediate attempt with
/mem0-cache sync.
All memories seen in any API response are harvested into the local corpus, so the fallback search gets richer the longer you use it.
Upstream bug workaround (mem0ai/mem0#6168)
The pi mem0 plugin's global scope is asymmetric: writes store app_id: null, reads filter app_id: "*", and mem0's * wildcard matches only non-null values — so global memories are permanently unreachable. This extension normalizes read requests before they hit the API: entity filters (user_id/agent_id/app_id/run_id) whose value is "*" are dropped, restoring the intended "unconstrained" semantics. Normalization happens before cache-key computation, so wildcard and non-wildcard variants of the same read share one cache entry.
Install
Add to ~/.pi/agent/settings.json:
{
"packages": [
"npm:pi-mem0-cache"
]
}
or from the git source directly: "git:https://github.com/ArtrixTech/pi-mem0-cache". Then restart pi — no configuration needed. It works alongside @mem0/pi-agent-plugin (or any mem0 client using global fetch).
Usage
/mem0-cache stats # cache size, pending local memories, counters, last sync result
/mem0-cache sync # force-upload pending local memories to mem0 now
/mem0-cache refresh # clear the freshness gate + 429 breaker; next read hits the API
/mem0-cache clear # wipe the read cache (keep local memories)
/mem0-cache clear-all # wipe everything
/mem0-cache path # show store location
Storage
Everything lives in one JSON file: ~/.pi/agent/mem0-cache.json (override with MEM0_CACHE_PATH). Human-readable; safe to inspect or hand-edit while pi is stopped.
TTL defaults to 24h; override with MEM0_CACHE_TTL_MS. The freshness gate defaults to 1h; override with MEM0_CACHE_REMOTE_READ_INTERVAL_MS.
Design notes
- Interception layer: pi's
tool_callhook can only block or mutate tool arguments — it cannot inject a synthetic successful result. The mem0 SDK resolves globalfetchat call time, so wrappingfetchis the cleanest transparent seam: no patchingnode_modules, no local proxy process, survives plugin upgrades. - TTL over write-invalidation: mem0 search is semantic; results drift as memories are added. A 24h TTL is a simple, predictable freshness bound.
- Global freshness gate over per-query caching alone: measured hit rate of exact-match query caching on real agent traffic was ~9% (queries rarely repeat verbatim); the 1h gate is what actually bounds retrieval-quota burn.
- Auto-sync on first success: the trigger for uploading pending local memories is any successful mem0 response — the earliest possible proof that quota/connectivity is back. Replays use the original add payload so scoping is preserved. Sync calls go through the unwrapped fetch, never re-entering the interceptor.
Limitations
- Local fallback search is keyword overlap, not semantic. It's a degradation ladder, not a mem0 replacement.
- The freshness gate and 429 breaker cover only locally-synthesizable reads (
search,get_all,get);historyand unknown reads stay on the network path. - A failed non-read (e.g.
history) with no cache and no local match returns the original API error. - Single-process assumption: concurrent pi instances share the JSON store via last-writer-wins debounced writes.
- Sync uploads are plain
adds — if a memory was also added to mem0 by another client during the outage, a duplicate may result.
Development
npm install
npm run typecheck
npm test
License
MIT