RedisStore
A CacheStore backed by Redis with the RediSearch module, for a cache shared across processes.
kmemo splits responsibilities so this adapter stays thin: the dev.kmemo.SemanticCache owns whether a candidate is good enough to serve (threshold, guards, verification); this store only keeps vectors and returns the nearest k in a scope. It therefore reimplements no match logic — the nearest-neighbour search is Redis's FT.SEARCH ... KNN, on an exact FLAT index so results match the conformance suite's ordering.
Layout. Each entry is a Redis hash at "<keyPrefix><id>". Scope is a TAG field, the embedding a FLOAT32 VECTOR field, and expiry a numeric expiresAt (epoch millis) field. A single RediSearch index (created lazily on the first put, once the embedding dimension is known) covers all of them.
Expiry. TTL is enforced two ways that agree in production and stay testable: every read filters on expiresAt > now(clock), so expiry follows the injected clock deterministically; and a real Redis key TTL is set for reclamation, so dead keys do not accumulate. An entry with no TTL stores expiresAt = Long.MAX_VALUE.
Requirements. Redis with the RediSearch module (e.g. redis/redis-stack). If the module is absent, the first put fails fast with a clear message rather than silently degrading to no search.
Parameters
an open Lettuce connection with a byte-array codec, speaking RESP2 (this store parses FT.SEARCH replies as classic RESP2 arrays; RESP3 returns a map it does not decode). The RedisClient secondary constructor handles both for you — prefer it.
RediSearch index name; give each logical cache its own.
prefix for this cache's hash keys; the index only sees keys under it.
how long an entry stays valid, or null to keep it until removed.
time source; substitute a fixed clock in tests instead of sleeping.
whether close should also close connection.