1use serde::{Deserialize, Serialize};
7
8use crate::error::Result;
9use crate::types::{
10 AgentFeedbackSummary, EdgeType, FeedbackHealthResponse, FeedbackHistoryResponse,
11 FeedbackResponse, FeedbackSignal, GraphExport, GraphLinkRequest, GraphLinkResponse,
12 GraphOptions, GraphPath, MemoryFeedbackBody, MemoryGraph, MemoryImportancePatch, TifScore,
13};
14use crate::DakeraClient;
15
16#[derive(Debug, Clone, Serialize, Deserialize, Default)]
22#[serde(rename_all = "lowercase")]
23pub enum MemoryType {
24 #[default]
25 Episodic,
26 Semantic,
27 Procedural,
28 Working,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct StoreMemoryRequest {
34 pub agent_id: String,
35 pub content: String,
36 #[serde(default)]
37 pub memory_type: MemoryType,
38 #[serde(default = "default_importance")]
39 pub importance: f32,
40 #[serde(default)]
41 pub tags: Vec<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub session_id: Option<String>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub metadata: Option<serde_json::Value>,
46 #[serde(skip_serializing_if = "Option::is_none")]
49 pub ttl_seconds: Option<u64>,
50 #[serde(skip_serializing_if = "Option::is_none")]
54 pub expires_at: Option<u64>,
55 #[serde(skip_serializing_if = "Option::is_none")]
59 pub valid_from: Option<i64>,
60}
61
62fn default_importance() -> f32 {
63 0.5
64}
65
66impl StoreMemoryRequest {
67 pub fn new(agent_id: impl Into<String>, content: impl Into<String>) -> Self {
69 Self {
70 agent_id: agent_id.into(),
71 content: content.into(),
72 memory_type: MemoryType::default(),
73 importance: 0.5,
74 tags: Vec::new(),
75 session_id: None,
76 metadata: None,
77 ttl_seconds: None,
78 expires_at: None,
79 valid_from: None,
80 }
81 }
82
83 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
85 self.memory_type = memory_type;
86 self
87 }
88
89 pub fn with_importance(mut self, importance: f32) -> Self {
91 self.importance = importance.clamp(0.0, 1.0);
92 self
93 }
94
95 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
97 self.tags = tags;
98 self
99 }
100
101 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
103 self.session_id = Some(session_id.into());
104 self
105 }
106
107 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
109 self.metadata = Some(metadata);
110 self
111 }
112
113 pub fn with_ttl(mut self, ttl_seconds: u64) -> Self {
116 self.ttl_seconds = Some(ttl_seconds);
117 self
118 }
119
120 pub fn with_expires_at(mut self, expires_at: u64) -> Self {
123 self.expires_at = Some(expires_at);
124 self
125 }
126
127 pub fn with_valid_from(mut self, valid_from: i64) -> Self {
130 self.valid_from = Some(valid_from);
131 self
132 }
133}
134
135#[derive(Debug, Clone, Serialize)]
142pub struct StoreMemoryResponse {
143 pub memory_id: String,
145 pub agent_id: String,
147 pub namespace: String,
149 pub embedding_time_ms: Option<u64>,
151}
152
153impl<'de> serde::Deserialize<'de> for StoreMemoryResponse {
154 fn deserialize<D: serde::Deserializer<'de>>(
155 deserializer: D,
156 ) -> std::result::Result<Self, D::Error> {
157 use serde::de::Error;
158 let val = serde_json::Value::deserialize(deserializer)?;
159
160 if let Some(memory) = val.get("memory") {
162 let memory_id = memory
163 .get("id")
164 .and_then(|v| v.as_str())
165 .ok_or_else(|| D::Error::missing_field("memory.id"))?
166 .to_string();
167 let agent_id = memory
168 .get("agent_id")
169 .and_then(|v| v.as_str())
170 .unwrap_or("")
171 .to_string();
172 let namespace = memory
173 .get("namespace")
174 .and_then(|v| v.as_str())
175 .unwrap_or("default")
176 .to_string();
177 let embedding_time_ms = val.get("embedding_time_ms").and_then(|v| v.as_u64());
178 return Ok(Self {
179 memory_id,
180 agent_id,
181 namespace,
182 embedding_time_ms,
183 });
184 }
185
186 let memory_id = val
188 .get("memory_id")
189 .and_then(|v| v.as_str())
190 .ok_or_else(|| D::Error::missing_field("memory_id"))?
191 .to_string();
192 let agent_id = val
193 .get("agent_id")
194 .and_then(|v| v.as_str())
195 .unwrap_or("")
196 .to_string();
197 let namespace = val
198 .get("namespace")
199 .and_then(|v| v.as_str())
200 .unwrap_or("default")
201 .to_string();
202 Ok(Self {
203 memory_id,
204 agent_id,
205 namespace,
206 embedding_time_ms: None,
207 })
208 }
209}
210
211#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
217#[serde(rename_all = "snake_case")]
218pub enum FusionStrategy {
219 #[default]
224 Rrf,
225 #[serde(rename = "minmax")]
227 MinMax,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
235#[serde(rename_all = "snake_case")]
236pub enum RoutingMode {
237 Auto,
239 Vector,
241 Bm25,
243 Hybrid,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct RecallRequest {
250 pub agent_id: String,
251 pub query: String,
252 #[serde(default = "default_top_k")]
253 pub top_k: usize,
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub memory_type: Option<MemoryType>,
256 #[serde(default)]
257 pub min_importance: f32,
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub session_id: Option<String>,
260 #[serde(default)]
261 pub tags: Vec<String>,
262 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
265 pub include_associated: bool,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub associated_memories_cap: Option<u32>,
269 #[serde(skip_serializing_if = "Option::is_none")]
271 pub associated_memories_depth: Option<u8>,
272 #[serde(skip_serializing_if = "Option::is_none")]
274 pub associated_memories_min_weight: Option<f32>,
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub since: Option<String>,
278 #[serde(skip_serializing_if = "Option::is_none")]
280 pub until: Option<String>,
281 #[serde(skip_serializing_if = "Option::is_none")]
283 pub routing: Option<RoutingMode>,
284 #[serde(skip_serializing_if = "Option::is_none")]
287 pub rerank: Option<bool>,
288 #[serde(skip_serializing_if = "Option::is_none")]
290 pub fusion: Option<FusionStrategy>,
291 #[serde(skip_serializing_if = "Option::is_none")]
296 pub vector_weight: Option<f32>,
297 #[serde(skip_serializing_if = "Option::is_none")]
302 pub iterations: Option<u8>,
303 #[serde(skip_serializing_if = "Option::is_none")]
307 pub neighborhood: Option<bool>,
308}
309
310fn default_top_k() -> usize {
311 5
312}
313
314impl RecallRequest {
315 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
317 Self {
318 agent_id: agent_id.into(),
319 query: query.into(),
320 top_k: 5,
321 memory_type: None,
322 min_importance: 0.0,
323 session_id: None,
324 tags: Vec::new(),
325 include_associated: false,
326 associated_memories_cap: None,
327 associated_memories_depth: None,
328 associated_memories_min_weight: None,
329 since: None,
330 until: None,
331 routing: None,
332 rerank: None,
333 fusion: None,
334 vector_weight: None,
335 iterations: None,
336 neighborhood: None,
337 }
338 }
339
340 pub fn with_top_k(mut self, top_k: usize) -> Self {
342 self.top_k = top_k;
343 self
344 }
345
346 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
348 self.memory_type = Some(memory_type);
349 self
350 }
351
352 pub fn with_min_importance(mut self, min: f32) -> Self {
354 self.min_importance = min;
355 self
356 }
357
358 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
360 self.session_id = Some(session_id.into());
361 self
362 }
363
364 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
366 self.tags = tags;
367 self
368 }
369
370 pub fn with_associated(mut self) -> Self {
372 self.include_associated = true;
373 self
374 }
375
376 pub fn with_associated_cap(mut self, cap: u32) -> Self {
378 self.include_associated = true;
379 self.associated_memories_cap = Some(cap);
380 self
381 }
382
383 pub fn with_since(mut self, since: impl Into<String>) -> Self {
385 self.since = Some(since.into());
386 self
387 }
388
389 pub fn with_until(mut self, until: impl Into<String>) -> Self {
391 self.until = Some(until.into());
392 self
393 }
394
395 pub fn with_routing(mut self, routing: RoutingMode) -> Self {
397 self.routing = Some(routing);
398 self
399 }
400
401 pub fn with_rerank(mut self, rerank: bool) -> Self {
403 self.rerank = Some(rerank);
404 self
405 }
406
407 pub fn with_associated_depth(mut self, depth: u8) -> Self {
409 self.include_associated = true;
410 self.associated_memories_depth = Some(depth);
411 self
412 }
413
414 pub fn with_associated_min_weight(mut self, weight: f32) -> Self {
416 self.associated_memories_min_weight = Some(weight);
417 self
418 }
419
420 pub fn with_fusion(mut self, fusion: FusionStrategy) -> Self {
422 self.fusion = Some(fusion);
423 self
424 }
425
426 pub fn with_vector_weight(mut self, weight: f32) -> Self {
430 self.vector_weight = Some(weight);
431 self
432 }
433
434 pub fn with_iterations(mut self, iterations: u8) -> Self {
439 self.iterations = Some(iterations);
440 self
441 }
442
443 pub fn with_neighborhood(mut self, neighborhood: bool) -> Self {
446 self.neighborhood = Some(neighborhood);
447 self
448 }
449}
450
451#[derive(Debug, Clone, Serialize)]
453pub struct RecalledMemory {
454 pub id: String,
455 pub content: String,
456 pub memory_type: MemoryType,
457 pub importance: f32,
458 pub score: f32,
460 #[serde(skip_serializing_if = "Option::is_none")]
462 pub smart_score: Option<f32>,
463 #[serde(skip_serializing_if = "Option::is_none")]
465 pub weighted_score: Option<f32>,
466 #[serde(default)]
467 pub tags: Vec<String>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub session_id: Option<String>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub metadata: Option<serde_json::Value>,
472 pub created_at: u64,
473 pub last_accessed_at: u64,
474 pub access_count: u32,
475 #[serde(skip_serializing_if = "Option::is_none")]
477 pub depth: Option<u8>,
478 #[serde(skip_serializing_if = "Option::is_none")]
480 pub vector_score: Option<f32>,
481 #[serde(skip_serializing_if = "Option::is_none")]
483 pub text_score: Option<f32>,
484}
485
486impl<'de> serde::Deserialize<'de> for RecalledMemory {
487 fn deserialize<D: serde::Deserializer<'de>>(
488 deserializer: D,
489 ) -> std::result::Result<Self, D::Error> {
490 use serde::de::Error as _;
491 let val = serde_json::Value::deserialize(deserializer)?;
492
493 let smart_score = val
497 .get("smart_score")
498 .and_then(|v| v.as_f64())
499 .map(|v| v as f32);
500 let weighted_score = val
501 .get("weighted_score")
502 .and_then(|v| v.as_f64())
503 .map(|v| v as f32);
504 let score = smart_score
505 .or(weighted_score)
506 .or_else(|| val.get("score").and_then(|v| v.as_f64()).map(|v| v as f32))
507 .unwrap_or(0.0);
508 let vector_score = val
510 .get("vector_score")
511 .and_then(|v| v.as_f64())
512 .map(|v| v as f32);
513 let text_score = val
514 .get("text_score")
515 .and_then(|v| v.as_f64())
516 .map(|v| v as f32);
517
518 let mem = val.get("memory").unwrap_or(&val);
519
520 let id = mem
521 .get("id")
522 .and_then(|v| v.as_str())
523 .ok_or_else(|| D::Error::missing_field("id"))?
524 .to_string();
525 let content = mem
526 .get("content")
527 .and_then(|v| v.as_str())
528 .ok_or_else(|| D::Error::missing_field("content"))?
529 .to_string();
530 let memory_type: MemoryType = mem
531 .get("memory_type")
532 .and_then(|v| serde_json::from_value(v.clone()).ok())
533 .unwrap_or(MemoryType::Episodic);
534 let importance = mem
535 .get("importance")
536 .and_then(|v| v.as_f64())
537 .unwrap_or(0.5) as f32;
538 let tags: Vec<String> = mem
539 .get("tags")
540 .and_then(|v| serde_json::from_value(v.clone()).ok())
541 .unwrap_or_default();
542 let session_id = mem
543 .get("session_id")
544 .and_then(|v| v.as_str())
545 .map(String::from);
546 let metadata = mem.get("metadata").cloned().filter(|v| !v.is_null());
547 let created_at = mem.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
548 let last_accessed_at = mem
549 .get("last_accessed_at")
550 .and_then(|v| v.as_u64())
551 .unwrap_or(0);
552 let access_count = mem
553 .get("access_count")
554 .and_then(|v| v.as_u64())
555 .unwrap_or(0) as u32;
556 let depth = mem.get("depth").and_then(|v| v.as_u64()).map(|v| v as u8);
557
558 Ok(Self {
559 id,
560 content,
561 memory_type,
562 importance,
563 score,
564 smart_score,
565 weighted_score,
566 tags,
567 session_id,
568 metadata,
569 created_at,
570 last_accessed_at,
571 access_count,
572 depth,
573 vector_score,
574 text_score,
575 })
576 }
577}
578
579#[derive(Debug, Clone, Serialize, Deserialize)]
581pub struct RecallResponse {
582 pub memories: Vec<RecalledMemory>,
583 #[serde(default)]
584 pub total_found: usize,
585 #[serde(skip_serializing_if = "Option::is_none")]
587 pub associated_memories: Option<Vec<RecalledMemory>>,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct ForgetRequest {
593 pub agent_id: String,
594 #[serde(default)]
595 pub memory_ids: Vec<String>,
596 #[serde(default)]
597 pub tags: Vec<String>,
598 #[serde(skip_serializing_if = "Option::is_none")]
599 pub session_id: Option<String>,
600 #[serde(skip_serializing_if = "Option::is_none")]
601 pub before_timestamp: Option<u64>,
602}
603
604impl ForgetRequest {
605 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
607 Self {
608 agent_id: agent_id.into(),
609 memory_ids: ids,
610 tags: Vec::new(),
611 session_id: None,
612 before_timestamp: None,
613 }
614 }
615
616 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
618 Self {
619 agent_id: agent_id.into(),
620 memory_ids: Vec::new(),
621 tags,
622 session_id: None,
623 before_timestamp: None,
624 }
625 }
626
627 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
629 Self {
630 agent_id: agent_id.into(),
631 memory_ids: Vec::new(),
632 tags: Vec::new(),
633 session_id: Some(session_id.into()),
634 before_timestamp: None,
635 }
636 }
637}
638
639#[derive(Debug, Clone, Serialize, Deserialize)]
641pub struct ForgetResponse {
642 pub deleted_count: u64,
643}
644
645#[derive(Debug, Clone, Serialize, Deserialize)]
647pub struct SessionStartRequest {
648 pub agent_id: String,
649 #[serde(skip_serializing_if = "Option::is_none")]
650 pub metadata: Option<serde_json::Value>,
651}
652
653#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct Session {
656 pub id: String,
657 pub agent_id: String,
658 pub started_at: u64,
659 #[serde(skip_serializing_if = "Option::is_none")]
660 pub ended_at: Option<u64>,
661 #[serde(skip_serializing_if = "Option::is_none")]
662 pub summary: Option<String>,
663 #[serde(skip_serializing_if = "Option::is_none")]
664 pub metadata: Option<serde_json::Value>,
665 #[serde(default)]
667 pub memory_count: usize,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct SessionEndRequest {
673 #[serde(skip_serializing_if = "Option::is_none")]
674 pub summary: Option<String>,
675}
676
677#[derive(Debug, Clone, Serialize, Deserialize)]
679pub struct SessionStartResponse {
680 pub session: Session,
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize)]
685pub struct SessionEndResponse {
686 pub session: Session,
687 pub memory_count: usize,
688}
689
690#[derive(Debug, Clone, Deserialize)]
692pub struct ListSessionsResponse {
693 pub sessions: Vec<Session>,
694 #[allow(dead_code)]
695 pub total: usize,
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize)]
700pub struct UpdateMemoryRequest {
701 #[serde(skip_serializing_if = "Option::is_none")]
702 pub content: Option<String>,
703 #[serde(skip_serializing_if = "Option::is_none")]
704 pub metadata: Option<serde_json::Value>,
705 #[serde(skip_serializing_if = "Option::is_none")]
706 pub memory_type: Option<MemoryType>,
707}
708
709#[derive(Debug, Clone, Serialize, Deserialize)]
711pub struct UpdateImportanceRequest {
712 pub memory_ids: Vec<String>,
713 pub importance: f32,
714}
715
716#[derive(Debug, Clone, Serialize, Deserialize, Default)]
718pub struct ConsolidationConfig {
719 #[serde(skip_serializing_if = "Option::is_none")]
721 pub algorithm: Option<String>,
722 #[serde(skip_serializing_if = "Option::is_none")]
724 pub min_samples: Option<u32>,
725 #[serde(skip_serializing_if = "Option::is_none")]
727 pub eps: Option<f32>,
728}
729
730#[derive(Debug, Clone, Serialize, Deserialize)]
732pub struct ConsolidationLogEntry {
733 pub step: String,
734 pub memories_before: usize,
735 pub memories_after: usize,
736 pub duration_ms: f64,
737}
738
739#[derive(Debug, Clone, Serialize, Deserialize, Default)]
741pub struct ConsolidateRequest {
742 #[serde(skip_serializing_if = "Option::is_none")]
743 pub memory_type: Option<String>,
744 #[serde(skip_serializing_if = "Option::is_none")]
745 pub threshold: Option<f32>,
746 #[serde(default)]
747 pub dry_run: bool,
748 #[serde(skip_serializing_if = "Option::is_none")]
750 pub config: Option<ConsolidationConfig>,
751}
752
753#[derive(Debug, Clone, Serialize)]
758pub struct ConsolidateResponse {
759 pub consolidated_count: usize,
761 pub removed_count: usize,
763 #[serde(default)]
765 pub new_memories: Vec<String>,
766 #[serde(default, skip_serializing_if = "Vec::is_empty")]
768 pub log: Vec<ConsolidationLogEntry>,
769}
770
771impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
772 fn deserialize<D: serde::Deserializer<'de>>(
773 deserializer: D,
774 ) -> std::result::Result<Self, D::Error> {
775 let val = serde_json::Value::deserialize(deserializer)?;
776 let removed = val
778 .get("memories_removed")
779 .and_then(|v| v.as_u64())
780 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
781 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
782 .unwrap_or(0) as usize;
783 let source_ids: Vec<String> = val
784 .get("source_memory_ids")
785 .and_then(|v| v.as_array())
786 .map(|arr| {
787 arr.iter()
788 .filter_map(|v| v.as_str().map(String::from))
789 .collect()
790 })
791 .unwrap_or_default();
792 Ok(Self {
793 consolidated_count: removed,
794 removed_count: removed,
795 new_memories: source_ids,
796 log: vec![],
797 })
798 }
799}
800
801#[derive(Debug, Clone, Serialize, Deserialize)]
807pub struct MemoryImportResponse {
808 pub imported_count: usize,
809 pub skipped_count: usize,
810 #[serde(default)]
811 pub errors: Vec<String>,
812}
813
814#[derive(Debug, Clone, Serialize, Deserialize)]
816pub struct MemoryExportResponse {
817 pub data: Vec<serde_json::Value>,
818 pub format: String,
819 pub count: usize,
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize)]
828pub struct AuditEvent {
829 pub id: String,
830 pub event_type: String,
831 #[serde(skip_serializing_if = "Option::is_none")]
832 pub agent_id: Option<String>,
833 #[serde(skip_serializing_if = "Option::is_none")]
834 pub namespace: Option<String>,
835 pub timestamp: u64,
836 #[serde(default)]
837 pub details: serde_json::Value,
838}
839
840#[derive(Debug, Clone, Serialize, Deserialize)]
842pub struct AuditListResponse {
843 pub events: Vec<AuditEvent>,
844 pub total: usize,
845 #[serde(skip_serializing_if = "Option::is_none")]
846 pub cursor: Option<String>,
847}
848
849#[derive(Debug, Clone, Serialize, Deserialize)]
851pub struct AuditExportResponse {
852 pub data: String,
853 pub format: String,
854 pub count: usize,
855}
856
857#[derive(Debug, Clone, Serialize, Deserialize, Default)]
859pub struct AuditQuery {
860 #[serde(skip_serializing_if = "Option::is_none")]
861 pub agent_id: Option<String>,
862 #[serde(skip_serializing_if = "Option::is_none")]
863 pub event_type: Option<String>,
864 #[serde(skip_serializing_if = "Option::is_none")]
865 pub from: Option<u64>,
866 #[serde(skip_serializing_if = "Option::is_none")]
867 pub to: Option<u64>,
868 #[serde(skip_serializing_if = "Option::is_none")]
869 pub limit: Option<u32>,
870 #[serde(skip_serializing_if = "Option::is_none")]
871 pub cursor: Option<String>,
872}
873
874#[derive(Debug, Clone, Serialize, Deserialize)]
880pub struct ExtractionResult {
881 pub entities: Vec<serde_json::Value>,
882 pub provider: String,
883 #[serde(skip_serializing_if = "Option::is_none")]
884 pub model: Option<String>,
885 pub duration_ms: f64,
886}
887
888#[derive(Debug, Clone, Serialize, Deserialize)]
890pub struct ExtractionProviderInfo {
891 pub name: String,
892 pub available: bool,
893 #[serde(default)]
894 pub models: Vec<String>,
895}
896
897#[derive(Debug, Clone, Serialize, Deserialize)]
899#[serde(untagged)]
900pub enum ExtractProvidersResponse {
901 List(Vec<ExtractionProviderInfo>),
902 Object {
903 providers: Vec<ExtractionProviderInfo>,
904 },
905}
906
907#[derive(Debug, Clone, Serialize, Deserialize)]
913pub struct RotateEncryptionKeyRequest {
914 pub new_key: String,
916 #[serde(skip_serializing_if = "Option::is_none")]
918 pub namespace: Option<String>,
919}
920
921#[derive(Debug, Clone, Serialize, Deserialize)]
923pub struct RotateEncryptionKeyResponse {
924 pub rotated: usize,
925 pub skipped: usize,
926 #[serde(default)]
927 pub namespaces: Vec<String>,
928}
929
930#[derive(Debug, Clone, Serialize, Deserialize)]
932pub struct FeedbackRequest {
933 pub memory_id: String,
934 pub feedback: String,
935 #[serde(skip_serializing_if = "Option::is_none")]
936 pub relevance_score: Option<f32>,
937}
938
939#[derive(Debug, Clone, Serialize, Deserialize)]
941pub struct LegacyFeedbackResponse {
942 pub status: String,
943 pub updated_importance: Option<f32>,
944}
945
946#[derive(Debug, Clone, Serialize, Deserialize, Default)]
955pub struct BatchMemoryFilter {
956 #[serde(skip_serializing_if = "Option::is_none")]
958 pub tags: Option<Vec<String>>,
959 #[serde(skip_serializing_if = "Option::is_none")]
961 pub min_importance: Option<f32>,
962 #[serde(skip_serializing_if = "Option::is_none")]
964 pub max_importance: Option<f32>,
965 #[serde(skip_serializing_if = "Option::is_none")]
967 pub created_after: Option<u64>,
968 #[serde(skip_serializing_if = "Option::is_none")]
970 pub created_before: Option<u64>,
971 #[serde(skip_serializing_if = "Option::is_none")]
973 pub memory_type: Option<MemoryType>,
974 #[serde(skip_serializing_if = "Option::is_none")]
976 pub session_id: Option<String>,
977}
978
979impl BatchMemoryFilter {
980 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
982 self.tags = Some(tags);
983 self
984 }
985
986 pub fn with_min_importance(mut self, min: f32) -> Self {
988 self.min_importance = Some(min);
989 self
990 }
991
992 pub fn with_max_importance(mut self, max: f32) -> Self {
994 self.max_importance = Some(max);
995 self
996 }
997
998 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
1000 self.session_id = Some(session_id.into());
1001 self
1002 }
1003}
1004
1005#[derive(Debug, Clone, Serialize, Deserialize)]
1007pub struct BatchRecallRequest {
1008 pub agent_id: String,
1010 #[serde(default)]
1012 pub filter: BatchMemoryFilter,
1013 #[serde(default = "default_batch_limit")]
1015 pub limit: usize,
1016}
1017
1018fn default_batch_limit() -> usize {
1019 100
1020}
1021
1022impl BatchRecallRequest {
1023 pub fn new(agent_id: impl Into<String>) -> Self {
1025 Self {
1026 agent_id: agent_id.into(),
1027 filter: BatchMemoryFilter::default(),
1028 limit: 100,
1029 }
1030 }
1031
1032 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
1034 self.filter = filter;
1035 self
1036 }
1037
1038 pub fn with_limit(mut self, limit: usize) -> Self {
1040 self.limit = limit;
1041 self
1042 }
1043}
1044
1045#[derive(Debug, Clone, Serialize, Deserialize)]
1047pub struct BatchRecallResponse {
1048 pub memories: Vec<RecalledMemory>,
1049 pub total: usize,
1051 pub filtered: usize,
1053}
1054
1055#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1063pub struct BatchStoreMemoryItem {
1064 pub content: String,
1066 #[serde(default)]
1067 pub memory_type: MemoryType,
1068 #[serde(skip_serializing_if = "Option::is_none")]
1069 pub session_id: Option<String>,
1070 #[serde(default = "default_importance")]
1071 pub importance: f32,
1072 #[serde(default)]
1073 pub tags: Vec<String>,
1074 #[serde(skip_serializing_if = "Option::is_none")]
1075 pub metadata: Option<serde_json::Value>,
1076 #[serde(skip_serializing_if = "Option::is_none")]
1077 pub ttl_seconds: Option<u64>,
1078 #[serde(skip_serializing_if = "Option::is_none")]
1079 pub expires_at: Option<u64>,
1080 #[serde(skip_serializing_if = "Option::is_none")]
1083 pub valid_from: Option<i64>,
1084 #[serde(skip_serializing_if = "Option::is_none")]
1086 pub id: Option<String>,
1087}
1088
1089impl BatchStoreMemoryItem {
1090 pub fn new(content: impl Into<String>) -> Self {
1092 Self {
1093 content: content.into(),
1094 importance: default_importance(),
1095 ..Default::default()
1096 }
1097 }
1098
1099 pub fn with_importance(mut self, importance: f32) -> Self {
1101 self.importance = importance;
1102 self
1103 }
1104
1105 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
1107 self.tags = tags;
1108 self
1109 }
1110
1111 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
1113 self.session_id = Some(session_id.into());
1114 self
1115 }
1116
1117 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
1119 self.metadata = Some(metadata);
1120 self
1121 }
1122
1123 pub fn with_valid_from(mut self, valid_from: i64) -> Self {
1126 self.valid_from = Some(valid_from);
1127 self
1128 }
1129
1130 pub fn with_id(mut self, id: impl Into<String>) -> Self {
1132 self.id = Some(id.into());
1133 self
1134 }
1135}
1136
1137#[derive(Debug, Clone, Serialize, Deserialize)]
1144pub struct BatchStoreMemoryRequest {
1145 pub agent_id: String,
1147 pub memories: Vec<BatchStoreMemoryItem>,
1149}
1150
1151impl BatchStoreMemoryRequest {
1152 pub fn new(agent_id: impl Into<String>, memories: Vec<BatchStoreMemoryItem>) -> Self {
1154 Self {
1155 agent_id: agent_id.into(),
1156 memories,
1157 }
1158 }
1159}
1160
1161#[derive(Debug, Clone, Serialize, Deserialize)]
1163pub struct BatchStoredMemory {
1164 pub id: String,
1165 pub content: String,
1166 pub agent_id: String,
1167 #[serde(default)]
1168 pub tags: Vec<String>,
1169 #[serde(default)]
1170 pub importance: f32,
1171 pub created_at: u64,
1172}
1173
1174#[derive(Debug, Clone, Serialize, Deserialize)]
1176pub struct BatchStoreMemoryResponse {
1177 pub stored: Vec<BatchStoredMemory>,
1179 pub stored_count: usize,
1181 pub total_embedding_time_ms: u64,
1183}
1184
1185#[derive(Debug, Clone, Serialize, Deserialize)]
1187pub struct BatchForgetRequest {
1188 pub agent_id: String,
1190 pub filter: BatchMemoryFilter,
1192}
1193
1194impl BatchForgetRequest {
1195 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
1197 Self {
1198 agent_id: agent_id.into(),
1199 filter,
1200 }
1201 }
1202}
1203
1204#[derive(Debug, Clone, Serialize, Deserialize)]
1206pub struct BatchForgetResponse {
1207 pub deleted_count: usize,
1208}
1209
1210impl DakeraClient {
1215 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1239 let url = format!("{}/v1/memory/store", self.base_url);
1240 let response = self.client.post(&url).json(&request).send().await?;
1241 self.handle_response(response).await
1242 }
1243
1244 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1265 let url = format!("{}/v1/memory/recall", self.base_url);
1266 let response = self.client.post(&url).json(&request).send().await?;
1267 self.handle_response(response).await
1268 }
1269
1270 pub async fn recall_simple(
1272 &self,
1273 agent_id: &str,
1274 query: &str,
1275 top_k: usize,
1276 ) -> Result<RecallResponse> {
1277 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1278 .await
1279 }
1280
1281 pub async fn get_memory(&self, agent_id: &str, memory_id: &str) -> Result<RecalledMemory> {
1283 let url = format!(
1284 "{}/v1/memory/get/{}?agent_id={}",
1285 self.base_url, memory_id, agent_id
1286 );
1287 let response = self.client.get(&url).send().await?;
1288 self.handle_response(response).await
1289 }
1290
1291 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1293 let url = format!("{}/v1/memory/forget", self.base_url);
1294 let response = self.client.post(&url).json(&request).send().await?;
1295 self.handle_response(response).await
1296 }
1297
1298 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1300 let url = format!("{}/v1/memory/search", self.base_url);
1301 let response = self.client.post(&url).json(&request).send().await?;
1302 self.handle_response(response).await
1303 }
1304
1305 pub async fn update_memory(
1307 &self,
1308 agent_id: &str,
1309 memory_id: &str,
1310 request: UpdateMemoryRequest,
1311 ) -> Result<StoreMemoryResponse> {
1312 let url = format!(
1313 "{}/v1/agents/{}/memories/{}",
1314 self.base_url, agent_id, memory_id
1315 );
1316 let response = self.client.put(&url).json(&request).send().await?;
1317 self.handle_response(response).await
1318 }
1319
1320 pub async fn update_importance(
1322 &self,
1323 agent_id: &str,
1324 request: UpdateImportanceRequest,
1325 ) -> Result<serde_json::Value> {
1326 let url = format!("{}/v1/memory/importance", self.base_url);
1327 let mut last_result = serde_json::Value::Null;
1328 for memory_id in &request.memory_ids {
1329 let body = serde_json::json!({
1330 "agent_id": agent_id,
1331 "memory_id": memory_id,
1332 "importance": request.importance,
1333 });
1334 let response = self.client.post(&url).json(&body).send().await?;
1335 last_result = self.handle_response(response).await?;
1336 }
1337 Ok(last_result)
1338 }
1339
1340 pub async fn consolidate(
1342 &self,
1343 agent_id: &str,
1344 request: ConsolidateRequest,
1345 ) -> Result<ConsolidateResponse> {
1346 let url = format!("{}/v1/memory/consolidate", self.base_url);
1348 let mut body = serde_json::to_value(&request)?;
1349 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1350 let response = self.client.post(&url).json(&body).send().await?;
1351 self.handle_response(response).await
1352 }
1353
1354 pub async fn memory_feedback(
1356 &self,
1357 agent_id: &str,
1358 request: FeedbackRequest,
1359 ) -> Result<LegacyFeedbackResponse> {
1360 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1361 let response = self.client.post(&url).json(&request).send().await?;
1362 self.handle_response(response).await
1363 }
1364
1365 pub async fn feedback_memory(
1385 &self,
1386 memory_id: &str,
1387 agent_id: &str,
1388 signal: FeedbackSignal,
1389 ) -> Result<FeedbackResponse> {
1390 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1391 let body = MemoryFeedbackBody {
1392 agent_id: agent_id.to_string(),
1393 signal,
1394 };
1395 let response = self.client.post(&url).json(&body).send().await?;
1396 self.handle_response(response).await
1397 }
1398
1399 pub async fn get_memory_feedback_history(
1401 &self,
1402 memory_id: &str,
1403 ) -> Result<FeedbackHistoryResponse> {
1404 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1405 let response = self.client.get(&url).send().await?;
1406 self.handle_response(response).await
1407 }
1408
1409 pub async fn evaluate_tif(&self, memory_id: &str) -> Result<TifScore> {
1417 let history = self.get_memory_feedback_history(memory_id).await?;
1418 Ok(TifScore::from_feedback_history(&history))
1419 }
1420
1421 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1423 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1424 let response = self.client.get(&url).send().await?;
1425 self.handle_response(response).await
1426 }
1427
1428 pub async fn patch_memory_importance(
1435 &self,
1436 memory_id: &str,
1437 agent_id: &str,
1438 importance: f32,
1439 ) -> Result<FeedbackResponse> {
1440 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1441 let body = MemoryImportancePatch {
1442 agent_id: agent_id.to_string(),
1443 importance,
1444 };
1445 let response = self.client.patch(&url).json(&body).send().await?;
1446 self.handle_response(response).await
1447 }
1448
1449 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1454 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1455 let response = self.client.get(&url).send().await?;
1456 self.handle_response(response).await
1457 }
1458
1459 pub async fn memory_graph(
1480 &self,
1481 memory_id: &str,
1482 options: GraphOptions,
1483 ) -> Result<MemoryGraph> {
1484 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1485 let depth = options.depth.unwrap_or(1);
1486 url.push_str(&format!("?depth={}", depth));
1487 if let Some(types) = &options.types {
1488 let type_strs: Vec<String> = types
1489 .iter()
1490 .map(|t| {
1491 serde_json::to_value(t)
1492 .unwrap()
1493 .as_str()
1494 .unwrap_or("")
1495 .to_string()
1496 })
1497 .collect();
1498 if !type_strs.is_empty() {
1499 url.push_str(&format!("&types={}", type_strs.join(",")));
1500 }
1501 }
1502 let response = self.client.get(&url).send().await?;
1503 self.handle_response(response).await
1504 }
1505
1506 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1519 let url = format!(
1520 "{}/v1/memories/{}/path?target={}",
1521 self.base_url,
1522 source_id,
1523 urlencoding::encode(target_id)
1524 );
1525 let response = self.client.get(&url).send().await?;
1526 self.handle_response(response).await
1527 }
1528
1529 pub async fn memory_link(
1542 &self,
1543 source_id: &str,
1544 target_id: &str,
1545 edge_type: EdgeType,
1546 ) -> Result<GraphLinkResponse> {
1547 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1548 let request = GraphLinkRequest {
1549 target_id: target_id.to_string(),
1550 edge_type,
1551 };
1552 let response = self.client.post(&url).json(&request).send().await?;
1553 self.handle_response(response).await
1554 }
1555
1556 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1564 let url = format!(
1565 "{}/v1/agents/{}/graph/export?format={}",
1566 self.base_url, agent_id, format
1567 );
1568 let response = self.client.get(&url).send().await?;
1569 self.handle_response(response).await
1570 }
1571
1572 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1578 let url = format!("{}/v1/sessions/start", self.base_url);
1579 let request = SessionStartRequest {
1580 agent_id: agent_id.to_string(),
1581 metadata: None,
1582 };
1583 let response = self.client.post(&url).json(&request).send().await?;
1584 let resp: SessionStartResponse = self.handle_response(response).await?;
1585 Ok(resp.session)
1586 }
1587
1588 pub async fn start_session_with_metadata(
1590 &self,
1591 agent_id: &str,
1592 metadata: serde_json::Value,
1593 ) -> Result<Session> {
1594 let url = format!("{}/v1/sessions/start", self.base_url);
1595 let request = SessionStartRequest {
1596 agent_id: agent_id.to_string(),
1597 metadata: Some(metadata),
1598 };
1599 let response = self.client.post(&url).json(&request).send().await?;
1600 let resp: SessionStartResponse = self.handle_response(response).await?;
1601 Ok(resp.session)
1602 }
1603
1604 pub async fn end_session(
1607 &self,
1608 session_id: &str,
1609 summary: Option<String>,
1610 ) -> Result<SessionEndResponse> {
1611 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1612 let request = SessionEndRequest { summary };
1613 let response = self.client.post(&url).json(&request).send().await?;
1614 self.handle_response(response).await
1615 }
1616
1617 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1619 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1620 let response = self.client.get(&url).send().await?;
1621 self.handle_response(response).await
1622 }
1623
1624 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1626 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1627 let response = self.client.get(&url).send().await?;
1628 let wrapper: ListSessionsResponse = self.handle_response(response).await?;
1629 Ok(wrapper.sessions)
1630 }
1631
1632 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1634 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1635 let response = self.client.get(&url).send().await?;
1636 self.handle_response(response).await
1637 }
1638
1639 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1663 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1664 let response = self.client.post(&url).json(&request).send().await?;
1665 self.handle_response(response).await
1666 }
1667
1668 pub async fn store_memories_batch(
1698 &self,
1699 request: BatchStoreMemoryRequest,
1700 ) -> Result<BatchStoreMemoryResponse> {
1701 let url = format!("{}/v1/memories/store/batch", self.base_url);
1702 let response = self.client.post(&url).json(&request).send().await?;
1703 self.handle_response(response).await
1704 }
1705
1706 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1726 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1727 let response = self.client.delete(&url).json(&request).send().await?;
1728 self.handle_response(response).await
1729 }
1730
1731 pub async fn import_memories(
1749 &self,
1750 data: serde_json::Value,
1751 format: &str,
1752 agent_id: Option<&str>,
1753 namespace: Option<&str>,
1754 ) -> Result<MemoryImportResponse> {
1755 let mut body = serde_json::json!({"data": data, "format": format});
1756 if let Some(aid) = agent_id {
1757 body["agent_id"] = serde_json::Value::String(aid.to_string());
1758 }
1759 if let Some(ns) = namespace {
1760 body["namespace"] = serde_json::Value::String(ns.to_string());
1761 }
1762 let url = format!("{}/v1/import", self.base_url);
1763 let response = self.client.post(&url).json(&body).send().await?;
1764 self.handle_response(response).await
1765 }
1766
1767 pub async fn export_memories(
1771 &self,
1772 format: &str,
1773 agent_id: Option<&str>,
1774 namespace: Option<&str>,
1775 limit: Option<u32>,
1776 ) -> Result<MemoryExportResponse> {
1777 let mut params = vec![("format", format.to_string())];
1778 if let Some(aid) = agent_id {
1779 params.push(("agent_id", aid.to_string()));
1780 }
1781 if let Some(ns) = namespace {
1782 params.push(("namespace", ns.to_string()));
1783 }
1784 if let Some(l) = limit {
1785 params.push(("limit", l.to_string()));
1786 }
1787 let url = format!("{}/v1/export", self.base_url);
1788 let response = self.client.get(&url).query(¶ms).send().await?;
1789 self.handle_response(response).await
1790 }
1791
1792 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1798 let url = format!("{}/v1/audit", self.base_url);
1799 let response = self.client.get(&url).query(&query).send().await?;
1800 self.handle_response(response).await
1801 }
1802
1803 pub async fn stream_audit_events(
1807 &self,
1808 agent_id: Option<&str>,
1809 event_type: Option<&str>,
1810 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1811 let mut params: Vec<(&str, String)> = Vec::new();
1812 if let Some(aid) = agent_id {
1813 params.push(("agent_id", aid.to_string()));
1814 }
1815 if let Some(et) = event_type {
1816 params.push(("event_type", et.to_string()));
1817 }
1818 let base = format!("{}/v1/audit/stream", self.base_url);
1819 let url = if params.is_empty() {
1820 base
1821 } else {
1822 let qs = params
1823 .iter()
1824 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1825 .collect::<Vec<_>>()
1826 .join("&");
1827 format!("{}?{}", base, qs)
1828 };
1829 self.stream_sse(url).await
1830 }
1831
1832 pub async fn export_audit(
1834 &self,
1835 format: &str,
1836 agent_id: Option<&str>,
1837 event_type: Option<&str>,
1838 from_ts: Option<u64>,
1839 to_ts: Option<u64>,
1840 ) -> Result<AuditExportResponse> {
1841 let mut body = serde_json::json!({"format": format});
1842 if let Some(aid) = agent_id {
1843 body["agent_id"] = serde_json::Value::String(aid.to_string());
1844 }
1845 if let Some(et) = event_type {
1846 body["event_type"] = serde_json::Value::String(et.to_string());
1847 }
1848 if let Some(f) = from_ts {
1849 body["from"] = serde_json::Value::Number(f.into());
1850 }
1851 if let Some(t) = to_ts {
1852 body["to"] = serde_json::Value::Number(t.into());
1853 }
1854 let url = format!("{}/v1/audit/export", self.base_url);
1855 let response = self.client.post(&url).json(&body).send().await?;
1856 self.handle_response(response).await
1857 }
1858
1859 pub async fn extract_text(
1868 &self,
1869 text: &str,
1870 namespace: Option<&str>,
1871 provider: Option<&str>,
1872 model: Option<&str>,
1873 ) -> Result<ExtractionResult> {
1874 let mut body = serde_json::json!({"text": text});
1875 if let Some(ns) = namespace {
1876 body["namespace"] = serde_json::Value::String(ns.to_string());
1877 }
1878 if let Some(p) = provider {
1879 body["provider"] = serde_json::Value::String(p.to_string());
1880 }
1881 if let Some(m) = model {
1882 body["model"] = serde_json::Value::String(m.to_string());
1883 }
1884 let url = format!("{}/v1/extract", self.base_url);
1885 let response = self.client.post(&url).json(&body).send().await?;
1886 self.handle_response(response).await
1887 }
1888
1889 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1891 let url = format!("{}/v1/extract/providers", self.base_url);
1892 let response = self.client.get(&url).send().await?;
1893 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1894 Ok(match result {
1895 ExtractProvidersResponse::List(v) => v,
1896 ExtractProvidersResponse::Object { providers } => providers,
1897 })
1898 }
1899
1900 pub async fn configure_namespace_extractor(
1902 &self,
1903 namespace: &str,
1904 provider: &str,
1905 model: Option<&str>,
1906 ) -> Result<serde_json::Value> {
1907 let mut body = serde_json::json!({"provider": provider});
1908 if let Some(m) = model {
1909 body["model"] = serde_json::Value::String(m.to_string());
1910 }
1911 let url = format!(
1912 "{}/v1/namespaces/{}/extractor",
1913 self.base_url,
1914 urlencoding::encode(namespace)
1915 );
1916 let response = self.client.patch(&url).json(&body).send().await?;
1917 self.handle_response(response).await
1918 }
1919
1920 pub async fn rotate_encryption_key(
1936 &self,
1937 new_key: &str,
1938 namespace: Option<&str>,
1939 ) -> Result<RotateEncryptionKeyResponse> {
1940 let body = RotateEncryptionKeyRequest {
1941 new_key: new_key.to_string(),
1942 namespace: namespace.map(|s| s.to_string()),
1943 };
1944 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1945 let response = self.client.post(&url).json(&body).send().await?;
1946 self.handle_response(response).await
1947 }
1948}
1949
1950#[cfg(test)]
1955mod tests {
1956 use super::*;
1957
1958 #[test]
1963 fn test_memory_type_serializes_lowercase() {
1964 assert_eq!(
1965 serde_json::to_string(&MemoryType::Episodic).unwrap(),
1966 "\"episodic\""
1967 );
1968 assert_eq!(
1969 serde_json::to_string(&MemoryType::Semantic).unwrap(),
1970 "\"semantic\""
1971 );
1972 assert_eq!(
1973 serde_json::to_string(&MemoryType::Procedural).unwrap(),
1974 "\"procedural\""
1975 );
1976 assert_eq!(
1977 serde_json::to_string(&MemoryType::Working).unwrap(),
1978 "\"working\""
1979 );
1980 }
1981
1982 #[test]
1983 fn test_memory_type_default_is_episodic() {
1984 let serialized = serde_json::to_string(&MemoryType::default()).unwrap();
1985 assert_eq!(serialized, "\"episodic\"");
1986 }
1987
1988 #[test]
1989 fn test_memory_type_deserializes() {
1990 let mt: MemoryType = serde_json::from_str("\"semantic\"").unwrap();
1991 assert!(matches!(mt, MemoryType::Semantic));
1992 }
1993
1994 #[test]
1999 fn test_fusion_strategy_rrf_serializes() {
2000 assert_eq!(
2001 serde_json::to_string(&FusionStrategy::Rrf).unwrap(),
2002 "\"rrf\""
2003 );
2004 }
2005
2006 #[test]
2007 fn test_fusion_strategy_minmax_serializes_as_minmax() {
2008 assert_eq!(
2010 serde_json::to_string(&FusionStrategy::MinMax).unwrap(),
2011 "\"minmax\""
2012 );
2013 }
2014
2015 #[test]
2016 fn test_fusion_strategy_default_is_rrf() {
2017 assert_eq!(FusionStrategy::default(), FusionStrategy::Rrf);
2018 }
2019
2020 #[test]
2025 fn test_routing_mode_serializes_snake_case() {
2026 assert_eq!(
2027 serde_json::to_string(&RoutingMode::Auto).unwrap(),
2028 "\"auto\""
2029 );
2030 assert_eq!(
2031 serde_json::to_string(&RoutingMode::Vector).unwrap(),
2032 "\"vector\""
2033 );
2034 assert_eq!(
2035 serde_json::to_string(&RoutingMode::Bm25).unwrap(),
2036 "\"bm25\""
2037 );
2038 assert_eq!(
2039 serde_json::to_string(&RoutingMode::Hybrid).unwrap(),
2040 "\"hybrid\""
2041 );
2042 }
2043
2044 #[test]
2045 fn test_routing_mode_deserializes() {
2046 let mode: RoutingMode = serde_json::from_str("\"hybrid\"").unwrap();
2047 assert_eq!(mode, RoutingMode::Hybrid);
2048 }
2049
2050 #[test]
2055 fn test_store_memory_request_minimal_no_optional_fields() {
2056 let req = StoreMemoryRequest::new("agent-1", "hello world");
2057 let json = serde_json::to_string(&req).unwrap();
2058 assert!(!json.contains("session_id"));
2060 assert!(!json.contains("metadata"));
2061 assert!(!json.contains("ttl_seconds"));
2062 assert!(!json.contains("expires_at"));
2063 assert!(json.contains("\"agent_id\":\"agent-1\""));
2064 assert!(json.contains("\"content\":\"hello world\""));
2065 }
2066
2067 #[test]
2068 fn test_store_memory_request_with_session_emits_session_id() {
2069 let req = StoreMemoryRequest::new("agent-1", "content").with_session("sess-abc");
2070 let json = serde_json::to_string(&req).unwrap();
2071 assert!(json.contains("\"session_id\":\"sess-abc\""));
2072 }
2073
2074 #[test]
2075 fn test_store_memory_request_with_ttl() {
2076 let req = StoreMemoryRequest::new("agent-1", "content").with_ttl(3600);
2077 let json = serde_json::to_string(&req).unwrap();
2078 assert!(json.contains("\"ttl_seconds\":3600"));
2079 assert!(!json.contains("expires_at"));
2080 }
2081
2082 #[test]
2083 fn test_store_memory_request_with_expires_at() {
2084 let req = StoreMemoryRequest::new("a", "c").with_expires_at(1800000000);
2085 let json = serde_json::to_string(&req).unwrap();
2086 assert!(json.contains("\"expires_at\":1800000000"));
2087 }
2088
2089 #[test]
2090 fn test_store_memory_request_importance_clamps_to_zero_one() {
2091 let req = StoreMemoryRequest::new("a", "c").with_importance(1.5);
2092 assert!((req.importance - 1.0).abs() < 1e-6);
2093
2094 let req2 = StoreMemoryRequest::new("a", "c").with_importance(-0.5);
2095 assert!((req2.importance - 0.0).abs() < 1e-6);
2096 }
2097
2098 #[test]
2099 fn test_store_memory_request_default_importance_is_half() {
2100 let req = StoreMemoryRequest::new("a", "c");
2101 assert!((req.importance - 0.5).abs() < 1e-6);
2102 }
2103
2104 #[test]
2109 fn test_store_memory_response_deserializes_server_format() {
2110 let json = r#"{
2111 "memory": {
2112 "id": "mem-abc123",
2113 "agent_id": "agent-1",
2114 "namespace": "default"
2115 },
2116 "embedding_time_ms": 42
2117 }"#;
2118 let resp: StoreMemoryResponse = serde_json::from_str(json).unwrap();
2119 assert_eq!(resp.memory_id, "mem-abc123");
2120 assert_eq!(resp.agent_id, "agent-1");
2121 assert_eq!(resp.namespace, "default");
2122 assert_eq!(resp.embedding_time_ms, Some(42));
2123 }
2124
2125 #[test]
2126 fn test_store_memory_response_server_format_namespace_defaults_to_default() {
2127 let json = r#"{"memory": {"id": "mem-x", "agent_id": "a"}, "embedding_time_ms": 10}"#;
2128 let resp: StoreMemoryResponse = serde_json::from_str(json).unwrap();
2129 assert_eq!(resp.namespace, "default");
2130 }
2131
2132 #[test]
2133 fn test_store_memory_response_deserializes_legacy_format() {
2134 let json = r#"{"memory_id": "mem-legacy", "agent_id": "a", "namespace": "custom"}"#;
2135 let resp: StoreMemoryResponse = serde_json::from_str(json).unwrap();
2136 assert_eq!(resp.memory_id, "mem-legacy");
2137 assert_eq!(resp.namespace, "custom");
2138 assert!(resp.embedding_time_ms.is_none());
2139 }
2140
2141 #[test]
2142 fn test_store_memory_response_missing_id_returns_error() {
2143 let json = r#"{"memory": {"agent_id": "a"}, "embedding_time_ms": 5}"#;
2145 assert!(serde_json::from_str::<StoreMemoryResponse>(json).is_err());
2146 }
2147
2148 #[test]
2153 fn test_recall_request_minimal_no_optional_keys() {
2154 let req = RecallRequest::new("agent-1", "what did I eat?");
2155 let json = serde_json::to_string(&req).unwrap();
2156 assert!(!json.contains("memory_type"));
2158 assert!(!json.contains("session_id"));
2159 assert!(!json.contains("since"));
2160 assert!(!json.contains("until"));
2161 assert!(!json.contains("routing"));
2162 assert!(!json.contains("rerank"));
2163 assert!(!json.contains("fusion"));
2164 assert!(!json.contains("vector_weight"));
2165 assert!(!json.contains("iterations"));
2166 assert!(!json.contains("neighborhood"));
2167 assert!(!json.contains("include_associated"));
2169 assert!(json.contains("\"agent_id\":\"agent-1\""));
2170 assert!(json.contains("\"top_k\":5"));
2171 }
2172
2173 #[test]
2174 fn test_recall_request_with_fusion_emits_fusion() {
2175 let req = RecallRequest::new("a", "q").with_fusion(FusionStrategy::MinMax);
2176 let json = serde_json::to_string(&req).unwrap();
2177 assert!(json.contains("\"fusion\":\"minmax\""));
2178 }
2179
2180 #[test]
2181 fn test_recall_request_with_routing_bm25() {
2182 let req = RecallRequest::new("a", "q").with_routing(RoutingMode::Bm25);
2183 let json = serde_json::to_string(&req).unwrap();
2184 assert!(json.contains("\"routing\":\"bm25\""));
2185 }
2186
2187 #[test]
2188 fn test_recall_request_with_associated_sets_flag() {
2189 let req = RecallRequest::new("a", "q").with_associated();
2190 assert!(req.include_associated);
2191 let json = serde_json::to_string(&req).unwrap();
2192 assert!(json.contains("\"include_associated\":true"));
2193 }
2194
2195 #[test]
2196 fn test_recall_request_with_associated_depth_implies_associated() {
2197 let req = RecallRequest::new("a", "q").with_associated_depth(2);
2198 assert!(req.include_associated);
2199 assert_eq!(req.associated_memories_depth, Some(2));
2200 }
2201
2202 #[test]
2203 fn test_recall_request_with_associated_cap_implies_associated() {
2204 let req = RecallRequest::new("a", "q").with_associated_cap(5);
2205 assert!(req.include_associated);
2206 assert_eq!(req.associated_memories_cap, Some(5));
2207 }
2208
2209 #[test]
2210 fn test_recall_request_with_rerank_false() {
2211 let req = RecallRequest::new("a", "q").with_rerank(false);
2212 let json = serde_json::to_string(&req).unwrap();
2213 assert!(json.contains("\"rerank\":false"));
2214 }
2215
2216 #[test]
2217 fn test_recall_request_with_neighborhood_false() {
2218 let req = RecallRequest::new("a", "q").with_neighborhood(false);
2219 let json = serde_json::to_string(&req).unwrap();
2220 assert!(json.contains("\"neighborhood\":false"));
2221 }
2222
2223 #[test]
2228 fn test_forget_request_by_ids_serializes() {
2229 let req = ForgetRequest::by_ids("agent-1", vec!["mem-1".to_string(), "mem-2".to_string()]);
2230 let json = serde_json::to_string(&req).unwrap();
2231 assert!(json.contains("\"memory_ids\":[\"mem-1\",\"mem-2\"]"));
2232 assert!(json.contains("\"agent_id\":\"agent-1\""));
2233 assert!(!json.contains("session_id"));
2234 assert!(!json.contains("before_timestamp"));
2235 }
2236
2237 #[test]
2238 fn test_forget_request_by_tags_serializes() {
2239 let req = ForgetRequest::by_tags("a", vec!["tag-x".to_string()]);
2240 let json = serde_json::to_string(&req).unwrap();
2241 assert!(json.contains("\"tags\":[\"tag-x\"]"));
2242 }
2243
2244 #[test]
2245 fn test_forget_request_by_session_serializes() {
2246 let req = ForgetRequest::by_session("agent-1", "sess-abc");
2247 let json = serde_json::to_string(&req).unwrap();
2248 assert!(json.contains("\"session_id\":\"sess-abc\""));
2249 }
2250
2251 #[test]
2252 fn test_forget_response_deserializes() {
2253 let json = r#"{"deleted_count": 7}"#;
2254 let resp: ForgetResponse = serde_json::from_str(json).unwrap();
2255 assert_eq!(resp.deleted_count, 7);
2256 }
2257
2258 #[test]
2263 fn test_session_deserializes_minimal() {
2264 let json = r#"{
2265 "id": "sess-1",
2266 "agent_id": "agent-1",
2267 "started_at": 1785000000
2268 }"#;
2269 let session: Session = serde_json::from_str(json).unwrap();
2270 assert_eq!(session.id, "sess-1");
2271 assert!(session.ended_at.is_none());
2272 assert!(session.summary.is_none());
2273 assert_eq!(session.memory_count, 0); }
2275
2276 #[test]
2277 fn test_session_deserializes_with_all_fields() {
2278 let json = r#"{
2279 "id": "sess-2",
2280 "agent_id": "agent-1",
2281 "started_at": 1785000000,
2282 "ended_at": 1785001000,
2283 "summary": "completed task",
2284 "memory_count": 5
2285 }"#;
2286 let session: Session = serde_json::from_str(json).unwrap();
2287 assert_eq!(session.ended_at, Some(1785001000));
2288 assert_eq!(session.summary.as_deref(), Some("completed task"));
2289 assert_eq!(session.memory_count, 5);
2290 }
2291
2292 #[test]
2297 fn test_update_memory_request_all_none_emits_empty_object() {
2298 let req = UpdateMemoryRequest {
2299 content: None,
2300 metadata: None,
2301 memory_type: None,
2302 };
2303 let json = serde_json::to_string(&req).unwrap();
2304 assert_eq!(json, "{}");
2305 }
2306
2307 #[test]
2308 fn test_update_memory_request_with_content_only() {
2309 let req = UpdateMemoryRequest {
2310 content: Some("new content".to_string()),
2311 metadata: None,
2312 memory_type: None,
2313 };
2314 let json = serde_json::to_string(&req).unwrap();
2315 assert!(json.contains("\"content\":\"new content\""));
2316 assert!(!json.contains("metadata"));
2317 assert!(!json.contains("memory_type"));
2318 }
2319}