Repository navigation
Conversation
Semantic cache users have no built-in way to see whether the cache is working: the index statistics RedisVL exposes do not say how many checks hit, how many missed, or how long the lookup took. Every BaseLLMCache now counts the check/acheck calls the bundled caches make (hit, miss, error) and the stores that complete, plus the wall-clock time spent in checks, and exposes a frozen CacheStats snapshot with hit_rate and avg_lookup_seconds derived from them. Counting lives in decorators on the concrete caches, applied once each; an override that calls super() is counted, one that does not is not. The counters are per process and per object, not locked, and nothing is written to Redis or recorded from prompts and responses.
vishal-bala
left a comment
There was a problem hiding this comment.
Thanks for picking up #684! There's an open OpenTelemetry stack for the same issue (#755, #776, #777). It already records checks, hits, misses, errors, stores and lookup latency for SemanticCache, and it decorates the same four methods, so this PR would duplicate it and conflict with it.
What's still missing is LangCacheSemanticCache, which the stack doesn't instrument yet. Could you rework this PR to cover just that, on top of #777?
- Drop
stats.py,BaseLLMCache.statsand theSemanticCachedecorators. - Decorate the LangCache
check/acheckwith_telemetry.operation("check", lookup=True),store/astorewithoperation("store"), and thedelete_by_*methods withoperation("drop"). - Skip the retrieval span and the distance histogram, since LangCache's search is an HTTP call and its misses return no candidates.
- Test against an in-memory metric reader, as
tests/unit/test_cache_telemetry.pydoes. Your mockedLangCachefixture should still work there. - Add LangCache to the observability guide.
Keep targeting main, and rebase once the stack merges.
|
The rework is written and passes locally: LangCache I am holding the push until |
Motivation
A semantic cache saves latency and money, but RedisVL gives no built-in way to see whether it is working: the index statistics it exposes say how many entries exist, not how many checks hit, how many missed, or how long a lookup took. Today every deployment writes its own counters around
check()/store(). Closes #684.Changes
A
CacheStatssnapshot on every cacheBaseLLMCache.statsreturns a frozenCacheStatswithchecks,hits,misses,errors,stores,lookup_seconds, and the derivedhit_rateandavg_lookup_seconds. Absent fromhits + misses + errorsis impossible by construction: a check that raises incrementschecksanderrorsand is never counted as a miss.Counting in decorators, applied once per method
The counters live in
redisvl/extensions/cache/llm/stats.pyasobserve_check/observe_acheck/count_store/count_astore, applied tocheck,acheck,storeandastoreonSemanticCacheandLangCacheSemanticCache.functools.wrapskeeps the public signatures, docstrings andiscoroutinefunctionintact, and there is a test that says so.Per process, per object, no content
Nothing is written to Redis, so two workers sharing a cache name do not aggregate, and no prompt, response or metadata is recorded. The increments are not locked, so a cache object shared across threads counts best-effort.
Release Notes
Every LLM cache now exposes
cache.stats, with hits, misses, errors, completed stores, hit rate and average lookup time. The counters are per cache object and per process; nothing changes on the wire, and sharing a cache object across threads makes the totals approximate.Reviewer Brief
Start at
stats.py— the four decorators and theCacheStatsdataclass — thenbase.pyfor_record_check/_record_errorand thestatsproperty. The risk is in the wrappers rather than the arithmetic: an async wrapper that swallowed an exception would look fine in the happy-path tests, sotests/unit/test_cache_stats.pypins the async error path and the preserved signature separately.Not verified: the integration test in
tests/integration/test_llmcache.pyneeds a live Redis and was not run here; the unit tests mock the LangCache client and ran green (pytest tests/unit— 1360 passed, with themcpandgoogle-genaimodules failing to collect for missing optional dependencies, as they do onmain).Note
Low Risk
Additive observability around existing check/store methods; cache semantics and Redis behavior are unchanged aside from lightweight in-process counting.
Overview
Adds built-in, in-process usage metrics for LLM caches so callers can read hit/miss behavior without wrapping every
check/storecall.BaseLLMCache.statsreturns a frozenCacheStatssnapshot: checks, hits, misses, errors, stores, total lookup time, plus derivedhit_rate(errors excluded from the denominator) andavg_lookup_seconds. Counters live on each cache instance only—nothing is persisted to Redis and no prompt/response content is recorded.Instrumentation is applied via
observe_check/observe_acheck/count_store/count_astoreonSemanticCacheandLangCacheSemanticCache. Failed checks incrementerrors(not misses); stores count only on successful completion.CacheStatsis exported from the public LLM cache module and documented indocs/api/cache.rst.Integration tests cover
SemanticCache; unit tests (mocked LangCache client) cover error paths, snapshot immutability, per-instance isolation, and preserved method signatures.Reviewed by Cursor Bugbot for commit 27393bf. Bugbot is set up for automated code reviews on this repo. Configure here.