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,
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}
56
57fn default_importance() -> f32 {
58 0.5
59}
60
61impl StoreMemoryRequest {
62 pub fn new(agent_id: impl Into<String>, content: impl Into<String>) -> Self {
64 Self {
65 agent_id: agent_id.into(),
66 content: content.into(),
67 memory_type: MemoryType::default(),
68 importance: 0.5,
69 tags: Vec::new(),
70 session_id: None,
71 metadata: None,
72 ttl_seconds: None,
73 expires_at: None,
74 }
75 }
76
77 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
79 self.memory_type = memory_type;
80 self
81 }
82
83 pub fn with_importance(mut self, importance: f32) -> Self {
85 self.importance = importance.clamp(0.0, 1.0);
86 self
87 }
88
89 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
91 self.tags = tags;
92 self
93 }
94
95 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
97 self.session_id = Some(session_id.into());
98 self
99 }
100
101 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
103 self.metadata = Some(metadata);
104 self
105 }
106
107 pub fn with_ttl(mut self, ttl_seconds: u64) -> Self {
110 self.ttl_seconds = Some(ttl_seconds);
111 self
112 }
113
114 pub fn with_expires_at(mut self, expires_at: u64) -> Self {
117 self.expires_at = Some(expires_at);
118 self
119 }
120}
121
122#[derive(Debug, Clone, Serialize)]
129pub struct StoreMemoryResponse {
130 pub memory_id: String,
132 pub agent_id: String,
134 pub namespace: String,
136 pub embedding_time_ms: Option<u64>,
138}
139
140impl<'de> serde::Deserialize<'de> for StoreMemoryResponse {
141 fn deserialize<D: serde::Deserializer<'de>>(
142 deserializer: D,
143 ) -> std::result::Result<Self, D::Error> {
144 use serde::de::Error;
145 let val = serde_json::Value::deserialize(deserializer)?;
146
147 if let Some(memory) = val.get("memory") {
149 let memory_id = memory
150 .get("id")
151 .and_then(|v| v.as_str())
152 .ok_or_else(|| D::Error::missing_field("memory.id"))?
153 .to_string();
154 let agent_id = memory
155 .get("agent_id")
156 .and_then(|v| v.as_str())
157 .unwrap_or("")
158 .to_string();
159 let namespace = memory
160 .get("namespace")
161 .and_then(|v| v.as_str())
162 .unwrap_or("default")
163 .to_string();
164 let embedding_time_ms = val.get("embedding_time_ms").and_then(|v| v.as_u64());
165 return Ok(Self {
166 memory_id,
167 agent_id,
168 namespace,
169 embedding_time_ms,
170 });
171 }
172
173 let memory_id = val
175 .get("memory_id")
176 .and_then(|v| v.as_str())
177 .ok_or_else(|| D::Error::missing_field("memory_id"))?
178 .to_string();
179 let agent_id = val
180 .get("agent_id")
181 .and_then(|v| v.as_str())
182 .unwrap_or("")
183 .to_string();
184 let namespace = val
185 .get("namespace")
186 .and_then(|v| v.as_str())
187 .unwrap_or("default")
188 .to_string();
189 Ok(Self {
190 memory_id,
191 agent_id,
192 namespace,
193 embedding_time_ms: None,
194 })
195 }
196}
197
198#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
204#[serde(rename_all = "snake_case")]
205pub enum FusionStrategy {
206 #[default]
211 Rrf,
212 #[serde(rename = "minmax")]
214 MinMax,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
222#[serde(rename_all = "snake_case")]
223pub enum RoutingMode {
224 Auto,
226 Vector,
228 Bm25,
230 Hybrid,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct RecallRequest {
237 pub agent_id: String,
238 pub query: String,
239 #[serde(default = "default_top_k")]
240 pub top_k: usize,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub memory_type: Option<MemoryType>,
243 #[serde(default)]
244 pub min_importance: f32,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub session_id: Option<String>,
247 #[serde(default)]
248 pub tags: Vec<String>,
249 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
252 pub include_associated: bool,
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub associated_memories_cap: Option<u32>,
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub associated_memories_depth: Option<u8>,
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub associated_memories_min_weight: Option<f32>,
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub since: Option<String>,
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub until: Option<String>,
268 #[serde(skip_serializing_if = "Option::is_none")]
270 pub routing: Option<RoutingMode>,
271 #[serde(skip_serializing_if = "Option::is_none")]
274 pub rerank: Option<bool>,
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub fusion: Option<FusionStrategy>,
278 #[serde(skip_serializing_if = "Option::is_none")]
283 pub vector_weight: Option<f32>,
284 #[serde(skip_serializing_if = "Option::is_none")]
289 pub iterations: Option<u8>,
290 #[serde(skip_serializing_if = "Option::is_none")]
294 pub neighborhood: Option<bool>,
295}
296
297fn default_top_k() -> usize {
298 5
299}
300
301impl RecallRequest {
302 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
304 Self {
305 agent_id: agent_id.into(),
306 query: query.into(),
307 top_k: 5,
308 memory_type: None,
309 min_importance: 0.0,
310 session_id: None,
311 tags: Vec::new(),
312 include_associated: false,
313 associated_memories_cap: None,
314 associated_memories_depth: None,
315 associated_memories_min_weight: None,
316 since: None,
317 until: None,
318 routing: None,
319 rerank: None,
320 fusion: None,
321 vector_weight: None,
322 iterations: None,
323 neighborhood: None,
324 }
325 }
326
327 pub fn with_top_k(mut self, top_k: usize) -> Self {
329 self.top_k = top_k;
330 self
331 }
332
333 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
335 self.memory_type = Some(memory_type);
336 self
337 }
338
339 pub fn with_min_importance(mut self, min: f32) -> Self {
341 self.min_importance = min;
342 self
343 }
344
345 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
347 self.session_id = Some(session_id.into());
348 self
349 }
350
351 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
353 self.tags = tags;
354 self
355 }
356
357 pub fn with_associated(mut self) -> Self {
359 self.include_associated = true;
360 self
361 }
362
363 pub fn with_associated_cap(mut self, cap: u32) -> Self {
365 self.include_associated = true;
366 self.associated_memories_cap = Some(cap);
367 self
368 }
369
370 pub fn with_since(mut self, since: impl Into<String>) -> Self {
372 self.since = Some(since.into());
373 self
374 }
375
376 pub fn with_until(mut self, until: impl Into<String>) -> Self {
378 self.until = Some(until.into());
379 self
380 }
381
382 pub fn with_routing(mut self, routing: RoutingMode) -> Self {
384 self.routing = Some(routing);
385 self
386 }
387
388 pub fn with_rerank(mut self, rerank: bool) -> Self {
390 self.rerank = Some(rerank);
391 self
392 }
393
394 pub fn with_associated_depth(mut self, depth: u8) -> Self {
396 self.include_associated = true;
397 self.associated_memories_depth = Some(depth);
398 self
399 }
400
401 pub fn with_associated_min_weight(mut self, weight: f32) -> Self {
403 self.associated_memories_min_weight = Some(weight);
404 self
405 }
406
407 pub fn with_fusion(mut self, fusion: FusionStrategy) -> Self {
409 self.fusion = Some(fusion);
410 self
411 }
412
413 pub fn with_vector_weight(mut self, weight: f32) -> Self {
417 self.vector_weight = Some(weight);
418 self
419 }
420
421 pub fn with_iterations(mut self, iterations: u8) -> Self {
426 self.iterations = Some(iterations);
427 self
428 }
429
430 pub fn with_neighborhood(mut self, neighborhood: bool) -> Self {
433 self.neighborhood = Some(neighborhood);
434 self
435 }
436}
437
438#[derive(Debug, Clone, Serialize)]
440pub struct RecalledMemory {
441 pub id: String,
442 pub content: String,
443 pub memory_type: MemoryType,
444 pub importance: f32,
445 pub score: f32,
446 #[serde(default)]
447 pub tags: Vec<String>,
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub session_id: Option<String>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 pub metadata: Option<serde_json::Value>,
452 pub created_at: u64,
453 pub last_accessed_at: u64,
454 pub access_count: u32,
455 #[serde(skip_serializing_if = "Option::is_none")]
457 pub depth: Option<u8>,
458}
459
460impl<'de> serde::Deserialize<'de> for RecalledMemory {
461 fn deserialize<D: serde::Deserializer<'de>>(
462 deserializer: D,
463 ) -> std::result::Result<Self, D::Error> {
464 use serde::de::Error as _;
465 let val = serde_json::Value::deserialize(deserializer)?;
466
467 let score = val
470 .get("score")
471 .and_then(|v| v.as_f64())
472 .or_else(|| val.get("weighted_score").and_then(|v| v.as_f64()))
473 .unwrap_or(0.0) as f32;
474
475 let mem = val.get("memory").unwrap_or(&val);
476
477 let id = mem
478 .get("id")
479 .and_then(|v| v.as_str())
480 .ok_or_else(|| D::Error::missing_field("id"))?
481 .to_string();
482 let content = mem
483 .get("content")
484 .and_then(|v| v.as_str())
485 .ok_or_else(|| D::Error::missing_field("content"))?
486 .to_string();
487 let memory_type: MemoryType = mem
488 .get("memory_type")
489 .and_then(|v| serde_json::from_value(v.clone()).ok())
490 .unwrap_or(MemoryType::Episodic);
491 let importance = mem
492 .get("importance")
493 .and_then(|v| v.as_f64())
494 .unwrap_or(0.5) as f32;
495 let tags: Vec<String> = mem
496 .get("tags")
497 .and_then(|v| serde_json::from_value(v.clone()).ok())
498 .unwrap_or_default();
499 let session_id = mem
500 .get("session_id")
501 .and_then(|v| v.as_str())
502 .map(String::from);
503 let metadata = mem.get("metadata").cloned().filter(|v| !v.is_null());
504 let created_at = mem.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
505 let last_accessed_at = mem
506 .get("last_accessed_at")
507 .and_then(|v| v.as_u64())
508 .unwrap_or(0);
509 let access_count = mem
510 .get("access_count")
511 .and_then(|v| v.as_u64())
512 .unwrap_or(0) as u32;
513 let depth = mem.get("depth").and_then(|v| v.as_u64()).map(|v| v as u8);
514
515 Ok(Self {
516 id,
517 content,
518 memory_type,
519 importance,
520 score,
521 tags,
522 session_id,
523 metadata,
524 created_at,
525 last_accessed_at,
526 access_count,
527 depth,
528 })
529 }
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize)]
534pub struct RecallResponse {
535 pub memories: Vec<RecalledMemory>,
536 #[serde(default)]
537 pub total_found: usize,
538 #[serde(skip_serializing_if = "Option::is_none")]
540 pub associated_memories: Option<Vec<RecalledMemory>>,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct ForgetRequest {
546 pub agent_id: String,
547 #[serde(default)]
548 pub memory_ids: Vec<String>,
549 #[serde(default)]
550 pub tags: Vec<String>,
551 #[serde(skip_serializing_if = "Option::is_none")]
552 pub session_id: Option<String>,
553 #[serde(skip_serializing_if = "Option::is_none")]
554 pub before_timestamp: Option<u64>,
555}
556
557impl ForgetRequest {
558 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
560 Self {
561 agent_id: agent_id.into(),
562 memory_ids: ids,
563 tags: Vec::new(),
564 session_id: None,
565 before_timestamp: None,
566 }
567 }
568
569 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
571 Self {
572 agent_id: agent_id.into(),
573 memory_ids: Vec::new(),
574 tags,
575 session_id: None,
576 before_timestamp: None,
577 }
578 }
579
580 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
582 Self {
583 agent_id: agent_id.into(),
584 memory_ids: Vec::new(),
585 tags: Vec::new(),
586 session_id: Some(session_id.into()),
587 before_timestamp: None,
588 }
589 }
590}
591
592#[derive(Debug, Clone, Serialize, Deserialize)]
594pub struct ForgetResponse {
595 pub deleted_count: u64,
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct SessionStartRequest {
601 pub agent_id: String,
602 #[serde(skip_serializing_if = "Option::is_none")]
603 pub metadata: Option<serde_json::Value>,
604}
605
606#[derive(Debug, Clone, Serialize, Deserialize)]
608pub struct Session {
609 pub id: String,
610 pub agent_id: String,
611 pub started_at: u64,
612 #[serde(skip_serializing_if = "Option::is_none")]
613 pub ended_at: Option<u64>,
614 #[serde(skip_serializing_if = "Option::is_none")]
615 pub summary: Option<String>,
616 #[serde(skip_serializing_if = "Option::is_none")]
617 pub metadata: Option<serde_json::Value>,
618 #[serde(default)]
620 pub memory_count: usize,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct SessionEndRequest {
626 #[serde(skip_serializing_if = "Option::is_none")]
627 pub summary: Option<String>,
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize)]
632pub struct SessionStartResponse {
633 pub session: Session,
634}
635
636#[derive(Debug, Clone, Serialize, Deserialize)]
638pub struct SessionEndResponse {
639 pub session: Session,
640 pub memory_count: usize,
641}
642
643#[derive(Debug, Clone, Deserialize)]
645pub struct ListSessionsResponse {
646 pub sessions: Vec<Session>,
647 #[allow(dead_code)]
648 pub total: usize,
649}
650
651#[derive(Debug, Clone, Serialize, Deserialize)]
653pub struct UpdateMemoryRequest {
654 #[serde(skip_serializing_if = "Option::is_none")]
655 pub content: Option<String>,
656 #[serde(skip_serializing_if = "Option::is_none")]
657 pub metadata: Option<serde_json::Value>,
658 #[serde(skip_serializing_if = "Option::is_none")]
659 pub memory_type: Option<MemoryType>,
660}
661
662#[derive(Debug, Clone, Serialize, Deserialize)]
664pub struct UpdateImportanceRequest {
665 pub memory_ids: Vec<String>,
666 pub importance: f32,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize, Default)]
671pub struct ConsolidationConfig {
672 #[serde(skip_serializing_if = "Option::is_none")]
674 pub algorithm: Option<String>,
675 #[serde(skip_serializing_if = "Option::is_none")]
677 pub min_samples: Option<u32>,
678 #[serde(skip_serializing_if = "Option::is_none")]
680 pub eps: Option<f32>,
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize)]
685pub struct ConsolidationLogEntry {
686 pub step: String,
687 pub memories_before: usize,
688 pub memories_after: usize,
689 pub duration_ms: f64,
690}
691
692#[derive(Debug, Clone, Serialize, Deserialize, Default)]
694pub struct ConsolidateRequest {
695 #[serde(skip_serializing_if = "Option::is_none")]
696 pub memory_type: Option<String>,
697 #[serde(skip_serializing_if = "Option::is_none")]
698 pub threshold: Option<f32>,
699 #[serde(default)]
700 pub dry_run: bool,
701 #[serde(skip_serializing_if = "Option::is_none")]
703 pub config: Option<ConsolidationConfig>,
704}
705
706#[derive(Debug, Clone, Serialize)]
711pub struct ConsolidateResponse {
712 pub consolidated_count: usize,
714 pub removed_count: usize,
716 #[serde(default)]
718 pub new_memories: Vec<String>,
719 #[serde(default, skip_serializing_if = "Vec::is_empty")]
721 pub log: Vec<ConsolidationLogEntry>,
722}
723
724impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
725 fn deserialize<D: serde::Deserializer<'de>>(
726 deserializer: D,
727 ) -> std::result::Result<Self, D::Error> {
728 let val = serde_json::Value::deserialize(deserializer)?;
729 let removed = val
731 .get("memories_removed")
732 .and_then(|v| v.as_u64())
733 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
734 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
735 .unwrap_or(0) as usize;
736 let source_ids: Vec<String> = val
737 .get("source_memory_ids")
738 .and_then(|v| v.as_array())
739 .map(|arr| {
740 arr.iter()
741 .filter_map(|v| v.as_str().map(String::from))
742 .collect()
743 })
744 .unwrap_or_default();
745 Ok(Self {
746 consolidated_count: removed,
747 removed_count: removed,
748 new_memories: source_ids,
749 log: vec![],
750 })
751 }
752}
753
754#[derive(Debug, Clone, Serialize, Deserialize)]
760pub struct MemoryImportResponse {
761 pub imported_count: usize,
762 pub skipped_count: usize,
763 #[serde(default)]
764 pub errors: Vec<String>,
765}
766
767#[derive(Debug, Clone, Serialize, Deserialize)]
769pub struct MemoryExportResponse {
770 pub data: Vec<serde_json::Value>,
771 pub format: String,
772 pub count: usize,
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize)]
781pub struct AuditEvent {
782 pub id: String,
783 pub event_type: String,
784 #[serde(skip_serializing_if = "Option::is_none")]
785 pub agent_id: Option<String>,
786 #[serde(skip_serializing_if = "Option::is_none")]
787 pub namespace: Option<String>,
788 pub timestamp: u64,
789 #[serde(default)]
790 pub details: serde_json::Value,
791}
792
793#[derive(Debug, Clone, Serialize, Deserialize)]
795pub struct AuditListResponse {
796 pub events: Vec<AuditEvent>,
797 pub total: usize,
798 #[serde(skip_serializing_if = "Option::is_none")]
799 pub cursor: Option<String>,
800}
801
802#[derive(Debug, Clone, Serialize, Deserialize)]
804pub struct AuditExportResponse {
805 pub data: String,
806 pub format: String,
807 pub count: usize,
808}
809
810#[derive(Debug, Clone, Serialize, Deserialize, Default)]
812pub struct AuditQuery {
813 #[serde(skip_serializing_if = "Option::is_none")]
814 pub agent_id: Option<String>,
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub event_type: Option<String>,
817 #[serde(skip_serializing_if = "Option::is_none")]
818 pub from: Option<u64>,
819 #[serde(skip_serializing_if = "Option::is_none")]
820 pub to: Option<u64>,
821 #[serde(skip_serializing_if = "Option::is_none")]
822 pub limit: Option<u32>,
823 #[serde(skip_serializing_if = "Option::is_none")]
824 pub cursor: Option<String>,
825}
826
827#[derive(Debug, Clone, Serialize, Deserialize)]
833pub struct ExtractionResult {
834 pub entities: Vec<serde_json::Value>,
835 pub provider: String,
836 #[serde(skip_serializing_if = "Option::is_none")]
837 pub model: Option<String>,
838 pub duration_ms: f64,
839}
840
841#[derive(Debug, Clone, Serialize, Deserialize)]
843pub struct ExtractionProviderInfo {
844 pub name: String,
845 pub available: bool,
846 #[serde(default)]
847 pub models: Vec<String>,
848}
849
850#[derive(Debug, Clone, Serialize, Deserialize)]
852#[serde(untagged)]
853pub enum ExtractProvidersResponse {
854 List(Vec<ExtractionProviderInfo>),
855 Object {
856 providers: Vec<ExtractionProviderInfo>,
857 },
858}
859
860#[derive(Debug, Clone, Serialize, Deserialize)]
866pub struct RotateEncryptionKeyRequest {
867 pub new_key: String,
869 #[serde(skip_serializing_if = "Option::is_none")]
871 pub namespace: Option<String>,
872}
873
874#[derive(Debug, Clone, Serialize, Deserialize)]
876pub struct RotateEncryptionKeyResponse {
877 pub rotated: usize,
878 pub skipped: usize,
879 #[serde(default)]
880 pub namespaces: Vec<String>,
881}
882
883#[derive(Debug, Clone, Serialize, Deserialize)]
885pub struct FeedbackRequest {
886 pub memory_id: String,
887 pub feedback: String,
888 #[serde(skip_serializing_if = "Option::is_none")]
889 pub relevance_score: Option<f32>,
890}
891
892#[derive(Debug, Clone, Serialize, Deserialize)]
894pub struct LegacyFeedbackResponse {
895 pub status: String,
896 pub updated_importance: Option<f32>,
897}
898
899#[derive(Debug, Clone, Serialize, Deserialize, Default)]
908pub struct BatchMemoryFilter {
909 #[serde(skip_serializing_if = "Option::is_none")]
911 pub tags: Option<Vec<String>>,
912 #[serde(skip_serializing_if = "Option::is_none")]
914 pub min_importance: Option<f32>,
915 #[serde(skip_serializing_if = "Option::is_none")]
917 pub max_importance: Option<f32>,
918 #[serde(skip_serializing_if = "Option::is_none")]
920 pub created_after: Option<u64>,
921 #[serde(skip_serializing_if = "Option::is_none")]
923 pub created_before: Option<u64>,
924 #[serde(skip_serializing_if = "Option::is_none")]
926 pub memory_type: Option<MemoryType>,
927 #[serde(skip_serializing_if = "Option::is_none")]
929 pub session_id: Option<String>,
930}
931
932impl BatchMemoryFilter {
933 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
935 self.tags = Some(tags);
936 self
937 }
938
939 pub fn with_min_importance(mut self, min: f32) -> Self {
941 self.min_importance = Some(min);
942 self
943 }
944
945 pub fn with_max_importance(mut self, max: f32) -> Self {
947 self.max_importance = Some(max);
948 self
949 }
950
951 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
953 self.session_id = Some(session_id.into());
954 self
955 }
956}
957
958#[derive(Debug, Clone, Serialize, Deserialize)]
960pub struct BatchRecallRequest {
961 pub agent_id: String,
963 #[serde(default)]
965 pub filter: BatchMemoryFilter,
966 #[serde(default = "default_batch_limit")]
968 pub limit: usize,
969}
970
971fn default_batch_limit() -> usize {
972 100
973}
974
975impl BatchRecallRequest {
976 pub fn new(agent_id: impl Into<String>) -> Self {
978 Self {
979 agent_id: agent_id.into(),
980 filter: BatchMemoryFilter::default(),
981 limit: 100,
982 }
983 }
984
985 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
987 self.filter = filter;
988 self
989 }
990
991 pub fn with_limit(mut self, limit: usize) -> Self {
993 self.limit = limit;
994 self
995 }
996}
997
998#[derive(Debug, Clone, Serialize, Deserialize)]
1000pub struct BatchRecallResponse {
1001 pub memories: Vec<RecalledMemory>,
1002 pub total: usize,
1004 pub filtered: usize,
1006}
1007
1008#[derive(Debug, Clone, Serialize, Deserialize)]
1010pub struct BatchForgetRequest {
1011 pub agent_id: String,
1013 pub filter: BatchMemoryFilter,
1015}
1016
1017impl BatchForgetRequest {
1018 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
1020 Self {
1021 agent_id: agent_id.into(),
1022 filter,
1023 }
1024 }
1025}
1026
1027#[derive(Debug, Clone, Serialize, Deserialize)]
1029pub struct BatchForgetResponse {
1030 pub deleted_count: usize,
1031}
1032
1033impl DakeraClient {
1038 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1062 let url = format!("{}/v1/memory/store", self.base_url);
1063 let response = self.client.post(&url).json(&request).send().await?;
1064 self.handle_response(response).await
1065 }
1066
1067 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1088 let url = format!("{}/v1/memory/recall", self.base_url);
1089 let response = self.client.post(&url).json(&request).send().await?;
1090 self.handle_response(response).await
1091 }
1092
1093 pub async fn recall_simple(
1095 &self,
1096 agent_id: &str,
1097 query: &str,
1098 top_k: usize,
1099 ) -> Result<RecallResponse> {
1100 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1101 .await
1102 }
1103
1104 pub async fn get_memory(&self, agent_id: &str, memory_id: &str) -> Result<RecalledMemory> {
1106 let url = format!(
1107 "{}/v1/memory/get/{}?agent_id={}",
1108 self.base_url, memory_id, agent_id
1109 );
1110 let response = self.client.get(&url).send().await?;
1111 self.handle_response(response).await
1112 }
1113
1114 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1116 let url = format!("{}/v1/memory/forget", self.base_url);
1117 let response = self.client.post(&url).json(&request).send().await?;
1118 self.handle_response(response).await
1119 }
1120
1121 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1123 let url = format!("{}/v1/memory/search", self.base_url);
1124 let response = self.client.post(&url).json(&request).send().await?;
1125 self.handle_response(response).await
1126 }
1127
1128 pub async fn update_memory(
1130 &self,
1131 agent_id: &str,
1132 memory_id: &str,
1133 request: UpdateMemoryRequest,
1134 ) -> Result<StoreMemoryResponse> {
1135 let url = format!(
1136 "{}/v1/agents/{}/memories/{}",
1137 self.base_url, agent_id, memory_id
1138 );
1139 let response = self.client.put(&url).json(&request).send().await?;
1140 self.handle_response(response).await
1141 }
1142
1143 pub async fn update_importance(
1145 &self,
1146 agent_id: &str,
1147 request: UpdateImportanceRequest,
1148 ) -> Result<serde_json::Value> {
1149 let url = format!("{}/v1/memory/importance", self.base_url);
1150 let mut last_result = serde_json::Value::Null;
1151 for memory_id in &request.memory_ids {
1152 let body = serde_json::json!({
1153 "agent_id": agent_id,
1154 "memory_id": memory_id,
1155 "importance": request.importance,
1156 });
1157 let response = self.client.post(&url).json(&body).send().await?;
1158 last_result = self.handle_response(response).await?;
1159 }
1160 Ok(last_result)
1161 }
1162
1163 pub async fn consolidate(
1165 &self,
1166 agent_id: &str,
1167 request: ConsolidateRequest,
1168 ) -> Result<ConsolidateResponse> {
1169 let url = format!("{}/v1/memory/consolidate", self.base_url);
1171 let mut body = serde_json::to_value(&request)?;
1172 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1173 let response = self.client.post(&url).json(&body).send().await?;
1174 self.handle_response(response).await
1175 }
1176
1177 pub async fn memory_feedback(
1179 &self,
1180 agent_id: &str,
1181 request: FeedbackRequest,
1182 ) -> Result<LegacyFeedbackResponse> {
1183 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1184 let response = self.client.post(&url).json(&request).send().await?;
1185 self.handle_response(response).await
1186 }
1187
1188 pub async fn feedback_memory(
1208 &self,
1209 memory_id: &str,
1210 agent_id: &str,
1211 signal: FeedbackSignal,
1212 ) -> Result<FeedbackResponse> {
1213 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1214 let body = MemoryFeedbackBody {
1215 agent_id: agent_id.to_string(),
1216 signal,
1217 };
1218 let response = self.client.post(&url).json(&body).send().await?;
1219 self.handle_response(response).await
1220 }
1221
1222 pub async fn get_memory_feedback_history(
1224 &self,
1225 memory_id: &str,
1226 ) -> Result<FeedbackHistoryResponse> {
1227 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1228 let response = self.client.get(&url).send().await?;
1229 self.handle_response(response).await
1230 }
1231
1232 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1234 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1235 let response = self.client.get(&url).send().await?;
1236 self.handle_response(response).await
1237 }
1238
1239 pub async fn patch_memory_importance(
1246 &self,
1247 memory_id: &str,
1248 agent_id: &str,
1249 importance: f32,
1250 ) -> Result<FeedbackResponse> {
1251 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1252 let body = MemoryImportancePatch {
1253 agent_id: agent_id.to_string(),
1254 importance,
1255 };
1256 let response = self.client.patch(&url).json(&body).send().await?;
1257 self.handle_response(response).await
1258 }
1259
1260 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1265 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1266 let response = self.client.get(&url).send().await?;
1267 self.handle_response(response).await
1268 }
1269
1270 pub async fn memory_graph(
1291 &self,
1292 memory_id: &str,
1293 options: GraphOptions,
1294 ) -> Result<MemoryGraph> {
1295 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1296 let depth = options.depth.unwrap_or(1);
1297 url.push_str(&format!("?depth={}", depth));
1298 if let Some(types) = &options.types {
1299 let type_strs: Vec<String> = types
1300 .iter()
1301 .map(|t| {
1302 serde_json::to_value(t)
1303 .unwrap()
1304 .as_str()
1305 .unwrap_or("")
1306 .to_string()
1307 })
1308 .collect();
1309 if !type_strs.is_empty() {
1310 url.push_str(&format!("&types={}", type_strs.join(",")));
1311 }
1312 }
1313 let response = self.client.get(&url).send().await?;
1314 self.handle_response(response).await
1315 }
1316
1317 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1330 let url = format!(
1331 "{}/v1/memories/{}/path?target={}",
1332 self.base_url,
1333 source_id,
1334 urlencoding::encode(target_id)
1335 );
1336 let response = self.client.get(&url).send().await?;
1337 self.handle_response(response).await
1338 }
1339
1340 pub async fn memory_link(
1353 &self,
1354 source_id: &str,
1355 target_id: &str,
1356 edge_type: EdgeType,
1357 ) -> Result<GraphLinkResponse> {
1358 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1359 let request = GraphLinkRequest {
1360 target_id: target_id.to_string(),
1361 edge_type,
1362 };
1363 let response = self.client.post(&url).json(&request).send().await?;
1364 self.handle_response(response).await
1365 }
1366
1367 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1375 let url = format!(
1376 "{}/v1/agents/{}/graph/export?format={}",
1377 self.base_url, agent_id, format
1378 );
1379 let response = self.client.get(&url).send().await?;
1380 self.handle_response(response).await
1381 }
1382
1383 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1389 let url = format!("{}/v1/sessions/start", self.base_url);
1390 let request = SessionStartRequest {
1391 agent_id: agent_id.to_string(),
1392 metadata: None,
1393 };
1394 let response = self.client.post(&url).json(&request).send().await?;
1395 let resp: SessionStartResponse = self.handle_response(response).await?;
1396 Ok(resp.session)
1397 }
1398
1399 pub async fn start_session_with_metadata(
1401 &self,
1402 agent_id: &str,
1403 metadata: serde_json::Value,
1404 ) -> Result<Session> {
1405 let url = format!("{}/v1/sessions/start", self.base_url);
1406 let request = SessionStartRequest {
1407 agent_id: agent_id.to_string(),
1408 metadata: Some(metadata),
1409 };
1410 let response = self.client.post(&url).json(&request).send().await?;
1411 let resp: SessionStartResponse = self.handle_response(response).await?;
1412 Ok(resp.session)
1413 }
1414
1415 pub async fn end_session(
1418 &self,
1419 session_id: &str,
1420 summary: Option<String>,
1421 ) -> Result<SessionEndResponse> {
1422 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1423 let request = SessionEndRequest { summary };
1424 let response = self.client.post(&url).json(&request).send().await?;
1425 self.handle_response(response).await
1426 }
1427
1428 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1430 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1431 let response = self.client.get(&url).send().await?;
1432 self.handle_response(response).await
1433 }
1434
1435 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1437 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1438 let response = self.client.get(&url).send().await?;
1439 let wrapper: ListSessionsResponse = self.handle_response(response).await?;
1440 Ok(wrapper.sessions)
1441 }
1442
1443 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1445 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1446 let response = self.client.get(&url).send().await?;
1447 self.handle_response(response).await
1448 }
1449
1450 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1474 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1475 let response = self.client.post(&url).json(&request).send().await?;
1476 self.handle_response(response).await
1477 }
1478
1479 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1499 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1500 let response = self.client.delete(&url).json(&request).send().await?;
1501 self.handle_response(response).await
1502 }
1503
1504 pub async fn import_memories(
1522 &self,
1523 data: serde_json::Value,
1524 format: &str,
1525 agent_id: Option<&str>,
1526 namespace: Option<&str>,
1527 ) -> Result<MemoryImportResponse> {
1528 let mut body = serde_json::json!({"data": data, "format": format});
1529 if let Some(aid) = agent_id {
1530 body["agent_id"] = serde_json::Value::String(aid.to_string());
1531 }
1532 if let Some(ns) = namespace {
1533 body["namespace"] = serde_json::Value::String(ns.to_string());
1534 }
1535 let url = format!("{}/v1/import", self.base_url);
1536 let response = self.client.post(&url).json(&body).send().await?;
1537 self.handle_response(response).await
1538 }
1539
1540 pub async fn export_memories(
1544 &self,
1545 format: &str,
1546 agent_id: Option<&str>,
1547 namespace: Option<&str>,
1548 limit: Option<u32>,
1549 ) -> Result<MemoryExportResponse> {
1550 let mut params = vec![("format", format.to_string())];
1551 if let Some(aid) = agent_id {
1552 params.push(("agent_id", aid.to_string()));
1553 }
1554 if let Some(ns) = namespace {
1555 params.push(("namespace", ns.to_string()));
1556 }
1557 if let Some(l) = limit {
1558 params.push(("limit", l.to_string()));
1559 }
1560 let url = format!("{}/v1/export", self.base_url);
1561 let response = self.client.get(&url).query(¶ms).send().await?;
1562 self.handle_response(response).await
1563 }
1564
1565 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1571 let url = format!("{}/v1/audit", self.base_url);
1572 let response = self.client.get(&url).query(&query).send().await?;
1573 self.handle_response(response).await
1574 }
1575
1576 pub async fn stream_audit_events(
1580 &self,
1581 agent_id: Option<&str>,
1582 event_type: Option<&str>,
1583 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1584 let mut params: Vec<(&str, String)> = Vec::new();
1585 if let Some(aid) = agent_id {
1586 params.push(("agent_id", aid.to_string()));
1587 }
1588 if let Some(et) = event_type {
1589 params.push(("event_type", et.to_string()));
1590 }
1591 let base = format!("{}/v1/audit/stream", self.base_url);
1592 let url = if params.is_empty() {
1593 base
1594 } else {
1595 let qs = params
1596 .iter()
1597 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1598 .collect::<Vec<_>>()
1599 .join("&");
1600 format!("{}?{}", base, qs)
1601 };
1602 self.stream_sse(url).await
1603 }
1604
1605 pub async fn export_audit(
1607 &self,
1608 format: &str,
1609 agent_id: Option<&str>,
1610 event_type: Option<&str>,
1611 from_ts: Option<u64>,
1612 to_ts: Option<u64>,
1613 ) -> Result<AuditExportResponse> {
1614 let mut body = serde_json::json!({"format": format});
1615 if let Some(aid) = agent_id {
1616 body["agent_id"] = serde_json::Value::String(aid.to_string());
1617 }
1618 if let Some(et) = event_type {
1619 body["event_type"] = serde_json::Value::String(et.to_string());
1620 }
1621 if let Some(f) = from_ts {
1622 body["from"] = serde_json::Value::Number(f.into());
1623 }
1624 if let Some(t) = to_ts {
1625 body["to"] = serde_json::Value::Number(t.into());
1626 }
1627 let url = format!("{}/v1/audit/export", self.base_url);
1628 let response = self.client.post(&url).json(&body).send().await?;
1629 self.handle_response(response).await
1630 }
1631
1632 pub async fn extract_text(
1641 &self,
1642 text: &str,
1643 namespace: Option<&str>,
1644 provider: Option<&str>,
1645 model: Option<&str>,
1646 ) -> Result<ExtractionResult> {
1647 let mut body = serde_json::json!({"text": text});
1648 if let Some(ns) = namespace {
1649 body["namespace"] = serde_json::Value::String(ns.to_string());
1650 }
1651 if let Some(p) = provider {
1652 body["provider"] = serde_json::Value::String(p.to_string());
1653 }
1654 if let Some(m) = model {
1655 body["model"] = serde_json::Value::String(m.to_string());
1656 }
1657 let url = format!("{}/v1/extract", self.base_url);
1658 let response = self.client.post(&url).json(&body).send().await?;
1659 self.handle_response(response).await
1660 }
1661
1662 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1664 let url = format!("{}/v1/extract/providers", self.base_url);
1665 let response = self.client.get(&url).send().await?;
1666 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1667 Ok(match result {
1668 ExtractProvidersResponse::List(v) => v,
1669 ExtractProvidersResponse::Object { providers } => providers,
1670 })
1671 }
1672
1673 pub async fn configure_namespace_extractor(
1675 &self,
1676 namespace: &str,
1677 provider: &str,
1678 model: Option<&str>,
1679 ) -> Result<serde_json::Value> {
1680 let mut body = serde_json::json!({"provider": provider});
1681 if let Some(m) = model {
1682 body["model"] = serde_json::Value::String(m.to_string());
1683 }
1684 let url = format!(
1685 "{}/v1/namespaces/{}/extractor",
1686 self.base_url,
1687 urlencoding::encode(namespace)
1688 );
1689 let response = self.client.patch(&url).json(&body).send().await?;
1690 self.handle_response(response).await
1691 }
1692
1693 pub async fn rotate_encryption_key(
1709 &self,
1710 new_key: &str,
1711 namespace: Option<&str>,
1712 ) -> Result<RotateEncryptionKeyResponse> {
1713 let body = RotateEncryptionKeyRequest {
1714 new_key: new_key.to_string(),
1715 namespace: namespace.map(|s| s.to_string()),
1716 };
1717 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1718 let response = self.client.post(&url).json(&body).send().await?;
1719 self.handle_response(response).await
1720 }
1721}