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)]
11pub struct Conversation {
12 pub id: String,
14 pub title: Option<String>,
16 pub metadata: Option<Value>,
18 pub created_at: i64,
20 pub updated_at: i64,
22}
23
24#[derive(Debug, Clone)]
26pub struct Message {
27 pub id: String,
29 pub conversation_id: String,
31 pub role: String,
33 pub content: String,
35 pub metadata: Option<Value>,
37 pub created_at: i64,
39}
40
41pub struct ConversationStore {
43 conn: Arc<Mutex<Connection>>,
44}
45
46impl ConversationStore {
47 pub(crate) fn new(conn: Arc<Mutex<Connection>>) -> Self {
48 Self { conn }
49 }
50
51 pub fn create_conversation(
54 &self,
55 id: &str,
56 title: Option<&str>,
57 metadata: Option<Value>,
58 ) -> Result<()> {
59 let conn = self.conn.lock().unwrap();
60 let meta_str = metadata.as_ref().map(|m| m.to_string());
61 let now = now_ms();
62 conn.execute(
63 "INSERT INTO _adb_conversations (id, title, metadata, created_at, updated_at)
64 VALUES (?1, ?2, ?3, ?4, ?5)",
65 params![id, title, meta_str, now, now],
66 )?;
67 Ok(())
68 }
69
70 pub fn add_message(
77 &self,
78 conversation_id: &str,
79 role: &str,
80 content: &str,
81 metadata: Option<Value>,
82 ) -> Result<String> {
83 let msg_id = Uuid::new_v4().to_string();
84 let meta_str = metadata.as_ref().map(|m| m.to_string());
85 let now = now_ms();
86 let conn = self.conn.lock().unwrap();
87 conn.execute(
88 "INSERT INTO _adb_messages
89 (id, conversation_id, role, content, metadata, created_at)
90 VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
91 params![msg_id, conversation_id, role, content, meta_str, now],
92 )?;
93 conn.execute(
94 "UPDATE _adb_conversations SET updated_at = ?1 WHERE id = ?2",
95 params![now, conversation_id],
96 )?;
97 Ok(msg_id)
98 }
99
100 pub fn get_messages(
105 &self,
106 conversation_id: &str,
107 limit: Option<usize>,
108 ) -> Result<Vec<Message>> {
109 let conn = self.conn.lock().unwrap();
110 let rows: Vec<Message> = match limit {
111 Some(n) => {
112 let mut stmt = conn.prepare(
115 "SELECT id, conversation_id, role, content, metadata, created_at
116 FROM (
117 SELECT id, conversation_id, role, content, metadata, created_at
118 FROM _adb_messages
119 WHERE conversation_id = ?1
120 ORDER BY created_at DESC
121 LIMIT ?2
122 )
123 ORDER BY created_at ASC",
124 )?;
125 let rows = stmt.query_map(params![conversation_id, n as i64], parse_message)?;
126 rows.map(|r| r.map_err(AgentDbError::Sqlite))
127 .collect::<Result<Vec<_>>>()?
128 }
129 None => {
130 let mut stmt = conn.prepare(
131 "SELECT id, conversation_id, role, content, metadata, created_at
132 FROM _adb_messages
133 WHERE conversation_id = ?1
134 ORDER BY created_at ASC",
135 )?;
136 let rows = stmt.query_map(params![conversation_id], parse_message)?;
137 rows.map(|r| r.map_err(AgentDbError::Sqlite))
138 .collect::<Result<Vec<_>>>()?
139 }
140 };
141 Ok(rows)
142 }
143
144 pub fn list_conversations(&self) -> Result<Vec<Conversation>> {
146 let conn = self.conn.lock().unwrap();
147 let mut stmt = conn.prepare(
148 "SELECT id, title, metadata, created_at, updated_at
149 FROM _adb_conversations
150 ORDER BY updated_at DESC",
151 )?;
152 let rows = stmt.query_map([], parse_conversation)?;
153 rows.map(|r| r.map_err(AgentDbError::Sqlite)).collect()
154 }
155
156 pub fn delete_conversation(&self, id: &str) -> Result<()> {
158 let conn = self.conn.lock().unwrap();
159 conn.execute("DELETE FROM _adb_conversations WHERE id = ?1", params![id])?;
160 Ok(())
161 }
162}
163
164fn parse_conversation(row: &rusqlite::Row) -> rusqlite::Result<Conversation> {
167 let meta_str: Option<String> = row.get(2)?;
168 Ok(Conversation {
169 id: row.get(0)?,
170 title: row.get(1)?,
171 metadata: meta_str
172 .as_deref()
173 .and_then(|s| serde_json::from_str(s).ok()),
174 created_at: row.get(3)?,
175 updated_at: row.get(4)?,
176 })
177}
178
179fn parse_message(row: &rusqlite::Row) -> rusqlite::Result<Message> {
180 let meta_str: Option<String> = row.get(4)?;
181 Ok(Message {
182 id: row.get(0)?,
183 conversation_id: row.get(1)?,
184 role: row.get(2)?,
185 content: row.get(3)?,
186 metadata: meta_str
187 .as_deref()
188 .and_then(|s| serde_json::from_str(s).ok()),
189 created_at: row.get(5)?,
190 })
191}