offline_intelligence/memory_db/
session_summaries_store.rs1use std::sync::Arc;
8use r2d2::Pool;
9use r2d2_sqlite::SqliteConnectionManager;
10use tracing::debug;
11
12use crate::memory_db::schema::SessionSummary;
13
14pub struct SessionSummariesStore {
15 pool: Arc<Pool<SqliteConnectionManager>>,
16}
17
18impl SessionSummariesStore {
19 pub fn new(pool: Arc<Pool<SqliteConnectionManager>>) -> Self {
20 Self { pool }
21 }
22
23 pub fn upsert(
27 &self,
28 session_id: &str,
29 summary_text: &str,
30 token_count: i32,
31 total_message_count: i32,
32 ) -> anyhow::Result<()> {
33 let conn = self.pool.get()?;
34
35 let existing_clear_count: i32 = conn
37 .query_row(
38 "SELECT clear_count FROM session_summaries WHERE session_id = ?1",
39 [session_id],
40 |row| row.get(0),
41 )
42 .unwrap_or(0);
43
44 conn.execute(
45 "INSERT OR REPLACE INTO session_summaries
46 (session_id, summary_text, token_count, total_message_count, clear_count, last_updated)
47 VALUES (?1, ?2, ?3, ?4, ?5, CURRENT_TIMESTAMP)",
48 rusqlite::params![
49 session_id,
50 summary_text,
51 token_count,
52 total_message_count,
53 existing_clear_count + 1,
54 ],
55 )?;
56
57 debug!(
58 "Upserted cumulative summary for session {} (clear #{}, {} tokens)",
59 session_id,
60 existing_clear_count + 1,
61 token_count
62 );
63 Ok(())
64 }
65
66 pub fn get(&self, session_id: &str) -> anyhow::Result<Option<SessionSummary>> {
68 let conn = self.pool.get()?;
69 let mut stmt = conn.prepare(
70 "SELECT session_id, summary_text, token_count, total_message_count,
71 clear_count, last_updated
72 FROM session_summaries
73 WHERE session_id = ?1",
74 )?;
75
76 let mut rows = stmt.query([session_id])?;
77 if let Some(row) = rows.next()? {
78 let last_updated_str: String = row.get(5)?;
79 let last_updated = chrono::DateTime::parse_from_rfc3339(&last_updated_str)
80 .map_err(|e| anyhow::anyhow!("Failed to parse timestamp: {}", e))?
81 .with_timezone(&chrono::Utc);
82
83 return Ok(Some(SessionSummary {
84 session_id: row.get(0)?,
85 summary_text: row.get(1)?,
86 token_count: row.get(2)?,
87 total_message_count: row.get(3)?,
88 clear_count: row.get(4)?,
89 last_updated,
90 }));
91 }
92
93 Ok(None)
94 }
95
96 pub fn delete_for_session(&self, session_id: &str) -> anyhow::Result<usize> {
98 let conn = self.pool.get()?;
99 let deleted = conn.execute(
100 "DELETE FROM session_summaries WHERE session_id = ?1",
101 [session_id],
102 )?;
103 Ok(deleted)
104 }
105}