11"""Cache that uses SQLite to store cached values."""
22
33from time import time
4+ from typing import Any
45
56import sqlite3
67import json
910from cache .cache_error import CacheError
1011from models .cache_entry import CacheEntry
1112from models .config import SQLiteDatabaseConfiguration
13+ from models .requests import Attachment
1214from models .responses import ConversationData
1315from utils .connection_decorator import connection
1416from utils .types import ReferencedDocument , ToolCallSummary , ToolResultSummary
@@ -37,6 +39,7 @@ class SQLiteCache(Cache):
3739 referenced_documents | text | |
3840 tool_calls | text | |
3941 tool_results | text | |
42+ attachments | text | |
4043 Indexes:
4144 "cache_pkey" PRIMARY KEY, btree (user_id, conversation_id, created_at)
4245 "cache_key_key" UNIQUE CONSTRAINT, btree (key)
@@ -45,6 +48,26 @@ class SQLiteCache(Cache):
4548 ```
4649 """
4750
51+ @staticmethod
52+ def _safe_json_dumps_models (
53+ items : list [Any ] | None , conversation_id : str , field_name : str
54+ ) -> str | None :
55+ """Serialize a list of Pydantic models to JSON, returning None on failure."""
56+ if not items :
57+ return None
58+
59+ try :
60+ as_dicts = [item .model_dump (mode = "json" ) for item in items ]
61+ return json .dumps (as_dicts )
62+ except (TypeError , ValueError ) as e :
63+ logger .warning (
64+ "Failed to serialize %s for conversation %s: %s" ,
65+ field_name ,
66+ conversation_id ,
67+ e ,
68+ )
69+ return None
70+
4871 CREATE_CACHE_TABLE = """
4972 CREATE TABLE IF NOT EXISTS cache (
5073 user_id text NOT NULL,
@@ -59,6 +82,7 @@ class SQLiteCache(Cache):
5982 referenced_documents text,
6083 tool_calls text,
6184 tool_results text,
85+ attachments text,
6286 PRIMARY KEY(user_id, conversation_id, created_at)
6387 );
6488 """
@@ -80,7 +104,7 @@ class SQLiteCache(Cache):
80104
81105 SELECT_CONVERSATION_HISTORY_STATEMENT = """
82106 SELECT query, response, provider, model, started_at, completed_at,
83- referenced_documents, tool_calls, tool_results
107+ referenced_documents, tool_calls, tool_results, attachments
84108 FROM cache
85109 WHERE user_id=? AND conversation_id=?
86110 ORDER BY created_at
@@ -89,8 +113,8 @@ class SQLiteCache(Cache):
89113 INSERT_CONVERSATION_HISTORY_STATEMENT = """
90114 INSERT INTO cache(user_id, conversation_id, created_at, started_at, completed_at,
91115 query, response, provider, model, referenced_documents,
92- tool_calls, tool_results)
93- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
116+ tool_calls, tool_results, attachments )
117+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
94118 """
95119
96120 QUERY_CACHE_SIZE = """
@@ -301,6 +325,22 @@ def get( # pylint: disable=R0914
301325 e ,
302326 )
303327
328+ # Parse attachments back into Attachment objects
329+ attachments_json_str = conversation_entry [9 ]
330+ attachments_obj = None
331+ if attachments_json_str :
332+ try :
333+ attachments_data = json .loads (attachments_json_str )
334+ attachments_obj = [
335+ Attachment .model_validate (att ) for att in attachments_data
336+ ]
337+ except (json .JSONDecodeError , ValueError ) as e :
338+ logger .warning (
339+ "Failed to deserialize attachments for conversation %s: %s" ,
340+ conversation_id ,
341+ e ,
342+ )
343+
304344 cache_entry = CacheEntry (
305345 query = conversation_entry [0 ],
306346 response = conversation_entry [1 ],
@@ -311,6 +351,7 @@ def get( # pylint: disable=R0914
311351 referenced_documents = docs_obj ,
312352 tool_calls = tool_calls_obj ,
313353 tool_results = tool_results_obj ,
354+ attachments = attachments_obj ,
314355 )
315356 result .append (cache_entry )
316357
@@ -342,49 +383,20 @@ def insert_or_append(
342383 cursor = self .connection .cursor ()
343384 current_time = time ()
344385
345- referenced_documents_json = None
346- if cache_entry .referenced_documents :
347- try :
348- docs_as_dicts = [
349- doc .model_dump (mode = "json" )
350- for doc in cache_entry .referenced_documents
351- ]
352- referenced_documents_json = json .dumps (docs_as_dicts )
353- except (TypeError , ValueError ) as e :
354- logger .warning (
355- "Failed to serialize referenced_documents for "
356- "conversation %s: %s" ,
357- conversation_id ,
358- e ,
359- )
360-
361- tool_calls_json = None
362- if cache_entry .tool_calls :
363- try :
364- tool_calls_as_dicts = [
365- tc .model_dump (mode = "json" ) for tc in cache_entry .tool_calls
366- ]
367- tool_calls_json = json .dumps (tool_calls_as_dicts )
368- except (TypeError , ValueError ) as e :
369- logger .warning (
370- "Failed to serialize tool_calls for conversation %s: %s" ,
371- conversation_id ,
372- e ,
373- )
374-
375- tool_results_json = None
376- if cache_entry .tool_results :
377- try :
378- tool_results_as_dicts = [
379- tr .model_dump (mode = "json" ) for tr in cache_entry .tool_results
380- ]
381- tool_results_json = json .dumps (tool_results_as_dicts )
382- except (TypeError , ValueError ) as e :
383- logger .warning (
384- "Failed to serialize tool_results for conversation %s: %s" ,
385- conversation_id ,
386- e ,
387- )
386+ referenced_documents_json = self ._safe_json_dumps_models (
387+ cache_entry .referenced_documents ,
388+ conversation_id ,
389+ "referenced_documents" ,
390+ )
391+ tool_calls_json = self ._safe_json_dumps_models (
392+ cache_entry .tool_calls , conversation_id , "tool_calls"
393+ )
394+ tool_results_json = self ._safe_json_dumps_models (
395+ cache_entry .tool_results , conversation_id , "tool_results"
396+ )
397+ attachments_json = self ._safe_json_dumps_models (
398+ cache_entry .attachments , conversation_id , "attachments"
399+ )
388400
389401 cursor .execute (
390402 self .INSERT_CONVERSATION_HISTORY_STATEMENT ,
@@ -401,6 +413,7 @@ def insert_or_append(
401413 referenced_documents_json ,
402414 tool_calls_json ,
403415 tool_results_json ,
416+ attachments_json ,
404417 ),
405418 )
406419
0 commit comments