1use serde::{Deserialize, Serialize};
7
8use crate::error::Result;
9use crate::types::{
10 EdgeType, GraphExport, GraphLinkRequest, GraphLinkResponse, GraphOptions, GraphPath,
11 MemoryGraph,
12};
13use crate::DakeraClient;
14
15#[derive(Debug, Clone, Serialize, Deserialize, Default)]
21pub enum MemoryType {
22 #[default]
23 Episodic,
24 Semantic,
25 Procedural,
26 Working,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct StoreMemoryRequest {
32 pub agent_id: String,
33 pub content: String,
34 #[serde(default)]
35 pub memory_type: MemoryType,
36 #[serde(default = "default_importance")]
37 pub importance: f32,
38 #[serde(default)]
39 pub tags: Vec<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub session_id: Option<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub metadata: Option<serde_json::Value>,
44 #[serde(skip_serializing_if = "Option::is_none")]
47 pub ttl_seconds: Option<u64>,
48 #[serde(skip_serializing_if = "Option::is_none")]
52 pub expires_at: Option<u64>,
53}
54
55fn default_importance() -> f32 {
56 0.5
57}
58
59impl StoreMemoryRequest {
60 pub fn new(agent_id: impl Into<String>, content: impl Into<String>) -> Self {
62 Self {
63 agent_id: agent_id.into(),
64 content: content.into(),
65 memory_type: MemoryType::default(),
66 importance: 0.5,
67 tags: Vec::new(),
68 session_id: None,
69 metadata: None,
70 ttl_seconds: None,
71 expires_at: None,
72 }
73 }
74
75 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
77 self.memory_type = memory_type;
78 self
79 }
80
81 pub fn with_importance(mut self, importance: f32) -> Self {
83 self.importance = importance.clamp(0.0, 1.0);
84 self
85 }
86
87 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
89 self.tags = tags;
90 self
91 }
92
93 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
95 self.session_id = Some(session_id.into());
96 self
97 }
98
99 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
101 self.metadata = Some(metadata);
102 self
103 }
104
105 pub fn with_ttl(mut self, ttl_seconds: u64) -> Self {
108 self.ttl_seconds = Some(ttl_seconds);
109 self
110 }
111
112 pub fn with_expires_at(mut self, expires_at: u64) -> Self {
115 self.expires_at = Some(expires_at);
116 self
117 }
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct StoreMemoryResponse {
123 pub memory_id: String,
124 pub agent_id: String,
125 pub namespace: String,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct RecallRequest {
131 pub agent_id: String,
132 pub query: String,
133 #[serde(default = "default_top_k")]
134 pub top_k: usize,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub memory_type: Option<MemoryType>,
137 #[serde(default)]
138 pub min_importance: f32,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub session_id: Option<String>,
141 #[serde(default)]
142 pub tags: Vec<String>,
143}
144
145fn default_top_k() -> usize {
146 5
147}
148
149impl RecallRequest {
150 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
152 Self {
153 agent_id: agent_id.into(),
154 query: query.into(),
155 top_k: 5,
156 memory_type: None,
157 min_importance: 0.0,
158 session_id: None,
159 tags: Vec::new(),
160 }
161 }
162
163 pub fn with_top_k(mut self, top_k: usize) -> Self {
165 self.top_k = top_k;
166 self
167 }
168
169 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
171 self.memory_type = Some(memory_type);
172 self
173 }
174
175 pub fn with_min_importance(mut self, min: f32) -> Self {
177 self.min_importance = min;
178 self
179 }
180
181 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
183 self.session_id = Some(session_id.into());
184 self
185 }
186
187 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
189 self.tags = tags;
190 self
191 }
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct RecalledMemory {
197 pub id: String,
198 pub content: String,
199 pub memory_type: MemoryType,
200 pub importance: f32,
201 pub score: f32,
202 #[serde(default)]
203 pub tags: Vec<String>,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub session_id: Option<String>,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub metadata: Option<serde_json::Value>,
208 pub created_at: u64,
209 pub last_accessed_at: u64,
210 pub access_count: u32,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct RecallResponse {
216 pub memories: Vec<RecalledMemory>,
217 pub total_found: usize,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct ForgetRequest {
223 pub agent_id: String,
224 #[serde(default)]
225 pub memory_ids: Vec<String>,
226 #[serde(default)]
227 pub tags: Vec<String>,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub session_id: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub before_timestamp: Option<u64>,
232}
233
234impl ForgetRequest {
235 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
237 Self {
238 agent_id: agent_id.into(),
239 memory_ids: ids,
240 tags: Vec::new(),
241 session_id: None,
242 before_timestamp: None,
243 }
244 }
245
246 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
248 Self {
249 agent_id: agent_id.into(),
250 memory_ids: Vec::new(),
251 tags,
252 session_id: None,
253 before_timestamp: None,
254 }
255 }
256
257 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
259 Self {
260 agent_id: agent_id.into(),
261 memory_ids: Vec::new(),
262 tags: Vec::new(),
263 session_id: Some(session_id.into()),
264 before_timestamp: None,
265 }
266 }
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct ForgetResponse {
272 pub deleted_count: u64,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct SessionStartRequest {
278 pub agent_id: String,
279 #[serde(skip_serializing_if = "Option::is_none")]
280 pub metadata: Option<serde_json::Value>,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct Session {
286 pub id: String,
287 pub agent_id: String,
288 pub started_at: u64,
289 #[serde(skip_serializing_if = "Option::is_none")]
290 pub ended_at: Option<u64>,
291 #[serde(skip_serializing_if = "Option::is_none")]
292 pub summary: Option<String>,
293 #[serde(skip_serializing_if = "Option::is_none")]
294 pub metadata: Option<serde_json::Value>,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct SessionEndRequest {
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub summary: Option<String>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct UpdateMemoryRequest {
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub content: Option<String>,
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub metadata: Option<serde_json::Value>,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub memory_type: Option<MemoryType>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct UpdateImportanceRequest {
318 pub memory_ids: Vec<String>,
319 pub importance: f32,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct ConsolidateRequest {
325 #[serde(skip_serializing_if = "Option::is_none")]
326 pub memory_type: Option<String>,
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub threshold: Option<f32>,
329 #[serde(default)]
330 pub dry_run: bool,
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
335pub struct ConsolidateResponse {
336 pub consolidated_count: usize,
337 pub removed_count: usize,
338 pub new_memories: Vec<String>,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct FeedbackRequest {
344 pub memory_id: String,
345 pub feedback: String,
346 #[serde(skip_serializing_if = "Option::is_none")]
347 pub relevance_score: Option<f32>,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct FeedbackResponse {
353 pub status: String,
354 pub updated_importance: Option<f32>,
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize, Default)]
366pub struct BatchMemoryFilter {
367 #[serde(skip_serializing_if = "Option::is_none")]
369 pub tags: Option<Vec<String>>,
370 #[serde(skip_serializing_if = "Option::is_none")]
372 pub min_importance: Option<f32>,
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub max_importance: Option<f32>,
376 #[serde(skip_serializing_if = "Option::is_none")]
378 pub created_after: Option<u64>,
379 #[serde(skip_serializing_if = "Option::is_none")]
381 pub created_before: Option<u64>,
382 #[serde(skip_serializing_if = "Option::is_none")]
384 pub memory_type: Option<MemoryType>,
385 #[serde(skip_serializing_if = "Option::is_none")]
387 pub session_id: Option<String>,
388}
389
390impl BatchMemoryFilter {
391 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
393 self.tags = Some(tags);
394 self
395 }
396
397 pub fn with_min_importance(mut self, min: f32) -> Self {
399 self.min_importance = Some(min);
400 self
401 }
402
403 pub fn with_max_importance(mut self, max: f32) -> Self {
405 self.max_importance = Some(max);
406 self
407 }
408
409 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
411 self.session_id = Some(session_id.into());
412 self
413 }
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct BatchRecallRequest {
419 pub agent_id: String,
421 #[serde(default)]
423 pub filter: BatchMemoryFilter,
424 #[serde(default = "default_batch_limit")]
426 pub limit: usize,
427}
428
429fn default_batch_limit() -> usize {
430 100
431}
432
433impl BatchRecallRequest {
434 pub fn new(agent_id: impl Into<String>) -> Self {
436 Self {
437 agent_id: agent_id.into(),
438 filter: BatchMemoryFilter::default(),
439 limit: 100,
440 }
441 }
442
443 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
445 self.filter = filter;
446 self
447 }
448
449 pub fn with_limit(mut self, limit: usize) -> Self {
451 self.limit = limit;
452 self
453 }
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct BatchRecallResponse {
459 pub memories: Vec<RecalledMemory>,
460 pub total: usize,
462 pub filtered: usize,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct BatchForgetRequest {
469 pub agent_id: String,
471 pub filter: BatchMemoryFilter,
473}
474
475impl BatchForgetRequest {
476 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
478 Self {
479 agent_id: agent_id.into(),
480 filter,
481 }
482 }
483}
484
485#[derive(Debug, Clone, Serialize, Deserialize)]
487pub struct BatchForgetResponse {
488 pub deleted_count: usize,
489}
490
491impl DakeraClient {
496 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
520 let url = format!("{}/v1/memory/store", self.base_url);
521 let response = self.client.post(&url).json(&request).send().await?;
522 self.handle_response(response).await
523 }
524
525 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
546 let url = format!("{}/v1/memory/recall", self.base_url);
547 let response = self.client.post(&url).json(&request).send().await?;
548 self.handle_response(response).await
549 }
550
551 pub async fn recall_simple(
553 &self,
554 agent_id: &str,
555 query: &str,
556 top_k: usize,
557 ) -> Result<RecallResponse> {
558 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
559 .await
560 }
561
562 pub async fn get_memory(&self, memory_id: &str) -> Result<RecalledMemory> {
564 let url = format!("{}/v1/memory/get/{}", self.base_url, memory_id);
565 let response = self.client.get(&url).send().await?;
566 self.handle_response(response).await
567 }
568
569 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
571 let url = format!("{}/v1/memory/forget", self.base_url);
572 let response = self.client.post(&url).json(&request).send().await?;
573 self.handle_response(response).await
574 }
575
576 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
578 let url = format!("{}/v1/memory/search", self.base_url);
579 let response = self.client.post(&url).json(&request).send().await?;
580 self.handle_response(response).await
581 }
582
583 pub async fn update_memory(
585 &self,
586 agent_id: &str,
587 memory_id: &str,
588 request: UpdateMemoryRequest,
589 ) -> Result<StoreMemoryResponse> {
590 let url = format!(
591 "{}/v1/agents/{}/memories/{}",
592 self.base_url, agent_id, memory_id
593 );
594 let response = self.client.put(&url).json(&request).send().await?;
595 self.handle_response(response).await
596 }
597
598 pub async fn update_importance(
600 &self,
601 agent_id: &str,
602 request: UpdateImportanceRequest,
603 ) -> Result<serde_json::Value> {
604 let url = format!(
605 "{}/v1/agents/{}/memories/importance",
606 self.base_url, agent_id
607 );
608 let response = self.client.put(&url).json(&request).send().await?;
609 self.handle_response(response).await
610 }
611
612 pub async fn consolidate(
614 &self,
615 agent_id: &str,
616 request: ConsolidateRequest,
617 ) -> Result<ConsolidateResponse> {
618 let url = format!(
619 "{}/v1/agents/{}/memories/consolidate",
620 self.base_url, agent_id
621 );
622 let response = self.client.post(&url).json(&request).send().await?;
623 self.handle_response(response).await
624 }
625
626 pub async fn memory_feedback(
628 &self,
629 agent_id: &str,
630 request: FeedbackRequest,
631 ) -> Result<FeedbackResponse> {
632 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
633 let response = self.client.post(&url).json(&request).send().await?;
634 self.handle_response(response).await
635 }
636
637 pub async fn memory_graph(
658 &self,
659 memory_id: &str,
660 options: GraphOptions,
661 ) -> Result<MemoryGraph> {
662 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
663 let depth = options.depth.unwrap_or(1);
664 url.push_str(&format!("?depth={}", depth));
665 if let Some(types) = &options.types {
666 let type_strs: Vec<String> = types
667 .iter()
668 .map(|t| {
669 serde_json::to_value(t)
670 .unwrap()
671 .as_str()
672 .unwrap_or("")
673 .to_string()
674 })
675 .collect();
676 if !type_strs.is_empty() {
677 url.push_str(&format!("&types={}", type_strs.join(",")));
678 }
679 }
680 let response = self.client.get(&url).send().await?;
681 self.handle_response(response).await
682 }
683
684 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
697 let url = format!(
698 "{}/v1/memories/{}/path?target={}",
699 self.base_url,
700 source_id,
701 urlencoding::encode(target_id)
702 );
703 let response = self.client.get(&url).send().await?;
704 self.handle_response(response).await
705 }
706
707 pub async fn memory_link(
720 &self,
721 source_id: &str,
722 target_id: &str,
723 edge_type: EdgeType,
724 ) -> Result<GraphLinkResponse> {
725 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
726 let request = GraphLinkRequest {
727 target_id: target_id.to_string(),
728 edge_type,
729 };
730 let response = self.client.post(&url).json(&request).send().await?;
731 self.handle_response(response).await
732 }
733
734 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
742 let url = format!(
743 "{}/v1/agents/{}/graph/export?format={}",
744 self.base_url, agent_id, format
745 );
746 let response = self.client.get(&url).send().await?;
747 self.handle_response(response).await
748 }
749
750 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
756 let url = format!("{}/v1/sessions/start", self.base_url);
757 let request = SessionStartRequest {
758 agent_id: agent_id.to_string(),
759 metadata: None,
760 };
761 let response = self.client.post(&url).json(&request).send().await?;
762 self.handle_response(response).await
763 }
764
765 pub async fn start_session_with_metadata(
767 &self,
768 agent_id: &str,
769 metadata: serde_json::Value,
770 ) -> Result<Session> {
771 let url = format!("{}/v1/sessions/start", self.base_url);
772 let request = SessionStartRequest {
773 agent_id: agent_id.to_string(),
774 metadata: Some(metadata),
775 };
776 let response = self.client.post(&url).json(&request).send().await?;
777 self.handle_response(response).await
778 }
779
780 pub async fn end_session(&self, session_id: &str, summary: Option<String>) -> Result<Session> {
782 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
783 let request = SessionEndRequest { summary };
784 let response = self.client.post(&url).json(&request).send().await?;
785 self.handle_response(response).await
786 }
787
788 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
790 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
791 let response = self.client.get(&url).send().await?;
792 self.handle_response(response).await
793 }
794
795 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
797 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
798 let response = self.client.get(&url).send().await?;
799 self.handle_response(response).await
800 }
801
802 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
804 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
805 let response = self.client.get(&url).send().await?;
806 self.handle_response(response).await
807 }
808
809 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
833 let url = format!("{}/v1/memories/recall/batch", self.base_url);
834 let response = self.client.post(&url).json(&request).send().await?;
835 self.handle_response(response).await
836 }
837
838 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
858 let url = format!("{}/v1/memories/forget/batch", self.base_url);
859 let response = self.client.delete(&url).json(&request).send().await?;
860 self.handle_response(response).await
861 }
862}