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")]
288 pub neighborhood: Option<bool>,
289}
290
291fn default_top_k() -> usize {
292 5
293}
294
295impl RecallRequest {
296 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
298 Self {
299 agent_id: agent_id.into(),
300 query: query.into(),
301 top_k: 5,
302 memory_type: None,
303 min_importance: 0.0,
304 session_id: None,
305 tags: Vec::new(),
306 include_associated: false,
307 associated_memories_cap: None,
308 associated_memories_depth: None,
309 associated_memories_min_weight: None,
310 since: None,
311 until: None,
312 routing: None,
313 rerank: None,
314 fusion: None,
315 vector_weight: None,
316 neighborhood: None,
317 }
318 }
319
320 pub fn with_top_k(mut self, top_k: usize) -> Self {
322 self.top_k = top_k;
323 self
324 }
325
326 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
328 self.memory_type = Some(memory_type);
329 self
330 }
331
332 pub fn with_min_importance(mut self, min: f32) -> Self {
334 self.min_importance = min;
335 self
336 }
337
338 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
340 self.session_id = Some(session_id.into());
341 self
342 }
343
344 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
346 self.tags = tags;
347 self
348 }
349
350 pub fn with_associated(mut self) -> Self {
352 self.include_associated = true;
353 self
354 }
355
356 pub fn with_associated_cap(mut self, cap: u32) -> Self {
358 self.include_associated = true;
359 self.associated_memories_cap = Some(cap);
360 self
361 }
362
363 pub fn with_since(mut self, since: impl Into<String>) -> Self {
365 self.since = Some(since.into());
366 self
367 }
368
369 pub fn with_until(mut self, until: impl Into<String>) -> Self {
371 self.until = Some(until.into());
372 self
373 }
374
375 pub fn with_routing(mut self, routing: RoutingMode) -> Self {
377 self.routing = Some(routing);
378 self
379 }
380
381 pub fn with_rerank(mut self, rerank: bool) -> Self {
383 self.rerank = Some(rerank);
384 self
385 }
386
387 pub fn with_associated_depth(mut self, depth: u8) -> Self {
389 self.include_associated = true;
390 self.associated_memories_depth = Some(depth);
391 self
392 }
393
394 pub fn with_associated_min_weight(mut self, weight: f32) -> Self {
396 self.associated_memories_min_weight = Some(weight);
397 self
398 }
399
400 pub fn with_fusion(mut self, fusion: FusionStrategy) -> Self {
402 self.fusion = Some(fusion);
403 self
404 }
405
406 pub fn with_vector_weight(mut self, weight: f32) -> Self {
410 self.vector_weight = Some(weight);
411 self
412 }
413
414 pub fn with_neighborhood(mut self, neighborhood: bool) -> Self {
417 self.neighborhood = Some(neighborhood);
418 self
419 }
420}
421
422#[derive(Debug, Clone, Serialize)]
424pub struct RecalledMemory {
425 pub id: String,
426 pub content: String,
427 pub memory_type: MemoryType,
428 pub importance: f32,
429 pub score: f32,
430 #[serde(default)]
431 pub tags: Vec<String>,
432 #[serde(skip_serializing_if = "Option::is_none")]
433 pub session_id: Option<String>,
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub metadata: Option<serde_json::Value>,
436 pub created_at: u64,
437 pub last_accessed_at: u64,
438 pub access_count: u32,
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub depth: Option<u8>,
442}
443
444impl<'de> serde::Deserialize<'de> for RecalledMemory {
445 fn deserialize<D: serde::Deserializer<'de>>(
446 deserializer: D,
447 ) -> std::result::Result<Self, D::Error> {
448 use serde::de::Error as _;
449 let val = serde_json::Value::deserialize(deserializer)?;
450
451 let score = val
454 .get("score")
455 .and_then(|v| v.as_f64())
456 .or_else(|| val.get("weighted_score").and_then(|v| v.as_f64()))
457 .unwrap_or(0.0) as f32;
458
459 let mem = val.get("memory").unwrap_or(&val);
460
461 let id = mem
462 .get("id")
463 .and_then(|v| v.as_str())
464 .ok_or_else(|| D::Error::missing_field("id"))?
465 .to_string();
466 let content = mem
467 .get("content")
468 .and_then(|v| v.as_str())
469 .ok_or_else(|| D::Error::missing_field("content"))?
470 .to_string();
471 let memory_type: MemoryType = mem
472 .get("memory_type")
473 .and_then(|v| serde_json::from_value(v.clone()).ok())
474 .unwrap_or(MemoryType::Episodic);
475 let importance = mem
476 .get("importance")
477 .and_then(|v| v.as_f64())
478 .unwrap_or(0.5) as f32;
479 let tags: Vec<String> = mem
480 .get("tags")
481 .and_then(|v| serde_json::from_value(v.clone()).ok())
482 .unwrap_or_default();
483 let session_id = mem
484 .get("session_id")
485 .and_then(|v| v.as_str())
486 .map(String::from);
487 let metadata = mem.get("metadata").cloned().filter(|v| !v.is_null());
488 let created_at = mem.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
489 let last_accessed_at = mem
490 .get("last_accessed_at")
491 .and_then(|v| v.as_u64())
492 .unwrap_or(0);
493 let access_count = mem
494 .get("access_count")
495 .and_then(|v| v.as_u64())
496 .unwrap_or(0) as u32;
497 let depth = mem.get("depth").and_then(|v| v.as_u64()).map(|v| v as u8);
498
499 Ok(Self {
500 id,
501 content,
502 memory_type,
503 importance,
504 score,
505 tags,
506 session_id,
507 metadata,
508 created_at,
509 last_accessed_at,
510 access_count,
511 depth,
512 })
513 }
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct RecallResponse {
519 pub memories: Vec<RecalledMemory>,
520 #[serde(default)]
521 pub total_found: usize,
522 #[serde(skip_serializing_if = "Option::is_none")]
524 pub associated_memories: Option<Vec<RecalledMemory>>,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct ForgetRequest {
530 pub agent_id: String,
531 #[serde(default)]
532 pub memory_ids: Vec<String>,
533 #[serde(default)]
534 pub tags: Vec<String>,
535 #[serde(skip_serializing_if = "Option::is_none")]
536 pub session_id: Option<String>,
537 #[serde(skip_serializing_if = "Option::is_none")]
538 pub before_timestamp: Option<u64>,
539}
540
541impl ForgetRequest {
542 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
544 Self {
545 agent_id: agent_id.into(),
546 memory_ids: ids,
547 tags: Vec::new(),
548 session_id: None,
549 before_timestamp: None,
550 }
551 }
552
553 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
555 Self {
556 agent_id: agent_id.into(),
557 memory_ids: Vec::new(),
558 tags,
559 session_id: None,
560 before_timestamp: None,
561 }
562 }
563
564 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
566 Self {
567 agent_id: agent_id.into(),
568 memory_ids: Vec::new(),
569 tags: Vec::new(),
570 session_id: Some(session_id.into()),
571 before_timestamp: None,
572 }
573 }
574}
575
576#[derive(Debug, Clone, Serialize, Deserialize)]
578pub struct ForgetResponse {
579 pub deleted_count: u64,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize)]
584pub struct SessionStartRequest {
585 pub agent_id: String,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub metadata: Option<serde_json::Value>,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct Session {
593 pub id: String,
594 pub agent_id: String,
595 pub started_at: u64,
596 #[serde(skip_serializing_if = "Option::is_none")]
597 pub ended_at: Option<u64>,
598 #[serde(skip_serializing_if = "Option::is_none")]
599 pub summary: Option<String>,
600 #[serde(skip_serializing_if = "Option::is_none")]
601 pub metadata: Option<serde_json::Value>,
602 #[serde(default)]
604 pub memory_count: usize,
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize)]
609pub struct SessionEndRequest {
610 #[serde(skip_serializing_if = "Option::is_none")]
611 pub summary: Option<String>,
612}
613
614#[derive(Debug, Clone, Serialize, Deserialize)]
616pub struct SessionStartResponse {
617 pub session: Session,
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
622pub struct SessionEndResponse {
623 pub session: Session,
624 pub memory_count: usize,
625}
626
627#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct UpdateMemoryRequest {
630 #[serde(skip_serializing_if = "Option::is_none")]
631 pub content: Option<String>,
632 #[serde(skip_serializing_if = "Option::is_none")]
633 pub metadata: Option<serde_json::Value>,
634 #[serde(skip_serializing_if = "Option::is_none")]
635 pub memory_type: Option<MemoryType>,
636}
637
638#[derive(Debug, Clone, Serialize, Deserialize)]
640pub struct UpdateImportanceRequest {
641 pub memory_ids: Vec<String>,
642 pub importance: f32,
643}
644
645#[derive(Debug, Clone, Serialize, Deserialize, Default)]
647pub struct ConsolidationConfig {
648 #[serde(skip_serializing_if = "Option::is_none")]
650 pub algorithm: Option<String>,
651 #[serde(skip_serializing_if = "Option::is_none")]
653 pub min_samples: Option<u32>,
654 #[serde(skip_serializing_if = "Option::is_none")]
656 pub eps: Option<f32>,
657}
658
659#[derive(Debug, Clone, Serialize, Deserialize)]
661pub struct ConsolidationLogEntry {
662 pub step: String,
663 pub memories_before: usize,
664 pub memories_after: usize,
665 pub duration_ms: f64,
666}
667
668#[derive(Debug, Clone, Serialize, Deserialize, Default)]
670pub struct ConsolidateRequest {
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub memory_type: Option<String>,
673 #[serde(skip_serializing_if = "Option::is_none")]
674 pub threshold: Option<f32>,
675 #[serde(default)]
676 pub dry_run: bool,
677 #[serde(skip_serializing_if = "Option::is_none")]
679 pub config: Option<ConsolidationConfig>,
680}
681
682#[derive(Debug, Clone, Serialize)]
687pub struct ConsolidateResponse {
688 pub consolidated_count: usize,
690 pub removed_count: usize,
692 #[serde(default)]
694 pub new_memories: Vec<String>,
695 #[serde(default, skip_serializing_if = "Vec::is_empty")]
697 pub log: Vec<ConsolidationLogEntry>,
698}
699
700impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
701 fn deserialize<D: serde::Deserializer<'de>>(
702 deserializer: D,
703 ) -> std::result::Result<Self, D::Error> {
704 let val = serde_json::Value::deserialize(deserializer)?;
705 let removed = val
707 .get("memories_removed")
708 .and_then(|v| v.as_u64())
709 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
710 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
711 .unwrap_or(0) as usize;
712 let source_ids: Vec<String> = val
713 .get("source_memory_ids")
714 .and_then(|v| v.as_array())
715 .map(|arr| {
716 arr.iter()
717 .filter_map(|v| v.as_str().map(String::from))
718 .collect()
719 })
720 .unwrap_or_default();
721 Ok(Self {
722 consolidated_count: removed,
723 removed_count: removed,
724 new_memories: source_ids,
725 log: vec![],
726 })
727 }
728}
729
730#[derive(Debug, Clone, Serialize, Deserialize)]
736pub struct MemoryImportResponse {
737 pub imported_count: usize,
738 pub skipped_count: usize,
739 #[serde(default)]
740 pub errors: Vec<String>,
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize)]
745pub struct MemoryExportResponse {
746 pub data: Vec<serde_json::Value>,
747 pub format: String,
748 pub count: usize,
749}
750
751#[derive(Debug, Clone, Serialize, Deserialize)]
757pub struct AuditEvent {
758 pub id: String,
759 pub event_type: String,
760 #[serde(skip_serializing_if = "Option::is_none")]
761 pub agent_id: Option<String>,
762 #[serde(skip_serializing_if = "Option::is_none")]
763 pub namespace: Option<String>,
764 pub timestamp: u64,
765 #[serde(default)]
766 pub details: serde_json::Value,
767}
768
769#[derive(Debug, Clone, Serialize, Deserialize)]
771pub struct AuditListResponse {
772 pub events: Vec<AuditEvent>,
773 pub total: usize,
774 #[serde(skip_serializing_if = "Option::is_none")]
775 pub cursor: Option<String>,
776}
777
778#[derive(Debug, Clone, Serialize, Deserialize)]
780pub struct AuditExportResponse {
781 pub data: String,
782 pub format: String,
783 pub count: usize,
784}
785
786#[derive(Debug, Clone, Serialize, Deserialize, Default)]
788pub struct AuditQuery {
789 #[serde(skip_serializing_if = "Option::is_none")]
790 pub agent_id: Option<String>,
791 #[serde(skip_serializing_if = "Option::is_none")]
792 pub event_type: Option<String>,
793 #[serde(skip_serializing_if = "Option::is_none")]
794 pub from: Option<u64>,
795 #[serde(skip_serializing_if = "Option::is_none")]
796 pub to: Option<u64>,
797 #[serde(skip_serializing_if = "Option::is_none")]
798 pub limit: Option<u32>,
799 #[serde(skip_serializing_if = "Option::is_none")]
800 pub cursor: Option<String>,
801}
802
803#[derive(Debug, Clone, Serialize, Deserialize)]
809pub struct ExtractionResult {
810 pub entities: Vec<serde_json::Value>,
811 pub provider: String,
812 #[serde(skip_serializing_if = "Option::is_none")]
813 pub model: Option<String>,
814 pub duration_ms: f64,
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize)]
819pub struct ExtractionProviderInfo {
820 pub name: String,
821 pub available: bool,
822 #[serde(default)]
823 pub models: Vec<String>,
824}
825
826#[derive(Debug, Clone, Serialize, Deserialize)]
828#[serde(untagged)]
829pub enum ExtractProvidersResponse {
830 List(Vec<ExtractionProviderInfo>),
831 Object {
832 providers: Vec<ExtractionProviderInfo>,
833 },
834}
835
836#[derive(Debug, Clone, Serialize, Deserialize)]
842pub struct RotateEncryptionKeyRequest {
843 pub new_key: String,
845 #[serde(skip_serializing_if = "Option::is_none")]
847 pub namespace: Option<String>,
848}
849
850#[derive(Debug, Clone, Serialize, Deserialize)]
852pub struct RotateEncryptionKeyResponse {
853 pub rotated: usize,
854 pub skipped: usize,
855 #[serde(default)]
856 pub namespaces: Vec<String>,
857}
858
859#[derive(Debug, Clone, Serialize, Deserialize)]
861pub struct FeedbackRequest {
862 pub memory_id: String,
863 pub feedback: String,
864 #[serde(skip_serializing_if = "Option::is_none")]
865 pub relevance_score: Option<f32>,
866}
867
868#[derive(Debug, Clone, Serialize, Deserialize)]
870pub struct LegacyFeedbackResponse {
871 pub status: String,
872 pub updated_importance: Option<f32>,
873}
874
875#[derive(Debug, Clone, Serialize, Deserialize, Default)]
884pub struct BatchMemoryFilter {
885 #[serde(skip_serializing_if = "Option::is_none")]
887 pub tags: Option<Vec<String>>,
888 #[serde(skip_serializing_if = "Option::is_none")]
890 pub min_importance: Option<f32>,
891 #[serde(skip_serializing_if = "Option::is_none")]
893 pub max_importance: Option<f32>,
894 #[serde(skip_serializing_if = "Option::is_none")]
896 pub created_after: Option<u64>,
897 #[serde(skip_serializing_if = "Option::is_none")]
899 pub created_before: Option<u64>,
900 #[serde(skip_serializing_if = "Option::is_none")]
902 pub memory_type: Option<MemoryType>,
903 #[serde(skip_serializing_if = "Option::is_none")]
905 pub session_id: Option<String>,
906}
907
908impl BatchMemoryFilter {
909 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
911 self.tags = Some(tags);
912 self
913 }
914
915 pub fn with_min_importance(mut self, min: f32) -> Self {
917 self.min_importance = Some(min);
918 self
919 }
920
921 pub fn with_max_importance(mut self, max: f32) -> Self {
923 self.max_importance = Some(max);
924 self
925 }
926
927 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
929 self.session_id = Some(session_id.into());
930 self
931 }
932}
933
934#[derive(Debug, Clone, Serialize, Deserialize)]
936pub struct BatchRecallRequest {
937 pub agent_id: String,
939 #[serde(default)]
941 pub filter: BatchMemoryFilter,
942 #[serde(default = "default_batch_limit")]
944 pub limit: usize,
945}
946
947fn default_batch_limit() -> usize {
948 100
949}
950
951impl BatchRecallRequest {
952 pub fn new(agent_id: impl Into<String>) -> Self {
954 Self {
955 agent_id: agent_id.into(),
956 filter: BatchMemoryFilter::default(),
957 limit: 100,
958 }
959 }
960
961 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
963 self.filter = filter;
964 self
965 }
966
967 pub fn with_limit(mut self, limit: usize) -> Self {
969 self.limit = limit;
970 self
971 }
972}
973
974#[derive(Debug, Clone, Serialize, Deserialize)]
976pub struct BatchRecallResponse {
977 pub memories: Vec<RecalledMemory>,
978 pub total: usize,
980 pub filtered: usize,
982}
983
984#[derive(Debug, Clone, Serialize, Deserialize)]
986pub struct BatchForgetRequest {
987 pub agent_id: String,
989 pub filter: BatchMemoryFilter,
991}
992
993impl BatchForgetRequest {
994 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
996 Self {
997 agent_id: agent_id.into(),
998 filter,
999 }
1000 }
1001}
1002
1003#[derive(Debug, Clone, Serialize, Deserialize)]
1005pub struct BatchForgetResponse {
1006 pub deleted_count: usize,
1007}
1008
1009impl DakeraClient {
1014 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1038 let url = format!("{}/v1/memory/store", self.base_url);
1039 let response = self.client.post(&url).json(&request).send().await?;
1040 self.handle_response(response).await
1041 }
1042
1043 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1064 let url = format!("{}/v1/memory/recall", self.base_url);
1065 let response = self.client.post(&url).json(&request).send().await?;
1066 self.handle_response(response).await
1067 }
1068
1069 pub async fn recall_simple(
1071 &self,
1072 agent_id: &str,
1073 query: &str,
1074 top_k: usize,
1075 ) -> Result<RecallResponse> {
1076 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1077 .await
1078 }
1079
1080 pub async fn get_memory(&self, memory_id: &str) -> Result<RecalledMemory> {
1082 let url = format!("{}/v1/memory/get/{}", self.base_url, memory_id);
1083 let response = self.client.get(&url).send().await?;
1084 self.handle_response(response).await
1085 }
1086
1087 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1089 let url = format!("{}/v1/memory/forget", self.base_url);
1090 let response = self.client.post(&url).json(&request).send().await?;
1091 self.handle_response(response).await
1092 }
1093
1094 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1096 let url = format!("{}/v1/memory/search", self.base_url);
1097 let response = self.client.post(&url).json(&request).send().await?;
1098 self.handle_response(response).await
1099 }
1100
1101 pub async fn update_memory(
1103 &self,
1104 agent_id: &str,
1105 memory_id: &str,
1106 request: UpdateMemoryRequest,
1107 ) -> Result<StoreMemoryResponse> {
1108 let url = format!(
1109 "{}/v1/agents/{}/memories/{}",
1110 self.base_url, agent_id, memory_id
1111 );
1112 let response = self.client.put(&url).json(&request).send().await?;
1113 self.handle_response(response).await
1114 }
1115
1116 pub async fn update_importance(
1118 &self,
1119 agent_id: &str,
1120 request: UpdateImportanceRequest,
1121 ) -> Result<serde_json::Value> {
1122 let url = format!(
1123 "{}/v1/agents/{}/memories/importance",
1124 self.base_url, agent_id
1125 );
1126 let response = self.client.put(&url).json(&request).send().await?;
1127 self.handle_response(response).await
1128 }
1129
1130 pub async fn consolidate(
1132 &self,
1133 agent_id: &str,
1134 request: ConsolidateRequest,
1135 ) -> Result<ConsolidateResponse> {
1136 let url = format!("{}/v1/memory/consolidate", self.base_url);
1138 let mut body = serde_json::to_value(&request)?;
1139 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1140 let response = self.client.post(&url).json(&body).send().await?;
1141 self.handle_response(response).await
1142 }
1143
1144 pub async fn memory_feedback(
1146 &self,
1147 agent_id: &str,
1148 request: FeedbackRequest,
1149 ) -> Result<LegacyFeedbackResponse> {
1150 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1151 let response = self.client.post(&url).json(&request).send().await?;
1152 self.handle_response(response).await
1153 }
1154
1155 pub async fn feedback_memory(
1175 &self,
1176 memory_id: &str,
1177 agent_id: &str,
1178 signal: FeedbackSignal,
1179 ) -> Result<FeedbackResponse> {
1180 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1181 let body = MemoryFeedbackBody {
1182 agent_id: agent_id.to_string(),
1183 signal,
1184 };
1185 let response = self.client.post(&url).json(&body).send().await?;
1186 self.handle_response(response).await
1187 }
1188
1189 pub async fn get_memory_feedback_history(
1191 &self,
1192 memory_id: &str,
1193 ) -> Result<FeedbackHistoryResponse> {
1194 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1195 let response = self.client.get(&url).send().await?;
1196 self.handle_response(response).await
1197 }
1198
1199 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1201 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1202 let response = self.client.get(&url).send().await?;
1203 self.handle_response(response).await
1204 }
1205
1206 pub async fn patch_memory_importance(
1213 &self,
1214 memory_id: &str,
1215 agent_id: &str,
1216 importance: f32,
1217 ) -> Result<FeedbackResponse> {
1218 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1219 let body = MemoryImportancePatch {
1220 agent_id: agent_id.to_string(),
1221 importance,
1222 };
1223 let response = self.client.patch(&url).json(&body).send().await?;
1224 self.handle_response(response).await
1225 }
1226
1227 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1232 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1233 let response = self.client.get(&url).send().await?;
1234 self.handle_response(response).await
1235 }
1236
1237 pub async fn memory_graph(
1258 &self,
1259 memory_id: &str,
1260 options: GraphOptions,
1261 ) -> Result<MemoryGraph> {
1262 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1263 let depth = options.depth.unwrap_or(1);
1264 url.push_str(&format!("?depth={}", depth));
1265 if let Some(types) = &options.types {
1266 let type_strs: Vec<String> = types
1267 .iter()
1268 .map(|t| {
1269 serde_json::to_value(t)
1270 .unwrap()
1271 .as_str()
1272 .unwrap_or("")
1273 .to_string()
1274 })
1275 .collect();
1276 if !type_strs.is_empty() {
1277 url.push_str(&format!("&types={}", type_strs.join(",")));
1278 }
1279 }
1280 let response = self.client.get(&url).send().await?;
1281 self.handle_response(response).await
1282 }
1283
1284 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1297 let url = format!(
1298 "{}/v1/memories/{}/path?target={}",
1299 self.base_url,
1300 source_id,
1301 urlencoding::encode(target_id)
1302 );
1303 let response = self.client.get(&url).send().await?;
1304 self.handle_response(response).await
1305 }
1306
1307 pub async fn memory_link(
1320 &self,
1321 source_id: &str,
1322 target_id: &str,
1323 edge_type: EdgeType,
1324 ) -> Result<GraphLinkResponse> {
1325 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1326 let request = GraphLinkRequest {
1327 target_id: target_id.to_string(),
1328 edge_type,
1329 };
1330 let response = self.client.post(&url).json(&request).send().await?;
1331 self.handle_response(response).await
1332 }
1333
1334 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1342 let url = format!(
1343 "{}/v1/agents/{}/graph/export?format={}",
1344 self.base_url, agent_id, format
1345 );
1346 let response = self.client.get(&url).send().await?;
1347 self.handle_response(response).await
1348 }
1349
1350 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1356 let url = format!("{}/v1/sessions/start", self.base_url);
1357 let request = SessionStartRequest {
1358 agent_id: agent_id.to_string(),
1359 metadata: None,
1360 };
1361 let response = self.client.post(&url).json(&request).send().await?;
1362 let resp: SessionStartResponse = self.handle_response(response).await?;
1363 Ok(resp.session)
1364 }
1365
1366 pub async fn start_session_with_metadata(
1368 &self,
1369 agent_id: &str,
1370 metadata: serde_json::Value,
1371 ) -> 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: Some(metadata),
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 end_session(
1385 &self,
1386 session_id: &str,
1387 summary: Option<String>,
1388 ) -> Result<SessionEndResponse> {
1389 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1390 let request = SessionEndRequest { summary };
1391 let response = self.client.post(&url).json(&request).send().await?;
1392 self.handle_response(response).await
1393 }
1394
1395 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1397 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1398 let response = self.client.get(&url).send().await?;
1399 self.handle_response(response).await
1400 }
1401
1402 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1404 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1405 let response = self.client.get(&url).send().await?;
1406 self.handle_response(response).await
1407 }
1408
1409 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1411 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1412 let response = self.client.get(&url).send().await?;
1413 self.handle_response(response).await
1414 }
1415
1416 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1440 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1441 let response = self.client.post(&url).json(&request).send().await?;
1442 self.handle_response(response).await
1443 }
1444
1445 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1465 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1466 let response = self.client.delete(&url).json(&request).send().await?;
1467 self.handle_response(response).await
1468 }
1469
1470 pub async fn import_memories(
1488 &self,
1489 data: serde_json::Value,
1490 format: &str,
1491 agent_id: Option<&str>,
1492 namespace: Option<&str>,
1493 ) -> Result<MemoryImportResponse> {
1494 let mut body = serde_json::json!({"data": data, "format": format});
1495 if let Some(aid) = agent_id {
1496 body["agent_id"] = serde_json::Value::String(aid.to_string());
1497 }
1498 if let Some(ns) = namespace {
1499 body["namespace"] = serde_json::Value::String(ns.to_string());
1500 }
1501 let url = format!("{}/v1/import", self.base_url);
1502 let response = self.client.post(&url).json(&body).send().await?;
1503 self.handle_response(response).await
1504 }
1505
1506 pub async fn export_memories(
1510 &self,
1511 format: &str,
1512 agent_id: Option<&str>,
1513 namespace: Option<&str>,
1514 limit: Option<u32>,
1515 ) -> Result<MemoryExportResponse> {
1516 let mut params = vec![("format", format.to_string())];
1517 if let Some(aid) = agent_id {
1518 params.push(("agent_id", aid.to_string()));
1519 }
1520 if let Some(ns) = namespace {
1521 params.push(("namespace", ns.to_string()));
1522 }
1523 if let Some(l) = limit {
1524 params.push(("limit", l.to_string()));
1525 }
1526 let url = format!("{}/v1/export", self.base_url);
1527 let response = self.client.get(&url).query(¶ms).send().await?;
1528 self.handle_response(response).await
1529 }
1530
1531 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1537 let url = format!("{}/v1/audit", self.base_url);
1538 let response = self.client.get(&url).query(&query).send().await?;
1539 self.handle_response(response).await
1540 }
1541
1542 pub async fn stream_audit_events(
1546 &self,
1547 agent_id: Option<&str>,
1548 event_type: Option<&str>,
1549 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1550 let mut params: Vec<(&str, String)> = Vec::new();
1551 if let Some(aid) = agent_id {
1552 params.push(("agent_id", aid.to_string()));
1553 }
1554 if let Some(et) = event_type {
1555 params.push(("event_type", et.to_string()));
1556 }
1557 let base = format!("{}/v1/audit/stream", self.base_url);
1558 let url = if params.is_empty() {
1559 base
1560 } else {
1561 let qs = params
1562 .iter()
1563 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1564 .collect::<Vec<_>>()
1565 .join("&");
1566 format!("{}?{}", base, qs)
1567 };
1568 self.stream_sse(url).await
1569 }
1570
1571 pub async fn export_audit(
1573 &self,
1574 format: &str,
1575 agent_id: Option<&str>,
1576 event_type: Option<&str>,
1577 from_ts: Option<u64>,
1578 to_ts: Option<u64>,
1579 ) -> Result<AuditExportResponse> {
1580 let mut body = serde_json::json!({"format": format});
1581 if let Some(aid) = agent_id {
1582 body["agent_id"] = serde_json::Value::String(aid.to_string());
1583 }
1584 if let Some(et) = event_type {
1585 body["event_type"] = serde_json::Value::String(et.to_string());
1586 }
1587 if let Some(f) = from_ts {
1588 body["from"] = serde_json::Value::Number(f.into());
1589 }
1590 if let Some(t) = to_ts {
1591 body["to"] = serde_json::Value::Number(t.into());
1592 }
1593 let url = format!("{}/v1/audit/export", self.base_url);
1594 let response = self.client.post(&url).json(&body).send().await?;
1595 self.handle_response(response).await
1596 }
1597
1598 pub async fn extract_text(
1607 &self,
1608 text: &str,
1609 namespace: Option<&str>,
1610 provider: Option<&str>,
1611 model: Option<&str>,
1612 ) -> Result<ExtractionResult> {
1613 let mut body = serde_json::json!({"text": text});
1614 if let Some(ns) = namespace {
1615 body["namespace"] = serde_json::Value::String(ns.to_string());
1616 }
1617 if let Some(p) = provider {
1618 body["provider"] = serde_json::Value::String(p.to_string());
1619 }
1620 if let Some(m) = model {
1621 body["model"] = serde_json::Value::String(m.to_string());
1622 }
1623 let url = format!("{}/v1/extract", self.base_url);
1624 let response = self.client.post(&url).json(&body).send().await?;
1625 self.handle_response(response).await
1626 }
1627
1628 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1630 let url = format!("{}/v1/extract/providers", self.base_url);
1631 let response = self.client.get(&url).send().await?;
1632 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1633 Ok(match result {
1634 ExtractProvidersResponse::List(v) => v,
1635 ExtractProvidersResponse::Object { providers } => providers,
1636 })
1637 }
1638
1639 pub async fn configure_namespace_extractor(
1641 &self,
1642 namespace: &str,
1643 provider: &str,
1644 model: Option<&str>,
1645 ) -> Result<serde_json::Value> {
1646 let mut body = serde_json::json!({"provider": provider});
1647 if let Some(m) = model {
1648 body["model"] = serde_json::Value::String(m.to_string());
1649 }
1650 let url = format!(
1651 "{}/v1/namespaces/{}/extractor",
1652 self.base_url,
1653 urlencoding::encode(namespace)
1654 );
1655 let response = self.client.patch(&url).json(&body).send().await?;
1656 self.handle_response(response).await
1657 }
1658
1659 pub async fn rotate_encryption_key(
1675 &self,
1676 new_key: &str,
1677 namespace: Option<&str>,
1678 ) -> Result<RotateEncryptionKeyResponse> {
1679 let body = RotateEncryptionKeyRequest {
1680 new_key: new_key.to_string(),
1681 namespace: namespace.map(|s| s.to_string()),
1682 };
1683 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1684 let response = self.client.post(&url).json(&body).send().await?;
1685 self.handle_response(response).await
1686 }
1687}