Skip to content

Commit 8217062

Browse files
Support semantic cache TTL overrides on writes (#217)
1 parent df41114 commit 8217062

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

redisvl/extensions/llmcache/semantic.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ def index(self) -> SearchIndex:
185185
"""
186186
return self._index
187187

188+
@property
189+
def aindex(self) -> Optional[AsyncSearchIndex]:
190+
"""The underlying AsyncSearchIndex for the cache.
191+
192+
Returns:
193+
AsyncSearchIndex: The async search index.
194+
"""
195+
return self._aindex
196+
188197
@property
189198
def distance_threshold(self) -> float:
190199
"""The semantic distance threshold for the cache.
@@ -481,6 +490,7 @@ def store(
481490
vector: Optional[List[float]] = None,
482491
metadata: Optional[Dict[str, Any]] = None,
483492
filters: Optional[Dict[str, Any]] = None,
493+
ttl: Optional[int] = None,
484494
) -> str:
485495
"""Stores the specified key-value pair in the cache along with metadata.
486496
@@ -494,6 +504,8 @@ def store(
494504
alongside the prompt and response. Defaults to None.
495505
filters (Optional[Dict[str, Any]]): The optional tag to assign to the cache entry.
496506
Defaults to None.
507+
ttl (Optional[int]): The optional TTL override to use on this individual cache
508+
entry. Defaults to the global TTL setting.
497509
498510
Returns:
499511
str: The Redis key for the entries added to the semantic cache.
@@ -513,7 +525,6 @@ def store(
513525
"""
514526
# Vectorize prompt if necessary and create cache payload
515527
vector = vector or self._vectorize_prompt(prompt)
516-
517528
self._check_vector_dims(vector)
518529

519530
# Build cache entry for the cache
@@ -526,9 +537,10 @@ def store(
526537
)
527538

528539
# Load cache entry with TTL
540+
ttl = ttl or self._ttl
529541
keys = self._index.load(
530542
data=[cache_entry.to_dict()],
531-
ttl=self._ttl,
543+
ttl=ttl,
532544
id_field=self.entry_id_field_name,
533545
)
534546
return keys[0]
@@ -540,6 +552,7 @@ async def astore(
540552
vector: Optional[List[float]] = None,
541553
metadata: Optional[Dict[str, Any]] = None,
542554
filters: Optional[Dict[str, Any]] = None,
555+
ttl: Optional[int] = None,
543556
) -> str:
544557
"""Async stores the specified key-value pair in the cache along with metadata.
545558
@@ -553,6 +566,8 @@ async def astore(
553566
alongside the prompt and response. Defaults to None.
554567
filters (Optional[Dict[str, Any]]): The optional tag to assign to the cache entry.
555568
Defaults to None.
569+
ttl (Optional[int]): The optional TTL override to use on this individual cache
570+
entry. Defaults to the global TTL setting.
556571
557572
Returns:
558573
str: The Redis key for the entries added to the semantic cache.
@@ -574,7 +589,6 @@ async def astore(
574589

575590
# Vectorize prompt if necessary and create cache payload
576591
vector = vector or self._vectorize_prompt(prompt)
577-
578592
self._check_vector_dims(vector)
579593

580594
# Build cache entry for the cache
@@ -587,9 +601,10 @@ async def astore(
587601
)
588602

589603
# Load cache entry with TTL
604+
ttl = ttl or self._ttl
590605
keys = await aindex.load(
591606
data=[cache_entry.to_dict()],
592-
ttl=self._ttl,
607+
ttl=ttl,
593608
id_field=self.entry_id_field_name,
594609
)
595610
return keys[0]

tests/integration/test_llmcache.py

+28
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ async def test_get_async_index(cache):
106106
async def test_get_async_index_from_provided_client(cache_with_redis_client):
107107
aindex = await cache_with_redis_client._get_async_index()
108108
assert isinstance(aindex, AsyncSearchIndex)
109+
assert aindex == cache_with_redis_client.aindex
109110

110111

111112
def test_delete(cache_no_cleanup):
@@ -275,6 +276,33 @@ async def test_async_ttl_expiration(cache_with_ttl, vectorizer):
275276
assert len(check_result) == 0
276277

277278

279+
def test_custom_ttl(cache_with_ttl, vectorizer):
280+
prompt = "This is a test prompt."
281+
response = "This is a test response."
282+
vector = vectorizer.embed(prompt)
283+
284+
cache_with_ttl.store(prompt, response, vector=vector, ttl=5)
285+
sleep(3)
286+
287+
check_result = cache_with_ttl.check(vector=vector)
288+
assert len(check_result) != 0
289+
assert cache_with_ttl.ttl == 2
290+
291+
292+
@pytest.mark.asyncio
293+
async def test_async_custom_ttl(cache_with_ttl, vectorizer):
294+
prompt = "This is a test prompt."
295+
response = "This is a test response."
296+
vector = vectorizer.embed(prompt)
297+
298+
await cache_with_ttl.astore(prompt, response, vector=vector, ttl=5)
299+
await asyncio.sleep(3)
300+
301+
check_result = await cache_with_ttl.acheck(vector=vector)
302+
assert len(check_result) != 0
303+
assert cache_with_ttl.ttl == 2
304+
305+
278306
def test_ttl_refresh(cache_with_ttl, vectorizer):
279307
prompt = "This is a test prompt."
280308
response = "This is a test response."

0 commit comments

Comments
 (0)