1use std::collections::HashMap;
7
8use reqwest::Method;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::client::Client;
13use crate::error::BoxxkiteError;
14
15#[derive(Debug, Clone, Deserialize)]
17pub struct Memory {
18 pub id: String,
19 pub scope: String,
20 pub kind: String,
21 pub content: String,
22 #[serde(default)]
23 pub metadata: HashMap<String, Value>,
24 pub source_session_id: Option<String>,
25 pub created_at: String,
26 pub updated_at: String,
27 pub expires_at: Option<String>,
28 pub source_content: Option<String>,
29 pub document_date: Option<String>,
30 #[serde(default)]
31 pub event_dates: Vec<String>,
32 pub importance: f64,
33 pub access_count: i64,
34 pub last_accessed_at: Option<String>,
35 pub superseded_at: Option<String>,
36 pub superseded_by_id: Option<String>,
37}
38
39#[derive(Debug, Clone, Deserialize)]
41pub struct MemorySearchHit {
42 #[serde(flatten)]
43 pub memory: Memory,
44 pub score: f64,
45}
46
47#[derive(Debug, Clone, Deserialize)]
49pub struct MemorySearchResponse {
50 pub query: String,
51 pub memories: Vec<MemorySearchHit>,
52}
53
54#[derive(Debug, Clone, Deserialize)]
56pub struct MemoryIngestResponse {
57 pub source_session_id: Option<String>,
58 pub memories: Vec<Memory>,
59}
60
61#[derive(Debug, Clone, Deserialize)]
64pub struct MemoryProfileResponse {
65 pub r#static: Vec<Memory>,
66 pub dynamic: Vec<Memory>,
67}
68
69#[derive(Debug, Clone, Deserialize)]
71pub struct MemoryRelation {
72 pub source_memory_id: String,
73 pub target_memory_id: String,
74 pub relation_type: String,
75 pub confidence: f64,
76 pub created_at: String,
77}
78
79#[derive(Debug, Clone, Deserialize)]
81pub struct MemoryRelationsResponse {
82 pub memory_id: String,
83 pub relations: Vec<MemoryRelation>,
84}
85
86#[derive(Debug, Clone, Default, Serialize)]
88pub struct RememberOptions {
89 #[serde(skip_serializing_if = "Option::is_none")]
90 scope: Option<String>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 kind: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 metadata: Option<HashMap<String, Value>>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 source_session_id: Option<String>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 importance: Option<f64>,
99}
100
101impl RememberOptions {
102 pub fn new() -> Self {
103 Self::default()
104 }
105
106 pub fn scope(mut self, scope: impl Into<String>) -> Self {
111 self.scope = Some(scope.into());
112 self
113 }
114
115 pub fn kind(mut self, kind: impl Into<String>) -> Self {
118 self.kind = Some(kind.into());
119 self
120 }
121
122 pub fn metadata(mut self, metadata: HashMap<String, Value>) -> Self {
123 self.metadata = Some(metadata);
124 self
125 }
126
127 pub fn source_session_id(mut self, source_session_id: impl Into<String>) -> Self {
128 self.source_session_id = Some(source_session_id.into());
129 self
130 }
131
132 pub fn importance(mut self, importance: f64) -> Self {
134 self.importance = Some(importance);
135 self
136 }
137}
138
139#[derive(Debug, Clone, Default, Serialize)]
141pub struct IngestMemoryOptions {
142 #[serde(skip_serializing_if = "Option::is_none")]
143 scope: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 source_session_id: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 metadata: Option<HashMap<String, Value>>,
148}
149
150impl IngestMemoryOptions {
151 pub fn new() -> Self {
152 Self::default()
153 }
154
155 pub fn scope(mut self, scope: impl Into<String>) -> Self {
156 self.scope = Some(scope.into());
157 self
158 }
159
160 pub fn source_session_id(mut self, source_session_id: impl Into<String>) -> Self {
161 self.source_session_id = Some(source_session_id.into());
162 self
163 }
164
165 pub fn metadata(mut self, metadata: HashMap<String, Value>) -> Self {
166 self.metadata = Some(metadata);
167 self
168 }
169}
170
171#[derive(Debug, Clone, Default)]
173pub struct ListMemoriesOptions {
174 pub scope: Option<String>,
175 pub include_superseded: bool,
176 pub limit: Option<u32>,
177 pub offset: Option<u32>,
178}
179
180impl Client {
181 pub async fn remember(
186 &self,
187 content: &str,
188 options: RememberOptions,
189 ) -> Result<Memory, BoxxkiteError> {
190 #[derive(Serialize)]
191 struct Body<'a> {
192 content: &'a str,
193 #[serde(flatten)]
194 options: &'a RememberOptions,
195 }
196 let builder = self.request(Method::POST, "/v1/memory").json(&Body {
197 content,
198 options: &options,
199 });
200 self.send(builder).await
201 }
202
203 pub async fn ingest_memory(
207 &self,
208 content: &str,
209 options: IngestMemoryOptions,
210 ) -> Result<MemoryIngestResponse, BoxxkiteError> {
211 #[derive(Serialize)]
212 struct Body<'a> {
213 content: &'a str,
214 #[serde(flatten)]
215 options: &'a IngestMemoryOptions,
216 }
217 let builder = self.request(Method::POST, "/v1/memory/ingest").json(&Body {
218 content,
219 options: &options,
220 });
221 self.send(builder).await
222 }
223
224 pub async fn recall(
227 &self,
228 query: &str,
229 scope: Option<&str>,
230 limit: Option<u32>,
231 ) -> Result<MemorySearchResponse, BoxxkiteError> {
232 let mut params = vec![("q".to_string(), query.to_string())];
233 params.push(("limit".to_string(), limit.unwrap_or(10).to_string()));
234 if let Some(scope) = scope {
235 params.push(("scope".to_string(), scope.to_string()));
236 }
237 let builder = self
238 .request(Method::GET, "/v1/memory/search")
239 .query(¶ms);
240 self.send(builder).await
241 }
242
243 pub async fn memory_profile(
246 &self,
247 scope: Option<&str>,
248 limit: Option<u32>,
249 ) -> Result<MemoryProfileResponse, BoxxkiteError> {
250 let mut params = vec![("limit".to_string(), limit.unwrap_or(20).to_string())];
251 if let Some(scope) = scope {
252 params.push(("scope".to_string(), scope.to_string()));
253 }
254 let builder = self
255 .request(Method::GET, "/v1/memory/profile")
256 .query(¶ms);
257 self.send(builder).await
258 }
259
260 pub async fn list_memories(
262 &self,
263 options: ListMemoriesOptions,
264 ) -> Result<Vec<Memory>, BoxxkiteError> {
265 let mut params = vec![
266 (
267 "include_superseded".to_string(),
268 options.include_superseded.to_string(),
269 ),
270 ("limit".to_string(), options.limit.unwrap_or(50).to_string()),
271 (
272 "offset".to_string(),
273 options.offset.unwrap_or(0).to_string(),
274 ),
275 ];
276 if let Some(scope) = options.scope {
277 params.push(("scope".to_string(), scope));
278 }
279 let builder = self.request(Method::GET, "/v1/memory").query(¶ms);
280 self.send_or_default(builder).await
281 }
282
283 pub async fn get_memory(&self, memory_id: &str) -> Result<Memory, BoxxkiteError> {
285 let builder = self.request(Method::GET, &format!("/v1/memory/{memory_id}"));
286 self.send(builder).await
287 }
288
289 pub async fn memory_relations(
292 &self,
293 memory_id: &str,
294 limit: Option<u32>,
295 ) -> Result<MemoryRelationsResponse, BoxxkiteError> {
296 let builder = self
297 .request(Method::GET, &format!("/v1/memory/{memory_id}/relations"))
298 .query(&[("limit", limit.unwrap_or(20).to_string())]);
299 self.send(builder).await
300 }
301
302 pub async fn forget_memory(&self, memory_id: &str) -> Result<(), BoxxkiteError> {
305 let builder = self.request(Method::DELETE, &format!("/v1/memory/{memory_id}"));
306 self.send_no_content(builder).await
307 }
308
309 pub async fn export_memories(&self, scope: Option<&str>) -> Result<Value, BoxxkiteError> {
312 let mut builder = self.request(Method::GET, "/v1/memory/export");
313 if let Some(scope) = scope {
314 builder = builder.query(&[("scope", scope)]);
315 }
316 self.send(builder).await
317 }
318
319 pub async fn import_memories(
322 &self,
323 memories: Vec<Value>,
324 relations: Vec<Value>,
325 ) -> Result<MemoryIngestResponse, BoxxkiteError> {
326 #[derive(Serialize)]
327 struct Body {
328 memories: Vec<Value>,
329 relations: Vec<Value>,
330 }
331 let builder = self.request(Method::POST, "/v1/memory/import").json(&Body {
332 memories,
333 relations,
334 });
335 self.send(builder).await
336 }
337}