Observed since 20 Oct, 22:21 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
LLM response cache for AgenticStack: provider wrapper with in-memory LRU and SQLite backends
LLM response cache for AgenticStack: serve repeated prompts from a local store instead of paying for the same completion twice.
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)).
pip install agenticstack-cache
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))
sha256 over the request's semantic identity:
(role, content) pairsAnything that changes the request (different history, different temperature kwarg, different tools) is a different key.
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.
| 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.
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.
filesystem — the SQLite backend writes a local database file.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.0 | 2 Aug 2026 | 1 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 22:21 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:5a2be8c27b0c4d10a4667a64c9c2f62e1952b0bfc310d9a5c00a42ad4b3c87c3