LLM response cache for AgenticStack: provider wrapper with in-memory LRU and SQLite backends
Project description
agenticstack-cache
LLM response cache for AgenticStack: serve repeated prompts from a local store instead of paying for the same completion twice.
Why a provider wrapper, not a hook plugin
AgenticStack's hook points (BEFORE_LLM_CALL / AFTER_LLM_CALL) can
observe and transform requests and responses, but they cannot skip
the provider call — a filter chain always falls through to the actual
generate(). A cache's whole job is to short-circuit that call, so the
cache is implemented as a provider wrapper: CachedProvider is
itself an LLMProvider that wraps any other provider and answers hits
locally. It drops in anywhere a provider does, including
Agent(provider=CachedProvider(inner)).
Install
pip install agenticstack-cache
Use
from agenticstack_cache import CachedProvider, InMemoryCache, SQLiteCache
# In-memory LRU (per-process)
provider = CachedProvider(inner, cache=InMemoryCache(max_entries=512))
# Persistent across processes (stdlib sqlite3)
provider = CachedProvider(inner, cache=SQLiteCache("cache.db", ttl=3600))
response = await provider.generate(messages) # miss -> calls inner
response = await provider.generate(messages) # hit -> no inner call
provider.hits, provider.misses # -> 1, 1
provider.stats # -> {"hits": 1, "misses": 1}
With an agent:
from agenticstack import Agent
agent = Agent(name="Helper", provider=CachedProvider(inner))
Cache key
sha256 over the request's semantic identity:
- inner provider's model name
- message
(role, content)pairs - tool names offered
- sorted extra kwargs
Anything that changes the request (different history, different temperature kwarg, different tools) is a different key.
What gets cached
Only pure-text responses. Responses carrying tool_calls are
passed through uncached — tool-call turns are stateful (their IDs must
pair with fresh tool-result messages), so replaying them would corrupt
conversations. Cached entries store content, model, and
finish_reason only; a hit reconstructs an LLMResponse with zero
token usage (no tokens were spent) and raw_response["cached"] = True.
Backends
| Backend | Storage | Eviction | TTL |
|---|---|---|---|
InMemoryCache(max_entries=1024, ttl=None) |
process dict | LRU beyond max_entries |
optional seconds |
SQLiteCache(path, ttl=None) |
SQLite file | none (unbounded) | optional seconds |
ttl=None (the default) means entries never expire. Expired entries
read as misses and are purged on access.
Plugin form
CachePlugin participates in the plugin runtime (manifest, lifecycle)
but registers no capabilities — a cache must wrap a specific provider
instance, so the primary API is the explicit wrapper above.
Permissions
filesystem — the SQLite backend writes a local database file.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agenticstack_cache-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agenticstack_cache-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
baa364e7652db82107abd5961118ddee69fd79d9dca346102e7a212d0790beb7
|
|
| MD5 |
44f554a8c44b7650eee461d268993281
|
|
| BLAKE2b-256 |
4b2a22953881e74448cbcfadb0fc1a038e468d5c071f01523bf398ad6afba75d
|