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 Trace {
12 pub id: String,
14 pub session_id: Option<String>,
16 pub parent_id: Option<String>,
18 pub trace_type: String,
20 pub content: String,
22 pub metadata: Option<Value>,
24 pub created_at: i64,
26}
27
28pub struct TraceStore {
30 conn: Arc<Mutex<Connection>>,
31}
32
33impl TraceStore {
34 pub(crate) fn new(conn: Arc<Mutex<Connection>>) -> Self {
35 Self { conn }
36 }
37
38 pub fn add_trace(
46 &self,
47 session_id: Option<&str>,
48 parent_id: Option<&str>,
49 trace_type: &str,
50 content: &str,
51 metadata: Option<Value>,
52 ) -> Result<String> {
53 let trace_id = Uuid::new_v4().to_string();
54 let meta_str = metadata.as_ref().map(|m| m.to_string());
55 let now = now_ms();
56 let conn = self.conn.lock().unwrap();
57 conn.execute(
58 "INSERT INTO _adb_traces
59 (id, session_id, parent_id, trace_type, content, metadata, created_at)
60 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
61 params![trace_id, session_id, parent_id, trace_type, content, meta_str, now],
62 )?;
63 Ok(trace_id)
64 }
65
66 pub fn get_traces(&self, session_id: &str) -> Result<Vec<Trace>> {
68 let conn = self.conn.lock().unwrap();
69 let mut stmt = conn.prepare(
70 "SELECT id, session_id, parent_id, trace_type, content, metadata, created_at
71 FROM _adb_traces
72 WHERE session_id = ?1
73 ORDER BY created_at ASC",
74 )?;
75 let rows = stmt.query_map(params![session_id], parse_trace)?;
76 rows.map(|r| r.map_err(AgentDbError::Sqlite)).collect()
77 }
78
79 pub fn get_trace_tree(&self, root_id: &str) -> Result<Vec<Trace>> {
84 let conn = self.conn.lock().unwrap();
85 let mut stmt = conn.prepare(
86 "WITH RECURSIVE tree(id) AS (
87 SELECT id FROM _adb_traces WHERE id = ?1
88 UNION ALL
89 SELECT t.id
90 FROM _adb_traces t
91 JOIN tree ON t.parent_id = tree.id
92 )
93 SELECT t.id, t.session_id, t.parent_id, t.trace_type,
94 t.content, t.metadata, t.created_at
95 FROM _adb_traces t
96 JOIN tree ON t.id = tree.id
97 ORDER BY t.created_at ASC",
98 )?;
99 let rows = stmt.query_map(params![root_id], parse_trace)?;
100 rows.map(|r| r.map_err(AgentDbError::Sqlite)).collect()
101 }
102}
103
104fn parse_trace(row: &rusqlite::Row) -> rusqlite::Result<Trace> {
107 let meta_str: Option<String> = row.get(5)?;
108 Ok(Trace {
109 id: row.get(0)?,
110 session_id: row.get(1)?,
111 parent_id: row.get(2)?,
112 trace_type: row.get(3)?,
113 content: row.get(4)?,
114 metadata: meta_str
115 .as_deref()
116 .and_then(|s| serde_json::from_str(s).ok()),
117 created_at: row.get(6)?,
118 })
119}