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,
447 #[serde(skip_serializing_if = "Option::is_none")]
449 pub smart_score: Option<f32>,
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub weighted_score: Option<f32>,
453 #[serde(default)]
454 pub tags: Vec<String>,
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub session_id: Option<String>,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub metadata: Option<serde_json::Value>,
459 pub created_at: u64,
460 pub last_accessed_at: u64,
461 pub access_count: u32,
462 #[serde(skip_serializing_if = "Option::is_none")]
464 pub depth: Option<u8>,
465}
466
467impl<'de> serde::Deserialize<'de> for RecalledMemory {
468 fn deserialize<D: serde::Deserializer<'de>>(
469 deserializer: D,
470 ) -> std::result::Result<Self, D::Error> {
471 use serde::de::Error as _;
472 let val = serde_json::Value::deserialize(deserializer)?;
473
474 let smart_score = val
478 .get("smart_score")
479 .and_then(|v| v.as_f64())
480 .map(|v| v as f32);
481 let weighted_score = val
482 .get("weighted_score")
483 .and_then(|v| v.as_f64())
484 .map(|v| v as f32);
485 let score = smart_score
486 .or(weighted_score)
487 .or_else(|| val.get("score").and_then(|v| v.as_f64()).map(|v| v as f32))
488 .unwrap_or(0.0);
489
490 let mem = val.get("memory").unwrap_or(&val);
491
492 let id = mem
493 .get("id")
494 .and_then(|v| v.as_str())
495 .ok_or_else(|| D::Error::missing_field("id"))?
496 .to_string();
497 let content = mem
498 .get("content")
499 .and_then(|v| v.as_str())
500 .ok_or_else(|| D::Error::missing_field("content"))?
501 .to_string();
502 let memory_type: MemoryType = mem
503 .get("memory_type")
504 .and_then(|v| serde_json::from_value(v.clone()).ok())
505 .unwrap_or(MemoryType::Episodic);
506 let importance = mem
507 .get("importance")
508 .and_then(|v| v.as_f64())
509 .unwrap_or(0.5) as f32;
510 let tags: Vec<String> = mem
511 .get("tags")
512 .and_then(|v| serde_json::from_value(v.clone()).ok())
513 .unwrap_or_default();
514 let session_id = mem
515 .get("session_id")
516 .and_then(|v| v.as_str())
517 .map(String::from);
518 let metadata = mem.get("metadata").cloned().filter(|v| !v.is_null());
519 let created_at = mem.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
520 let last_accessed_at = mem
521 .get("last_accessed_at")
522 .and_then(|v| v.as_u64())
523 .unwrap_or(0);
524 let access_count = mem
525 .get("access_count")
526 .and_then(|v| v.as_u64())
527 .unwrap_or(0) as u32;
528 let depth = mem.get("depth").and_then(|v| v.as_u64()).map(|v| v as u8);
529
530 Ok(Self {
531 id,
532 content,
533 memory_type,
534 importance,
535 score,
536 smart_score,
537 weighted_score,
538 tags,
539 session_id,
540 metadata,
541 created_at,
542 last_accessed_at,
543 access_count,
544 depth,
545 })
546 }
547}
548
549#[derive(Debug, Clone, Serialize, Deserialize)]
551pub struct RecallResponse {
552 pub memories: Vec<RecalledMemory>,
553 #[serde(default)]
554 pub total_found: usize,
555 #[serde(skip_serializing_if = "Option::is_none")]
557 pub associated_memories: Option<Vec<RecalledMemory>>,
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct ForgetRequest {
563 pub agent_id: String,
564 #[serde(default)]
565 pub memory_ids: Vec<String>,
566 #[serde(default)]
567 pub tags: Vec<String>,
568 #[serde(skip_serializing_if = "Option::is_none")]
569 pub session_id: Option<String>,
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub before_timestamp: Option<u64>,
572}
573
574impl ForgetRequest {
575 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
577 Self {
578 agent_id: agent_id.into(),
579 memory_ids: ids,
580 tags: Vec::new(),
581 session_id: None,
582 before_timestamp: None,
583 }
584 }
585
586 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
588 Self {
589 agent_id: agent_id.into(),
590 memory_ids: Vec::new(),
591 tags,
592 session_id: None,
593 before_timestamp: None,
594 }
595 }
596
597 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
599 Self {
600 agent_id: agent_id.into(),
601 memory_ids: Vec::new(),
602 tags: Vec::new(),
603 session_id: Some(session_id.into()),
604 before_timestamp: None,
605 }
606 }
607}
608
609#[derive(Debug, Clone, Serialize, Deserialize)]
611pub struct ForgetResponse {
612 pub deleted_count: u64,
613}
614
615#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct SessionStartRequest {
618 pub agent_id: String,
619 #[serde(skip_serializing_if = "Option::is_none")]
620 pub metadata: Option<serde_json::Value>,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct Session {
626 pub id: String,
627 pub agent_id: String,
628 pub started_at: u64,
629 #[serde(skip_serializing_if = "Option::is_none")]
630 pub ended_at: Option<u64>,
631 #[serde(skip_serializing_if = "Option::is_none")]
632 pub summary: Option<String>,
633 #[serde(skip_serializing_if = "Option::is_none")]
634 pub metadata: Option<serde_json::Value>,
635 #[serde(default)]
637 pub memory_count: usize,
638}
639
640#[derive(Debug, Clone, Serialize, Deserialize)]
642pub struct SessionEndRequest {
643 #[serde(skip_serializing_if = "Option::is_none")]
644 pub summary: Option<String>,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
649pub struct SessionStartResponse {
650 pub session: Session,
651}
652
653#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct SessionEndResponse {
656 pub session: Session,
657 pub memory_count: usize,
658}
659
660#[derive(Debug, Clone, Deserialize)]
662pub struct ListSessionsResponse {
663 pub sessions: Vec<Session>,
664 #[allow(dead_code)]
665 pub total: usize,
666}
667
668#[derive(Debug, Clone, Serialize, Deserialize)]
670pub struct UpdateMemoryRequest {
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub content: Option<String>,
673 #[serde(skip_serializing_if = "Option::is_none")]
674 pub metadata: Option<serde_json::Value>,
675 #[serde(skip_serializing_if = "Option::is_none")]
676 pub memory_type: Option<MemoryType>,
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize)]
681pub struct UpdateImportanceRequest {
682 pub memory_ids: Vec<String>,
683 pub importance: f32,
684}
685
686#[derive(Debug, Clone, Serialize, Deserialize, Default)]
688pub struct ConsolidationConfig {
689 #[serde(skip_serializing_if = "Option::is_none")]
691 pub algorithm: Option<String>,
692 #[serde(skip_serializing_if = "Option::is_none")]
694 pub min_samples: Option<u32>,
695 #[serde(skip_serializing_if = "Option::is_none")]
697 pub eps: Option<f32>,
698}
699
700#[derive(Debug, Clone, Serialize, Deserialize)]
702pub struct ConsolidationLogEntry {
703 pub step: String,
704 pub memories_before: usize,
705 pub memories_after: usize,
706 pub duration_ms: f64,
707}
708
709#[derive(Debug, Clone, Serialize, Deserialize, Default)]
711pub struct ConsolidateRequest {
712 #[serde(skip_serializing_if = "Option::is_none")]
713 pub memory_type: Option<String>,
714 #[serde(skip_serializing_if = "Option::is_none")]
715 pub threshold: Option<f32>,
716 #[serde(default)]
717 pub dry_run: bool,
718 #[serde(skip_serializing_if = "Option::is_none")]
720 pub config: Option<ConsolidationConfig>,
721}
722
723#[derive(Debug, Clone, Serialize)]
728pub struct ConsolidateResponse {
729 pub consolidated_count: usize,
731 pub removed_count: usize,
733 #[serde(default)]
735 pub new_memories: Vec<String>,
736 #[serde(default, skip_serializing_if = "Vec::is_empty")]
738 pub log: Vec<ConsolidationLogEntry>,
739}
740
741impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
742 fn deserialize<D: serde::Deserializer<'de>>(
743 deserializer: D,
744 ) -> std::result::Result<Self, D::Error> {
745 let val = serde_json::Value::deserialize(deserializer)?;
746 let removed = val
748 .get("memories_removed")
749 .and_then(|v| v.as_u64())
750 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
751 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
752 .unwrap_or(0) as usize;
753 let source_ids: Vec<String> = val
754 .get("source_memory_ids")
755 .and_then(|v| v.as_array())
756 .map(|arr| {
757 arr.iter()
758 .filter_map(|v| v.as_str().map(String::from))
759 .collect()
760 })
761 .unwrap_or_default();
762 Ok(Self {
763 consolidated_count: removed,
764 removed_count: removed,
765 new_memories: source_ids,
766 log: vec![],
767 })
768 }
769}
770
771#[derive(Debug, Clone, Serialize, Deserialize)]
777pub struct MemoryImportResponse {
778 pub imported_count: usize,
779 pub skipped_count: usize,
780 #[serde(default)]
781 pub errors: Vec<String>,
782}
783
784#[derive(Debug, Clone, Serialize, Deserialize)]
786pub struct MemoryExportResponse {
787 pub data: Vec<serde_json::Value>,
788 pub format: String,
789 pub count: usize,
790}
791
792#[derive(Debug, Clone, Serialize, Deserialize)]
798pub struct AuditEvent {
799 pub id: String,
800 pub event_type: String,
801 #[serde(skip_serializing_if = "Option::is_none")]
802 pub agent_id: Option<String>,
803 #[serde(skip_serializing_if = "Option::is_none")]
804 pub namespace: Option<String>,
805 pub timestamp: u64,
806 #[serde(default)]
807 pub details: serde_json::Value,
808}
809
810#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct AuditListResponse {
813 pub events: Vec<AuditEvent>,
814 pub total: usize,
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub cursor: Option<String>,
817}
818
819#[derive(Debug, Clone, Serialize, Deserialize)]
821pub struct AuditExportResponse {
822 pub data: String,
823 pub format: String,
824 pub count: usize,
825}
826
827#[derive(Debug, Clone, Serialize, Deserialize, Default)]
829pub struct AuditQuery {
830 #[serde(skip_serializing_if = "Option::is_none")]
831 pub agent_id: Option<String>,
832 #[serde(skip_serializing_if = "Option::is_none")]
833 pub event_type: Option<String>,
834 #[serde(skip_serializing_if = "Option::is_none")]
835 pub from: Option<u64>,
836 #[serde(skip_serializing_if = "Option::is_none")]
837 pub to: Option<u64>,
838 #[serde(skip_serializing_if = "Option::is_none")]
839 pub limit: Option<u32>,
840 #[serde(skip_serializing_if = "Option::is_none")]
841 pub cursor: Option<String>,
842}
843
844#[derive(Debug, Clone, Serialize, Deserialize)]
850pub struct ExtractionResult {
851 pub entities: Vec<serde_json::Value>,
852 pub provider: String,
853 #[serde(skip_serializing_if = "Option::is_none")]
854 pub model: Option<String>,
855 pub duration_ms: f64,
856}
857
858#[derive(Debug, Clone, Serialize, Deserialize)]
860pub struct ExtractionProviderInfo {
861 pub name: String,
862 pub available: bool,
863 #[serde(default)]
864 pub models: Vec<String>,
865}
866
867#[derive(Debug, Clone, Serialize, Deserialize)]
869#[serde(untagged)]
870pub enum ExtractProvidersResponse {
871 List(Vec<ExtractionProviderInfo>),
872 Object {
873 providers: Vec<ExtractionProviderInfo>,
874 },
875}
876
877#[derive(Debug, Clone, Serialize, Deserialize)]
883pub struct RotateEncryptionKeyRequest {
884 pub new_key: String,
886 #[serde(skip_serializing_if = "Option::is_none")]
888 pub namespace: Option<String>,
889}
890
891#[derive(Debug, Clone, Serialize, Deserialize)]
893pub struct RotateEncryptionKeyResponse {
894 pub rotated: usize,
895 pub skipped: usize,
896 #[serde(default)]
897 pub namespaces: Vec<String>,
898}
899
900#[derive(Debug, Clone, Serialize, Deserialize)]
902pub struct FeedbackRequest {
903 pub memory_id: String,
904 pub feedback: String,
905 #[serde(skip_serializing_if = "Option::is_none")]
906 pub relevance_score: Option<f32>,
907}
908
909#[derive(Debug, Clone, Serialize, Deserialize)]
911pub struct LegacyFeedbackResponse {
912 pub status: String,
913 pub updated_importance: Option<f32>,
914}
915
916#[derive(Debug, Clone, Serialize, Deserialize, Default)]
925pub struct BatchMemoryFilter {
926 #[serde(skip_serializing_if = "Option::is_none")]
928 pub tags: Option<Vec<String>>,
929 #[serde(skip_serializing_if = "Option::is_none")]
931 pub min_importance: Option<f32>,
932 #[serde(skip_serializing_if = "Option::is_none")]
934 pub max_importance: Option<f32>,
935 #[serde(skip_serializing_if = "Option::is_none")]
937 pub created_after: Option<u64>,
938 #[serde(skip_serializing_if = "Option::is_none")]
940 pub created_before: Option<u64>,
941 #[serde(skip_serializing_if = "Option::is_none")]
943 pub memory_type: Option<MemoryType>,
944 #[serde(skip_serializing_if = "Option::is_none")]
946 pub session_id: Option<String>,
947}
948
949impl BatchMemoryFilter {
950 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
952 self.tags = Some(tags);
953 self
954 }
955
956 pub fn with_min_importance(mut self, min: f32) -> Self {
958 self.min_importance = Some(min);
959 self
960 }
961
962 pub fn with_max_importance(mut self, max: f32) -> Self {
964 self.max_importance = Some(max);
965 self
966 }
967
968 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
970 self.session_id = Some(session_id.into());
971 self
972 }
973}
974
975#[derive(Debug, Clone, Serialize, Deserialize)]
977pub struct BatchRecallRequest {
978 pub agent_id: String,
980 #[serde(default)]
982 pub filter: BatchMemoryFilter,
983 #[serde(default = "default_batch_limit")]
985 pub limit: usize,
986}
987
988fn default_batch_limit() -> usize {
989 100
990}
991
992impl BatchRecallRequest {
993 pub fn new(agent_id: impl Into<String>) -> Self {
995 Self {
996 agent_id: agent_id.into(),
997 filter: BatchMemoryFilter::default(),
998 limit: 100,
999 }
1000 }
1001
1002 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
1004 self.filter = filter;
1005 self
1006 }
1007
1008 pub fn with_limit(mut self, limit: usize) -> Self {
1010 self.limit = limit;
1011 self
1012 }
1013}
1014
1015#[derive(Debug, Clone, Serialize, Deserialize)]
1017pub struct BatchRecallResponse {
1018 pub memories: Vec<RecalledMemory>,
1019 pub total: usize,
1021 pub filtered: usize,
1023}
1024
1025#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1033pub struct BatchStoreMemoryItem {
1034 pub content: String,
1036 #[serde(default)]
1037 pub memory_type: MemoryType,
1038 #[serde(skip_serializing_if = "Option::is_none")]
1039 pub session_id: Option<String>,
1040 #[serde(default = "default_importance")]
1041 pub importance: f32,
1042 #[serde(default)]
1043 pub tags: Vec<String>,
1044 #[serde(skip_serializing_if = "Option::is_none")]
1045 pub metadata: Option<serde_json::Value>,
1046 #[serde(skip_serializing_if = "Option::is_none")]
1047 pub ttl_seconds: Option<u64>,
1048 #[serde(skip_serializing_if = "Option::is_none")]
1049 pub expires_at: Option<u64>,
1050 #[serde(skip_serializing_if = "Option::is_none")]
1052 pub id: Option<String>,
1053}
1054
1055impl BatchStoreMemoryItem {
1056 pub fn new(content: impl Into<String>) -> Self {
1058 Self {
1059 content: content.into(),
1060 importance: default_importance(),
1061 ..Default::default()
1062 }
1063 }
1064
1065 pub fn with_importance(mut self, importance: f32) -> Self {
1067 self.importance = importance;
1068 self
1069 }
1070
1071 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
1073 self.tags = tags;
1074 self
1075 }
1076
1077 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
1079 self.session_id = Some(session_id.into());
1080 self
1081 }
1082
1083 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
1085 self.metadata = Some(metadata);
1086 self
1087 }
1088
1089 pub fn with_id(mut self, id: impl Into<String>) -> Self {
1091 self.id = Some(id.into());
1092 self
1093 }
1094}
1095
1096#[derive(Debug, Clone, Serialize, Deserialize)]
1103pub struct BatchStoreMemoryRequest {
1104 pub agent_id: String,
1106 pub memories: Vec<BatchStoreMemoryItem>,
1108}
1109
1110impl BatchStoreMemoryRequest {
1111 pub fn new(agent_id: impl Into<String>, memories: Vec<BatchStoreMemoryItem>) -> Self {
1113 Self {
1114 agent_id: agent_id.into(),
1115 memories,
1116 }
1117 }
1118}
1119
1120#[derive(Debug, Clone, Serialize, Deserialize)]
1122pub struct BatchStoredMemory {
1123 pub id: String,
1124 pub content: String,
1125 pub agent_id: String,
1126 #[serde(default)]
1127 pub tags: Vec<String>,
1128 #[serde(default)]
1129 pub importance: f32,
1130 pub created_at: u64,
1131}
1132
1133#[derive(Debug, Clone, Serialize, Deserialize)]
1135pub struct BatchStoreMemoryResponse {
1136 pub stored: Vec<BatchStoredMemory>,
1138 pub stored_count: usize,
1140 pub total_embedding_time_ms: u64,
1142}
1143
1144#[derive(Debug, Clone, Serialize, Deserialize)]
1146pub struct BatchForgetRequest {
1147 pub agent_id: String,
1149 pub filter: BatchMemoryFilter,
1151}
1152
1153impl BatchForgetRequest {
1154 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
1156 Self {
1157 agent_id: agent_id.into(),
1158 filter,
1159 }
1160 }
1161}
1162
1163#[derive(Debug, Clone, Serialize, Deserialize)]
1165pub struct BatchForgetResponse {
1166 pub deleted_count: usize,
1167}
1168
1169impl DakeraClient {
1174 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1198 let url = format!("{}/v1/memory/store", self.base_url);
1199 let response = self.client.post(&url).json(&request).send().await?;
1200 self.handle_response(response).await
1201 }
1202
1203 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1224 let url = format!("{}/v1/memory/recall", self.base_url);
1225 let response = self.client.post(&url).json(&request).send().await?;
1226 self.handle_response(response).await
1227 }
1228
1229 pub async fn recall_simple(
1231 &self,
1232 agent_id: &str,
1233 query: &str,
1234 top_k: usize,
1235 ) -> Result<RecallResponse> {
1236 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1237 .await
1238 }
1239
1240 pub async fn get_memory(&self, agent_id: &str, memory_id: &str) -> Result<RecalledMemory> {
1242 let url = format!(
1243 "{}/v1/memory/get/{}?agent_id={}",
1244 self.base_url, memory_id, agent_id
1245 );
1246 let response = self.client.get(&url).send().await?;
1247 self.handle_response(response).await
1248 }
1249
1250 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1252 let url = format!("{}/v1/memory/forget", self.base_url);
1253 let response = self.client.post(&url).json(&request).send().await?;
1254 self.handle_response(response).await
1255 }
1256
1257 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1259 let url = format!("{}/v1/memory/search", self.base_url);
1260 let response = self.client.post(&url).json(&request).send().await?;
1261 self.handle_response(response).await
1262 }
1263
1264 pub async fn update_memory(
1266 &self,
1267 agent_id: &str,
1268 memory_id: &str,
1269 request: UpdateMemoryRequest,
1270 ) -> Result<StoreMemoryResponse> {
1271 let url = format!(
1272 "{}/v1/agents/{}/memories/{}",
1273 self.base_url, agent_id, memory_id
1274 );
1275 let response = self.client.put(&url).json(&request).send().await?;
1276 self.handle_response(response).await
1277 }
1278
1279 pub async fn update_importance(
1281 &self,
1282 agent_id: &str,
1283 request: UpdateImportanceRequest,
1284 ) -> Result<serde_json::Value> {
1285 let url = format!("{}/v1/memory/importance", self.base_url);
1286 let mut last_result = serde_json::Value::Null;
1287 for memory_id in &request.memory_ids {
1288 let body = serde_json::json!({
1289 "agent_id": agent_id,
1290 "memory_id": memory_id,
1291 "importance": request.importance,
1292 });
1293 let response = self.client.post(&url).json(&body).send().await?;
1294 last_result = self.handle_response(response).await?;
1295 }
1296 Ok(last_result)
1297 }
1298
1299 pub async fn consolidate(
1301 &self,
1302 agent_id: &str,
1303 request: ConsolidateRequest,
1304 ) -> Result<ConsolidateResponse> {
1305 let url = format!("{}/v1/memory/consolidate", self.base_url);
1307 let mut body = serde_json::to_value(&request)?;
1308 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1309 let response = self.client.post(&url).json(&body).send().await?;
1310 self.handle_response(response).await
1311 }
1312
1313 pub async fn memory_feedback(
1315 &self,
1316 agent_id: &str,
1317 request: FeedbackRequest,
1318 ) -> Result<LegacyFeedbackResponse> {
1319 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1320 let response = self.client.post(&url).json(&request).send().await?;
1321 self.handle_response(response).await
1322 }
1323
1324 pub async fn feedback_memory(
1344 &self,
1345 memory_id: &str,
1346 agent_id: &str,
1347 signal: FeedbackSignal,
1348 ) -> Result<FeedbackResponse> {
1349 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1350 let body = MemoryFeedbackBody {
1351 agent_id: agent_id.to_string(),
1352 signal,
1353 };
1354 let response = self.client.post(&url).json(&body).send().await?;
1355 self.handle_response(response).await
1356 }
1357
1358 pub async fn get_memory_feedback_history(
1360 &self,
1361 memory_id: &str,
1362 ) -> Result<FeedbackHistoryResponse> {
1363 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1364 let response = self.client.get(&url).send().await?;
1365 self.handle_response(response).await
1366 }
1367
1368 pub async fn evaluate_tif(&self, memory_id: &str) -> Result<TifScore> {
1376 let history = self.get_memory_feedback_history(memory_id).await?;
1377 Ok(TifScore::from_feedback_history(&history))
1378 }
1379
1380 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1382 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1383 let response = self.client.get(&url).send().await?;
1384 self.handle_response(response).await
1385 }
1386
1387 pub async fn patch_memory_importance(
1394 &self,
1395 memory_id: &str,
1396 agent_id: &str,
1397 importance: f32,
1398 ) -> Result<FeedbackResponse> {
1399 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1400 let body = MemoryImportancePatch {
1401 agent_id: agent_id.to_string(),
1402 importance,
1403 };
1404 let response = self.client.patch(&url).json(&body).send().await?;
1405 self.handle_response(response).await
1406 }
1407
1408 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1413 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1414 let response = self.client.get(&url).send().await?;
1415 self.handle_response(response).await
1416 }
1417
1418 pub async fn memory_graph(
1439 &self,
1440 memory_id: &str,
1441 options: GraphOptions,
1442 ) -> Result<MemoryGraph> {
1443 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1444 let depth = options.depth.unwrap_or(1);
1445 url.push_str(&format!("?depth={}", depth));
1446 if let Some(types) = &options.types {
1447 let type_strs: Vec<String> = types
1448 .iter()
1449 .map(|t| {
1450 serde_json::to_value(t)
1451 .unwrap()
1452 .as_str()
1453 .unwrap_or("")
1454 .to_string()
1455 })
1456 .collect();
1457 if !type_strs.is_empty() {
1458 url.push_str(&format!("&types={}", type_strs.join(",")));
1459 }
1460 }
1461 let response = self.client.get(&url).send().await?;
1462 self.handle_response(response).await
1463 }
1464
1465 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1478 let url = format!(
1479 "{}/v1/memories/{}/path?target={}",
1480 self.base_url,
1481 source_id,
1482 urlencoding::encode(target_id)
1483 );
1484 let response = self.client.get(&url).send().await?;
1485 self.handle_response(response).await
1486 }
1487
1488 pub async fn memory_link(
1501 &self,
1502 source_id: &str,
1503 target_id: &str,
1504 edge_type: EdgeType,
1505 ) -> Result<GraphLinkResponse> {
1506 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1507 let request = GraphLinkRequest {
1508 target_id: target_id.to_string(),
1509 edge_type,
1510 };
1511 let response = self.client.post(&url).json(&request).send().await?;
1512 self.handle_response(response).await
1513 }
1514
1515 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1523 let url = format!(
1524 "{}/v1/agents/{}/graph/export?format={}",
1525 self.base_url, agent_id, format
1526 );
1527 let response = self.client.get(&url).send().await?;
1528 self.handle_response(response).await
1529 }
1530
1531 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1537 let url = format!("{}/v1/sessions/start", self.base_url);
1538 let request = SessionStartRequest {
1539 agent_id: agent_id.to_string(),
1540 metadata: None,
1541 };
1542 let response = self.client.post(&url).json(&request).send().await?;
1543 let resp: SessionStartResponse = self.handle_response(response).await?;
1544 Ok(resp.session)
1545 }
1546
1547 pub async fn start_session_with_metadata(
1549 &self,
1550 agent_id: &str,
1551 metadata: serde_json::Value,
1552 ) -> Result<Session> {
1553 let url = format!("{}/v1/sessions/start", self.base_url);
1554 let request = SessionStartRequest {
1555 agent_id: agent_id.to_string(),
1556 metadata: Some(metadata),
1557 };
1558 let response = self.client.post(&url).json(&request).send().await?;
1559 let resp: SessionStartResponse = self.handle_response(response).await?;
1560 Ok(resp.session)
1561 }
1562
1563 pub async fn end_session(
1566 &self,
1567 session_id: &str,
1568 summary: Option<String>,
1569 ) -> Result<SessionEndResponse> {
1570 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1571 let request = SessionEndRequest { summary };
1572 let response = self.client.post(&url).json(&request).send().await?;
1573 self.handle_response(response).await
1574 }
1575
1576 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1578 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1579 let response = self.client.get(&url).send().await?;
1580 self.handle_response(response).await
1581 }
1582
1583 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1585 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1586 let response = self.client.get(&url).send().await?;
1587 let wrapper: ListSessionsResponse = self.handle_response(response).await?;
1588 Ok(wrapper.sessions)
1589 }
1590
1591 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1593 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1594 let response = self.client.get(&url).send().await?;
1595 self.handle_response(response).await
1596 }
1597
1598 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1622 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1623 let response = self.client.post(&url).json(&request).send().await?;
1624 self.handle_response(response).await
1625 }
1626
1627 pub async fn store_memories_batch(
1657 &self,
1658 request: BatchStoreMemoryRequest,
1659 ) -> Result<BatchStoreMemoryResponse> {
1660 let url = format!("{}/v1/memories/store/batch", self.base_url);
1661 let response = self.client.post(&url).json(&request).send().await?;
1662 self.handle_response(response).await
1663 }
1664
1665 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1685 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1686 let response = self.client.delete(&url).json(&request).send().await?;
1687 self.handle_response(response).await
1688 }
1689
1690 pub async fn import_memories(
1708 &self,
1709 data: serde_json::Value,
1710 format: &str,
1711 agent_id: Option<&str>,
1712 namespace: Option<&str>,
1713 ) -> Result<MemoryImportResponse> {
1714 let mut body = serde_json::json!({"data": data, "format": format});
1715 if let Some(aid) = agent_id {
1716 body["agent_id"] = serde_json::Value::String(aid.to_string());
1717 }
1718 if let Some(ns) = namespace {
1719 body["namespace"] = serde_json::Value::String(ns.to_string());
1720 }
1721 let url = format!("{}/v1/import", self.base_url);
1722 let response = self.client.post(&url).json(&body).send().await?;
1723 self.handle_response(response).await
1724 }
1725
1726 pub async fn export_memories(
1730 &self,
1731 format: &str,
1732 agent_id: Option<&str>,
1733 namespace: Option<&str>,
1734 limit: Option<u32>,
1735 ) -> Result<MemoryExportResponse> {
1736 let mut params = vec![("format", format.to_string())];
1737 if let Some(aid) = agent_id {
1738 params.push(("agent_id", aid.to_string()));
1739 }
1740 if let Some(ns) = namespace {
1741 params.push(("namespace", ns.to_string()));
1742 }
1743 if let Some(l) = limit {
1744 params.push(("limit", l.to_string()));
1745 }
1746 let url = format!("{}/v1/export", self.base_url);
1747 let response = self.client.get(&url).query(¶ms).send().await?;
1748 self.handle_response(response).await
1749 }
1750
1751 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1757 let url = format!("{}/v1/audit", self.base_url);
1758 let response = self.client.get(&url).query(&query).send().await?;
1759 self.handle_response(response).await
1760 }
1761
1762 pub async fn stream_audit_events(
1766 &self,
1767 agent_id: Option<&str>,
1768 event_type: Option<&str>,
1769 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1770 let mut params: Vec<(&str, String)> = Vec::new();
1771 if let Some(aid) = agent_id {
1772 params.push(("agent_id", aid.to_string()));
1773 }
1774 if let Some(et) = event_type {
1775 params.push(("event_type", et.to_string()));
1776 }
1777 let base = format!("{}/v1/audit/stream", self.base_url);
1778 let url = if params.is_empty() {
1779 base
1780 } else {
1781 let qs = params
1782 .iter()
1783 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1784 .collect::<Vec<_>>()
1785 .join("&");
1786 format!("{}?{}", base, qs)
1787 };
1788 self.stream_sse(url).await
1789 }
1790
1791 pub async fn export_audit(
1793 &self,
1794 format: &str,
1795 agent_id: Option<&str>,
1796 event_type: Option<&str>,
1797 from_ts: Option<u64>,
1798 to_ts: Option<u64>,
1799 ) -> Result<AuditExportResponse> {
1800 let mut body = serde_json::json!({"format": format});
1801 if let Some(aid) = agent_id {
1802 body["agent_id"] = serde_json::Value::String(aid.to_string());
1803 }
1804 if let Some(et) = event_type {
1805 body["event_type"] = serde_json::Value::String(et.to_string());
1806 }
1807 if let Some(f) = from_ts {
1808 body["from"] = serde_json::Value::Number(f.into());
1809 }
1810 if let Some(t) = to_ts {
1811 body["to"] = serde_json::Value::Number(t.into());
1812 }
1813 let url = format!("{}/v1/audit/export", self.base_url);
1814 let response = self.client.post(&url).json(&body).send().await?;
1815 self.handle_response(response).await
1816 }
1817
1818 pub async fn extract_text(
1827 &self,
1828 text: &str,
1829 namespace: Option<&str>,
1830 provider: Option<&str>,
1831 model: Option<&str>,
1832 ) -> Result<ExtractionResult> {
1833 let mut body = serde_json::json!({"text": text});
1834 if let Some(ns) = namespace {
1835 body["namespace"] = serde_json::Value::String(ns.to_string());
1836 }
1837 if let Some(p) = provider {
1838 body["provider"] = serde_json::Value::String(p.to_string());
1839 }
1840 if let Some(m) = model {
1841 body["model"] = serde_json::Value::String(m.to_string());
1842 }
1843 let url = format!("{}/v1/extract", self.base_url);
1844 let response = self.client.post(&url).json(&body).send().await?;
1845 self.handle_response(response).await
1846 }
1847
1848 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1850 let url = format!("{}/v1/extract/providers", self.base_url);
1851 let response = self.client.get(&url).send().await?;
1852 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1853 Ok(match result {
1854 ExtractProvidersResponse::List(v) => v,
1855 ExtractProvidersResponse::Object { providers } => providers,
1856 })
1857 }
1858
1859 pub async fn configure_namespace_extractor(
1861 &self,
1862 namespace: &str,
1863 provider: &str,
1864 model: Option<&str>,
1865 ) -> Result<serde_json::Value> {
1866 let mut body = serde_json::json!({"provider": provider});
1867 if let Some(m) = model {
1868 body["model"] = serde_json::Value::String(m.to_string());
1869 }
1870 let url = format!(
1871 "{}/v1/namespaces/{}/extractor",
1872 self.base_url,
1873 urlencoding::encode(namespace)
1874 );
1875 let response = self.client.patch(&url).json(&body).send().await?;
1876 self.handle_response(response).await
1877 }
1878
1879 pub async fn rotate_encryption_key(
1895 &self,
1896 new_key: &str,
1897 namespace: Option<&str>,
1898 ) -> Result<RotateEncryptionKeyResponse> {
1899 let body = RotateEncryptionKeyRequest {
1900 new_key: new_key.to_string(),
1901 namespace: namespace.map(|s| s.to_string()),
1902 };
1903 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1904 let response = self.client.post(&url).json(&body).send().await?;
1905 self.handle_response(response).await
1906 }
1907}