claw_core/store/
active.rs1use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10use sqlx::SqlitePool;
11use uuid::Uuid;
12
13use crate::error::{ClawError, ClawResult};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ActiveMemoryRecord {
18 pub id: Uuid,
20 pub agent_id: String,
22 pub key: String,
24 pub value: serde_json::Value,
26 pub updated_at: DateTime<Utc>,
28 pub expires_at: Option<DateTime<Utc>>,
30}
31
32#[derive(Debug)]
34pub struct ActiveMemoryStore<'a> {
35 pool: &'a SqlitePool,
36}
37
38impl<'a> ActiveMemoryStore<'a> {
39 pub fn new(pool: &'a SqlitePool) -> Self {
41 ActiveMemoryStore { pool }
42 }
43
44 pub async fn upsert(&self, record: &ActiveMemoryRecord) -> ClawResult<()> {
53 sqlx::query(
54 r#"
55 INSERT INTO active_memory (id, agent_id, key, value, updated_at, expires_at)
56 VALUES (?, ?, ?, ?, ?, ?)
57 ON CONFLICT (agent_id, key)
58 DO UPDATE SET value = excluded.value,
59 updated_at = excluded.updated_at,
60 expires_at = excluded.expires_at
61 "#,
62 )
63 .bind(record.id.to_string())
64 .bind(&record.agent_id)
65 .bind(&record.key)
66 .bind(serde_json::to_string(&record.value)?)
67 .bind(record.updated_at.to_rfc3339())
68 .bind(record.expires_at.map(|t| t.to_rfc3339()))
69 .execute(self.pool)
70 .await?;
71
72 Ok(())
73 }
74
75 pub async fn get(&self, agent_id: &str, key: &str) -> ClawResult<Option<ActiveMemoryRecord>> {
81 let row = sqlx::query_as::<_, (String, String, String, String, String, Option<String>)>(
82 "SELECT id, agent_id, key, value, updated_at, expires_at \
83 FROM active_memory WHERE agent_id = ? AND key = ?",
84 )
85 .bind(agent_id)
86 .bind(key)
87 .fetch_optional(self.pool)
88 .await?;
89
90 row.map(|(id, agent_id, key, value, updated_at, expires_at)| {
91 Ok(ActiveMemoryRecord {
92 id: Uuid::parse_str(&id).map_err(|e| ClawError::Store(e.to_string()))?,
93 agent_id,
94 key,
95 value: serde_json::from_str(&value)?,
96 updated_at: DateTime::parse_from_rfc3339(&updated_at)
97 .map_err(|e| ClawError::Store(e.to_string()))?
98 .with_timezone(&Utc),
99 expires_at: expires_at
100 .map(|s| {
101 DateTime::parse_from_rfc3339(&s)
102 .map(|dt| dt.with_timezone(&Utc))
103 .map_err(|e| ClawError::Store(e.to_string()))
104 })
105 .transpose()?,
106 })
107 })
108 .transpose()
109 }
110
111 pub async fn clear_agent(&self, agent_id: &str) -> ClawResult<u64> {
117 let result = sqlx::query("DELETE FROM active_memory WHERE agent_id = ?")
118 .bind(agent_id)
119 .execute(self.pool)
120 .await?;
121
122 Ok(result.rows_affected())
123 }
124}