1use crate::error::{AgentDbError, Result};
2use crate::schema::now_ms;
3use rusqlite::params;
4use rusqlite::Connection;
5use serde_json::Value;
6use std::sync::{Arc, Mutex};
7use uuid::Uuid;
8
9#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
11pub struct MessageSearchResult {
12 pub message_id: String,
14 pub conversation_id: String,
16 pub snippet: String,
18 pub rank: f64,
20}
21
22#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
24pub struct Conversation {
25 pub id: String,
27 pub title: Option<String>,
29 pub metadata: Option<Value>,
31 pub created_at: i64,
33 pub updated_at: i64,
35}
36
37#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
39pub struct Message {
40 pub id: String,
42 pub conversation_id: String,
44 pub role: String,
46 pub content: String,
48 pub metadata: Option<Value>,
50 pub created_at: i64,
52}
53
54pub struct ConversationStore {
56 conn: Arc<Mutex<Connection>>,
57}
58
59impl ConversationStore {
60 pub(crate) fn new(conn: Arc<Mutex<Connection>>) -> Self {
61 Self { conn }
62 }
63
64 pub fn create_conversation(
67 &self,
68 id: &str,
69 title: Option<&str>,
70 metadata: Option<Value>,
71 ) -> Result<()> {
72 let conn = self.conn.lock().unwrap();
73 let meta_str = metadata.as_ref().map(|m| m.to_string());
74 let now = now_ms();
75 conn.execute(
76 "INSERT INTO _adb_conversations (id, title, metadata, created_at, updated_at)
77 VALUES (?1, ?2, ?3, ?4, ?5)",
78 params![id, title, meta_str, now, now],
79 )?;
80 Ok(())
81 }
82
83 pub fn add_message(
90 &self,
91 conversation_id: &str,
92 role: &str,
93 content: &str,
94 metadata: Option<Value>,
95 ) -> Result<String> {
96 let msg_id = Uuid::new_v4().to_string();
97 let meta_str = metadata.as_ref().map(|m| m.to_string());
98 let now = now_ms();
99 let conn = self.conn.lock().unwrap();
100 conn.execute(
101 "INSERT INTO _adb_messages
102 (id, conversation_id, role, content, metadata, created_at)
103 VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
104 params![msg_id, conversation_id, role, content, meta_str, now],
105 )?;
106 conn.execute(
107 "UPDATE _adb_conversations SET updated_at = ?1 WHERE id = ?2",
108 params![now, conversation_id],
109 )?;
110 conn.execute(
111 "INSERT INTO _adb_messages_fts (message_id, conversation_id, content)
112 VALUES (?1, ?2, ?3)",
113 params![&msg_id, conversation_id, content],
114 )?;
115 Ok(msg_id)
116 }
117
118 pub fn get_messages(
123 &self,
124 conversation_id: &str,
125 limit: Option<usize>,
126 ) -> Result<Vec<Message>> {
127 let conn = self.conn.lock().unwrap();
128 let rows: Vec<Message> = match limit {
129 Some(n) => {
130 let mut stmt = conn.prepare(
133 "SELECT id, conversation_id, role, content, metadata, created_at
134 FROM (
135 SELECT id, conversation_id, role, content, metadata, created_at
136 FROM _adb_messages
137 WHERE conversation_id = ?1
138 ORDER BY created_at DESC
139 LIMIT ?2
140 )
141 ORDER BY created_at ASC",
142 )?;
143 let rows = stmt.query_map(params![conversation_id, n as i64], parse_message)?;
144 rows.map(|r| r.map_err(AgentDbError::Sqlite))
145 .collect::<Result<Vec<_>>>()?
146 }
147 None => {
148 let mut stmt = conn.prepare(
149 "SELECT id, conversation_id, role, content, metadata, created_at
150 FROM _adb_messages
151 WHERE conversation_id = ?1
152 ORDER BY created_at ASC",
153 )?;
154 let rows = stmt.query_map(params![conversation_id], parse_message)?;
155 rows.map(|r| r.map_err(AgentDbError::Sqlite))
156 .collect::<Result<Vec<_>>>()?
157 }
158 };
159 Ok(rows)
160 }
161
162 pub fn list_conversations(&self) -> Result<Vec<Conversation>> {
164 let conn = self.conn.lock().unwrap();
165 let mut stmt = conn.prepare(
166 "SELECT id, title, metadata, created_at, updated_at
167 FROM _adb_conversations
168 ORDER BY updated_at DESC",
169 )?;
170 let rows = stmt.query_map([], parse_conversation)?;
171 rows.map(|r| r.map_err(AgentDbError::Sqlite)).collect()
172 }
173
174 pub fn delete_conversation(&self, id: &str) -> Result<()> {
176 let conn = self.conn.lock().unwrap();
177 conn.execute(
178 "DELETE FROM _adb_messages_fts WHERE conversation_id = ?1",
179 params![id],
180 )?;
181 conn.execute("DELETE FROM _adb_conversations WHERE id = ?1", params![id])?;
182 Ok(())
183 }
184
185 pub fn search_messages(
190 &self,
191 query: &str,
192 top_k: usize,
193 conversation_id: Option<&str>,
194 ) -> Result<Vec<MessageSearchResult>> {
195 let conn = self.conn.lock().unwrap();
196 let rows = match conversation_id {
197 Some(cid) => {
198 let mut stmt = conn.prepare(
199 "SELECT message_id, conversation_id,
200 snippet(_adb_messages_fts, 2, '<b>', '</b>', '...', 10),
201 rank
202 FROM _adb_messages_fts
203 WHERE _adb_messages_fts MATCH ?1
204 AND conversation_id = ?2
205 ORDER BY rank
206 LIMIT ?3",
207 )?;
208 let rows = stmt.query_map(
209 params![query, cid, top_k as i64],
210 parse_message_search_result,
211 )?;
212 rows.map(|r| r.map_err(AgentDbError::Sqlite))
213 .collect::<Result<Vec<_>>>()?
214 }
215 None => {
216 let mut stmt = conn.prepare(
217 "SELECT message_id, conversation_id,
218 snippet(_adb_messages_fts, 2, '<b>', '</b>', '...', 10),
219 rank
220 FROM _adb_messages_fts
221 WHERE _adb_messages_fts MATCH ?1
222 ORDER BY rank
223 LIMIT ?2",
224 )?;
225 let rows =
226 stmt.query_map(params![query, top_k as i64], parse_message_search_result)?;
227 rows.map(|r| r.map_err(AgentDbError::Sqlite))
228 .collect::<Result<Vec<_>>>()?
229 }
230 };
231 Ok(rows)
232 }
233}
234
235fn parse_message_search_result(row: &rusqlite::Row) -> rusqlite::Result<MessageSearchResult> {
238 Ok(MessageSearchResult {
239 message_id: row.get(0)?,
240 conversation_id: row.get(1)?,
241 snippet: row.get(2)?,
242 rank: row.get(3)?,
243 })
244}
245
246fn parse_conversation(row: &rusqlite::Row) -> rusqlite::Result<Conversation> {
247 let meta_str: Option<String> = row.get(2)?;
248 Ok(Conversation {
249 id: row.get(0)?,
250 title: row.get(1)?,
251 metadata: meta_str
252 .as_deref()
253 .and_then(|s| serde_json::from_str(s).ok()),
254 created_at: row.get(3)?,
255 updated_at: row.get(4)?,
256 })
257}
258
259fn parse_message(row: &rusqlite::Row) -> rusqlite::Result<Message> {
260 let meta_str: Option<String> = row.get(4)?;
261 Ok(Message {
262 id: row.get(0)?,
263 conversation_id: row.get(1)?,
264 role: row.get(2)?,
265 content: row.get(3)?,
266 metadata: meta_str
267 .as_deref()
268 .and_then(|s| serde_json::from_str(s).ok()),
269 created_at: row.get(5)?,
270 })
271}