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}
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, Default)]
1016pub struct BatchStoreMemoryItem {
1017 pub content: String,
1019 #[serde(default)]
1020 pub memory_type: MemoryType,
1021 #[serde(skip_serializing_if = "Option::is_none")]
1022 pub session_id: Option<String>,
1023 #[serde(default = "default_importance")]
1024 pub importance: f32,
1025 #[serde(default)]
1026 pub tags: Vec<String>,
1027 #[serde(skip_serializing_if = "Option::is_none")]
1028 pub metadata: Option<serde_json::Value>,
1029 #[serde(skip_serializing_if = "Option::is_none")]
1030 pub ttl_seconds: Option<u64>,
1031 #[serde(skip_serializing_if = "Option::is_none")]
1032 pub expires_at: Option<u64>,
1033 #[serde(skip_serializing_if = "Option::is_none")]
1035 pub id: Option<String>,
1036}
1037
1038impl BatchStoreMemoryItem {
1039 pub fn new(content: impl Into<String>) -> Self {
1041 Self {
1042 content: content.into(),
1043 importance: default_importance(),
1044 ..Default::default()
1045 }
1046 }
1047
1048 pub fn with_importance(mut self, importance: f32) -> Self {
1050 self.importance = importance;
1051 self
1052 }
1053
1054 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
1056 self.tags = tags;
1057 self
1058 }
1059
1060 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
1062 self.session_id = Some(session_id.into());
1063 self
1064 }
1065
1066 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
1068 self.metadata = Some(metadata);
1069 self
1070 }
1071
1072 pub fn with_id(mut self, id: impl Into<String>) -> Self {
1074 self.id = Some(id.into());
1075 self
1076 }
1077}
1078
1079#[derive(Debug, Clone, Serialize, Deserialize)]
1086pub struct BatchStoreMemoryRequest {
1087 pub agent_id: String,
1089 pub memories: Vec<BatchStoreMemoryItem>,
1091}
1092
1093impl BatchStoreMemoryRequest {
1094 pub fn new(agent_id: impl Into<String>, memories: Vec<BatchStoreMemoryItem>) -> Self {
1096 Self {
1097 agent_id: agent_id.into(),
1098 memories,
1099 }
1100 }
1101}
1102
1103#[derive(Debug, Clone, Serialize, Deserialize)]
1105pub struct BatchStoredMemory {
1106 pub id: String,
1107 pub content: String,
1108 pub agent_id: String,
1109 #[serde(default)]
1110 pub tags: Vec<String>,
1111 #[serde(default)]
1112 pub importance: f32,
1113 pub created_at: u64,
1114}
1115
1116#[derive(Debug, Clone, Serialize, Deserialize)]
1118pub struct BatchStoreMemoryResponse {
1119 pub stored: Vec<BatchStoredMemory>,
1121 pub stored_count: usize,
1123 pub total_embedding_time_ms: u64,
1125}
1126
1127#[derive(Debug, Clone, Serialize, Deserialize)]
1129pub struct BatchForgetRequest {
1130 pub agent_id: String,
1132 pub filter: BatchMemoryFilter,
1134}
1135
1136impl BatchForgetRequest {
1137 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
1139 Self {
1140 agent_id: agent_id.into(),
1141 filter,
1142 }
1143 }
1144}
1145
1146#[derive(Debug, Clone, Serialize, Deserialize)]
1148pub struct BatchForgetResponse {
1149 pub deleted_count: usize,
1150}
1151
1152impl DakeraClient {
1157 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1181 let url = format!("{}/v1/memory/store", self.base_url);
1182 let response = self.client.post(&url).json(&request).send().await?;
1183 self.handle_response(response).await
1184 }
1185
1186 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1207 let url = format!("{}/v1/memory/recall", self.base_url);
1208 let response = self.client.post(&url).json(&request).send().await?;
1209 self.handle_response(response).await
1210 }
1211
1212 pub async fn recall_simple(
1214 &self,
1215 agent_id: &str,
1216 query: &str,
1217 top_k: usize,
1218 ) -> Result<RecallResponse> {
1219 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1220 .await
1221 }
1222
1223 pub async fn get_memory(&self, agent_id: &str, memory_id: &str) -> Result<RecalledMemory> {
1225 let url = format!(
1226 "{}/v1/memory/get/{}?agent_id={}",
1227 self.base_url, memory_id, agent_id
1228 );
1229 let response = self.client.get(&url).send().await?;
1230 self.handle_response(response).await
1231 }
1232
1233 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1235 let url = format!("{}/v1/memory/forget", self.base_url);
1236 let response = self.client.post(&url).json(&request).send().await?;
1237 self.handle_response(response).await
1238 }
1239
1240 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1242 let url = format!("{}/v1/memory/search", self.base_url);
1243 let response = self.client.post(&url).json(&request).send().await?;
1244 self.handle_response(response).await
1245 }
1246
1247 pub async fn update_memory(
1249 &self,
1250 agent_id: &str,
1251 memory_id: &str,
1252 request: UpdateMemoryRequest,
1253 ) -> Result<StoreMemoryResponse> {
1254 let url = format!(
1255 "{}/v1/agents/{}/memories/{}",
1256 self.base_url, agent_id, memory_id
1257 );
1258 let response = self.client.put(&url).json(&request).send().await?;
1259 self.handle_response(response).await
1260 }
1261
1262 pub async fn update_importance(
1264 &self,
1265 agent_id: &str,
1266 request: UpdateImportanceRequest,
1267 ) -> Result<serde_json::Value> {
1268 let url = format!("{}/v1/memory/importance", self.base_url);
1269 let mut last_result = serde_json::Value::Null;
1270 for memory_id in &request.memory_ids {
1271 let body = serde_json::json!({
1272 "agent_id": agent_id,
1273 "memory_id": memory_id,
1274 "importance": request.importance,
1275 });
1276 let response = self.client.post(&url).json(&body).send().await?;
1277 last_result = self.handle_response(response).await?;
1278 }
1279 Ok(last_result)
1280 }
1281
1282 pub async fn consolidate(
1284 &self,
1285 agent_id: &str,
1286 request: ConsolidateRequest,
1287 ) -> Result<ConsolidateResponse> {
1288 let url = format!("{}/v1/memory/consolidate", self.base_url);
1290 let mut body = serde_json::to_value(&request)?;
1291 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1292 let response = self.client.post(&url).json(&body).send().await?;
1293 self.handle_response(response).await
1294 }
1295
1296 pub async fn memory_feedback(
1298 &self,
1299 agent_id: &str,
1300 request: FeedbackRequest,
1301 ) -> Result<LegacyFeedbackResponse> {
1302 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1303 let response = self.client.post(&url).json(&request).send().await?;
1304 self.handle_response(response).await
1305 }
1306
1307 pub async fn feedback_memory(
1327 &self,
1328 memory_id: &str,
1329 agent_id: &str,
1330 signal: FeedbackSignal,
1331 ) -> Result<FeedbackResponse> {
1332 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1333 let body = MemoryFeedbackBody {
1334 agent_id: agent_id.to_string(),
1335 signal,
1336 };
1337 let response = self.client.post(&url).json(&body).send().await?;
1338 self.handle_response(response).await
1339 }
1340
1341 pub async fn get_memory_feedback_history(
1343 &self,
1344 memory_id: &str,
1345 ) -> Result<FeedbackHistoryResponse> {
1346 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1347 let response = self.client.get(&url).send().await?;
1348 self.handle_response(response).await
1349 }
1350
1351 pub async fn evaluate_tif(&self, memory_id: &str) -> Result<TifScore> {
1359 let history = self.get_memory_feedback_history(memory_id).await?;
1360 Ok(TifScore::from_feedback_history(&history))
1361 }
1362
1363 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1365 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1366 let response = self.client.get(&url).send().await?;
1367 self.handle_response(response).await
1368 }
1369
1370 pub async fn patch_memory_importance(
1377 &self,
1378 memory_id: &str,
1379 agent_id: &str,
1380 importance: f32,
1381 ) -> Result<FeedbackResponse> {
1382 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1383 let body = MemoryImportancePatch {
1384 agent_id: agent_id.to_string(),
1385 importance,
1386 };
1387 let response = self.client.patch(&url).json(&body).send().await?;
1388 self.handle_response(response).await
1389 }
1390
1391 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1396 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1397 let response = self.client.get(&url).send().await?;
1398 self.handle_response(response).await
1399 }
1400
1401 pub async fn memory_graph(
1422 &self,
1423 memory_id: &str,
1424 options: GraphOptions,
1425 ) -> Result<MemoryGraph> {
1426 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1427 let depth = options.depth.unwrap_or(1);
1428 url.push_str(&format!("?depth={}", depth));
1429 if let Some(types) = &options.types {
1430 let type_strs: Vec<String> = types
1431 .iter()
1432 .map(|t| {
1433 serde_json::to_value(t)
1434 .unwrap()
1435 .as_str()
1436 .unwrap_or("")
1437 .to_string()
1438 })
1439 .collect();
1440 if !type_strs.is_empty() {
1441 url.push_str(&format!("&types={}", type_strs.join(",")));
1442 }
1443 }
1444 let response = self.client.get(&url).send().await?;
1445 self.handle_response(response).await
1446 }
1447
1448 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1461 let url = format!(
1462 "{}/v1/memories/{}/path?target={}",
1463 self.base_url,
1464 source_id,
1465 urlencoding::encode(target_id)
1466 );
1467 let response = self.client.get(&url).send().await?;
1468 self.handle_response(response).await
1469 }
1470
1471 pub async fn memory_link(
1484 &self,
1485 source_id: &str,
1486 target_id: &str,
1487 edge_type: EdgeType,
1488 ) -> Result<GraphLinkResponse> {
1489 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1490 let request = GraphLinkRequest {
1491 target_id: target_id.to_string(),
1492 edge_type,
1493 };
1494 let response = self.client.post(&url).json(&request).send().await?;
1495 self.handle_response(response).await
1496 }
1497
1498 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1506 let url = format!(
1507 "{}/v1/agents/{}/graph/export?format={}",
1508 self.base_url, agent_id, format
1509 );
1510 let response = self.client.get(&url).send().await?;
1511 self.handle_response(response).await
1512 }
1513
1514 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1520 let url = format!("{}/v1/sessions/start", self.base_url);
1521 let request = SessionStartRequest {
1522 agent_id: agent_id.to_string(),
1523 metadata: None,
1524 };
1525 let response = self.client.post(&url).json(&request).send().await?;
1526 let resp: SessionStartResponse = self.handle_response(response).await?;
1527 Ok(resp.session)
1528 }
1529
1530 pub async fn start_session_with_metadata(
1532 &self,
1533 agent_id: &str,
1534 metadata: serde_json::Value,
1535 ) -> Result<Session> {
1536 let url = format!("{}/v1/sessions/start", self.base_url);
1537 let request = SessionStartRequest {
1538 agent_id: agent_id.to_string(),
1539 metadata: Some(metadata),
1540 };
1541 let response = self.client.post(&url).json(&request).send().await?;
1542 let resp: SessionStartResponse = self.handle_response(response).await?;
1543 Ok(resp.session)
1544 }
1545
1546 pub async fn end_session(
1549 &self,
1550 session_id: &str,
1551 summary: Option<String>,
1552 ) -> Result<SessionEndResponse> {
1553 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1554 let request = SessionEndRequest { summary };
1555 let response = self.client.post(&url).json(&request).send().await?;
1556 self.handle_response(response).await
1557 }
1558
1559 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1561 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1562 let response = self.client.get(&url).send().await?;
1563 self.handle_response(response).await
1564 }
1565
1566 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1568 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1569 let response = self.client.get(&url).send().await?;
1570 let wrapper: ListSessionsResponse = self.handle_response(response).await?;
1571 Ok(wrapper.sessions)
1572 }
1573
1574 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1576 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1577 let response = self.client.get(&url).send().await?;
1578 self.handle_response(response).await
1579 }
1580
1581 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1605 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1606 let response = self.client.post(&url).json(&request).send().await?;
1607 self.handle_response(response).await
1608 }
1609
1610 pub async fn store_memories_batch(
1640 &self,
1641 request: BatchStoreMemoryRequest,
1642 ) -> Result<BatchStoreMemoryResponse> {
1643 let url = format!("{}/v1/memories/store/batch", self.base_url);
1644 let response = self.client.post(&url).json(&request).send().await?;
1645 self.handle_response(response).await
1646 }
1647
1648 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1668 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1669 let response = self.client.delete(&url).json(&request).send().await?;
1670 self.handle_response(response).await
1671 }
1672
1673 pub async fn import_memories(
1691 &self,
1692 data: serde_json::Value,
1693 format: &str,
1694 agent_id: Option<&str>,
1695 namespace: Option<&str>,
1696 ) -> Result<MemoryImportResponse> {
1697 let mut body = serde_json::json!({"data": data, "format": format});
1698 if let Some(aid) = agent_id {
1699 body["agent_id"] = serde_json::Value::String(aid.to_string());
1700 }
1701 if let Some(ns) = namespace {
1702 body["namespace"] = serde_json::Value::String(ns.to_string());
1703 }
1704 let url = format!("{}/v1/import", self.base_url);
1705 let response = self.client.post(&url).json(&body).send().await?;
1706 self.handle_response(response).await
1707 }
1708
1709 pub async fn export_memories(
1713 &self,
1714 format: &str,
1715 agent_id: Option<&str>,
1716 namespace: Option<&str>,
1717 limit: Option<u32>,
1718 ) -> Result<MemoryExportResponse> {
1719 let mut params = vec![("format", format.to_string())];
1720 if let Some(aid) = agent_id {
1721 params.push(("agent_id", aid.to_string()));
1722 }
1723 if let Some(ns) = namespace {
1724 params.push(("namespace", ns.to_string()));
1725 }
1726 if let Some(l) = limit {
1727 params.push(("limit", l.to_string()));
1728 }
1729 let url = format!("{}/v1/export", self.base_url);
1730 let response = self.client.get(&url).query(¶ms).send().await?;
1731 self.handle_response(response).await
1732 }
1733
1734 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1740 let url = format!("{}/v1/audit", self.base_url);
1741 let response = self.client.get(&url).query(&query).send().await?;
1742 self.handle_response(response).await
1743 }
1744
1745 pub async fn stream_audit_events(
1749 &self,
1750 agent_id: Option<&str>,
1751 event_type: Option<&str>,
1752 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1753 let mut params: Vec<(&str, String)> = Vec::new();
1754 if let Some(aid) = agent_id {
1755 params.push(("agent_id", aid.to_string()));
1756 }
1757 if let Some(et) = event_type {
1758 params.push(("event_type", et.to_string()));
1759 }
1760 let base = format!("{}/v1/audit/stream", self.base_url);
1761 let url = if params.is_empty() {
1762 base
1763 } else {
1764 let qs = params
1765 .iter()
1766 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1767 .collect::<Vec<_>>()
1768 .join("&");
1769 format!("{}?{}", base, qs)
1770 };
1771 self.stream_sse(url).await
1772 }
1773
1774 pub async fn export_audit(
1776 &self,
1777 format: &str,
1778 agent_id: Option<&str>,
1779 event_type: Option<&str>,
1780 from_ts: Option<u64>,
1781 to_ts: Option<u64>,
1782 ) -> Result<AuditExportResponse> {
1783 let mut body = serde_json::json!({"format": format});
1784 if let Some(aid) = agent_id {
1785 body["agent_id"] = serde_json::Value::String(aid.to_string());
1786 }
1787 if let Some(et) = event_type {
1788 body["event_type"] = serde_json::Value::String(et.to_string());
1789 }
1790 if let Some(f) = from_ts {
1791 body["from"] = serde_json::Value::Number(f.into());
1792 }
1793 if let Some(t) = to_ts {
1794 body["to"] = serde_json::Value::Number(t.into());
1795 }
1796 let url = format!("{}/v1/audit/export", self.base_url);
1797 let response = self.client.post(&url).json(&body).send().await?;
1798 self.handle_response(response).await
1799 }
1800
1801 pub async fn extract_text(
1810 &self,
1811 text: &str,
1812 namespace: Option<&str>,
1813 provider: Option<&str>,
1814 model: Option<&str>,
1815 ) -> Result<ExtractionResult> {
1816 let mut body = serde_json::json!({"text": text});
1817 if let Some(ns) = namespace {
1818 body["namespace"] = serde_json::Value::String(ns.to_string());
1819 }
1820 if let Some(p) = provider {
1821 body["provider"] = serde_json::Value::String(p.to_string());
1822 }
1823 if let Some(m) = model {
1824 body["model"] = serde_json::Value::String(m.to_string());
1825 }
1826 let url = format!("{}/v1/extract", self.base_url);
1827 let response = self.client.post(&url).json(&body).send().await?;
1828 self.handle_response(response).await
1829 }
1830
1831 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1833 let url = format!("{}/v1/extract/providers", self.base_url);
1834 let response = self.client.get(&url).send().await?;
1835 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1836 Ok(match result {
1837 ExtractProvidersResponse::List(v) => v,
1838 ExtractProvidersResponse::Object { providers } => providers,
1839 })
1840 }
1841
1842 pub async fn configure_namespace_extractor(
1844 &self,
1845 namespace: &str,
1846 provider: &str,
1847 model: Option<&str>,
1848 ) -> Result<serde_json::Value> {
1849 let mut body = serde_json::json!({"provider": provider});
1850 if let Some(m) = model {
1851 body["model"] = serde_json::Value::String(m.to_string());
1852 }
1853 let url = format!(
1854 "{}/v1/namespaces/{}/extractor",
1855 self.base_url,
1856 urlencoding::encode(namespace)
1857 );
1858 let response = self.client.patch(&url).json(&body).send().await?;
1859 self.handle_response(response).await
1860 }
1861
1862 pub async fn rotate_encryption_key(
1878 &self,
1879 new_key: &str,
1880 namespace: Option<&str>,
1881 ) -> Result<RotateEncryptionKeyResponse> {
1882 let body = RotateEncryptionKeyRequest {
1883 new_key: new_key.to_string(),
1884 namespace: namespace.map(|s| s.to_string()),
1885 };
1886 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1887 let response = self.client.post(&url).json(&body).send().await?;
1888 self.handle_response(response).await
1889 }
1890}