55- Redis (preferred, for production)
66- LRU in-memory cache (fallback for development or when Redis is unavailable)
77
8- Cache key is a SHA-256 hash of (document_id, question) to ensure keys are
9- short, stable, and unique across all question/document combinations.
8+ Cache key is a SHA-256 hash of (user_id, document_id, question) to ensure
9+ keys are short, stable, and unique across all user/question/document
10+ combinations — never shared between different users.
1011"""
1112
1213import hashlib
@@ -101,25 +102,28 @@ def _lru_delete(key: str) -> None:
101102# ---------------------------------------------------------------------------
102103
103104
104- def make_cache_key (document_id : str , question : str ) -> str :
105+ def make_cache_key (user_id : str , document_id : str , question : str ) -> str :
105106 """
106- Generate a stable, short cache key from document_id + question.
107+ Generate a stable, short cache key from user_id + document_id + question.
107108
108109 SHA-256 gives us a 64-char hex string that is:
109110 - Always the same length regardless of question length
110- - Unique per (document_id, question) pair
111+ - Unique per (user_id, document_id, question) triple — user_id is
112+ required so two different users asking the identical question never
113+ collide on the same cache entry, even when document_id is empty
114+ (cross-document queries against a user's own private knowledge base)
111115 - Safe for Redis keys and dict keys
112116 """
113- raw = f"{ document_id } :{ question .strip ().lower ()} "
117+ raw = f"{ user_id } : { document_id } :{ question .strip ().lower ()} "
114118 return hashlib .sha256 (raw .encode ("utf-8" )).hexdigest ()
115119
116120
117- def get_cached_response (document_id : str , question : str ) -> Optional [str ]:
121+ def get_cached_response (user_id : str , document_id : str , question : str ) -> Optional [str ]:
118122 """
119- Look up a cached answer for a (document_id, question) pair .
123+ Look up a cached answer for a (user_id, document_id, question) triple .
120124 Returns the answer string on hit, None on miss.
121125 """
122- key = make_cache_key (document_id , question )
126+ key = make_cache_key (user_id , document_id , question )
123127 r = _get_redis ()
124128
125129 if r is not None :
@@ -140,12 +144,13 @@ def get_cached_response(document_id: str, question: str) -> Optional[str]:
140144 return None
141145
142146
143- def set_cached_response (document_id : str , question : str , answer : str ) -> None :
147+
148+ def set_cached_response (user_id : str , document_id : str , question : str , answer : str ) -> None :
144149 """
145150 Store an answer. Tries Redis first; falls back to LRU.
146151 TTL is controlled by the CACHE_TTL environment variable.
147152 """
148- key = make_cache_key (document_id , question )
153+ key = make_cache_key (user_id , document_id , question )
149154 serialised = json .dumps (answer )
150155 r = _get_redis ()
151156
@@ -161,9 +166,9 @@ def set_cached_response(document_id: str, question: str, answer: str) -> None:
161166 logger .debug ("Cache SET (LRU) key %s" , key [:12 ])
162167
163168
164- def invalidate_cache (document_id : str , question : str ) -> None :
169+ def invalidate_cache (user_id : str , document_id : str , question : str ) -> None :
165170 """Remove one cache entry — useful when a document is re-indexed."""
166- key = make_cache_key (document_id , question )
171+ key = make_cache_key (user_id , document_id , question )
167172 r = _get_redis ()
168173 if r is not None :
169174 try :
0 commit comments