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")]
282 pub neighborhood: Option<bool>,
283}
284
285fn default_top_k() -> usize {
286 5
287}
288
289impl RecallRequest {
290 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
292 Self {
293 agent_id: agent_id.into(),
294 query: query.into(),
295 top_k: 5,
296 memory_type: None,
297 min_importance: 0.0,
298 session_id: None,
299 tags: Vec::new(),
300 include_associated: false,
301 associated_memories_cap: None,
302 associated_memories_depth: None,
303 associated_memories_min_weight: None,
304 since: None,
305 until: None,
306 routing: None,
307 rerank: None,
308 fusion: None,
309 neighborhood: None,
310 }
311 }
312
313 pub fn with_top_k(mut self, top_k: usize) -> Self {
315 self.top_k = top_k;
316 self
317 }
318
319 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
321 self.memory_type = Some(memory_type);
322 self
323 }
324
325 pub fn with_min_importance(mut self, min: f32) -> Self {
327 self.min_importance = min;
328 self
329 }
330
331 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
333 self.session_id = Some(session_id.into());
334 self
335 }
336
337 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
339 self.tags = tags;
340 self
341 }
342
343 pub fn with_associated(mut self) -> Self {
345 self.include_associated = true;
346 self
347 }
348
349 pub fn with_associated_cap(mut self, cap: u32) -> Self {
351 self.include_associated = true;
352 self.associated_memories_cap = Some(cap);
353 self
354 }
355
356 pub fn with_since(mut self, since: impl Into<String>) -> Self {
358 self.since = Some(since.into());
359 self
360 }
361
362 pub fn with_until(mut self, until: impl Into<String>) -> Self {
364 self.until = Some(until.into());
365 self
366 }
367
368 pub fn with_routing(mut self, routing: RoutingMode) -> Self {
370 self.routing = Some(routing);
371 self
372 }
373
374 pub fn with_rerank(mut self, rerank: bool) -> Self {
376 self.rerank = Some(rerank);
377 self
378 }
379
380 pub fn with_associated_depth(mut self, depth: u8) -> Self {
382 self.include_associated = true;
383 self.associated_memories_depth = Some(depth);
384 self
385 }
386
387 pub fn with_associated_min_weight(mut self, weight: f32) -> Self {
389 self.associated_memories_min_weight = Some(weight);
390 self
391 }
392
393 pub fn with_fusion(mut self, fusion: FusionStrategy) -> Self {
395 self.fusion = Some(fusion);
396 self
397 }
398
399 pub fn with_neighborhood(mut self, neighborhood: bool) -> Self {
402 self.neighborhood = Some(neighborhood);
403 self
404 }
405}
406
407#[derive(Debug, Clone, Serialize)]
409pub struct RecalledMemory {
410 pub id: String,
411 pub content: String,
412 pub memory_type: MemoryType,
413 pub importance: f32,
414 pub score: f32,
415 #[serde(default)]
416 pub tags: Vec<String>,
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub session_id: Option<String>,
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub metadata: Option<serde_json::Value>,
421 pub created_at: u64,
422 pub last_accessed_at: u64,
423 pub access_count: u32,
424 #[serde(skip_serializing_if = "Option::is_none")]
426 pub depth: Option<u8>,
427}
428
429impl<'de> serde::Deserialize<'de> for RecalledMemory {
430 fn deserialize<D: serde::Deserializer<'de>>(
431 deserializer: D,
432 ) -> std::result::Result<Self, D::Error> {
433 use serde::de::Error as _;
434 let val = serde_json::Value::deserialize(deserializer)?;
435
436 let score = val
439 .get("score")
440 .and_then(|v| v.as_f64())
441 .or_else(|| val.get("weighted_score").and_then(|v| v.as_f64()))
442 .unwrap_or(0.0) as f32;
443
444 let mem = val.get("memory").unwrap_or(&val);
445
446 let id = mem
447 .get("id")
448 .and_then(|v| v.as_str())
449 .ok_or_else(|| D::Error::missing_field("id"))?
450 .to_string();
451 let content = mem
452 .get("content")
453 .and_then(|v| v.as_str())
454 .ok_or_else(|| D::Error::missing_field("content"))?
455 .to_string();
456 let memory_type: MemoryType = mem
457 .get("memory_type")
458 .and_then(|v| serde_json::from_value(v.clone()).ok())
459 .unwrap_or(MemoryType::Episodic);
460 let importance = mem
461 .get("importance")
462 .and_then(|v| v.as_f64())
463 .unwrap_or(0.5) as f32;
464 let tags: Vec<String> = mem
465 .get("tags")
466 .and_then(|v| serde_json::from_value(v.clone()).ok())
467 .unwrap_or_default();
468 let session_id = mem
469 .get("session_id")
470 .and_then(|v| v.as_str())
471 .map(String::from);
472 let metadata = mem.get("metadata").cloned().filter(|v| !v.is_null());
473 let created_at = mem.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
474 let last_accessed_at = mem
475 .get("last_accessed_at")
476 .and_then(|v| v.as_u64())
477 .unwrap_or(0);
478 let access_count = mem
479 .get("access_count")
480 .and_then(|v| v.as_u64())
481 .unwrap_or(0) as u32;
482 let depth = mem.get("depth").and_then(|v| v.as_u64()).map(|v| v as u8);
483
484 Ok(Self {
485 id,
486 content,
487 memory_type,
488 importance,
489 score,
490 tags,
491 session_id,
492 metadata,
493 created_at,
494 last_accessed_at,
495 access_count,
496 depth,
497 })
498 }
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct RecallResponse {
504 pub memories: Vec<RecalledMemory>,
505 #[serde(default)]
506 pub total_found: usize,
507 #[serde(skip_serializing_if = "Option::is_none")]
509 pub associated_memories: Option<Vec<RecalledMemory>>,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct ForgetRequest {
515 pub agent_id: String,
516 #[serde(default)]
517 pub memory_ids: Vec<String>,
518 #[serde(default)]
519 pub tags: Vec<String>,
520 #[serde(skip_serializing_if = "Option::is_none")]
521 pub session_id: Option<String>,
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub before_timestamp: Option<u64>,
524}
525
526impl ForgetRequest {
527 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
529 Self {
530 agent_id: agent_id.into(),
531 memory_ids: ids,
532 tags: Vec::new(),
533 session_id: None,
534 before_timestamp: None,
535 }
536 }
537
538 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
540 Self {
541 agent_id: agent_id.into(),
542 memory_ids: Vec::new(),
543 tags,
544 session_id: None,
545 before_timestamp: None,
546 }
547 }
548
549 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
551 Self {
552 agent_id: agent_id.into(),
553 memory_ids: Vec::new(),
554 tags: Vec::new(),
555 session_id: Some(session_id.into()),
556 before_timestamp: None,
557 }
558 }
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct ForgetResponse {
564 pub deleted_count: u64,
565}
566
567#[derive(Debug, Clone, Serialize, Deserialize)]
569pub struct SessionStartRequest {
570 pub agent_id: String,
571 #[serde(skip_serializing_if = "Option::is_none")]
572 pub metadata: Option<serde_json::Value>,
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize)]
577pub struct Session {
578 pub id: String,
579 pub agent_id: String,
580 pub started_at: u64,
581 #[serde(skip_serializing_if = "Option::is_none")]
582 pub ended_at: Option<u64>,
583 #[serde(skip_serializing_if = "Option::is_none")]
584 pub summary: Option<String>,
585 #[serde(skip_serializing_if = "Option::is_none")]
586 pub metadata: Option<serde_json::Value>,
587 #[serde(default)]
589 pub memory_count: usize,
590}
591
592#[derive(Debug, Clone, Serialize, Deserialize)]
594pub struct SessionEndRequest {
595 #[serde(skip_serializing_if = "Option::is_none")]
596 pub summary: Option<String>,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct SessionStartResponse {
602 pub session: Session,
603}
604
605#[derive(Debug, Clone, Serialize, Deserialize)]
607pub struct SessionEndResponse {
608 pub session: Session,
609 pub memory_count: usize,
610}
611
612#[derive(Debug, Clone, Serialize, Deserialize)]
614pub struct UpdateMemoryRequest {
615 #[serde(skip_serializing_if = "Option::is_none")]
616 pub content: Option<String>,
617 #[serde(skip_serializing_if = "Option::is_none")]
618 pub metadata: Option<serde_json::Value>,
619 #[serde(skip_serializing_if = "Option::is_none")]
620 pub memory_type: Option<MemoryType>,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct UpdateImportanceRequest {
626 pub memory_ids: Vec<String>,
627 pub importance: f32,
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize, Default)]
632pub struct ConsolidationConfig {
633 #[serde(skip_serializing_if = "Option::is_none")]
635 pub algorithm: Option<String>,
636 #[serde(skip_serializing_if = "Option::is_none")]
638 pub min_samples: Option<u32>,
639 #[serde(skip_serializing_if = "Option::is_none")]
641 pub eps: Option<f32>,
642}
643
644#[derive(Debug, Clone, Serialize, Deserialize)]
646pub struct ConsolidationLogEntry {
647 pub step: String,
648 pub memories_before: usize,
649 pub memories_after: usize,
650 pub duration_ms: f64,
651}
652
653#[derive(Debug, Clone, Serialize, Deserialize, Default)]
655pub struct ConsolidateRequest {
656 #[serde(skip_serializing_if = "Option::is_none")]
657 pub memory_type: Option<String>,
658 #[serde(skip_serializing_if = "Option::is_none")]
659 pub threshold: Option<f32>,
660 #[serde(default)]
661 pub dry_run: bool,
662 #[serde(skip_serializing_if = "Option::is_none")]
664 pub config: Option<ConsolidationConfig>,
665}
666
667#[derive(Debug, Clone, Serialize)]
672pub struct ConsolidateResponse {
673 pub consolidated_count: usize,
675 pub removed_count: usize,
677 #[serde(default)]
679 pub new_memories: Vec<String>,
680 #[serde(default, skip_serializing_if = "Vec::is_empty")]
682 pub log: Vec<ConsolidationLogEntry>,
683}
684
685impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
686 fn deserialize<D: serde::Deserializer<'de>>(
687 deserializer: D,
688 ) -> std::result::Result<Self, D::Error> {
689 let val = serde_json::Value::deserialize(deserializer)?;
690 let removed = val
692 .get("memories_removed")
693 .and_then(|v| v.as_u64())
694 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
695 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
696 .unwrap_or(0) as usize;
697 let source_ids: Vec<String> = val
698 .get("source_memory_ids")
699 .and_then(|v| v.as_array())
700 .map(|arr| {
701 arr.iter()
702 .filter_map(|v| v.as_str().map(String::from))
703 .collect()
704 })
705 .unwrap_or_default();
706 Ok(Self {
707 consolidated_count: removed,
708 removed_count: removed,
709 new_memories: source_ids,
710 log: vec![],
711 })
712 }
713}
714
715#[derive(Debug, Clone, Serialize, Deserialize)]
721pub struct MemoryImportResponse {
722 pub imported_count: usize,
723 pub skipped_count: usize,
724 #[serde(default)]
725 pub errors: Vec<String>,
726}
727
728#[derive(Debug, Clone, Serialize, Deserialize)]
730pub struct MemoryExportResponse {
731 pub data: Vec<serde_json::Value>,
732 pub format: String,
733 pub count: usize,
734}
735
736#[derive(Debug, Clone, Serialize, Deserialize)]
742pub struct AuditEvent {
743 pub id: String,
744 pub event_type: String,
745 #[serde(skip_serializing_if = "Option::is_none")]
746 pub agent_id: Option<String>,
747 #[serde(skip_serializing_if = "Option::is_none")]
748 pub namespace: Option<String>,
749 pub timestamp: u64,
750 #[serde(default)]
751 pub details: serde_json::Value,
752}
753
754#[derive(Debug, Clone, Serialize, Deserialize)]
756pub struct AuditListResponse {
757 pub events: Vec<AuditEvent>,
758 pub total: usize,
759 #[serde(skip_serializing_if = "Option::is_none")]
760 pub cursor: Option<String>,
761}
762
763#[derive(Debug, Clone, Serialize, Deserialize)]
765pub struct AuditExportResponse {
766 pub data: String,
767 pub format: String,
768 pub count: usize,
769}
770
771#[derive(Debug, Clone, Serialize, Deserialize, Default)]
773pub struct AuditQuery {
774 #[serde(skip_serializing_if = "Option::is_none")]
775 pub agent_id: Option<String>,
776 #[serde(skip_serializing_if = "Option::is_none")]
777 pub event_type: Option<String>,
778 #[serde(skip_serializing_if = "Option::is_none")]
779 pub from: Option<u64>,
780 #[serde(skip_serializing_if = "Option::is_none")]
781 pub to: Option<u64>,
782 #[serde(skip_serializing_if = "Option::is_none")]
783 pub limit: Option<u32>,
784 #[serde(skip_serializing_if = "Option::is_none")]
785 pub cursor: Option<String>,
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize)]
794pub struct ExtractionResult {
795 pub entities: Vec<serde_json::Value>,
796 pub provider: String,
797 #[serde(skip_serializing_if = "Option::is_none")]
798 pub model: Option<String>,
799 pub duration_ms: f64,
800}
801
802#[derive(Debug, Clone, Serialize, Deserialize)]
804pub struct ExtractionProviderInfo {
805 pub name: String,
806 pub available: bool,
807 #[serde(default)]
808 pub models: Vec<String>,
809}
810
811#[derive(Debug, Clone, Serialize, Deserialize)]
813#[serde(untagged)]
814pub enum ExtractProvidersResponse {
815 List(Vec<ExtractionProviderInfo>),
816 Object {
817 providers: Vec<ExtractionProviderInfo>,
818 },
819}
820
821#[derive(Debug, Clone, Serialize, Deserialize)]
827pub struct RotateEncryptionKeyRequest {
828 pub new_key: String,
830 #[serde(skip_serializing_if = "Option::is_none")]
832 pub namespace: Option<String>,
833}
834
835#[derive(Debug, Clone, Serialize, Deserialize)]
837pub struct RotateEncryptionKeyResponse {
838 pub rotated: usize,
839 pub skipped: usize,
840 #[serde(default)]
841 pub namespaces: Vec<String>,
842}
843
844#[derive(Debug, Clone, Serialize, Deserialize)]
846pub struct FeedbackRequest {
847 pub memory_id: String,
848 pub feedback: String,
849 #[serde(skip_serializing_if = "Option::is_none")]
850 pub relevance_score: Option<f32>,
851}
852
853#[derive(Debug, Clone, Serialize, Deserialize)]
855pub struct LegacyFeedbackResponse {
856 pub status: String,
857 pub updated_importance: Option<f32>,
858}
859
860#[derive(Debug, Clone, Serialize, Deserialize, Default)]
869pub struct BatchMemoryFilter {
870 #[serde(skip_serializing_if = "Option::is_none")]
872 pub tags: Option<Vec<String>>,
873 #[serde(skip_serializing_if = "Option::is_none")]
875 pub min_importance: Option<f32>,
876 #[serde(skip_serializing_if = "Option::is_none")]
878 pub max_importance: Option<f32>,
879 #[serde(skip_serializing_if = "Option::is_none")]
881 pub created_after: Option<u64>,
882 #[serde(skip_serializing_if = "Option::is_none")]
884 pub created_before: Option<u64>,
885 #[serde(skip_serializing_if = "Option::is_none")]
887 pub memory_type: Option<MemoryType>,
888 #[serde(skip_serializing_if = "Option::is_none")]
890 pub session_id: Option<String>,
891}
892
893impl BatchMemoryFilter {
894 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
896 self.tags = Some(tags);
897 self
898 }
899
900 pub fn with_min_importance(mut self, min: f32) -> Self {
902 self.min_importance = Some(min);
903 self
904 }
905
906 pub fn with_max_importance(mut self, max: f32) -> Self {
908 self.max_importance = Some(max);
909 self
910 }
911
912 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
914 self.session_id = Some(session_id.into());
915 self
916 }
917}
918
919#[derive(Debug, Clone, Serialize, Deserialize)]
921pub struct BatchRecallRequest {
922 pub agent_id: String,
924 #[serde(default)]
926 pub filter: BatchMemoryFilter,
927 #[serde(default = "default_batch_limit")]
929 pub limit: usize,
930}
931
932fn default_batch_limit() -> usize {
933 100
934}
935
936impl BatchRecallRequest {
937 pub fn new(agent_id: impl Into<String>) -> Self {
939 Self {
940 agent_id: agent_id.into(),
941 filter: BatchMemoryFilter::default(),
942 limit: 100,
943 }
944 }
945
946 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
948 self.filter = filter;
949 self
950 }
951
952 pub fn with_limit(mut self, limit: usize) -> Self {
954 self.limit = limit;
955 self
956 }
957}
958
959#[derive(Debug, Clone, Serialize, Deserialize)]
961pub struct BatchRecallResponse {
962 pub memories: Vec<RecalledMemory>,
963 pub total: usize,
965 pub filtered: usize,
967}
968
969#[derive(Debug, Clone, Serialize, Deserialize)]
971pub struct BatchForgetRequest {
972 pub agent_id: String,
974 pub filter: BatchMemoryFilter,
976}
977
978impl BatchForgetRequest {
979 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
981 Self {
982 agent_id: agent_id.into(),
983 filter,
984 }
985 }
986}
987
988#[derive(Debug, Clone, Serialize, Deserialize)]
990pub struct BatchForgetResponse {
991 pub deleted_count: usize,
992}
993
994impl DakeraClient {
999 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1023 let url = format!("{}/v1/memory/store", self.base_url);
1024 let response = self.client.post(&url).json(&request).send().await?;
1025 self.handle_response(response).await
1026 }
1027
1028 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1049 let url = format!("{}/v1/memory/recall", self.base_url);
1050 let response = self.client.post(&url).json(&request).send().await?;
1051 self.handle_response(response).await
1052 }
1053
1054 pub async fn recall_simple(
1056 &self,
1057 agent_id: &str,
1058 query: &str,
1059 top_k: usize,
1060 ) -> Result<RecallResponse> {
1061 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1062 .await
1063 }
1064
1065 pub async fn get_memory(&self, memory_id: &str) -> Result<RecalledMemory> {
1067 let url = format!("{}/v1/memory/get/{}", self.base_url, memory_id);
1068 let response = self.client.get(&url).send().await?;
1069 self.handle_response(response).await
1070 }
1071
1072 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1074 let url = format!("{}/v1/memory/forget", self.base_url);
1075 let response = self.client.post(&url).json(&request).send().await?;
1076 self.handle_response(response).await
1077 }
1078
1079 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1081 let url = format!("{}/v1/memory/search", self.base_url);
1082 let response = self.client.post(&url).json(&request).send().await?;
1083 self.handle_response(response).await
1084 }
1085
1086 pub async fn update_memory(
1088 &self,
1089 agent_id: &str,
1090 memory_id: &str,
1091 request: UpdateMemoryRequest,
1092 ) -> Result<StoreMemoryResponse> {
1093 let url = format!(
1094 "{}/v1/agents/{}/memories/{}",
1095 self.base_url, agent_id, memory_id
1096 );
1097 let response = self.client.put(&url).json(&request).send().await?;
1098 self.handle_response(response).await
1099 }
1100
1101 pub async fn update_importance(
1103 &self,
1104 agent_id: &str,
1105 request: UpdateImportanceRequest,
1106 ) -> Result<serde_json::Value> {
1107 let url = format!(
1108 "{}/v1/agents/{}/memories/importance",
1109 self.base_url, agent_id
1110 );
1111 let response = self.client.put(&url).json(&request).send().await?;
1112 self.handle_response(response).await
1113 }
1114
1115 pub async fn consolidate(
1117 &self,
1118 agent_id: &str,
1119 request: ConsolidateRequest,
1120 ) -> Result<ConsolidateResponse> {
1121 let url = format!("{}/v1/memory/consolidate", self.base_url);
1123 let mut body = serde_json::to_value(&request)?;
1124 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1125 let response = self.client.post(&url).json(&body).send().await?;
1126 self.handle_response(response).await
1127 }
1128
1129 pub async fn memory_feedback(
1131 &self,
1132 agent_id: &str,
1133 request: FeedbackRequest,
1134 ) -> Result<LegacyFeedbackResponse> {
1135 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1136 let response = self.client.post(&url).json(&request).send().await?;
1137 self.handle_response(response).await
1138 }
1139
1140 pub async fn feedback_memory(
1160 &self,
1161 memory_id: &str,
1162 agent_id: &str,
1163 signal: FeedbackSignal,
1164 ) -> Result<FeedbackResponse> {
1165 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1166 let body = MemoryFeedbackBody {
1167 agent_id: agent_id.to_string(),
1168 signal,
1169 };
1170 let response = self.client.post(&url).json(&body).send().await?;
1171 self.handle_response(response).await
1172 }
1173
1174 pub async fn get_memory_feedback_history(
1176 &self,
1177 memory_id: &str,
1178 ) -> Result<FeedbackHistoryResponse> {
1179 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1180 let response = self.client.get(&url).send().await?;
1181 self.handle_response(response).await
1182 }
1183
1184 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1186 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1187 let response = self.client.get(&url).send().await?;
1188 self.handle_response(response).await
1189 }
1190
1191 pub async fn patch_memory_importance(
1198 &self,
1199 memory_id: &str,
1200 agent_id: &str,
1201 importance: f32,
1202 ) -> Result<FeedbackResponse> {
1203 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1204 let body = MemoryImportancePatch {
1205 agent_id: agent_id.to_string(),
1206 importance,
1207 };
1208 let response = self.client.patch(&url).json(&body).send().await?;
1209 self.handle_response(response).await
1210 }
1211
1212 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1217 let url = format!("{}/v1/feedback/health?agent_id={}", 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 memory_graph(
1243 &self,
1244 memory_id: &str,
1245 options: GraphOptions,
1246 ) -> Result<MemoryGraph> {
1247 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1248 let depth = options.depth.unwrap_or(1);
1249 url.push_str(&format!("?depth={}", depth));
1250 if let Some(types) = &options.types {
1251 let type_strs: Vec<String> = types
1252 .iter()
1253 .map(|t| {
1254 serde_json::to_value(t)
1255 .unwrap()
1256 .as_str()
1257 .unwrap_or("")
1258 .to_string()
1259 })
1260 .collect();
1261 if !type_strs.is_empty() {
1262 url.push_str(&format!("&types={}", type_strs.join(",")));
1263 }
1264 }
1265 let response = self.client.get(&url).send().await?;
1266 self.handle_response(response).await
1267 }
1268
1269 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1282 let url = format!(
1283 "{}/v1/memories/{}/path?target={}",
1284 self.base_url,
1285 source_id,
1286 urlencoding::encode(target_id)
1287 );
1288 let response = self.client.get(&url).send().await?;
1289 self.handle_response(response).await
1290 }
1291
1292 pub async fn memory_link(
1305 &self,
1306 source_id: &str,
1307 target_id: &str,
1308 edge_type: EdgeType,
1309 ) -> Result<GraphLinkResponse> {
1310 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1311 let request = GraphLinkRequest {
1312 target_id: target_id.to_string(),
1313 edge_type,
1314 };
1315 let response = self.client.post(&url).json(&request).send().await?;
1316 self.handle_response(response).await
1317 }
1318
1319 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1327 let url = format!(
1328 "{}/v1/agents/{}/graph/export?format={}",
1329 self.base_url, agent_id, format
1330 );
1331 let response = self.client.get(&url).send().await?;
1332 self.handle_response(response).await
1333 }
1334
1335 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1341 let url = format!("{}/v1/sessions/start", self.base_url);
1342 let request = SessionStartRequest {
1343 agent_id: agent_id.to_string(),
1344 metadata: None,
1345 };
1346 let response = self.client.post(&url).json(&request).send().await?;
1347 let resp: SessionStartResponse = self.handle_response(response).await?;
1348 Ok(resp.session)
1349 }
1350
1351 pub async fn start_session_with_metadata(
1353 &self,
1354 agent_id: &str,
1355 metadata: serde_json::Value,
1356 ) -> Result<Session> {
1357 let url = format!("{}/v1/sessions/start", self.base_url);
1358 let request = SessionStartRequest {
1359 agent_id: agent_id.to_string(),
1360 metadata: Some(metadata),
1361 };
1362 let response = self.client.post(&url).json(&request).send().await?;
1363 let resp: SessionStartResponse = self.handle_response(response).await?;
1364 Ok(resp.session)
1365 }
1366
1367 pub async fn end_session(
1370 &self,
1371 session_id: &str,
1372 summary: Option<String>,
1373 ) -> Result<SessionEndResponse> {
1374 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1375 let request = SessionEndRequest { summary };
1376 let response = self.client.post(&url).json(&request).send().await?;
1377 self.handle_response(response).await
1378 }
1379
1380 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1382 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1383 let response = self.client.get(&url).send().await?;
1384 self.handle_response(response).await
1385 }
1386
1387 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1389 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1390 let response = self.client.get(&url).send().await?;
1391 self.handle_response(response).await
1392 }
1393
1394 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1396 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1397 let response = self.client.get(&url).send().await?;
1398 self.handle_response(response).await
1399 }
1400
1401 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1425 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1426 let response = self.client.post(&url).json(&request).send().await?;
1427 self.handle_response(response).await
1428 }
1429
1430 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1450 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1451 let response = self.client.delete(&url).json(&request).send().await?;
1452 self.handle_response(response).await
1453 }
1454
1455 pub async fn import_memories(
1473 &self,
1474 data: serde_json::Value,
1475 format: &str,
1476 agent_id: Option<&str>,
1477 namespace: Option<&str>,
1478 ) -> Result<MemoryImportResponse> {
1479 let mut body = serde_json::json!({"data": data, "format": format});
1480 if let Some(aid) = agent_id {
1481 body["agent_id"] = serde_json::Value::String(aid.to_string());
1482 }
1483 if let Some(ns) = namespace {
1484 body["namespace"] = serde_json::Value::String(ns.to_string());
1485 }
1486 let url = format!("{}/v1/import", self.base_url);
1487 let response = self.client.post(&url).json(&body).send().await?;
1488 self.handle_response(response).await
1489 }
1490
1491 pub async fn export_memories(
1495 &self,
1496 format: &str,
1497 agent_id: Option<&str>,
1498 namespace: Option<&str>,
1499 limit: Option<u32>,
1500 ) -> Result<MemoryExportResponse> {
1501 let mut params = vec![("format", format.to_string())];
1502 if let Some(aid) = agent_id {
1503 params.push(("agent_id", aid.to_string()));
1504 }
1505 if let Some(ns) = namespace {
1506 params.push(("namespace", ns.to_string()));
1507 }
1508 if let Some(l) = limit {
1509 params.push(("limit", l.to_string()));
1510 }
1511 let url = format!("{}/v1/export", self.base_url);
1512 let response = self.client.get(&url).query(¶ms).send().await?;
1513 self.handle_response(response).await
1514 }
1515
1516 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1522 let url = format!("{}/v1/audit", self.base_url);
1523 let response = self.client.get(&url).query(&query).send().await?;
1524 self.handle_response(response).await
1525 }
1526
1527 pub async fn stream_audit_events(
1531 &self,
1532 agent_id: Option<&str>,
1533 event_type: Option<&str>,
1534 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1535 let mut params: Vec<(&str, String)> = Vec::new();
1536 if let Some(aid) = agent_id {
1537 params.push(("agent_id", aid.to_string()));
1538 }
1539 if let Some(et) = event_type {
1540 params.push(("event_type", et.to_string()));
1541 }
1542 let base = format!("{}/v1/audit/stream", self.base_url);
1543 let url = if params.is_empty() {
1544 base
1545 } else {
1546 let qs = params
1547 .iter()
1548 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1549 .collect::<Vec<_>>()
1550 .join("&");
1551 format!("{}?{}", base, qs)
1552 };
1553 self.stream_sse(url).await
1554 }
1555
1556 pub async fn export_audit(
1558 &self,
1559 format: &str,
1560 agent_id: Option<&str>,
1561 event_type: Option<&str>,
1562 from_ts: Option<u64>,
1563 to_ts: Option<u64>,
1564 ) -> Result<AuditExportResponse> {
1565 let mut body = serde_json::json!({"format": format});
1566 if let Some(aid) = agent_id {
1567 body["agent_id"] = serde_json::Value::String(aid.to_string());
1568 }
1569 if let Some(et) = event_type {
1570 body["event_type"] = serde_json::Value::String(et.to_string());
1571 }
1572 if let Some(f) = from_ts {
1573 body["from"] = serde_json::Value::Number(f.into());
1574 }
1575 if let Some(t) = to_ts {
1576 body["to"] = serde_json::Value::Number(t.into());
1577 }
1578 let url = format!("{}/v1/audit/export", self.base_url);
1579 let response = self.client.post(&url).json(&body).send().await?;
1580 self.handle_response(response).await
1581 }
1582
1583 pub async fn extract_text(
1592 &self,
1593 text: &str,
1594 namespace: Option<&str>,
1595 provider: Option<&str>,
1596 model: Option<&str>,
1597 ) -> Result<ExtractionResult> {
1598 let mut body = serde_json::json!({"text": text});
1599 if let Some(ns) = namespace {
1600 body["namespace"] = serde_json::Value::String(ns.to_string());
1601 }
1602 if let Some(p) = provider {
1603 body["provider"] = serde_json::Value::String(p.to_string());
1604 }
1605 if let Some(m) = model {
1606 body["model"] = serde_json::Value::String(m.to_string());
1607 }
1608 let url = format!("{}/v1/extract", self.base_url);
1609 let response = self.client.post(&url).json(&body).send().await?;
1610 self.handle_response(response).await
1611 }
1612
1613 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1615 let url = format!("{}/v1/extract/providers", self.base_url);
1616 let response = self.client.get(&url).send().await?;
1617 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1618 Ok(match result {
1619 ExtractProvidersResponse::List(v) => v,
1620 ExtractProvidersResponse::Object { providers } => providers,
1621 })
1622 }
1623
1624 pub async fn configure_namespace_extractor(
1626 &self,
1627 namespace: &str,
1628 provider: &str,
1629 model: Option<&str>,
1630 ) -> Result<serde_json::Value> {
1631 let mut body = serde_json::json!({"provider": provider});
1632 if let Some(m) = model {
1633 body["model"] = serde_json::Value::String(m.to_string());
1634 }
1635 let url = format!(
1636 "{}/v1/namespaces/{}/extractor",
1637 self.base_url,
1638 urlencoding::encode(namespace)
1639 );
1640 let response = self.client.patch(&url).json(&body).send().await?;
1641 self.handle_response(response).await
1642 }
1643
1644 pub async fn rotate_encryption_key(
1660 &self,
1661 new_key: &str,
1662 namespace: Option<&str>,
1663 ) -> Result<RotateEncryptionKeyResponse> {
1664 let body = RotateEncryptionKeyRequest {
1665 new_key: new_key.to_string(),
1666 namespace: namespace.map(|s| s.to_string()),
1667 };
1668 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1669 let response = self.client.post(&url).json(&body).send().await?;
1670 self.handle_response(response).await
1671 }
1672}