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(
74 &self,
75 conversation_id: &str,
76 role: &str,
77 content: &str,
78 metadata: Option<Value>,
79 ) -> Result<String> {
80 let msg_id = Uuid::new_v4().to_string();
81 let meta_str = metadata.as_ref().map(|m| m.to_string());
82 let now = now_ms();
83 {
84 let conn = self.conn.lock().unwrap();
85 conn.execute(
86 "INSERT INTO _adb_messages
87 (id, conversation_id, role, content, metadata, created_at)
88 VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
89 params![msg_id, conversation_id, role, content, meta_str, now],
90 )?;
91 }
92 {
94 let conn = self.conn.lock().unwrap();
95 conn.execute(
96 "UPDATE _adb_conversations SET updated_at = ?1 WHERE id = ?2",
97 params![now, conversation_id],
98 )?;
99 }
100 Ok(msg_id)
101 }
102
103 pub fn get_messages(
108 &self,
109 conversation_id: &str,
110 limit: Option<usize>,
111 ) -> Result<Vec<Message>> {
112 let conn = self.conn.lock().unwrap();
113 let rows: Vec<Message> = match limit {
114 Some(n) => {
115 let mut stmt = conn.prepare(
118 "SELECT id, conversation_id, role, content, metadata, created_at
119 FROM (
120 SELECT id, conversation_id, role, content, metadata, created_at
121 FROM _adb_messages
122 WHERE conversation_id = ?1
123 ORDER BY created_at DESC
124 LIMIT ?2
125 )
126 ORDER BY created_at ASC",
127 )?;
128 let rows = stmt.query_map(params![conversation_id, n as i64], parse_message)?;
129 rows.map(|r| r.map_err(AgentDbError::Sqlite))
130 .collect::<Result<Vec<_>>>()?
131 }
132 None => {
133 let mut stmt = conn.prepare(
134 "SELECT id, conversation_id, role, content, metadata, created_at
135 FROM _adb_messages
136 WHERE conversation_id = ?1
137 ORDER BY created_at ASC",
138 )?;
139 let rows = stmt.query_map(params![conversation_id], parse_message)?;
140 rows.map(|r| r.map_err(AgentDbError::Sqlite))
141 .collect::<Result<Vec<_>>>()?
142 }
143 };
144 Ok(rows)
145 }
146
147 pub fn list_conversations(&self) -> Result<Vec<Conversation>> {
149 let conn = self.conn.lock().unwrap();
150 let mut stmt = conn.prepare(
151 "SELECT id, title, metadata, created_at, updated_at
152 FROM _adb_conversations
153 ORDER BY updated_at DESC",
154 )?;
155 let rows = stmt.query_map([], parse_conversation)?;
156 rows.map(|r| r.map_err(AgentDbError::Sqlite)).collect()
157 }
158
159 pub fn delete_conversation(&self, id: &str) -> Result<()> {
161 let conn = self.conn.lock().unwrap();
162 conn.execute("DELETE FROM _adb_conversations WHERE id = ?1", params![id])?;
163 Ok(())
164 }
165}
166
167fn parse_conversation(row: &rusqlite::Row) -> rusqlite::Result<Conversation> {
170 let meta_str: Option<String> = row.get(2)?;
171 Ok(Conversation {
172 id: row.get(0)?,
173 title: row.get(1)?,
174 metadata: meta_str
175 .as_deref()
176 .and_then(|s| serde_json::from_str(s).ok()),
177 created_at: row.get(3)?,
178 updated_at: row.get(4)?,
179 })
180}
181
182fn parse_message(row: &rusqlite::Row) -> rusqlite::Result<Message> {
183 let meta_str: Option<String> = row.get(4)?;
184 Ok(Message {
185 id: row.get(0)?,
186 conversation_id: row.get(1)?,
187 role: row.get(2)?,
188 content: row.get(3)?,
189 metadata: meta_str
190 .as_deref()
191 .and_then(|s| serde_json::from_str(s).ok()),
192 created_at: row.get(5)?,
193 })
194}