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, Serialize, Deserialize, PartialEq, Eq)]
203#[serde(rename_all = "snake_case")]
204pub enum RoutingMode {
205 Auto,
207 Vector,
209 Bm25,
211 Hybrid,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct RecallRequest {
218 pub agent_id: String,
219 pub query: String,
220 #[serde(default = "default_top_k")]
221 pub top_k: usize,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub memory_type: Option<MemoryType>,
224 #[serde(default)]
225 pub min_importance: f32,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub session_id: Option<String>,
228 #[serde(default)]
229 pub tags: Vec<String>,
230 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
233 pub include_associated: bool,
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub associated_memories_cap: Option<u32>,
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub associated_memories_depth: Option<u8>,
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub associated_memories_min_weight: Option<f32>,
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub since: Option<String>,
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub until: Option<String>,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub routing: Option<RoutingMode>,
252}
253
254fn default_top_k() -> usize {
255 5
256}
257
258impl RecallRequest {
259 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
261 Self {
262 agent_id: agent_id.into(),
263 query: query.into(),
264 top_k: 5,
265 memory_type: None,
266 min_importance: 0.0,
267 session_id: None,
268 tags: Vec::new(),
269 include_associated: false,
270 associated_memories_cap: None,
271 associated_memories_depth: None,
272 associated_memories_min_weight: None,
273 since: None,
274 until: None,
275 routing: None,
276 }
277 }
278
279 pub fn with_top_k(mut self, top_k: usize) -> Self {
281 self.top_k = top_k;
282 self
283 }
284
285 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
287 self.memory_type = Some(memory_type);
288 self
289 }
290
291 pub fn with_min_importance(mut self, min: f32) -> Self {
293 self.min_importance = min;
294 self
295 }
296
297 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
299 self.session_id = Some(session_id.into());
300 self
301 }
302
303 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
305 self.tags = tags;
306 self
307 }
308
309 pub fn with_associated(mut self) -> Self {
311 self.include_associated = true;
312 self
313 }
314
315 pub fn with_associated_cap(mut self, cap: u32) -> Self {
317 self.include_associated = true;
318 self.associated_memories_cap = Some(cap);
319 self
320 }
321
322 pub fn with_since(mut self, since: impl Into<String>) -> Self {
324 self.since = Some(since.into());
325 self
326 }
327
328 pub fn with_until(mut self, until: impl Into<String>) -> Self {
330 self.until = Some(until.into());
331 self
332 }
333
334 pub fn with_routing(mut self, routing: RoutingMode) -> Self {
336 self.routing = Some(routing);
337 self
338 }
339
340 pub fn with_associated_depth(mut self, depth: u8) -> Self {
342 self.include_associated = true;
343 self.associated_memories_depth = Some(depth);
344 self
345 }
346
347 pub fn with_associated_min_weight(mut self, weight: f32) -> Self {
349 self.associated_memories_min_weight = Some(weight);
350 self
351 }
352}
353
354#[derive(Debug, Clone, Serialize)]
356pub struct RecalledMemory {
357 pub id: String,
358 pub content: String,
359 pub memory_type: MemoryType,
360 pub importance: f32,
361 pub score: f32,
362 #[serde(default)]
363 pub tags: Vec<String>,
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub session_id: Option<String>,
366 #[serde(skip_serializing_if = "Option::is_none")]
367 pub metadata: Option<serde_json::Value>,
368 pub created_at: u64,
369 pub last_accessed_at: u64,
370 pub access_count: u32,
371 #[serde(skip_serializing_if = "Option::is_none")]
373 pub depth: Option<u8>,
374}
375
376impl<'de> serde::Deserialize<'de> for RecalledMemory {
377 fn deserialize<D: serde::Deserializer<'de>>(
378 deserializer: D,
379 ) -> std::result::Result<Self, D::Error> {
380 use serde::de::Error as _;
381 let val = serde_json::Value::deserialize(deserializer)?;
382
383 let score = val
386 .get("score")
387 .and_then(|v| v.as_f64())
388 .or_else(|| val.get("weighted_score").and_then(|v| v.as_f64()))
389 .unwrap_or(0.0) as f32;
390
391 let mem = val.get("memory").unwrap_or(&val);
392
393 let id = mem
394 .get("id")
395 .and_then(|v| v.as_str())
396 .ok_or_else(|| D::Error::missing_field("id"))?
397 .to_string();
398 let content = mem
399 .get("content")
400 .and_then(|v| v.as_str())
401 .ok_or_else(|| D::Error::missing_field("content"))?
402 .to_string();
403 let memory_type: MemoryType = mem
404 .get("memory_type")
405 .and_then(|v| serde_json::from_value(v.clone()).ok())
406 .unwrap_or(MemoryType::Episodic);
407 let importance = mem
408 .get("importance")
409 .and_then(|v| v.as_f64())
410 .unwrap_or(0.5) as f32;
411 let tags: Vec<String> = mem
412 .get("tags")
413 .and_then(|v| serde_json::from_value(v.clone()).ok())
414 .unwrap_or_default();
415 let session_id = mem
416 .get("session_id")
417 .and_then(|v| v.as_str())
418 .map(String::from);
419 let metadata = mem.get("metadata").cloned().filter(|v| !v.is_null());
420 let created_at = mem.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
421 let last_accessed_at = mem
422 .get("last_accessed_at")
423 .and_then(|v| v.as_u64())
424 .unwrap_or(0);
425 let access_count = mem
426 .get("access_count")
427 .and_then(|v| v.as_u64())
428 .unwrap_or(0) as u32;
429 let depth = mem.get("depth").and_then(|v| v.as_u64()).map(|v| v as u8);
430
431 Ok(Self {
432 id,
433 content,
434 memory_type,
435 importance,
436 score,
437 tags,
438 session_id,
439 metadata,
440 created_at,
441 last_accessed_at,
442 access_count,
443 depth,
444 })
445 }
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize)]
450pub struct RecallResponse {
451 pub memories: Vec<RecalledMemory>,
452 #[serde(default)]
453 pub total_found: usize,
454 #[serde(skip_serializing_if = "Option::is_none")]
456 pub associated_memories: Option<Vec<RecalledMemory>>,
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize)]
461pub struct ForgetRequest {
462 pub agent_id: String,
463 #[serde(default)]
464 pub memory_ids: Vec<String>,
465 #[serde(default)]
466 pub tags: Vec<String>,
467 #[serde(skip_serializing_if = "Option::is_none")]
468 pub session_id: Option<String>,
469 #[serde(skip_serializing_if = "Option::is_none")]
470 pub before_timestamp: Option<u64>,
471}
472
473impl ForgetRequest {
474 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
476 Self {
477 agent_id: agent_id.into(),
478 memory_ids: ids,
479 tags: Vec::new(),
480 session_id: None,
481 before_timestamp: None,
482 }
483 }
484
485 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
487 Self {
488 agent_id: agent_id.into(),
489 memory_ids: Vec::new(),
490 tags,
491 session_id: None,
492 before_timestamp: None,
493 }
494 }
495
496 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
498 Self {
499 agent_id: agent_id.into(),
500 memory_ids: Vec::new(),
501 tags: Vec::new(),
502 session_id: Some(session_id.into()),
503 before_timestamp: None,
504 }
505 }
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct ForgetResponse {
511 pub deleted_count: u64,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
516pub struct SessionStartRequest {
517 pub agent_id: String,
518 #[serde(skip_serializing_if = "Option::is_none")]
519 pub metadata: Option<serde_json::Value>,
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize)]
524pub struct Session {
525 pub id: String,
526 pub agent_id: String,
527 pub started_at: u64,
528 #[serde(skip_serializing_if = "Option::is_none")]
529 pub ended_at: Option<u64>,
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub summary: Option<String>,
532 #[serde(skip_serializing_if = "Option::is_none")]
533 pub metadata: Option<serde_json::Value>,
534 #[serde(default)]
536 pub memory_count: usize,
537}
538
539#[derive(Debug, Clone, Serialize, Deserialize)]
541pub struct SessionEndRequest {
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub summary: Option<String>,
544}
545
546#[derive(Debug, Clone, Serialize, Deserialize)]
548pub struct SessionStartResponse {
549 pub session: Session,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct SessionEndResponse {
555 pub session: Session,
556 pub memory_count: usize,
557}
558
559#[derive(Debug, Clone, Serialize, Deserialize)]
561pub struct UpdateMemoryRequest {
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub content: Option<String>,
564 #[serde(skip_serializing_if = "Option::is_none")]
565 pub metadata: Option<serde_json::Value>,
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub memory_type: Option<MemoryType>,
568}
569
570#[derive(Debug, Clone, Serialize, Deserialize)]
572pub struct UpdateImportanceRequest {
573 pub memory_ids: Vec<String>,
574 pub importance: f32,
575}
576
577#[derive(Debug, Clone, Serialize, Deserialize, Default)]
579pub struct ConsolidationConfig {
580 #[serde(skip_serializing_if = "Option::is_none")]
582 pub algorithm: Option<String>,
583 #[serde(skip_serializing_if = "Option::is_none")]
585 pub min_samples: Option<u32>,
586 #[serde(skip_serializing_if = "Option::is_none")]
588 pub eps: Option<f32>,
589}
590
591#[derive(Debug, Clone, Serialize, Deserialize)]
593pub struct ConsolidationLogEntry {
594 pub step: String,
595 pub memories_before: usize,
596 pub memories_after: usize,
597 pub duration_ms: f64,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize, Default)]
602pub struct ConsolidateRequest {
603 #[serde(skip_serializing_if = "Option::is_none")]
604 pub memory_type: Option<String>,
605 #[serde(skip_serializing_if = "Option::is_none")]
606 pub threshold: Option<f32>,
607 #[serde(default)]
608 pub dry_run: bool,
609 #[serde(skip_serializing_if = "Option::is_none")]
611 pub config: Option<ConsolidationConfig>,
612}
613
614#[derive(Debug, Clone, Serialize)]
619pub struct ConsolidateResponse {
620 pub consolidated_count: usize,
622 pub removed_count: usize,
624 #[serde(default)]
626 pub new_memories: Vec<String>,
627 #[serde(default, skip_serializing_if = "Vec::is_empty")]
629 pub log: Vec<ConsolidationLogEntry>,
630}
631
632impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
633 fn deserialize<D: serde::Deserializer<'de>>(
634 deserializer: D,
635 ) -> std::result::Result<Self, D::Error> {
636 let val = serde_json::Value::deserialize(deserializer)?;
637 let removed = val
639 .get("memories_removed")
640 .and_then(|v| v.as_u64())
641 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
642 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
643 .unwrap_or(0) as usize;
644 let source_ids: Vec<String> = val
645 .get("source_memory_ids")
646 .and_then(|v| v.as_array())
647 .map(|arr| {
648 arr.iter()
649 .filter_map(|v| v.as_str().map(String::from))
650 .collect()
651 })
652 .unwrap_or_default();
653 Ok(Self {
654 consolidated_count: removed,
655 removed_count: removed,
656 new_memories: source_ids,
657 log: vec![],
658 })
659 }
660}
661
662#[derive(Debug, Clone, Serialize, Deserialize)]
668pub struct MemoryImportResponse {
669 pub imported_count: usize,
670 pub skipped_count: usize,
671 #[serde(default)]
672 pub errors: Vec<String>,
673}
674
675#[derive(Debug, Clone, Serialize, Deserialize)]
677pub struct MemoryExportResponse {
678 pub data: Vec<serde_json::Value>,
679 pub format: String,
680 pub count: usize,
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize)]
689pub struct AuditEvent {
690 pub id: String,
691 pub event_type: String,
692 #[serde(skip_serializing_if = "Option::is_none")]
693 pub agent_id: Option<String>,
694 #[serde(skip_serializing_if = "Option::is_none")]
695 pub namespace: Option<String>,
696 pub timestamp: u64,
697 #[serde(default)]
698 pub details: serde_json::Value,
699}
700
701#[derive(Debug, Clone, Serialize, Deserialize)]
703pub struct AuditListResponse {
704 pub events: Vec<AuditEvent>,
705 pub total: usize,
706 #[serde(skip_serializing_if = "Option::is_none")]
707 pub cursor: Option<String>,
708}
709
710#[derive(Debug, Clone, Serialize, Deserialize)]
712pub struct AuditExportResponse {
713 pub data: String,
714 pub format: String,
715 pub count: usize,
716}
717
718#[derive(Debug, Clone, Serialize, Deserialize, Default)]
720pub struct AuditQuery {
721 #[serde(skip_serializing_if = "Option::is_none")]
722 pub agent_id: Option<String>,
723 #[serde(skip_serializing_if = "Option::is_none")]
724 pub event_type: Option<String>,
725 #[serde(skip_serializing_if = "Option::is_none")]
726 pub from: Option<u64>,
727 #[serde(skip_serializing_if = "Option::is_none")]
728 pub to: Option<u64>,
729 #[serde(skip_serializing_if = "Option::is_none")]
730 pub limit: Option<u32>,
731 #[serde(skip_serializing_if = "Option::is_none")]
732 pub cursor: Option<String>,
733}
734
735#[derive(Debug, Clone, Serialize, Deserialize)]
741pub struct ExtractionResult {
742 pub entities: Vec<serde_json::Value>,
743 pub provider: String,
744 #[serde(skip_serializing_if = "Option::is_none")]
745 pub model: Option<String>,
746 pub duration_ms: f64,
747}
748
749#[derive(Debug, Clone, Serialize, Deserialize)]
751pub struct ExtractionProviderInfo {
752 pub name: String,
753 pub available: bool,
754 #[serde(default)]
755 pub models: Vec<String>,
756}
757
758#[derive(Debug, Clone, Serialize, Deserialize)]
760#[serde(untagged)]
761pub enum ExtractProvidersResponse {
762 List(Vec<ExtractionProviderInfo>),
763 Object {
764 providers: Vec<ExtractionProviderInfo>,
765 },
766}
767
768#[derive(Debug, Clone, Serialize, Deserialize)]
774pub struct RotateEncryptionKeyRequest {
775 pub new_key: String,
777 #[serde(skip_serializing_if = "Option::is_none")]
779 pub namespace: Option<String>,
780}
781
782#[derive(Debug, Clone, Serialize, Deserialize)]
784pub struct RotateEncryptionKeyResponse {
785 pub rotated: usize,
786 pub skipped: usize,
787 #[serde(default)]
788 pub namespaces: Vec<String>,
789}
790
791#[derive(Debug, Clone, Serialize, Deserialize)]
793pub struct FeedbackRequest {
794 pub memory_id: String,
795 pub feedback: String,
796 #[serde(skip_serializing_if = "Option::is_none")]
797 pub relevance_score: Option<f32>,
798}
799
800#[derive(Debug, Clone, Serialize, Deserialize)]
802pub struct LegacyFeedbackResponse {
803 pub status: String,
804 pub updated_importance: Option<f32>,
805}
806
807#[derive(Debug, Clone, Serialize, Deserialize, Default)]
816pub struct BatchMemoryFilter {
817 #[serde(skip_serializing_if = "Option::is_none")]
819 pub tags: Option<Vec<String>>,
820 #[serde(skip_serializing_if = "Option::is_none")]
822 pub min_importance: Option<f32>,
823 #[serde(skip_serializing_if = "Option::is_none")]
825 pub max_importance: Option<f32>,
826 #[serde(skip_serializing_if = "Option::is_none")]
828 pub created_after: Option<u64>,
829 #[serde(skip_serializing_if = "Option::is_none")]
831 pub created_before: Option<u64>,
832 #[serde(skip_serializing_if = "Option::is_none")]
834 pub memory_type: Option<MemoryType>,
835 #[serde(skip_serializing_if = "Option::is_none")]
837 pub session_id: Option<String>,
838}
839
840impl BatchMemoryFilter {
841 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
843 self.tags = Some(tags);
844 self
845 }
846
847 pub fn with_min_importance(mut self, min: f32) -> Self {
849 self.min_importance = Some(min);
850 self
851 }
852
853 pub fn with_max_importance(mut self, max: f32) -> Self {
855 self.max_importance = Some(max);
856 self
857 }
858
859 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
861 self.session_id = Some(session_id.into());
862 self
863 }
864}
865
866#[derive(Debug, Clone, Serialize, Deserialize)]
868pub struct BatchRecallRequest {
869 pub agent_id: String,
871 #[serde(default)]
873 pub filter: BatchMemoryFilter,
874 #[serde(default = "default_batch_limit")]
876 pub limit: usize,
877}
878
879fn default_batch_limit() -> usize {
880 100
881}
882
883impl BatchRecallRequest {
884 pub fn new(agent_id: impl Into<String>) -> Self {
886 Self {
887 agent_id: agent_id.into(),
888 filter: BatchMemoryFilter::default(),
889 limit: 100,
890 }
891 }
892
893 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
895 self.filter = filter;
896 self
897 }
898
899 pub fn with_limit(mut self, limit: usize) -> Self {
901 self.limit = limit;
902 self
903 }
904}
905
906#[derive(Debug, Clone, Serialize, Deserialize)]
908pub struct BatchRecallResponse {
909 pub memories: Vec<RecalledMemory>,
910 pub total: usize,
912 pub filtered: usize,
914}
915
916#[derive(Debug, Clone, Serialize, Deserialize)]
918pub struct BatchForgetRequest {
919 pub agent_id: String,
921 pub filter: BatchMemoryFilter,
923}
924
925impl BatchForgetRequest {
926 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
928 Self {
929 agent_id: agent_id.into(),
930 filter,
931 }
932 }
933}
934
935#[derive(Debug, Clone, Serialize, Deserialize)]
937pub struct BatchForgetResponse {
938 pub deleted_count: usize,
939}
940
941impl DakeraClient {
946 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
970 let url = format!("{}/v1/memory/store", self.base_url);
971 let response = self.client.post(&url).json(&request).send().await?;
972 self.handle_response(response).await
973 }
974
975 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
996 let url = format!("{}/v1/memory/recall", self.base_url);
997 let response = self.client.post(&url).json(&request).send().await?;
998 self.handle_response(response).await
999 }
1000
1001 pub async fn recall_simple(
1003 &self,
1004 agent_id: &str,
1005 query: &str,
1006 top_k: usize,
1007 ) -> Result<RecallResponse> {
1008 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1009 .await
1010 }
1011
1012 pub async fn get_memory(&self, memory_id: &str) -> Result<RecalledMemory> {
1014 let url = format!("{}/v1/memory/get/{}", self.base_url, memory_id);
1015 let response = self.client.get(&url).send().await?;
1016 self.handle_response(response).await
1017 }
1018
1019 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1021 let url = format!("{}/v1/memory/forget", self.base_url);
1022 let response = self.client.post(&url).json(&request).send().await?;
1023 self.handle_response(response).await
1024 }
1025
1026 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1028 let url = format!("{}/v1/memory/search", self.base_url);
1029 let response = self.client.post(&url).json(&request).send().await?;
1030 self.handle_response(response).await
1031 }
1032
1033 pub async fn update_memory(
1035 &self,
1036 agent_id: &str,
1037 memory_id: &str,
1038 request: UpdateMemoryRequest,
1039 ) -> Result<StoreMemoryResponse> {
1040 let url = format!(
1041 "{}/v1/agents/{}/memories/{}",
1042 self.base_url, agent_id, memory_id
1043 );
1044 let response = self.client.put(&url).json(&request).send().await?;
1045 self.handle_response(response).await
1046 }
1047
1048 pub async fn update_importance(
1050 &self,
1051 agent_id: &str,
1052 request: UpdateImportanceRequest,
1053 ) -> Result<serde_json::Value> {
1054 let url = format!(
1055 "{}/v1/agents/{}/memories/importance",
1056 self.base_url, agent_id
1057 );
1058 let response = self.client.put(&url).json(&request).send().await?;
1059 self.handle_response(response).await
1060 }
1061
1062 pub async fn consolidate(
1064 &self,
1065 agent_id: &str,
1066 request: ConsolidateRequest,
1067 ) -> Result<ConsolidateResponse> {
1068 let url = format!("{}/v1/memory/consolidate", self.base_url);
1070 let mut body = serde_json::to_value(&request)?;
1071 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1072 let response = self.client.post(&url).json(&body).send().await?;
1073 self.handle_response(response).await
1074 }
1075
1076 pub async fn memory_feedback(
1078 &self,
1079 agent_id: &str,
1080 request: FeedbackRequest,
1081 ) -> Result<LegacyFeedbackResponse> {
1082 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1083 let response = self.client.post(&url).json(&request).send().await?;
1084 self.handle_response(response).await
1085 }
1086
1087 pub async fn feedback_memory(
1107 &self,
1108 memory_id: &str,
1109 agent_id: &str,
1110 signal: FeedbackSignal,
1111 ) -> Result<FeedbackResponse> {
1112 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1113 let body = MemoryFeedbackBody {
1114 agent_id: agent_id.to_string(),
1115 signal,
1116 };
1117 let response = self.client.post(&url).json(&body).send().await?;
1118 self.handle_response(response).await
1119 }
1120
1121 pub async fn get_memory_feedback_history(
1123 &self,
1124 memory_id: &str,
1125 ) -> Result<FeedbackHistoryResponse> {
1126 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1127 let response = self.client.get(&url).send().await?;
1128 self.handle_response(response).await
1129 }
1130
1131 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1133 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1134 let response = self.client.get(&url).send().await?;
1135 self.handle_response(response).await
1136 }
1137
1138 pub async fn patch_memory_importance(
1145 &self,
1146 memory_id: &str,
1147 agent_id: &str,
1148 importance: f32,
1149 ) -> Result<FeedbackResponse> {
1150 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1151 let body = MemoryImportancePatch {
1152 agent_id: agent_id.to_string(),
1153 importance,
1154 };
1155 let response = self.client.patch(&url).json(&body).send().await?;
1156 self.handle_response(response).await
1157 }
1158
1159 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1164 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1165 let response = self.client.get(&url).send().await?;
1166 self.handle_response(response).await
1167 }
1168
1169 pub async fn memory_graph(
1190 &self,
1191 memory_id: &str,
1192 options: GraphOptions,
1193 ) -> Result<MemoryGraph> {
1194 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1195 let depth = options.depth.unwrap_or(1);
1196 url.push_str(&format!("?depth={}", depth));
1197 if let Some(types) = &options.types {
1198 let type_strs: Vec<String> = types
1199 .iter()
1200 .map(|t| {
1201 serde_json::to_value(t)
1202 .unwrap()
1203 .as_str()
1204 .unwrap_or("")
1205 .to_string()
1206 })
1207 .collect();
1208 if !type_strs.is_empty() {
1209 url.push_str(&format!("&types={}", type_strs.join(",")));
1210 }
1211 }
1212 let response = self.client.get(&url).send().await?;
1213 self.handle_response(response).await
1214 }
1215
1216 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1229 let url = format!(
1230 "{}/v1/memories/{}/path?target={}",
1231 self.base_url,
1232 source_id,
1233 urlencoding::encode(target_id)
1234 );
1235 let response = self.client.get(&url).send().await?;
1236 self.handle_response(response).await
1237 }
1238
1239 pub async fn memory_link(
1252 &self,
1253 source_id: &str,
1254 target_id: &str,
1255 edge_type: EdgeType,
1256 ) -> Result<GraphLinkResponse> {
1257 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1258 let request = GraphLinkRequest {
1259 target_id: target_id.to_string(),
1260 edge_type,
1261 };
1262 let response = self.client.post(&url).json(&request).send().await?;
1263 self.handle_response(response).await
1264 }
1265
1266 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1274 let url = format!(
1275 "{}/v1/agents/{}/graph/export?format={}",
1276 self.base_url, agent_id, format
1277 );
1278 let response = self.client.get(&url).send().await?;
1279 self.handle_response(response).await
1280 }
1281
1282 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1288 let url = format!("{}/v1/sessions/start", self.base_url);
1289 let request = SessionStartRequest {
1290 agent_id: agent_id.to_string(),
1291 metadata: None,
1292 };
1293 let response = self.client.post(&url).json(&request).send().await?;
1294 let resp: SessionStartResponse = self.handle_response(response).await?;
1295 Ok(resp.session)
1296 }
1297
1298 pub async fn start_session_with_metadata(
1300 &self,
1301 agent_id: &str,
1302 metadata: serde_json::Value,
1303 ) -> Result<Session> {
1304 let url = format!("{}/v1/sessions/start", self.base_url);
1305 let request = SessionStartRequest {
1306 agent_id: agent_id.to_string(),
1307 metadata: Some(metadata),
1308 };
1309 let response = self.client.post(&url).json(&request).send().await?;
1310 let resp: SessionStartResponse = self.handle_response(response).await?;
1311 Ok(resp.session)
1312 }
1313
1314 pub async fn end_session(
1317 &self,
1318 session_id: &str,
1319 summary: Option<String>,
1320 ) -> Result<SessionEndResponse> {
1321 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1322 let request = SessionEndRequest { summary };
1323 let response = self.client.post(&url).json(&request).send().await?;
1324 self.handle_response(response).await
1325 }
1326
1327 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1329 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1330 let response = self.client.get(&url).send().await?;
1331 self.handle_response(response).await
1332 }
1333
1334 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1336 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1337 let response = self.client.get(&url).send().await?;
1338 self.handle_response(response).await
1339 }
1340
1341 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1343 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1344 let response = self.client.get(&url).send().await?;
1345 self.handle_response(response).await
1346 }
1347
1348 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1372 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1373 let response = self.client.post(&url).json(&request).send().await?;
1374 self.handle_response(response).await
1375 }
1376
1377 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1397 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1398 let response = self.client.delete(&url).json(&request).send().await?;
1399 self.handle_response(response).await
1400 }
1401
1402 pub async fn import_memories(
1420 &self,
1421 data: serde_json::Value,
1422 format: &str,
1423 agent_id: Option<&str>,
1424 namespace: Option<&str>,
1425 ) -> Result<MemoryImportResponse> {
1426 let mut body = serde_json::json!({"data": data, "format": format});
1427 if let Some(aid) = agent_id {
1428 body["agent_id"] = serde_json::Value::String(aid.to_string());
1429 }
1430 if let Some(ns) = namespace {
1431 body["namespace"] = serde_json::Value::String(ns.to_string());
1432 }
1433 let url = format!("{}/v1/import", self.base_url);
1434 let response = self.client.post(&url).json(&body).send().await?;
1435 self.handle_response(response).await
1436 }
1437
1438 pub async fn export_memories(
1442 &self,
1443 format: &str,
1444 agent_id: Option<&str>,
1445 namespace: Option<&str>,
1446 limit: Option<u32>,
1447 ) -> Result<MemoryExportResponse> {
1448 let mut params = vec![("format", format.to_string())];
1449 if let Some(aid) = agent_id {
1450 params.push(("agent_id", aid.to_string()));
1451 }
1452 if let Some(ns) = namespace {
1453 params.push(("namespace", ns.to_string()));
1454 }
1455 if let Some(l) = limit {
1456 params.push(("limit", l.to_string()));
1457 }
1458 let url = format!("{}/v1/export", self.base_url);
1459 let response = self.client.get(&url).query(¶ms).send().await?;
1460 self.handle_response(response).await
1461 }
1462
1463 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1469 let url = format!("{}/v1/audit", self.base_url);
1470 let response = self.client.get(&url).query(&query).send().await?;
1471 self.handle_response(response).await
1472 }
1473
1474 pub async fn stream_audit_events(
1478 &self,
1479 agent_id: Option<&str>,
1480 event_type: Option<&str>,
1481 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1482 let mut params: Vec<(&str, String)> = Vec::new();
1483 if let Some(aid) = agent_id {
1484 params.push(("agent_id", aid.to_string()));
1485 }
1486 if let Some(et) = event_type {
1487 params.push(("event_type", et.to_string()));
1488 }
1489 let base = format!("{}/v1/audit/stream", self.base_url);
1490 let url = if params.is_empty() {
1491 base
1492 } else {
1493 let qs = params
1494 .iter()
1495 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1496 .collect::<Vec<_>>()
1497 .join("&");
1498 format!("{}?{}", base, qs)
1499 };
1500 self.stream_sse(url).await
1501 }
1502
1503 pub async fn export_audit(
1505 &self,
1506 format: &str,
1507 agent_id: Option<&str>,
1508 event_type: Option<&str>,
1509 from_ts: Option<u64>,
1510 to_ts: Option<u64>,
1511 ) -> Result<AuditExportResponse> {
1512 let mut body = serde_json::json!({"format": format});
1513 if let Some(aid) = agent_id {
1514 body["agent_id"] = serde_json::Value::String(aid.to_string());
1515 }
1516 if let Some(et) = event_type {
1517 body["event_type"] = serde_json::Value::String(et.to_string());
1518 }
1519 if let Some(f) = from_ts {
1520 body["from"] = serde_json::Value::Number(f.into());
1521 }
1522 if let Some(t) = to_ts {
1523 body["to"] = serde_json::Value::Number(t.into());
1524 }
1525 let url = format!("{}/v1/audit/export", self.base_url);
1526 let response = self.client.post(&url).json(&body).send().await?;
1527 self.handle_response(response).await
1528 }
1529
1530 pub async fn extract_text(
1539 &self,
1540 text: &str,
1541 namespace: Option<&str>,
1542 provider: Option<&str>,
1543 model: Option<&str>,
1544 ) -> Result<ExtractionResult> {
1545 let mut body = serde_json::json!({"text": text});
1546 if let Some(ns) = namespace {
1547 body["namespace"] = serde_json::Value::String(ns.to_string());
1548 }
1549 if let Some(p) = provider {
1550 body["provider"] = serde_json::Value::String(p.to_string());
1551 }
1552 if let Some(m) = model {
1553 body["model"] = serde_json::Value::String(m.to_string());
1554 }
1555 let url = format!("{}/v1/extract", self.base_url);
1556 let response = self.client.post(&url).json(&body).send().await?;
1557 self.handle_response(response).await
1558 }
1559
1560 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1562 let url = format!("{}/v1/extract/providers", self.base_url);
1563 let response = self.client.get(&url).send().await?;
1564 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1565 Ok(match result {
1566 ExtractProvidersResponse::List(v) => v,
1567 ExtractProvidersResponse::Object { providers } => providers,
1568 })
1569 }
1570
1571 pub async fn configure_namespace_extractor(
1573 &self,
1574 namespace: &str,
1575 provider: &str,
1576 model: Option<&str>,
1577 ) -> Result<serde_json::Value> {
1578 let mut body = serde_json::json!({"provider": provider});
1579 if let Some(m) = model {
1580 body["model"] = serde_json::Value::String(m.to_string());
1581 }
1582 let url = format!(
1583 "{}/v1/namespaces/{}/extractor",
1584 self.base_url,
1585 urlencoding::encode(namespace)
1586 );
1587 let response = self.client.patch(&url).json(&body).send().await?;
1588 self.handle_response(response).await
1589 }
1590
1591 pub async fn rotate_encryption_key(
1607 &self,
1608 new_key: &str,
1609 namespace: Option<&str>,
1610 ) -> Result<RotateEncryptionKeyResponse> {
1611 let body = RotateEncryptionKeyRequest {
1612 new_key: new_key.to_string(),
1613 namespace: namespace.map(|s| s.to_string()),
1614 };
1615 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1616 let response = self.client.post(&url).json(&body).send().await?;
1617 self.handle_response(response).await
1618 }
1619}