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, Serialize, Deserialize)]
645pub struct UpdateMemoryRequest {
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub content: Option<String>,
648 #[serde(skip_serializing_if = "Option::is_none")]
649 pub metadata: Option<serde_json::Value>,
650 #[serde(skip_serializing_if = "Option::is_none")]
651 pub memory_type: Option<MemoryType>,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct UpdateImportanceRequest {
657 pub memory_ids: Vec<String>,
658 pub importance: f32,
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize, Default)]
663pub struct ConsolidationConfig {
664 #[serde(skip_serializing_if = "Option::is_none")]
666 pub algorithm: Option<String>,
667 #[serde(skip_serializing_if = "Option::is_none")]
669 pub min_samples: Option<u32>,
670 #[serde(skip_serializing_if = "Option::is_none")]
672 pub eps: Option<f32>,
673}
674
675#[derive(Debug, Clone, Serialize, Deserialize)]
677pub struct ConsolidationLogEntry {
678 pub step: String,
679 pub memories_before: usize,
680 pub memories_after: usize,
681 pub duration_ms: f64,
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize, Default)]
686pub struct ConsolidateRequest {
687 #[serde(skip_serializing_if = "Option::is_none")]
688 pub memory_type: Option<String>,
689 #[serde(skip_serializing_if = "Option::is_none")]
690 pub threshold: Option<f32>,
691 #[serde(default)]
692 pub dry_run: bool,
693 #[serde(skip_serializing_if = "Option::is_none")]
695 pub config: Option<ConsolidationConfig>,
696}
697
698#[derive(Debug, Clone, Serialize)]
703pub struct ConsolidateResponse {
704 pub consolidated_count: usize,
706 pub removed_count: usize,
708 #[serde(default)]
710 pub new_memories: Vec<String>,
711 #[serde(default, skip_serializing_if = "Vec::is_empty")]
713 pub log: Vec<ConsolidationLogEntry>,
714}
715
716impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
717 fn deserialize<D: serde::Deserializer<'de>>(
718 deserializer: D,
719 ) -> std::result::Result<Self, D::Error> {
720 let val = serde_json::Value::deserialize(deserializer)?;
721 let removed = val
723 .get("memories_removed")
724 .and_then(|v| v.as_u64())
725 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
726 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
727 .unwrap_or(0) as usize;
728 let source_ids: Vec<String> = val
729 .get("source_memory_ids")
730 .and_then(|v| v.as_array())
731 .map(|arr| {
732 arr.iter()
733 .filter_map(|v| v.as_str().map(String::from))
734 .collect()
735 })
736 .unwrap_or_default();
737 Ok(Self {
738 consolidated_count: removed,
739 removed_count: removed,
740 new_memories: source_ids,
741 log: vec![],
742 })
743 }
744}
745
746#[derive(Debug, Clone, Serialize, Deserialize)]
752pub struct MemoryImportResponse {
753 pub imported_count: usize,
754 pub skipped_count: usize,
755 #[serde(default)]
756 pub errors: Vec<String>,
757}
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
761pub struct MemoryExportResponse {
762 pub data: Vec<serde_json::Value>,
763 pub format: String,
764 pub count: usize,
765}
766
767#[derive(Debug, Clone, Serialize, Deserialize)]
773pub struct AuditEvent {
774 pub id: String,
775 pub event_type: String,
776 #[serde(skip_serializing_if = "Option::is_none")]
777 pub agent_id: Option<String>,
778 #[serde(skip_serializing_if = "Option::is_none")]
779 pub namespace: Option<String>,
780 pub timestamp: u64,
781 #[serde(default)]
782 pub details: serde_json::Value,
783}
784
785#[derive(Debug, Clone, Serialize, Deserialize)]
787pub struct AuditListResponse {
788 pub events: Vec<AuditEvent>,
789 pub total: usize,
790 #[serde(skip_serializing_if = "Option::is_none")]
791 pub cursor: Option<String>,
792}
793
794#[derive(Debug, Clone, Serialize, Deserialize)]
796pub struct AuditExportResponse {
797 pub data: String,
798 pub format: String,
799 pub count: usize,
800}
801
802#[derive(Debug, Clone, Serialize, Deserialize, Default)]
804pub struct AuditQuery {
805 #[serde(skip_serializing_if = "Option::is_none")]
806 pub agent_id: Option<String>,
807 #[serde(skip_serializing_if = "Option::is_none")]
808 pub event_type: Option<String>,
809 #[serde(skip_serializing_if = "Option::is_none")]
810 pub from: Option<u64>,
811 #[serde(skip_serializing_if = "Option::is_none")]
812 pub to: Option<u64>,
813 #[serde(skip_serializing_if = "Option::is_none")]
814 pub limit: Option<u32>,
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub cursor: Option<String>,
817}
818
819#[derive(Debug, Clone, Serialize, Deserialize)]
825pub struct ExtractionResult {
826 pub entities: Vec<serde_json::Value>,
827 pub provider: String,
828 #[serde(skip_serializing_if = "Option::is_none")]
829 pub model: Option<String>,
830 pub duration_ms: f64,
831}
832
833#[derive(Debug, Clone, Serialize, Deserialize)]
835pub struct ExtractionProviderInfo {
836 pub name: String,
837 pub available: bool,
838 #[serde(default)]
839 pub models: Vec<String>,
840}
841
842#[derive(Debug, Clone, Serialize, Deserialize)]
844#[serde(untagged)]
845pub enum ExtractProvidersResponse {
846 List(Vec<ExtractionProviderInfo>),
847 Object {
848 providers: Vec<ExtractionProviderInfo>,
849 },
850}
851
852#[derive(Debug, Clone, Serialize, Deserialize)]
858pub struct RotateEncryptionKeyRequest {
859 pub new_key: String,
861 #[serde(skip_serializing_if = "Option::is_none")]
863 pub namespace: Option<String>,
864}
865
866#[derive(Debug, Clone, Serialize, Deserialize)]
868pub struct RotateEncryptionKeyResponse {
869 pub rotated: usize,
870 pub skipped: usize,
871 #[serde(default)]
872 pub namespaces: Vec<String>,
873}
874
875#[derive(Debug, Clone, Serialize, Deserialize)]
877pub struct FeedbackRequest {
878 pub memory_id: String,
879 pub feedback: String,
880 #[serde(skip_serializing_if = "Option::is_none")]
881 pub relevance_score: Option<f32>,
882}
883
884#[derive(Debug, Clone, Serialize, Deserialize)]
886pub struct LegacyFeedbackResponse {
887 pub status: String,
888 pub updated_importance: Option<f32>,
889}
890
891#[derive(Debug, Clone, Serialize, Deserialize, Default)]
900pub struct BatchMemoryFilter {
901 #[serde(skip_serializing_if = "Option::is_none")]
903 pub tags: Option<Vec<String>>,
904 #[serde(skip_serializing_if = "Option::is_none")]
906 pub min_importance: Option<f32>,
907 #[serde(skip_serializing_if = "Option::is_none")]
909 pub max_importance: Option<f32>,
910 #[serde(skip_serializing_if = "Option::is_none")]
912 pub created_after: Option<u64>,
913 #[serde(skip_serializing_if = "Option::is_none")]
915 pub created_before: Option<u64>,
916 #[serde(skip_serializing_if = "Option::is_none")]
918 pub memory_type: Option<MemoryType>,
919 #[serde(skip_serializing_if = "Option::is_none")]
921 pub session_id: Option<String>,
922}
923
924impl BatchMemoryFilter {
925 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
927 self.tags = Some(tags);
928 self
929 }
930
931 pub fn with_min_importance(mut self, min: f32) -> Self {
933 self.min_importance = Some(min);
934 self
935 }
936
937 pub fn with_max_importance(mut self, max: f32) -> Self {
939 self.max_importance = Some(max);
940 self
941 }
942
943 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
945 self.session_id = Some(session_id.into());
946 self
947 }
948}
949
950#[derive(Debug, Clone, Serialize, Deserialize)]
952pub struct BatchRecallRequest {
953 pub agent_id: String,
955 #[serde(default)]
957 pub filter: BatchMemoryFilter,
958 #[serde(default = "default_batch_limit")]
960 pub limit: usize,
961}
962
963fn default_batch_limit() -> usize {
964 100
965}
966
967impl BatchRecallRequest {
968 pub fn new(agent_id: impl Into<String>) -> Self {
970 Self {
971 agent_id: agent_id.into(),
972 filter: BatchMemoryFilter::default(),
973 limit: 100,
974 }
975 }
976
977 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
979 self.filter = filter;
980 self
981 }
982
983 pub fn with_limit(mut self, limit: usize) -> Self {
985 self.limit = limit;
986 self
987 }
988}
989
990#[derive(Debug, Clone, Serialize, Deserialize)]
992pub struct BatchRecallResponse {
993 pub memories: Vec<RecalledMemory>,
994 pub total: usize,
996 pub filtered: usize,
998}
999
1000#[derive(Debug, Clone, Serialize, Deserialize)]
1002pub struct BatchForgetRequest {
1003 pub agent_id: String,
1005 pub filter: BatchMemoryFilter,
1007}
1008
1009impl BatchForgetRequest {
1010 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
1012 Self {
1013 agent_id: agent_id.into(),
1014 filter,
1015 }
1016 }
1017}
1018
1019#[derive(Debug, Clone, Serialize, Deserialize)]
1021pub struct BatchForgetResponse {
1022 pub deleted_count: usize,
1023}
1024
1025impl DakeraClient {
1030 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1054 let url = format!("{}/v1/memory/store", self.base_url);
1055 let response = self.client.post(&url).json(&request).send().await?;
1056 self.handle_response(response).await
1057 }
1058
1059 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1080 let url = format!("{}/v1/memory/recall", self.base_url);
1081 let response = self.client.post(&url).json(&request).send().await?;
1082 self.handle_response(response).await
1083 }
1084
1085 pub async fn recall_simple(
1087 &self,
1088 agent_id: &str,
1089 query: &str,
1090 top_k: usize,
1091 ) -> Result<RecallResponse> {
1092 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1093 .await
1094 }
1095
1096 pub async fn get_memory(&self, memory_id: &str) -> Result<RecalledMemory> {
1098 let url = format!("{}/v1/memory/get/{}", self.base_url, memory_id);
1099 let response = self.client.get(&url).send().await?;
1100 self.handle_response(response).await
1101 }
1102
1103 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1105 let url = format!("{}/v1/memory/forget", self.base_url);
1106 let response = self.client.post(&url).json(&request).send().await?;
1107 self.handle_response(response).await
1108 }
1109
1110 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1112 let url = format!("{}/v1/memory/search", self.base_url);
1113 let response = self.client.post(&url).json(&request).send().await?;
1114 self.handle_response(response).await
1115 }
1116
1117 pub async fn update_memory(
1119 &self,
1120 agent_id: &str,
1121 memory_id: &str,
1122 request: UpdateMemoryRequest,
1123 ) -> Result<StoreMemoryResponse> {
1124 let url = format!(
1125 "{}/v1/agents/{}/memories/{}",
1126 self.base_url, agent_id, memory_id
1127 );
1128 let response = self.client.put(&url).json(&request).send().await?;
1129 self.handle_response(response).await
1130 }
1131
1132 pub async fn update_importance(
1134 &self,
1135 agent_id: &str,
1136 request: UpdateImportanceRequest,
1137 ) -> Result<serde_json::Value> {
1138 let url = format!(
1139 "{}/v1/agents/{}/memories/importance",
1140 self.base_url, agent_id
1141 );
1142 let response = self.client.put(&url).json(&request).send().await?;
1143 self.handle_response(response).await
1144 }
1145
1146 pub async fn consolidate(
1148 &self,
1149 agent_id: &str,
1150 request: ConsolidateRequest,
1151 ) -> Result<ConsolidateResponse> {
1152 let url = format!("{}/v1/memory/consolidate", self.base_url);
1154 let mut body = serde_json::to_value(&request)?;
1155 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1156 let response = self.client.post(&url).json(&body).send().await?;
1157 self.handle_response(response).await
1158 }
1159
1160 pub async fn memory_feedback(
1162 &self,
1163 agent_id: &str,
1164 request: FeedbackRequest,
1165 ) -> Result<LegacyFeedbackResponse> {
1166 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1167 let response = self.client.post(&url).json(&request).send().await?;
1168 self.handle_response(response).await
1169 }
1170
1171 pub async fn feedback_memory(
1191 &self,
1192 memory_id: &str,
1193 agent_id: &str,
1194 signal: FeedbackSignal,
1195 ) -> Result<FeedbackResponse> {
1196 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1197 let body = MemoryFeedbackBody {
1198 agent_id: agent_id.to_string(),
1199 signal,
1200 };
1201 let response = self.client.post(&url).json(&body).send().await?;
1202 self.handle_response(response).await
1203 }
1204
1205 pub async fn get_memory_feedback_history(
1207 &self,
1208 memory_id: &str,
1209 ) -> Result<FeedbackHistoryResponse> {
1210 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1211 let response = self.client.get(&url).send().await?;
1212 self.handle_response(response).await
1213 }
1214
1215 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1217 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1218 let response = self.client.get(&url).send().await?;
1219 self.handle_response(response).await
1220 }
1221
1222 pub async fn patch_memory_importance(
1229 &self,
1230 memory_id: &str,
1231 agent_id: &str,
1232 importance: f32,
1233 ) -> Result<FeedbackResponse> {
1234 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1235 let body = MemoryImportancePatch {
1236 agent_id: agent_id.to_string(),
1237 importance,
1238 };
1239 let response = self.client.patch(&url).json(&body).send().await?;
1240 self.handle_response(response).await
1241 }
1242
1243 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1248 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1249 let response = self.client.get(&url).send().await?;
1250 self.handle_response(response).await
1251 }
1252
1253 pub async fn memory_graph(
1274 &self,
1275 memory_id: &str,
1276 options: GraphOptions,
1277 ) -> Result<MemoryGraph> {
1278 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1279 let depth = options.depth.unwrap_or(1);
1280 url.push_str(&format!("?depth={}", depth));
1281 if let Some(types) = &options.types {
1282 let type_strs: Vec<String> = types
1283 .iter()
1284 .map(|t| {
1285 serde_json::to_value(t)
1286 .unwrap()
1287 .as_str()
1288 .unwrap_or("")
1289 .to_string()
1290 })
1291 .collect();
1292 if !type_strs.is_empty() {
1293 url.push_str(&format!("&types={}", type_strs.join(",")));
1294 }
1295 }
1296 let response = self.client.get(&url).send().await?;
1297 self.handle_response(response).await
1298 }
1299
1300 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1313 let url = format!(
1314 "{}/v1/memories/{}/path?target={}",
1315 self.base_url,
1316 source_id,
1317 urlencoding::encode(target_id)
1318 );
1319 let response = self.client.get(&url).send().await?;
1320 self.handle_response(response).await
1321 }
1322
1323 pub async fn memory_link(
1336 &self,
1337 source_id: &str,
1338 target_id: &str,
1339 edge_type: EdgeType,
1340 ) -> Result<GraphLinkResponse> {
1341 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1342 let request = GraphLinkRequest {
1343 target_id: target_id.to_string(),
1344 edge_type,
1345 };
1346 let response = self.client.post(&url).json(&request).send().await?;
1347 self.handle_response(response).await
1348 }
1349
1350 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1358 let url = format!(
1359 "{}/v1/agents/{}/graph/export?format={}",
1360 self.base_url, agent_id, format
1361 );
1362 let response = self.client.get(&url).send().await?;
1363 self.handle_response(response).await
1364 }
1365
1366 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1372 let url = format!("{}/v1/sessions/start", self.base_url);
1373 let request = SessionStartRequest {
1374 agent_id: agent_id.to_string(),
1375 metadata: None,
1376 };
1377 let response = self.client.post(&url).json(&request).send().await?;
1378 let resp: SessionStartResponse = self.handle_response(response).await?;
1379 Ok(resp.session)
1380 }
1381
1382 pub async fn start_session_with_metadata(
1384 &self,
1385 agent_id: &str,
1386 metadata: serde_json::Value,
1387 ) -> Result<Session> {
1388 let url = format!("{}/v1/sessions/start", self.base_url);
1389 let request = SessionStartRequest {
1390 agent_id: agent_id.to_string(),
1391 metadata: Some(metadata),
1392 };
1393 let response = self.client.post(&url).json(&request).send().await?;
1394 let resp: SessionStartResponse = self.handle_response(response).await?;
1395 Ok(resp.session)
1396 }
1397
1398 pub async fn end_session(
1401 &self,
1402 session_id: &str,
1403 summary: Option<String>,
1404 ) -> Result<SessionEndResponse> {
1405 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1406 let request = SessionEndRequest { summary };
1407 let response = self.client.post(&url).json(&request).send().await?;
1408 self.handle_response(response).await
1409 }
1410
1411 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1413 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1414 let response = self.client.get(&url).send().await?;
1415 self.handle_response(response).await
1416 }
1417
1418 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1420 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1421 let response = self.client.get(&url).send().await?;
1422 self.handle_response(response).await
1423 }
1424
1425 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1427 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1428 let response = self.client.get(&url).send().await?;
1429 self.handle_response(response).await
1430 }
1431
1432 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1456 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1457 let response = self.client.post(&url).json(&request).send().await?;
1458 self.handle_response(response).await
1459 }
1460
1461 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1481 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1482 let response = self.client.delete(&url).json(&request).send().await?;
1483 self.handle_response(response).await
1484 }
1485
1486 pub async fn import_memories(
1504 &self,
1505 data: serde_json::Value,
1506 format: &str,
1507 agent_id: Option<&str>,
1508 namespace: Option<&str>,
1509 ) -> Result<MemoryImportResponse> {
1510 let mut body = serde_json::json!({"data": data, "format": format});
1511 if let Some(aid) = agent_id {
1512 body["agent_id"] = serde_json::Value::String(aid.to_string());
1513 }
1514 if let Some(ns) = namespace {
1515 body["namespace"] = serde_json::Value::String(ns.to_string());
1516 }
1517 let url = format!("{}/v1/import", self.base_url);
1518 let response = self.client.post(&url).json(&body).send().await?;
1519 self.handle_response(response).await
1520 }
1521
1522 pub async fn export_memories(
1526 &self,
1527 format: &str,
1528 agent_id: Option<&str>,
1529 namespace: Option<&str>,
1530 limit: Option<u32>,
1531 ) -> Result<MemoryExportResponse> {
1532 let mut params = vec![("format", format.to_string())];
1533 if let Some(aid) = agent_id {
1534 params.push(("agent_id", aid.to_string()));
1535 }
1536 if let Some(ns) = namespace {
1537 params.push(("namespace", ns.to_string()));
1538 }
1539 if let Some(l) = limit {
1540 params.push(("limit", l.to_string()));
1541 }
1542 let url = format!("{}/v1/export", self.base_url);
1543 let response = self.client.get(&url).query(¶ms).send().await?;
1544 self.handle_response(response).await
1545 }
1546
1547 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1553 let url = format!("{}/v1/audit", self.base_url);
1554 let response = self.client.get(&url).query(&query).send().await?;
1555 self.handle_response(response).await
1556 }
1557
1558 pub async fn stream_audit_events(
1562 &self,
1563 agent_id: Option<&str>,
1564 event_type: Option<&str>,
1565 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1566 let mut params: Vec<(&str, String)> = Vec::new();
1567 if let Some(aid) = agent_id {
1568 params.push(("agent_id", aid.to_string()));
1569 }
1570 if let Some(et) = event_type {
1571 params.push(("event_type", et.to_string()));
1572 }
1573 let base = format!("{}/v1/audit/stream", self.base_url);
1574 let url = if params.is_empty() {
1575 base
1576 } else {
1577 let qs = params
1578 .iter()
1579 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1580 .collect::<Vec<_>>()
1581 .join("&");
1582 format!("{}?{}", base, qs)
1583 };
1584 self.stream_sse(url).await
1585 }
1586
1587 pub async fn export_audit(
1589 &self,
1590 format: &str,
1591 agent_id: Option<&str>,
1592 event_type: Option<&str>,
1593 from_ts: Option<u64>,
1594 to_ts: Option<u64>,
1595 ) -> Result<AuditExportResponse> {
1596 let mut body = serde_json::json!({"format": format});
1597 if let Some(aid) = agent_id {
1598 body["agent_id"] = serde_json::Value::String(aid.to_string());
1599 }
1600 if let Some(et) = event_type {
1601 body["event_type"] = serde_json::Value::String(et.to_string());
1602 }
1603 if let Some(f) = from_ts {
1604 body["from"] = serde_json::Value::Number(f.into());
1605 }
1606 if let Some(t) = to_ts {
1607 body["to"] = serde_json::Value::Number(t.into());
1608 }
1609 let url = format!("{}/v1/audit/export", self.base_url);
1610 let response = self.client.post(&url).json(&body).send().await?;
1611 self.handle_response(response).await
1612 }
1613
1614 pub async fn extract_text(
1623 &self,
1624 text: &str,
1625 namespace: Option<&str>,
1626 provider: Option<&str>,
1627 model: Option<&str>,
1628 ) -> Result<ExtractionResult> {
1629 let mut body = serde_json::json!({"text": text});
1630 if let Some(ns) = namespace {
1631 body["namespace"] = serde_json::Value::String(ns.to_string());
1632 }
1633 if let Some(p) = provider {
1634 body["provider"] = serde_json::Value::String(p.to_string());
1635 }
1636 if let Some(m) = model {
1637 body["model"] = serde_json::Value::String(m.to_string());
1638 }
1639 let url = format!("{}/v1/extract", self.base_url);
1640 let response = self.client.post(&url).json(&body).send().await?;
1641 self.handle_response(response).await
1642 }
1643
1644 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1646 let url = format!("{}/v1/extract/providers", self.base_url);
1647 let response = self.client.get(&url).send().await?;
1648 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1649 Ok(match result {
1650 ExtractProvidersResponse::List(v) => v,
1651 ExtractProvidersResponse::Object { providers } => providers,
1652 })
1653 }
1654
1655 pub async fn configure_namespace_extractor(
1657 &self,
1658 namespace: &str,
1659 provider: &str,
1660 model: Option<&str>,
1661 ) -> Result<serde_json::Value> {
1662 let mut body = serde_json::json!({"provider": provider});
1663 if let Some(m) = model {
1664 body["model"] = serde_json::Value::String(m.to_string());
1665 }
1666 let url = format!(
1667 "{}/v1/namespaces/{}/extractor",
1668 self.base_url,
1669 urlencoding::encode(namespace)
1670 );
1671 let response = self.client.patch(&url).json(&body).send().await?;
1672 self.handle_response(response).await
1673 }
1674
1675 pub async fn rotate_encryption_key(
1691 &self,
1692 new_key: &str,
1693 namespace: Option<&str>,
1694 ) -> Result<RotateEncryptionKeyResponse> {
1695 let body = RotateEncryptionKeyRequest {
1696 new_key: new_key.to_string(),
1697 namespace: namespace.map(|s| s.to_string()),
1698 };
1699 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1700 let response = self.client.post(&url).json(&body).send().await?;
1701 self.handle_response(response).await
1702 }
1703}