Skip to main content

dakera_client/
memory.rs

1//! Memory-oriented client methods for Dakera AI Agent Memory Platform
2//!
3//! Provides high-level methods for storing, recalling, and managing
4//! agent memories and sessions through the Dakera API.
5
6use serde::{Deserialize, Serialize};
7
8use crate::error::Result;
9use crate::DakeraClient;
10
11// ============================================================================
12// Memory Types (client-side)
13// ============================================================================
14
15/// Memory type classification
16#[derive(Debug, Clone, Serialize, Deserialize, Default)]
17pub enum MemoryType {
18    #[default]
19    Episodic,
20    Semantic,
21    Procedural,
22    Working,
23}
24
25/// Store a memory request
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct StoreMemoryRequest {
28    pub agent_id: String,
29    pub content: String,
30    #[serde(default)]
31    pub memory_type: MemoryType,
32    #[serde(default = "default_importance")]
33    pub importance: f32,
34    #[serde(default)]
35    pub tags: Vec<String>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub session_id: Option<String>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub metadata: Option<serde_json::Value>,
40}
41
42fn default_importance() -> f32 {
43    0.5
44}
45
46impl StoreMemoryRequest {
47    /// Create a new store memory request
48    pub fn new(agent_id: impl Into<String>, content: impl Into<String>) -> Self {
49        Self {
50            agent_id: agent_id.into(),
51            content: content.into(),
52            memory_type: MemoryType::default(),
53            importance: 0.5,
54            tags: Vec::new(),
55            session_id: None,
56            metadata: None,
57        }
58    }
59
60    /// Set memory type
61    pub fn with_type(mut self, memory_type: MemoryType) -> Self {
62        self.memory_type = memory_type;
63        self
64    }
65
66    /// Set importance score
67    pub fn with_importance(mut self, importance: f32) -> Self {
68        self.importance = importance.clamp(0.0, 1.0);
69        self
70    }
71
72    /// Set tags
73    pub fn with_tags(mut self, tags: Vec<String>) -> Self {
74        self.tags = tags;
75        self
76    }
77
78    /// Set session ID
79    pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
80        self.session_id = Some(session_id.into());
81        self
82    }
83
84    /// Set metadata
85    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
86        self.metadata = Some(metadata);
87        self
88    }
89}
90
91/// Stored memory response
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct StoreMemoryResponse {
94    pub memory_id: String,
95    pub agent_id: String,
96    pub namespace: String,
97}
98
99/// Recall memories request
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct RecallRequest {
102    pub agent_id: String,
103    pub query: String,
104    #[serde(default = "default_top_k")]
105    pub top_k: usize,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub memory_type: Option<MemoryType>,
108    #[serde(default)]
109    pub min_importance: f32,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub session_id: Option<String>,
112    #[serde(default)]
113    pub tags: Vec<String>,
114}
115
116fn default_top_k() -> usize {
117    5
118}
119
120impl RecallRequest {
121    /// Create a new recall request
122    pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
123        Self {
124            agent_id: agent_id.into(),
125            query: query.into(),
126            top_k: 5,
127            memory_type: None,
128            min_importance: 0.0,
129            session_id: None,
130            tags: Vec::new(),
131        }
132    }
133
134    /// Set number of results
135    pub fn with_top_k(mut self, top_k: usize) -> Self {
136        self.top_k = top_k;
137        self
138    }
139
140    /// Filter by memory type
141    pub fn with_type(mut self, memory_type: MemoryType) -> Self {
142        self.memory_type = Some(memory_type);
143        self
144    }
145
146    /// Set minimum importance threshold
147    pub fn with_min_importance(mut self, min: f32) -> Self {
148        self.min_importance = min;
149        self
150    }
151
152    /// Filter by session
153    pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
154        self.session_id = Some(session_id.into());
155        self
156    }
157
158    /// Filter by tags
159    pub fn with_tags(mut self, tags: Vec<String>) -> Self {
160        self.tags = tags;
161        self
162    }
163}
164
165/// A recalled memory
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct RecalledMemory {
168    pub id: String,
169    pub content: String,
170    pub memory_type: MemoryType,
171    pub importance: f32,
172    pub score: f32,
173    #[serde(default)]
174    pub tags: Vec<String>,
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub session_id: Option<String>,
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub metadata: Option<serde_json::Value>,
179    pub created_at: u64,
180    pub last_accessed_at: u64,
181    pub access_count: u32,
182}
183
184/// Recall response
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct RecallResponse {
187    pub memories: Vec<RecalledMemory>,
188    pub total_found: usize,
189}
190
191/// Forget (delete) memories request
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct ForgetRequest {
194    pub agent_id: String,
195    #[serde(default)]
196    pub memory_ids: Vec<String>,
197    #[serde(default)]
198    pub tags: Vec<String>,
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub session_id: Option<String>,
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub before_timestamp: Option<u64>,
203}
204
205impl ForgetRequest {
206    /// Forget specific memories by ID
207    pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
208        Self {
209            agent_id: agent_id.into(),
210            memory_ids: ids,
211            tags: Vec::new(),
212            session_id: None,
213            before_timestamp: None,
214        }
215    }
216
217    /// Forget memories with specific tags
218    pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
219        Self {
220            agent_id: agent_id.into(),
221            memory_ids: Vec::new(),
222            tags,
223            session_id: None,
224            before_timestamp: None,
225        }
226    }
227
228    /// Forget all memories in a session
229    pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
230        Self {
231            agent_id: agent_id.into(),
232            memory_ids: Vec::new(),
233            tags: Vec::new(),
234            session_id: Some(session_id.into()),
235            before_timestamp: None,
236        }
237    }
238}
239
240/// Forget response
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct ForgetResponse {
243    pub deleted_count: u64,
244}
245
246/// Session start request
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct SessionStartRequest {
249    pub agent_id: String,
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub metadata: Option<serde_json::Value>,
252}
253
254/// Session information
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct Session {
257    pub id: String,
258    pub agent_id: String,
259    pub started_at: u64,
260    #[serde(skip_serializing_if = "Option::is_none")]
261    pub ended_at: Option<u64>,
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub summary: Option<String>,
264    #[serde(skip_serializing_if = "Option::is_none")]
265    pub metadata: Option<serde_json::Value>,
266}
267
268/// Session end request
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct SessionEndRequest {
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub summary: Option<String>,
273}
274
275/// Request to update a memory
276#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct UpdateMemoryRequest {
278    #[serde(skip_serializing_if = "Option::is_none")]
279    pub content: Option<String>,
280    #[serde(skip_serializing_if = "Option::is_none")]
281    pub metadata: Option<serde_json::Value>,
282    #[serde(skip_serializing_if = "Option::is_none")]
283    pub memory_type: Option<MemoryType>,
284}
285
286/// Request to update memory importance
287#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct UpdateImportanceRequest {
289    pub memory_ids: Vec<String>,
290    pub importance: f32,
291}
292
293/// Request to consolidate memories
294#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct ConsolidateRequest {
296    #[serde(skip_serializing_if = "Option::is_none")]
297    pub memory_type: Option<String>,
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub threshold: Option<f32>,
300    #[serde(default)]
301    pub dry_run: bool,
302}
303
304/// Response from consolidation
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct ConsolidateResponse {
307    pub consolidated_count: usize,
308    pub removed_count: usize,
309    pub new_memories: Vec<String>,
310}
311
312/// Request for memory feedback
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct FeedbackRequest {
315    pub memory_id: String,
316    pub feedback: String,
317    #[serde(skip_serializing_if = "Option::is_none")]
318    pub relevance_score: Option<f32>,
319}
320
321/// Response from feedback
322#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct FeedbackResponse {
324    pub status: String,
325    pub updated_importance: Option<f32>,
326}
327
328// ============================================================================
329// Memory Client Methods
330// ============================================================================
331
332impl DakeraClient {
333    // ========================================================================
334    // Memory Operations
335    // ========================================================================
336
337    /// Store a memory for an agent
338    ///
339    /// # Example
340    ///
341    /// ```rust,no_run
342    /// use dakera_client::{DakeraClient, memory::StoreMemoryRequest};
343    ///
344    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
345    /// let client = DakeraClient::new("http://localhost:3000")?;
346    ///
347    /// let request = StoreMemoryRequest::new("agent-1", "The user prefers dark mode")
348    ///     .with_importance(0.8)
349    ///     .with_tags(vec!["preferences".to_string()]);
350    ///
351    /// let response = client.store_memory(request).await?;
352    /// println!("Stored memory: {}", response.memory_id);
353    /// # Ok(())
354    /// # }
355    /// ```
356    pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
357        let url = format!("{}/v1/memory/store", self.base_url);
358        let response = self.client.post(&url).json(&request).send().await?;
359        self.handle_response(response).await
360    }
361
362    /// Recall memories by semantic query
363    ///
364    /// # Example
365    ///
366    /// ```rust,no_run
367    /// use dakera_client::{DakeraClient, memory::RecallRequest};
368    ///
369    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
370    /// let client = DakeraClient::new("http://localhost:3000")?;
371    ///
372    /// let request = RecallRequest::new("agent-1", "user preferences")
373    ///     .with_top_k(10);
374    ///
375    /// let response = client.recall(request).await?;
376    /// for memory in response.memories {
377    ///     println!("{}: {} (score: {})", memory.id, memory.content, memory.score);
378    /// }
379    /// # Ok(())
380    /// # }
381    /// ```
382    pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
383        let url = format!("{}/v1/memory/recall", self.base_url);
384        let response = self.client.post(&url).json(&request).send().await?;
385        self.handle_response(response).await
386    }
387
388    /// Simple recall with just agent_id and query (convenience method)
389    pub async fn recall_simple(
390        &self,
391        agent_id: &str,
392        query: &str,
393        top_k: usize,
394    ) -> Result<RecallResponse> {
395        self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
396            .await
397    }
398
399    /// Get a specific memory by ID
400    pub async fn get_memory(&self, memory_id: &str) -> Result<RecalledMemory> {
401        let url = format!("{}/v1/memory/get/{}", self.base_url, memory_id);
402        let response = self.client.get(&url).send().await?;
403        self.handle_response(response).await
404    }
405
406    /// Forget (delete) memories
407    pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
408        let url = format!("{}/v1/memory/forget", self.base_url);
409        let response = self.client.post(&url).json(&request).send().await?;
410        self.handle_response(response).await
411    }
412
413    /// Search memories with advanced filters
414    pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
415        let url = format!("{}/v1/memory/search", self.base_url);
416        let response = self.client.post(&url).json(&request).send().await?;
417        self.handle_response(response).await
418    }
419
420    /// Update an existing memory
421    pub async fn update_memory(
422        &self,
423        agent_id: &str,
424        memory_id: &str,
425        request: UpdateMemoryRequest,
426    ) -> Result<StoreMemoryResponse> {
427        let url = format!(
428            "{}/v1/agents/{}/memories/{}",
429            self.base_url, agent_id, memory_id
430        );
431        let response = self.client.put(&url).json(&request).send().await?;
432        self.handle_response(response).await
433    }
434
435    /// Update importance of memories
436    pub async fn update_importance(
437        &self,
438        agent_id: &str,
439        request: UpdateImportanceRequest,
440    ) -> Result<serde_json::Value> {
441        let url = format!(
442            "{}/v1/agents/{}/memories/importance",
443            self.base_url, agent_id
444        );
445        let response = self.client.put(&url).json(&request).send().await?;
446        self.handle_response(response).await
447    }
448
449    /// Consolidate memories for an agent
450    pub async fn consolidate(
451        &self,
452        agent_id: &str,
453        request: ConsolidateRequest,
454    ) -> Result<ConsolidateResponse> {
455        let url = format!(
456            "{}/v1/agents/{}/memories/consolidate",
457            self.base_url, agent_id
458        );
459        let response = self.client.post(&url).json(&request).send().await?;
460        self.handle_response(response).await
461    }
462
463    /// Submit feedback on a memory recall
464    pub async fn memory_feedback(
465        &self,
466        agent_id: &str,
467        request: FeedbackRequest,
468    ) -> Result<FeedbackResponse> {
469        let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
470        let response = self.client.post(&url).json(&request).send().await?;
471        self.handle_response(response).await
472    }
473
474    // ========================================================================
475    // Session Operations
476    // ========================================================================
477
478    /// Start a new session for an agent
479    pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
480        let url = format!("{}/v1/sessions/start", self.base_url);
481        let request = SessionStartRequest {
482            agent_id: agent_id.to_string(),
483            metadata: None,
484        };
485        let response = self.client.post(&url).json(&request).send().await?;
486        self.handle_response(response).await
487    }
488
489    /// Start a session with metadata
490    pub async fn start_session_with_metadata(
491        &self,
492        agent_id: &str,
493        metadata: serde_json::Value,
494    ) -> Result<Session> {
495        let url = format!("{}/v1/sessions/start", self.base_url);
496        let request = SessionStartRequest {
497            agent_id: agent_id.to_string(),
498            metadata: Some(metadata),
499        };
500        let response = self.client.post(&url).json(&request).send().await?;
501        self.handle_response(response).await
502    }
503
504    /// End a session, optionally with a summary
505    pub async fn end_session(&self, session_id: &str, summary: Option<String>) -> Result<Session> {
506        let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
507        let request = SessionEndRequest { summary };
508        let response = self.client.post(&url).json(&request).send().await?;
509        self.handle_response(response).await
510    }
511
512    /// Get a session by ID
513    pub async fn get_session(&self, session_id: &str) -> Result<Session> {
514        let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
515        let response = self.client.get(&url).send().await?;
516        self.handle_response(response).await
517    }
518
519    /// List sessions for an agent
520    pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
521        let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
522        let response = self.client.get(&url).send().await?;
523        self.handle_response(response).await
524    }
525
526    /// Get memories in a session
527    pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
528        let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
529        let response = self.client.get(&url).send().await?;
530        self.handle_response(response).await
531    }
532}