MemoryCache & configuration¶
MemoryCache¶
memory_reuse.core.MemoryCache ¶
High-level cache client for AI agent workloads.
MemoryCache is the primary public interface. It wires together a
storage backend, the exact-match LLM cache, the TTL-backed tool cache,
and statistics tracking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
CacheConfig | None
|
Cache configuration. When omitted a default
:class: |
None
|
**kwargs
|
Any
|
Keyword arguments forwarded to :class: |
{}
|
Example::
from memory_reuse import MemoryCache, CacheConfig
cache = MemoryCache(CacheConfig(backend="memory", default_ttl=600))
cache.set_context(user_id="alice")
result = await cache.exact.get(["my-prompt"], scope="user",
scope_id="alice")
stats
property
¶
Current cache statistics snapshot.
Returns:
| Name | Type | Description |
|---|---|---|
A |
CacheStats
|
class: |
Example::
print(cache.stats.hit_rate)
analytics
property
¶
Current savings-analytics snapshot (Phase 5).
Returns an immutable :class:~memory_reuse.analytics.AnalyticsSnapshot
with the current hit rate (read from the same counters as
:attr:stats), plus tokens, cost, and latency saved. On a fresh cache —
and whenever enable_stats is False — the snapshot is fully
zeroed. Adding this accessor does not change :attr:stats or
:class:~memory_reuse.stats.CacheStats.
Returns:
| Name | Type | Description |
|---|---|---|
An |
AnalyticsSnapshot
|
class: |
Example::
print(cache.analytics.tokens_saved, cache.analytics.cost_saved)
from_env
classmethod
¶
Create a :class:MemoryCache from MEMORY_REUSE_* environment variables.
See :meth:~memory_reuse.config.CacheConfig.from_env for the full
list of recognised variables.
Returns:
| Type | Description |
|---|---|
MemoryCache
|
A configured :class: |
Example::
import os
os.environ["MEMORY_REUSE_BACKEND"] = "redis"
os.environ["MEMORY_REUSE_REDIS_URL"] = "redis://localhost:6379/0"
cache = MemoryCache.from_env()
set_context ¶
set_context(*, user_id: str | None = None, session_id: str | None = None, tenant_id: str | None = None) -> None
Set the user/session context for scoped cache keys.
Context values are used by the LangGraph decorators when no explicit
scope ID is passed. They do not affect calls that provide their
own scope_id argument.
The context is stored in a :class:contextvars.ContextVar, so a value
set in one asyncio task (or thread) is not visible to a concurrently
running task. This prevents one request's user_id from leaking into
another request handled on the same shared :class:MemoryCache. Only
the arguments you pass are updated; others are preserved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
user_id
|
str | None
|
Identifier for the current user. |
None
|
session_id
|
str | None
|
Identifier for the current session. |
None
|
tenant_id
|
str | None
|
Identifier for the current tenant (future use). |
None
|
Example::
cache.set_context(user_id="alice", session_id="sess-123")
clear_context ¶
Reset all context values to None for the current context.
Call this between requests in a shared server context to avoid
leaking one user's context into another request. Because the context
lives in a :class:contextvars.ContextVar, this only clears the
calling task's view.
context ¶
context(*, user_id: str | None = None, session_id: str | None = None, tenant_id: str | None = None) -> Iterator[None]
Temporarily set the request context, restoring it on exit.
Unlike :meth:set_context, this scopes the change to a with block
and always restores the previous context afterwards — including when the
body raises — so nested or concurrent request handling cannot leave a
stale user_id behind.
Example::
with cache.context(user_id="alice"):
await do_work() # sees user_id="alice"
# previous context restored here, even on exception
record_hit_event ¶
Record the savings a cache hit avoided (Phase 5).
Attribution is caller-supplied — the core caches do not know an
operation's token/cost/latency — so integrations or application code
call this on a hit to feed the analytics layer. Recording is best-effort
and never fatal, and is a no-op when enable_stats is False.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
CacheHitEvent
|
The :class: |
required |
Example::
cache.record_hit_event(CacheHitEvent(
tokens_in=1200, tokens_out=400, latency_saved=0.4,
operation="search_confluence"))
lookup
async
¶
lookup(key_parts: list, query_text: str, *, scope: str, scope_id: str | None, exact_only: bool = False, threshold: float | None = None, serializer: Callable[[Any], Any] | None = None, deserializer: Callable[[Any], Any] | None = None, operation: str | None = None) -> Any | None
Look up a cached value, trying the exact cache before the semantic cache.
The combined flow tries the fastest, cheapest path first: an exact hash-match is attempted before any embedding is computed. Only when the exact cache misses — and semantic caching is enabled and not disabled for this call — is the query embedded and matched by similarity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key_parts
|
list
|
Ordered list of values identifying the exact-cache entry. |
required |
query_text
|
str
|
The natural-language query used for semantic matching. |
required |
scope
|
str
|
Cache scope — |
required |
scope_id
|
str | None
|
User or session identifier for non-global scopes. |
required |
exact_only
|
bool
|
When |
False
|
threshold
|
float | None
|
Optional per-call similarity threshold overriding
:attr: |
None
|
serializer
|
Callable[[Any], Any] | None
|
Optional per-call serializer override used when promoting
a semantic hit to the exact cache. Falls back to the configured
serializer when |
None
|
deserializer
|
Callable[[Any], Any] | None
|
Optional per-call deserializer override applied to the
exact-cache read. Falls back to the configured deserializer when
|
None
|
operation
|
str | None
|
Optional operation identifier (tool/node/graph name) that
isolates the semantic search to that operation's own
similarity space. The decorators pass their stable operation
identity so a reworded match can only come from the same
tool/node/graph, never an unrelated one with similar query text.
When |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Any | None
|
The cached value on an exact or semantic hit, or |
|
Note |
Any | None
|
a cache miss and a stored |
Any | None
|
here. The decorators use the internal :meth: |
|
Any | None
|
a sentinel on a miss, so a cached false-like value is served as a hit |
|
Any | None
|
and not recomputed. |
Raises:
| Type | Description |
|---|---|
ScopeViolationError
|
If |
store
async
¶
store(key_parts: list, query_text: str, value: Any, *, scope: str, scope_id: str | None, ttl: int | None = None, exact_only: bool = False, serializer: Callable[[Any], Any] | None = None, operation: str | None = None) -> None
Store a value in the exact cache and, when enabled, the semantic cache.
The exact-match entry is always written so a subsequent identical request hits the faster exact path. When semantic caching is enabled and not disabled for this call, the query's embedding is also stored so reworded but equivalent requests can match later.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key_parts
|
list
|
Ordered list of values identifying the exact-cache entry. |
required |
query_text
|
str
|
The natural-language query whose embedding is stored. |
required |
value
|
Any
|
The value to cache. Must be JSON-serialisable. |
required |
scope
|
str
|
Cache scope — |
required |
scope_id
|
str | None
|
User or session identifier for non-global scopes. |
required |
ttl
|
int | None
|
Time-to-live in seconds. Falls back to
:attr: |
None
|
exact_only
|
bool
|
When |
False
|
serializer
|
Callable[[Any], Any] | None
|
Optional per-call serializer override. Falls back to the
configured serializer when |
None
|
operation
|
str | None
|
Optional operation identifier isolating the stored
semantic vector to that operation's namespace so it can only
be matched by a later lookup for the same operation. Must match
the |
None
|
Raises:
| Type | Description |
|---|---|
ScopeViolationError
|
If |
get_or_compute
async
¶
get_or_compute(key_parts: list, compute: Callable[[], Any], *, scope: str, scope_id: str | None, ttl: int | None = None, query_text: str = '', exact_only: bool = False) -> Any
Look up a value, computing it at most once across concurrent callers.
On a hit the stored value is returned without running compute. On a
miss, when single_flight is enabled, concurrent calls for the same
key coalesce so compute runs exactly once and its result is shared
with every waiter (Phase 6, Req 1); otherwise this is a plain
miss→compute→store. The computed value is stored via the combined
:meth:store flow (exact, plus semantic when query_text is supplied
and semantic is enabled).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key_parts
|
list
|
Ordered list of values identifying the entry. |
required |
compute
|
Callable[[], Any]
|
A zero-argument callable (sync or async) producing the value on a miss. |
required |
scope
|
str
|
Cache scope — |
required |
scope_id
|
str | None
|
User or session identifier for non-global scopes. |
required |
ttl
|
int | None
|
Time-to-live in seconds for the stored result. |
None
|
query_text
|
str
|
Optional natural-language query enabling semantic storage of the computed value. |
''
|
exact_only
|
bool
|
When |
False
|
Returns:
| Type | Description |
|---|---|
Any
|
The cached value on a hit, or the freshly computed value on a miss. |
Raises:
| Type | Description |
|---|---|
ScopeViolationError
|
If |
record_operation ¶
record_operation(operation: str, *, hit: bool, cost_saved: float | None = None, latency_saved: float | None = None, input_hash: str | None = None, cached: bool = False, volatile: bool = False) -> None
Record a per-operation observation for the effectiveness analyzer (Phase 6).
Best-effort and never fatal; a no-op when enable_stats is False.
The cached and volatile flags are caller-supplied signals the
recommendation rules use — the analyzer cannot infer a tool's caching
status or data-freshness on its own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operation
|
str
|
Label identifying the operation (tool/node/graph name). |
required |
hit
|
bool
|
|
required |
cost_saved
|
float | None
|
Optional attributed cost for this observation. |
None
|
latency_saved
|
float | None
|
Optional attributed latency (seconds). |
None
|
input_hash
|
str | None
|
Optional stable fingerprint of the inputs, used to estimate the repeat ratio. |
None
|
cached
|
bool
|
Whether the operation is already configured to be cached. |
False
|
volatile
|
bool
|
Whether the operation returns rapidly-changing data. |
False
|
analyze ¶
Return per-operation statistics and advisory cache recommendations (Phase 6).
The recommendations are advisory only — reading them never changes cache behaviour. On a cache with no recorded observations this returns an empty report rather than raising.
Returns:
| Name | Type | Description |
|---|---|---|
An |
EffectivenessReport
|
class: |
wrap_graph ¶
wrap_graph(graph: Any, *, semantic: bool = False, similarity_threshold: float | None = None, ttl: int | None = None, scope: Literal['global', 'user', 'session'] | None = None, key_fields: list[str] | None = None, exact_only: bool = False, graph_id: str | None = None, serialize_messages: str | bool = 'auto') -> CachedGraph
Wrap a compiled LangGraph graph so entire runs can be served from cache.
On invoke the wrapper checks a graph-level cache keyed on the initial input state; on a hit it returns the stored final result without running any node, and on a miss it runs the real graph and stores the result.
LangGraph is imported lazily here; the core package imports fine without it (Req 7).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Any
|
A compiled LangGraph graph exposing |
required |
semantic
|
bool
|
Enable semantic (similarity) matching for the whole-run
key. Requires |
False
|
similarity_threshold
|
float | None
|
Per-wrapper threshold overriding the config default for graph-level semantic lookups. |
None
|
ttl
|
int | None
|
TTL for stored final results; falls back to
:attr: |
None
|
scope
|
Literal['global', 'user', 'session'] | None
|
|
None
|
key_fields
|
list[str] | None
|
Subset of the initial input state used to derive the key and semantic query text. |
None
|
exact_only
|
bool
|
Force exact-only matching, never consulting the semantic cache regardless of config. |
False
|
graph_id
|
str | None
|
Stable identifier for this graph included in the key so different wrapped graphs never collide. Defaults to a value derived from the graph object. |
None
|
serialize_messages
|
str | bool
|
Controls automatic faithful serialization of the
wrapped graph's results so a cache hit replays real LangChain
message objects (with |
'auto'
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
CachedGraph
|
class: |
Raises:
| Type | Description |
|---|---|
BackendNotAvailableError
|
If LangGraph is not installed, naming the extra to install. |
Warning
Graph-level caching replays a full stored result and is unsuitable
for runs whose side effects must occur on every invocation. Use
bypass_cache=True / no_store=True per call, or leave such
graphs unwrapped.
invalidate_node
async
¶
invalidate_node(node: Callable | str, state: Any = None, *, scope: Literal['global', 'user', 'session'] = 'global', scope_id: str | None = None, key_fields: list[str] | None = None) -> None
Invalidate a cached node output for a specific node and input state.
Reconstructs the same key parts cached_node uses
([node_qualname, key_data]) and deletes the matching exact entry via
:meth:~memory_reuse.cache.exact.ExactCache.invalidate. Completes
without error when no entry exists (Req 13.6).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
Callable | str
|
The decorated node callable (its |
required |
state
|
Any
|
The node input state used to reconstruct the key. Uses the
full state, or the |
None
|
scope
|
Literal['global', 'user', 'session']
|
|
'global'
|
scope_id
|
str | None
|
Explicit scope identifier. When omitted for a non-global
scope it is resolved from |
None
|
key_fields
|
list[str] | None
|
Subset of the input state forming the key, matching the
node's |
None
|
Raises:
| Type | Description |
|---|---|
ScopeViolationError
|
If a non-global scope cannot be resolved. |
ping
async
¶
Check whether the backend is reachable.
Returns:
| Type | Description |
|---|---|
bool
|
|
close
async
¶
Close backend connections gracefully.
Should be called on application shutdown to prevent resource leaks, especially when using the Redis backend.
Example::
async with asyncio.timeout(5):
await cache.close()
flush
async
¶
Flush this cache's entries from the backend (and vector index).
On the Redis backend this clears only keys under the configured
key_prefix (and only vectors under the vector index's own prefix) via
a batched SCAN sweep, so unrelated application keys and other cache
instances sharing the database are never deleted. On the in-memory
backend it clears this instance's store.
When semantic caching is enabled the backing vector index is flushed too, so a flush does not leave orphaned semantic entries behind.
Warning
This irreversibly removes every cached entry for this cache. Use only in development or test environments.
CacheConfig¶
memory_reuse.config.CacheConfig
dataclass
¶
CacheConfig(backend: Literal['memory', 'redis', 'agentcore'] = 'memory', redis_url: str | None = None, default_ttl: int | None = 3600, default_scope: Literal['global', 'user', 'session'] = 'global', key_prefix: str = 'memreuse', max_key_size: int = 512, enable_stats: bool = True, semantic_enabled: bool = False, similarity_threshold: float = 0.95, embedding_provider: Literal['openai', 'local', 'litellm'] | None = None, embedding_model: str | None = None, max_vectors_per_namespace: int = 10000, store_exact_on_semantic_hit: bool = True, extract_answer: bool = False, extract_min_similarity: float = 0.5, agentcore_region: str | None = None, agentcore_memory_id: str | None = None, pricing: PricingConfig | None = None, cache_version: str | None = None, single_flight: bool = False, distributed_lock: bool = False, max_tracked_operations: int = 1000, serializer: Callable[[Any], Any] | None = None, deserializer: Callable[[Any], Any] | None = None)
Configuration for the MemoryCache.
All values can be overridden via environment variables when using
:meth:MemoryCache.from_env.
Attributes:
| Name | Type | Description |
|---|---|---|
backend |
Literal['memory', 'redis', 'agentcore']
|
Storage backend to use. |
redis_url |
str | None
|
Connection URL for the Redis backend. Should be supplied
via the |
agentcore_region |
str | None
|
AWS region hosting the AgentCore store. Used only
when |
agentcore_memory_id |
str | None
|
AgentCore memory / store resource identifier.
Used only when |
default_ttl |
int | None
|
Default time-to-live in seconds for cached entries.
|
default_scope |
Literal['global', 'user', 'session']
|
Default cache scope applied when no explicit scope is passed to cache operations. |
key_prefix |
str
|
String prepended to every cache key for namespacing. |
max_key_size |
int
|
Maximum allowed cache-key length in bytes. Requests
that would produce a longer key raise |
enable_stats |
bool
|
When |
semantic_enabled |
bool
|
When |
similarity_threshold |
float
|
Minimum cosine similarity (normalised to
|
embedding_provider |
Literal['openai', 'local', 'litellm'] | None
|
Which embedding provider to use for semantic
lookups. |
embedding_model |
str | None
|
Optional model name passed to the embedding provider.
|
max_vectors_per_namespace |
int
|
Maximum number of stored vectors per scope namespace before LRU eviction applies. |
store_exact_on_semantic_hit |
bool
|
When |
extract_answer |
bool
|
When |
extract_min_similarity |
float
|
Minimum normalised cosine similarity
( |
pricing |
PricingConfig | None
|
Optional per-token
:class: |
cache_version |
str | None
|
Optional version token folded into every cache key so a
redeploy with updated prompts, models, or tools never reuses a result
generated by an incompatible older version. |
single_flight |
bool
|
When |
distributed_lock |
bool
|
When |
max_tracked_operations |
int
|
Maximum number of distinct operation aggregates the effectiveness analyzer retains, bounding its memory use. Once reached, observations for new operations are dropped. |
serializer |
Callable[[Any], Any] | None
|
Optional callable converting a value into a JSON-native
representation before storage. Paired with |
deserializer |
Callable[[Any], Any] | None
|
Optional callable reconstructing the original value from
its JSON-native representation on load. Must be paired with
|
Example::
config = CacheConfig(
backend="redis",
redis_url=os.environ["REDIS_URL"],
default_ttl=600,
default_scope="user",
)
from_env
classmethod
¶
Create a :class:CacheConfig from MEMORY_REUSE_* environment variables.
Recognised variables:
MEMORY_REUSE_BACKEND—"memory","redis", or"agentcore"MEMORY_REUSE_REDIS_URL— Redis connection URLMEMORY_REUSE_AGENTCORE_REGION— AWS region hosting the AgentCore store (required whenbackend="agentcore")MEMORY_REUSE_AGENTCORE_MEMORY_ID— AgentCore memory / store resource identifier (required whenbackend="agentcore")MEMORY_REUSE_DEFAULT_TTL— integer seconds or"none"MEMORY_REUSE_DEFAULT_SCOPE—"global","user", or"session"MEMORY_REUSE_KEY_PREFIX— string prefix for all keysMEMORY_REUSE_ENABLE_STATS—"true"/"false"MEMORY_REUSE_SEMANTIC_ENABLED—"true"/"false"MEMORY_REUSE_SIMILARITY_THRESHOLD— float in[0.0, 1.0]MEMORY_REUSE_EMBEDDING_PROVIDER—"openai","local", or"litellm"MEMORY_REUSE_EMBEDDING_MODEL— embedding model name
Returns:
| Name | Type | Description |
|---|---|---|
A |
CacheConfig
|
class: |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If |
Statistics¶
memory_reuse.stats.CacheStats
dataclass
¶
CacheStats(hits: int = 0, exact_hits: int = 0, semantic_hits: int = 0, misses: int = 0, errors: int = 0, total_requests: int = 0)
Snapshot of cache performance counters.
Attributes:
| Name | Type | Description |
|---|---|---|
hits |
int
|
Number of successful cache lookups. Always equals
|
exact_hits |
int
|
Number of hits served by the exact (hash-based) cache. |
semantic_hits |
int
|
Number of hits served by the semantic (similarity) cache. |
misses |
int
|
Number of cache lookups that found no entry. |
errors |
int
|
Number of backend errors encountered during cache operations. |
total_requests |
int
|
Total number of cache lookup attempts (hits + misses). |
Example::
stats = cache.stats
print(f"Hit rate: {stats.hit_rate:.1%}")
hit_rate
property
¶
Fraction of requests that were cache hits, in the range [0.0, 1.0].
Returns:
| Type | Description |
|---|---|
float
|
|
to_dict ¶
Return a plain-dict representation of the stats snapshot.
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary with keys |
dict
|
|
memory_reuse.stats.StatsTracker ¶
Thread-safe, asyncio-compatible statistics tracker.
Uses an :class:asyncio.Lock to serialise counter updates so that
concurrent coroutines always see a consistent view of the counters.
Hits are tracked by type: :meth:record_exact_hit and
:meth:record_semantic_hit each also bump the aggregate hits counter,
so hits always equals exact_hits + semantic_hits.
Statistics recording is best-effort and never fatal: every record_*
method swallows any internal error so a cache operation can always return
its result even if recording fails.
Example::
tracker = StatsTracker()
tracker.record_exact_hit()
tracker.record_miss()
print(tracker.get_stats().hit_rate) # 0.5
record_exact_hit ¶
Record a hit served by the exact cache.
Increments the exact-hit counter, the aggregate hit counter, and the total-request counter. Any internal error is swallowed so recording is never fatal to the caller.
record_semantic_hit ¶
Record a hit served by the semantic cache.
Increments the semantic-hit counter, the aggregate hit counter, and the total-request counter. Any internal error is swallowed so recording is never fatal to the caller.
record_hit ¶
Record an exact hit. Alias for :meth:record_exact_hit.
Retained for backward compatibility with Phase 1 callers.
record_miss ¶
Increment the miss counter and total-request counter.
Any internal error is swallowed so recording is never fatal.
record_error ¶
Increment the error counter (does not affect total_requests).
Any internal error is swallowed so recording is never fatal.
get_stats ¶
Return an immutable snapshot of the current counters.
Returns:
| Name | Type | Description |
|---|---|---|
A |
CacheStats
|
class: |
Exceptions¶
memory_reuse.exceptions ¶
Custom exceptions for the memory-reuse package.
AgentMemoryError ¶
Bases: Exception
Base exception for all memory-reuse errors.
All exceptions raised by this library inherit from this class, making it easy to catch any library error with a single handler.
BackendConnectionError ¶
Bases: AgentMemoryError
Raised when the cache backend cannot be reached.
Typically wraps connection failures from Redis or other network
backends. Check your redis_url and network connectivity.
ScopeViolationError ¶
Bases: AgentMemoryError
Raised when user-scoped data would be cached under the global scope.
This is a safety guard: if you call a cache operation with
scope='user' but no user_id is available in the current
context, this exception is raised rather than silently caching
data that could be shared across users.
InvalidTTLError ¶
Bases: AgentMemoryError
Raised when a TTL value is not valid.
TTL must be a positive integer (seconds) or None for no expiry.
BackendNotAvailableError ¶
Bases: AgentMemoryError
Raised when the requested backend is not importable or configured.
For example, requesting the redis backend without the redis
package installed will raise this exception.
EmbeddingProviderError ¶
Bases: AgentMemoryError
Raised when an embedding provider cannot be used.
Typically raised when the provider's optional dependency is not
installed (naming the extra to install, e.g.
pip install "memory-reuse[semantic]") or when the provider's
backing model or API call fails.
ProviderMismatchError ¶
Bases: AgentMemoryError
Raised when embeddings from different providers or models are mixed.
Vectors are namespaced by provider:model. Comparing vectors from
incompatible providers or models is refused rather than silently
producing meaningless similarity scores.
ConfigurationError ¶
Bases: AgentMemoryError
Raised when a :class:CacheConfig value is invalid.
For example, a similarity_threshold outside [0.0, 1.0] or
enabling semantic_enabled without selecting an
embedding_provider.
SerializationError ¶
Bases: AgentMemoryError
Raised when a value cannot be serialised for (or restored from) the cache.
The cache stores values as gzip-compressed JSON. A value containing a type
JSON cannot represent (and that no configured serializer converts) raises
this rather than being silently coerced to a lossy string — so an
unsupported value is never stored as a wrong value. It is also raised when a
stored entry cannot be decompressed or parsed back into a Python object.