1use serde::{Deserialize, Serialize};
7
8use crate::error::Result;
9use crate::types::{
10 AgentFeedbackSummary, EdgeType, FeedbackHealthResponse, FeedbackHistoryResponse,
11 FeedbackResponse, FeedbackSignal, GraphExport, GraphLinkRequest, GraphLinkResponse,
12 GraphOptions, GraphPath, MemoryFeedbackBody, MemoryGraph, MemoryImportancePatch, TifScore,
13};
14use crate::DakeraClient;
15
16#[derive(Debug, Clone, Serialize, Deserialize, Default)]
22#[serde(rename_all = "lowercase")]
23pub enum MemoryType {
24 #[default]
25 Episodic,
26 Semantic,
27 Procedural,
28 Working,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct StoreMemoryRequest {
34 pub agent_id: String,
35 pub content: String,
36 #[serde(default)]
37 pub memory_type: MemoryType,
38 #[serde(default = "default_importance")]
39 pub importance: f32,
40 #[serde(default)]
41 pub tags: Vec<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub session_id: Option<String>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub metadata: Option<serde_json::Value>,
46 #[serde(skip_serializing_if = "Option::is_none")]
49 pub ttl_seconds: Option<u64>,
50 #[serde(skip_serializing_if = "Option::is_none")]
54 pub expires_at: Option<u64>,
55}
56
57fn default_importance() -> f32 {
58 0.5
59}
60
61impl StoreMemoryRequest {
62 pub fn new(agent_id: impl Into<String>, content: impl Into<String>) -> Self {
64 Self {
65 agent_id: agent_id.into(),
66 content: content.into(),
67 memory_type: MemoryType::default(),
68 importance: 0.5,
69 tags: Vec::new(),
70 session_id: None,
71 metadata: None,
72 ttl_seconds: None,
73 expires_at: None,
74 }
75 }
76
77 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
79 self.memory_type = memory_type;
80 self
81 }
82
83 pub fn with_importance(mut self, importance: f32) -> Self {
85 self.importance = importance.clamp(0.0, 1.0);
86 self
87 }
88
89 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
91 self.tags = tags;
92 self
93 }
94
95 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
97 self.session_id = Some(session_id.into());
98 self
99 }
100
101 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
103 self.metadata = Some(metadata);
104 self
105 }
106
107 pub fn with_ttl(mut self, ttl_seconds: u64) -> Self {
110 self.ttl_seconds = Some(ttl_seconds);
111 self
112 }
113
114 pub fn with_expires_at(mut self, expires_at: u64) -> Self {
117 self.expires_at = Some(expires_at);
118 self
119 }
120}
121
122#[derive(Debug, Clone, Serialize)]
129pub struct StoreMemoryResponse {
130 pub memory_id: String,
132 pub agent_id: String,
134 pub namespace: String,
136 pub embedding_time_ms: Option<u64>,
138}
139
140impl<'de> serde::Deserialize<'de> for StoreMemoryResponse {
141 fn deserialize<D: serde::Deserializer<'de>>(
142 deserializer: D,
143 ) -> std::result::Result<Self, D::Error> {
144 use serde::de::Error;
145 let val = serde_json::Value::deserialize(deserializer)?;
146
147 if let Some(memory) = val.get("memory") {
149 let memory_id = memory
150 .get("id")
151 .and_then(|v| v.as_str())
152 .ok_or_else(|| D::Error::missing_field("memory.id"))?
153 .to_string();
154 let agent_id = memory
155 .get("agent_id")
156 .and_then(|v| v.as_str())
157 .unwrap_or("")
158 .to_string();
159 let namespace = memory
160 .get("namespace")
161 .and_then(|v| v.as_str())
162 .unwrap_or("default")
163 .to_string();
164 let embedding_time_ms = val.get("embedding_time_ms").and_then(|v| v.as_u64());
165 return Ok(Self {
166 memory_id,
167 agent_id,
168 namespace,
169 embedding_time_ms,
170 });
171 }
172
173 let memory_id = val
175 .get("memory_id")
176 .and_then(|v| v.as_str())
177 .ok_or_else(|| D::Error::missing_field("memory_id"))?
178 .to_string();
179 let agent_id = val
180 .get("agent_id")
181 .and_then(|v| v.as_str())
182 .unwrap_or("")
183 .to_string();
184 let namespace = val
185 .get("namespace")
186 .and_then(|v| v.as_str())
187 .unwrap_or("default")
188 .to_string();
189 Ok(Self {
190 memory_id,
191 agent_id,
192 namespace,
193 embedding_time_ms: None,
194 })
195 }
196}
197
198#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
204#[serde(rename_all = "snake_case")]
205pub enum FusionStrategy {
206 #[default]
211 Rrf,
212 #[serde(rename = "minmax")]
214 MinMax,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
222#[serde(rename_all = "snake_case")]
223pub enum RoutingMode {
224 Auto,
226 Vector,
228 Bm25,
230 Hybrid,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct RecallRequest {
237 pub agent_id: String,
238 pub query: String,
239 #[serde(default = "default_top_k")]
240 pub top_k: usize,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub memory_type: Option<MemoryType>,
243 #[serde(default)]
244 pub min_importance: f32,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub session_id: Option<String>,
247 #[serde(default)]
248 pub tags: Vec<String>,
249 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
252 pub include_associated: bool,
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub associated_memories_cap: Option<u32>,
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub associated_memories_depth: Option<u8>,
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub associated_memories_min_weight: Option<f32>,
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub since: Option<String>,
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub until: Option<String>,
268 #[serde(skip_serializing_if = "Option::is_none")]
270 pub routing: Option<RoutingMode>,
271 #[serde(skip_serializing_if = "Option::is_none")]
274 pub rerank: Option<bool>,
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub fusion: Option<FusionStrategy>,
278 #[serde(skip_serializing_if = "Option::is_none")]
283 pub vector_weight: Option<f32>,
284 #[serde(skip_serializing_if = "Option::is_none")]
289 pub iterations: Option<u8>,
290 #[serde(skip_serializing_if = "Option::is_none")]
294 pub neighborhood: Option<bool>,
295}
296
297fn default_top_k() -> usize {
298 5
299}
300
301impl RecallRequest {
302 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
304 Self {
305 agent_id: agent_id.into(),
306 query: query.into(),
307 top_k: 5,
308 memory_type: None,
309 min_importance: 0.0,
310 session_id: None,
311 tags: Vec::new(),
312 include_associated: false,
313 associated_memories_cap: None,
314 associated_memories_depth: None,
315 associated_memories_min_weight: None,
316 since: None,
317 until: None,
318 routing: None,
319 rerank: None,
320 fusion: None,
321 vector_weight: None,
322 iterations: None,
323 neighborhood: None,
324 }
325 }
326
327 pub fn with_top_k(mut self, top_k: usize) -> Self {
329 self.top_k = top_k;
330 self
331 }
332
333 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
335 self.memory_type = Some(memory_type);
336 self
337 }
338
339 pub fn with_min_importance(mut self, min: f32) -> Self {
341 self.min_importance = min;
342 self
343 }
344
345 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
347 self.session_id = Some(session_id.into());
348 self
349 }
350
351 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
353 self.tags = tags;
354 self
355 }
356
357 pub fn with_associated(mut self) -> Self {
359 self.include_associated = true;
360 self
361 }
362
363 pub fn with_associated_cap(mut self, cap: u32) -> Self {
365 self.include_associated = true;
366 self.associated_memories_cap = Some(cap);
367 self
368 }
369
370 pub fn with_since(mut self, since: impl Into<String>) -> Self {
372 self.since = Some(since.into());
373 self
374 }
375
376 pub fn with_until(mut self, until: impl Into<String>) -> Self {
378 self.until = Some(until.into());
379 self
380 }
381
382 pub fn with_routing(mut self, routing: RoutingMode) -> Self {
384 self.routing = Some(routing);
385 self
386 }
387
388 pub fn with_rerank(mut self, rerank: bool) -> Self {
390 self.rerank = Some(rerank);
391 self
392 }
393
394 pub fn with_associated_depth(mut self, depth: u8) -> Self {
396 self.include_associated = true;
397 self.associated_memories_depth = Some(depth);
398 self
399 }
400
401 pub fn with_associated_min_weight(mut self, weight: f32) -> Self {
403 self.associated_memories_min_weight = Some(weight);
404 self
405 }
406
407 pub fn with_fusion(mut self, fusion: FusionStrategy) -> Self {
409 self.fusion = Some(fusion);
410 self
411 }
412
413 pub fn with_vector_weight(mut self, weight: f32) -> Self {
417 self.vector_weight = Some(weight);
418 self
419 }
420
421 pub fn with_iterations(mut self, iterations: u8) -> Self {
426 self.iterations = Some(iterations);
427 self
428 }
429
430 pub fn with_neighborhood(mut self, neighborhood: bool) -> Self {
433 self.neighborhood = Some(neighborhood);
434 self
435 }
436}
437
438#[derive(Debug, Clone, Serialize)]
440pub struct RecalledMemory {
441 pub id: String,
442 pub content: String,
443 pub memory_type: MemoryType,
444 pub importance: f32,
445 pub score: f32,
447 #[serde(skip_serializing_if = "Option::is_none")]
449 pub smart_score: Option<f32>,
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub weighted_score: Option<f32>,
453 #[serde(default)]
454 pub tags: Vec<String>,
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub session_id: Option<String>,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub metadata: Option<serde_json::Value>,
459 pub created_at: u64,
460 pub last_accessed_at: u64,
461 pub access_count: u32,
462 #[serde(skip_serializing_if = "Option::is_none")]
464 pub depth: Option<u8>,
465 #[serde(skip_serializing_if = "Option::is_none")]
467 pub vector_score: Option<f32>,
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub text_score: Option<f32>,
471}
472
473impl<'de> serde::Deserialize<'de> for RecalledMemory {
474 fn deserialize<D: serde::Deserializer<'de>>(
475 deserializer: D,
476 ) -> std::result::Result<Self, D::Error> {
477 use serde::de::Error as _;
478 let val = serde_json::Value::deserialize(deserializer)?;
479
480 let smart_score = val
484 .get("smart_score")
485 .and_then(|v| v.as_f64())
486 .map(|v| v as f32);
487 let weighted_score = val
488 .get("weighted_score")
489 .and_then(|v| v.as_f64())
490 .map(|v| v as f32);
491 let score = smart_score
492 .or(weighted_score)
493 .or_else(|| val.get("score").and_then(|v| v.as_f64()).map(|v| v as f32))
494 .unwrap_or(0.0);
495 let vector_score = val
497 .get("vector_score")
498 .and_then(|v| v.as_f64())
499 .map(|v| v as f32);
500 let text_score = val
501 .get("text_score")
502 .and_then(|v| v.as_f64())
503 .map(|v| v as f32);
504
505 let mem = val.get("memory").unwrap_or(&val);
506
507 let id = mem
508 .get("id")
509 .and_then(|v| v.as_str())
510 .ok_or_else(|| D::Error::missing_field("id"))?
511 .to_string();
512 let content = mem
513 .get("content")
514 .and_then(|v| v.as_str())
515 .ok_or_else(|| D::Error::missing_field("content"))?
516 .to_string();
517 let memory_type: MemoryType = mem
518 .get("memory_type")
519 .and_then(|v| serde_json::from_value(v.clone()).ok())
520 .unwrap_or(MemoryType::Episodic);
521 let importance = mem
522 .get("importance")
523 .and_then(|v| v.as_f64())
524 .unwrap_or(0.5) as f32;
525 let tags: Vec<String> = mem
526 .get("tags")
527 .and_then(|v| serde_json::from_value(v.clone()).ok())
528 .unwrap_or_default();
529 let session_id = mem
530 .get("session_id")
531 .and_then(|v| v.as_str())
532 .map(String::from);
533 let metadata = mem.get("metadata").cloned().filter(|v| !v.is_null());
534 let created_at = mem.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
535 let last_accessed_at = mem
536 .get("last_accessed_at")
537 .and_then(|v| v.as_u64())
538 .unwrap_or(0);
539 let access_count = mem
540 .get("access_count")
541 .and_then(|v| v.as_u64())
542 .unwrap_or(0) as u32;
543 let depth = mem.get("depth").and_then(|v| v.as_u64()).map(|v| v as u8);
544
545 Ok(Self {
546 id,
547 content,
548 memory_type,
549 importance,
550 score,
551 smart_score,
552 weighted_score,
553 tags,
554 session_id,
555 metadata,
556 created_at,
557 last_accessed_at,
558 access_count,
559 depth,
560 vector_score,
561 text_score,
562 })
563 }
564}
565
566#[derive(Debug, Clone, Serialize, Deserialize)]
568pub struct RecallResponse {
569 pub memories: Vec<RecalledMemory>,
570 #[serde(default)]
571 pub total_found: usize,
572 #[serde(skip_serializing_if = "Option::is_none")]
574 pub associated_memories: Option<Vec<RecalledMemory>>,
575}
576
577#[derive(Debug, Clone, Serialize, Deserialize)]
579pub struct ForgetRequest {
580 pub agent_id: String,
581 #[serde(default)]
582 pub memory_ids: Vec<String>,
583 #[serde(default)]
584 pub tags: Vec<String>,
585 #[serde(skip_serializing_if = "Option::is_none")]
586 pub session_id: Option<String>,
587 #[serde(skip_serializing_if = "Option::is_none")]
588 pub before_timestamp: Option<u64>,
589}
590
591impl ForgetRequest {
592 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
594 Self {
595 agent_id: agent_id.into(),
596 memory_ids: ids,
597 tags: Vec::new(),
598 session_id: None,
599 before_timestamp: None,
600 }
601 }
602
603 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
605 Self {
606 agent_id: agent_id.into(),
607 memory_ids: Vec::new(),
608 tags,
609 session_id: None,
610 before_timestamp: None,
611 }
612 }
613
614 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
616 Self {
617 agent_id: agent_id.into(),
618 memory_ids: Vec::new(),
619 tags: Vec::new(),
620 session_id: Some(session_id.into()),
621 before_timestamp: None,
622 }
623 }
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
628pub struct ForgetResponse {
629 pub deleted_count: u64,
630}
631
632#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct SessionStartRequest {
635 pub agent_id: String,
636 #[serde(skip_serializing_if = "Option::is_none")]
637 pub metadata: Option<serde_json::Value>,
638}
639
640#[derive(Debug, Clone, Serialize, Deserialize)]
642pub struct Session {
643 pub id: String,
644 pub agent_id: String,
645 pub started_at: u64,
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub ended_at: Option<u64>,
648 #[serde(skip_serializing_if = "Option::is_none")]
649 pub summary: Option<String>,
650 #[serde(skip_serializing_if = "Option::is_none")]
651 pub metadata: Option<serde_json::Value>,
652 #[serde(default)]
654 pub memory_count: usize,
655}
656
657#[derive(Debug, Clone, Serialize, Deserialize)]
659pub struct SessionEndRequest {
660 #[serde(skip_serializing_if = "Option::is_none")]
661 pub summary: Option<String>,
662}
663
664#[derive(Debug, Clone, Serialize, Deserialize)]
666pub struct SessionStartResponse {
667 pub session: Session,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct SessionEndResponse {
673 pub session: Session,
674 pub memory_count: usize,
675}
676
677#[derive(Debug, Clone, Deserialize)]
679pub struct ListSessionsResponse {
680 pub sessions: Vec<Session>,
681 #[allow(dead_code)]
682 pub total: usize,
683}
684
685#[derive(Debug, Clone, Serialize, Deserialize)]
687pub struct UpdateMemoryRequest {
688 #[serde(skip_serializing_if = "Option::is_none")]
689 pub content: Option<String>,
690 #[serde(skip_serializing_if = "Option::is_none")]
691 pub metadata: Option<serde_json::Value>,
692 #[serde(skip_serializing_if = "Option::is_none")]
693 pub memory_type: Option<MemoryType>,
694}
695
696#[derive(Debug, Clone, Serialize, Deserialize)]
698pub struct UpdateImportanceRequest {
699 pub memory_ids: Vec<String>,
700 pub importance: f32,
701}
702
703#[derive(Debug, Clone, Serialize, Deserialize, Default)]
705pub struct ConsolidationConfig {
706 #[serde(skip_serializing_if = "Option::is_none")]
708 pub algorithm: Option<String>,
709 #[serde(skip_serializing_if = "Option::is_none")]
711 pub min_samples: Option<u32>,
712 #[serde(skip_serializing_if = "Option::is_none")]
714 pub eps: Option<f32>,
715}
716
717#[derive(Debug, Clone, Serialize, Deserialize)]
719pub struct ConsolidationLogEntry {
720 pub step: String,
721 pub memories_before: usize,
722 pub memories_after: usize,
723 pub duration_ms: f64,
724}
725
726#[derive(Debug, Clone, Serialize, Deserialize, Default)]
728pub struct ConsolidateRequest {
729 #[serde(skip_serializing_if = "Option::is_none")]
730 pub memory_type: Option<String>,
731 #[serde(skip_serializing_if = "Option::is_none")]
732 pub threshold: Option<f32>,
733 #[serde(default)]
734 pub dry_run: bool,
735 #[serde(skip_serializing_if = "Option::is_none")]
737 pub config: Option<ConsolidationConfig>,
738}
739
740#[derive(Debug, Clone, Serialize)]
745pub struct ConsolidateResponse {
746 pub consolidated_count: usize,
748 pub removed_count: usize,
750 #[serde(default)]
752 pub new_memories: Vec<String>,
753 #[serde(default, skip_serializing_if = "Vec::is_empty")]
755 pub log: Vec<ConsolidationLogEntry>,
756}
757
758impl<'de> serde::Deserialize<'de> for ConsolidateResponse {
759 fn deserialize<D: serde::Deserializer<'de>>(
760 deserializer: D,
761 ) -> std::result::Result<Self, D::Error> {
762 let val = serde_json::Value::deserialize(deserializer)?;
763 let removed = val
765 .get("memories_removed")
766 .and_then(|v| v.as_u64())
767 .or_else(|| val.get("removed_count").and_then(|v| v.as_u64()))
768 .or_else(|| val.get("consolidated_count").and_then(|v| v.as_u64()))
769 .unwrap_or(0) as usize;
770 let source_ids: Vec<String> = val
771 .get("source_memory_ids")
772 .and_then(|v| v.as_array())
773 .map(|arr| {
774 arr.iter()
775 .filter_map(|v| v.as_str().map(String::from))
776 .collect()
777 })
778 .unwrap_or_default();
779 Ok(Self {
780 consolidated_count: removed,
781 removed_count: removed,
782 new_memories: source_ids,
783 log: vec![],
784 })
785 }
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize)]
794pub struct MemoryImportResponse {
795 pub imported_count: usize,
796 pub skipped_count: usize,
797 #[serde(default)]
798 pub errors: Vec<String>,
799}
800
801#[derive(Debug, Clone, Serialize, Deserialize)]
803pub struct MemoryExportResponse {
804 pub data: Vec<serde_json::Value>,
805 pub format: String,
806 pub count: usize,
807}
808
809#[derive(Debug, Clone, Serialize, Deserialize)]
815pub struct AuditEvent {
816 pub id: String,
817 pub event_type: String,
818 #[serde(skip_serializing_if = "Option::is_none")]
819 pub agent_id: Option<String>,
820 #[serde(skip_serializing_if = "Option::is_none")]
821 pub namespace: Option<String>,
822 pub timestamp: u64,
823 #[serde(default)]
824 pub details: serde_json::Value,
825}
826
827#[derive(Debug, Clone, Serialize, Deserialize)]
829pub struct AuditListResponse {
830 pub events: Vec<AuditEvent>,
831 pub total: usize,
832 #[serde(skip_serializing_if = "Option::is_none")]
833 pub cursor: Option<String>,
834}
835
836#[derive(Debug, Clone, Serialize, Deserialize)]
838pub struct AuditExportResponse {
839 pub data: String,
840 pub format: String,
841 pub count: usize,
842}
843
844#[derive(Debug, Clone, Serialize, Deserialize, Default)]
846pub struct AuditQuery {
847 #[serde(skip_serializing_if = "Option::is_none")]
848 pub agent_id: Option<String>,
849 #[serde(skip_serializing_if = "Option::is_none")]
850 pub event_type: Option<String>,
851 #[serde(skip_serializing_if = "Option::is_none")]
852 pub from: Option<u64>,
853 #[serde(skip_serializing_if = "Option::is_none")]
854 pub to: Option<u64>,
855 #[serde(skip_serializing_if = "Option::is_none")]
856 pub limit: Option<u32>,
857 #[serde(skip_serializing_if = "Option::is_none")]
858 pub cursor: Option<String>,
859}
860
861#[derive(Debug, Clone, Serialize, Deserialize)]
867pub struct ExtractionResult {
868 pub entities: Vec<serde_json::Value>,
869 pub provider: String,
870 #[serde(skip_serializing_if = "Option::is_none")]
871 pub model: Option<String>,
872 pub duration_ms: f64,
873}
874
875#[derive(Debug, Clone, Serialize, Deserialize)]
877pub struct ExtractionProviderInfo {
878 pub name: String,
879 pub available: bool,
880 #[serde(default)]
881 pub models: Vec<String>,
882}
883
884#[derive(Debug, Clone, Serialize, Deserialize)]
886#[serde(untagged)]
887pub enum ExtractProvidersResponse {
888 List(Vec<ExtractionProviderInfo>),
889 Object {
890 providers: Vec<ExtractionProviderInfo>,
891 },
892}
893
894#[derive(Debug, Clone, Serialize, Deserialize)]
900pub struct RotateEncryptionKeyRequest {
901 pub new_key: String,
903 #[serde(skip_serializing_if = "Option::is_none")]
905 pub namespace: Option<String>,
906}
907
908#[derive(Debug, Clone, Serialize, Deserialize)]
910pub struct RotateEncryptionKeyResponse {
911 pub rotated: usize,
912 pub skipped: usize,
913 #[serde(default)]
914 pub namespaces: Vec<String>,
915}
916
917#[derive(Debug, Clone, Serialize, Deserialize)]
919pub struct FeedbackRequest {
920 pub memory_id: String,
921 pub feedback: String,
922 #[serde(skip_serializing_if = "Option::is_none")]
923 pub relevance_score: Option<f32>,
924}
925
926#[derive(Debug, Clone, Serialize, Deserialize)]
928pub struct LegacyFeedbackResponse {
929 pub status: String,
930 pub updated_importance: Option<f32>,
931}
932
933#[derive(Debug, Clone, Serialize, Deserialize, Default)]
942pub struct BatchMemoryFilter {
943 #[serde(skip_serializing_if = "Option::is_none")]
945 pub tags: Option<Vec<String>>,
946 #[serde(skip_serializing_if = "Option::is_none")]
948 pub min_importance: Option<f32>,
949 #[serde(skip_serializing_if = "Option::is_none")]
951 pub max_importance: Option<f32>,
952 #[serde(skip_serializing_if = "Option::is_none")]
954 pub created_after: Option<u64>,
955 #[serde(skip_serializing_if = "Option::is_none")]
957 pub created_before: Option<u64>,
958 #[serde(skip_serializing_if = "Option::is_none")]
960 pub memory_type: Option<MemoryType>,
961 #[serde(skip_serializing_if = "Option::is_none")]
963 pub session_id: Option<String>,
964}
965
966impl BatchMemoryFilter {
967 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
969 self.tags = Some(tags);
970 self
971 }
972
973 pub fn with_min_importance(mut self, min: f32) -> Self {
975 self.min_importance = Some(min);
976 self
977 }
978
979 pub fn with_max_importance(mut self, max: f32) -> Self {
981 self.max_importance = Some(max);
982 self
983 }
984
985 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
987 self.session_id = Some(session_id.into());
988 self
989 }
990}
991
992#[derive(Debug, Clone, Serialize, Deserialize)]
994pub struct BatchRecallRequest {
995 pub agent_id: String,
997 #[serde(default)]
999 pub filter: BatchMemoryFilter,
1000 #[serde(default = "default_batch_limit")]
1002 pub limit: usize,
1003}
1004
1005fn default_batch_limit() -> usize {
1006 100
1007}
1008
1009impl BatchRecallRequest {
1010 pub fn new(agent_id: impl Into<String>) -> Self {
1012 Self {
1013 agent_id: agent_id.into(),
1014 filter: BatchMemoryFilter::default(),
1015 limit: 100,
1016 }
1017 }
1018
1019 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
1021 self.filter = filter;
1022 self
1023 }
1024
1025 pub fn with_limit(mut self, limit: usize) -> Self {
1027 self.limit = limit;
1028 self
1029 }
1030}
1031
1032#[derive(Debug, Clone, Serialize, Deserialize)]
1034pub struct BatchRecallResponse {
1035 pub memories: Vec<RecalledMemory>,
1036 pub total: usize,
1038 pub filtered: usize,
1040}
1041
1042#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1050pub struct BatchStoreMemoryItem {
1051 pub content: String,
1053 #[serde(default)]
1054 pub memory_type: MemoryType,
1055 #[serde(skip_serializing_if = "Option::is_none")]
1056 pub session_id: Option<String>,
1057 #[serde(default = "default_importance")]
1058 pub importance: f32,
1059 #[serde(default)]
1060 pub tags: Vec<String>,
1061 #[serde(skip_serializing_if = "Option::is_none")]
1062 pub metadata: Option<serde_json::Value>,
1063 #[serde(skip_serializing_if = "Option::is_none")]
1064 pub ttl_seconds: Option<u64>,
1065 #[serde(skip_serializing_if = "Option::is_none")]
1066 pub expires_at: Option<u64>,
1067 #[serde(skip_serializing_if = "Option::is_none")]
1069 pub id: Option<String>,
1070}
1071
1072impl BatchStoreMemoryItem {
1073 pub fn new(content: impl Into<String>) -> Self {
1075 Self {
1076 content: content.into(),
1077 importance: default_importance(),
1078 ..Default::default()
1079 }
1080 }
1081
1082 pub fn with_importance(mut self, importance: f32) -> Self {
1084 self.importance = importance;
1085 self
1086 }
1087
1088 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
1090 self.tags = tags;
1091 self
1092 }
1093
1094 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
1096 self.session_id = Some(session_id.into());
1097 self
1098 }
1099
1100 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
1102 self.metadata = Some(metadata);
1103 self
1104 }
1105
1106 pub fn with_id(mut self, id: impl Into<String>) -> Self {
1108 self.id = Some(id.into());
1109 self
1110 }
1111}
1112
1113#[derive(Debug, Clone, Serialize, Deserialize)]
1120pub struct BatchStoreMemoryRequest {
1121 pub agent_id: String,
1123 pub memories: Vec<BatchStoreMemoryItem>,
1125}
1126
1127impl BatchStoreMemoryRequest {
1128 pub fn new(agent_id: impl Into<String>, memories: Vec<BatchStoreMemoryItem>) -> Self {
1130 Self {
1131 agent_id: agent_id.into(),
1132 memories,
1133 }
1134 }
1135}
1136
1137#[derive(Debug, Clone, Serialize, Deserialize)]
1139pub struct BatchStoredMemory {
1140 pub id: String,
1141 pub content: String,
1142 pub agent_id: String,
1143 #[serde(default)]
1144 pub tags: Vec<String>,
1145 #[serde(default)]
1146 pub importance: f32,
1147 pub created_at: u64,
1148}
1149
1150#[derive(Debug, Clone, Serialize, Deserialize)]
1152pub struct BatchStoreMemoryResponse {
1153 pub stored: Vec<BatchStoredMemory>,
1155 pub stored_count: usize,
1157 pub total_embedding_time_ms: u64,
1159}
1160
1161#[derive(Debug, Clone, Serialize, Deserialize)]
1163pub struct BatchForgetRequest {
1164 pub agent_id: String,
1166 pub filter: BatchMemoryFilter,
1168}
1169
1170impl BatchForgetRequest {
1171 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
1173 Self {
1174 agent_id: agent_id.into(),
1175 filter,
1176 }
1177 }
1178}
1179
1180#[derive(Debug, Clone, Serialize, Deserialize)]
1182pub struct BatchForgetResponse {
1183 pub deleted_count: usize,
1184}
1185
1186impl DakeraClient {
1191 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
1215 let url = format!("{}/v1/memory/store", self.base_url);
1216 let response = self.client.post(&url).json(&request).send().await?;
1217 self.handle_response(response).await
1218 }
1219
1220 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
1241 let url = format!("{}/v1/memory/recall", self.base_url);
1242 let response = self.client.post(&url).json(&request).send().await?;
1243 self.handle_response(response).await
1244 }
1245
1246 pub async fn recall_simple(
1248 &self,
1249 agent_id: &str,
1250 query: &str,
1251 top_k: usize,
1252 ) -> Result<RecallResponse> {
1253 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
1254 .await
1255 }
1256
1257 pub async fn get_memory(&self, agent_id: &str, memory_id: &str) -> Result<RecalledMemory> {
1259 let url = format!(
1260 "{}/v1/memory/get/{}?agent_id={}",
1261 self.base_url, memory_id, agent_id
1262 );
1263 let response = self.client.get(&url).send().await?;
1264 self.handle_response(response).await
1265 }
1266
1267 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
1269 let url = format!("{}/v1/memory/forget", self.base_url);
1270 let response = self.client.post(&url).json(&request).send().await?;
1271 self.handle_response(response).await
1272 }
1273
1274 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
1276 let url = format!("{}/v1/memory/search", self.base_url);
1277 let response = self.client.post(&url).json(&request).send().await?;
1278 self.handle_response(response).await
1279 }
1280
1281 pub async fn update_memory(
1283 &self,
1284 agent_id: &str,
1285 memory_id: &str,
1286 request: UpdateMemoryRequest,
1287 ) -> Result<StoreMemoryResponse> {
1288 let url = format!(
1289 "{}/v1/agents/{}/memories/{}",
1290 self.base_url, agent_id, memory_id
1291 );
1292 let response = self.client.put(&url).json(&request).send().await?;
1293 self.handle_response(response).await
1294 }
1295
1296 pub async fn update_importance(
1298 &self,
1299 agent_id: &str,
1300 request: UpdateImportanceRequest,
1301 ) -> Result<serde_json::Value> {
1302 let url = format!("{}/v1/memory/importance", self.base_url);
1303 let mut last_result = serde_json::Value::Null;
1304 for memory_id in &request.memory_ids {
1305 let body = serde_json::json!({
1306 "agent_id": agent_id,
1307 "memory_id": memory_id,
1308 "importance": request.importance,
1309 });
1310 let response = self.client.post(&url).json(&body).send().await?;
1311 last_result = self.handle_response(response).await?;
1312 }
1313 Ok(last_result)
1314 }
1315
1316 pub async fn consolidate(
1318 &self,
1319 agent_id: &str,
1320 request: ConsolidateRequest,
1321 ) -> Result<ConsolidateResponse> {
1322 let url = format!("{}/v1/memory/consolidate", self.base_url);
1324 let mut body = serde_json::to_value(&request)?;
1325 body["agent_id"] = serde_json::Value::String(agent_id.to_string());
1326 let response = self.client.post(&url).json(&body).send().await?;
1327 self.handle_response(response).await
1328 }
1329
1330 pub async fn memory_feedback(
1332 &self,
1333 agent_id: &str,
1334 request: FeedbackRequest,
1335 ) -> Result<LegacyFeedbackResponse> {
1336 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
1337 let response = self.client.post(&url).json(&request).send().await?;
1338 self.handle_response(response).await
1339 }
1340
1341 pub async fn feedback_memory(
1361 &self,
1362 memory_id: &str,
1363 agent_id: &str,
1364 signal: FeedbackSignal,
1365 ) -> Result<FeedbackResponse> {
1366 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1367 let body = MemoryFeedbackBody {
1368 agent_id: agent_id.to_string(),
1369 signal,
1370 };
1371 let response = self.client.post(&url).json(&body).send().await?;
1372 self.handle_response(response).await
1373 }
1374
1375 pub async fn get_memory_feedback_history(
1377 &self,
1378 memory_id: &str,
1379 ) -> Result<FeedbackHistoryResponse> {
1380 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
1381 let response = self.client.get(&url).send().await?;
1382 self.handle_response(response).await
1383 }
1384
1385 pub async fn evaluate_tif(&self, memory_id: &str) -> Result<TifScore> {
1393 let history = self.get_memory_feedback_history(memory_id).await?;
1394 Ok(TifScore::from_feedback_history(&history))
1395 }
1396
1397 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
1399 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
1400 let response = self.client.get(&url).send().await?;
1401 self.handle_response(response).await
1402 }
1403
1404 pub async fn patch_memory_importance(
1411 &self,
1412 memory_id: &str,
1413 agent_id: &str,
1414 importance: f32,
1415 ) -> Result<FeedbackResponse> {
1416 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
1417 let body = MemoryImportancePatch {
1418 agent_id: agent_id.to_string(),
1419 importance,
1420 };
1421 let response = self.client.patch(&url).json(&body).send().await?;
1422 self.handle_response(response).await
1423 }
1424
1425 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
1430 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
1431 let response = self.client.get(&url).send().await?;
1432 self.handle_response(response).await
1433 }
1434
1435 pub async fn memory_graph(
1456 &self,
1457 memory_id: &str,
1458 options: GraphOptions,
1459 ) -> Result<MemoryGraph> {
1460 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
1461 let depth = options.depth.unwrap_or(1);
1462 url.push_str(&format!("?depth={}", depth));
1463 if let Some(types) = &options.types {
1464 let type_strs: Vec<String> = types
1465 .iter()
1466 .map(|t| {
1467 serde_json::to_value(t)
1468 .unwrap()
1469 .as_str()
1470 .unwrap_or("")
1471 .to_string()
1472 })
1473 .collect();
1474 if !type_strs.is_empty() {
1475 url.push_str(&format!("&types={}", type_strs.join(",")));
1476 }
1477 }
1478 let response = self.client.get(&url).send().await?;
1479 self.handle_response(response).await
1480 }
1481
1482 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1495 let url = format!(
1496 "{}/v1/memories/{}/path?target={}",
1497 self.base_url,
1498 source_id,
1499 urlencoding::encode(target_id)
1500 );
1501 let response = self.client.get(&url).send().await?;
1502 self.handle_response(response).await
1503 }
1504
1505 pub async fn memory_link(
1518 &self,
1519 source_id: &str,
1520 target_id: &str,
1521 edge_type: EdgeType,
1522 ) -> Result<GraphLinkResponse> {
1523 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1524 let request = GraphLinkRequest {
1525 target_id: target_id.to_string(),
1526 edge_type,
1527 };
1528 let response = self.client.post(&url).json(&request).send().await?;
1529 self.handle_response(response).await
1530 }
1531
1532 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1540 let url = format!(
1541 "{}/v1/agents/{}/graph/export?format={}",
1542 self.base_url, agent_id, format
1543 );
1544 let response = self.client.get(&url).send().await?;
1545 self.handle_response(response).await
1546 }
1547
1548 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1554 let url = format!("{}/v1/sessions/start", self.base_url);
1555 let request = SessionStartRequest {
1556 agent_id: agent_id.to_string(),
1557 metadata: None,
1558 };
1559 let response = self.client.post(&url).json(&request).send().await?;
1560 let resp: SessionStartResponse = self.handle_response(response).await?;
1561 Ok(resp.session)
1562 }
1563
1564 pub async fn start_session_with_metadata(
1566 &self,
1567 agent_id: &str,
1568 metadata: serde_json::Value,
1569 ) -> Result<Session> {
1570 let url = format!("{}/v1/sessions/start", self.base_url);
1571 let request = SessionStartRequest {
1572 agent_id: agent_id.to_string(),
1573 metadata: Some(metadata),
1574 };
1575 let response = self.client.post(&url).json(&request).send().await?;
1576 let resp: SessionStartResponse = self.handle_response(response).await?;
1577 Ok(resp.session)
1578 }
1579
1580 pub async fn end_session(
1583 &self,
1584 session_id: &str,
1585 summary: Option<String>,
1586 ) -> Result<SessionEndResponse> {
1587 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1588 let request = SessionEndRequest { summary };
1589 let response = self.client.post(&url).json(&request).send().await?;
1590 self.handle_response(response).await
1591 }
1592
1593 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1595 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1596 let response = self.client.get(&url).send().await?;
1597 self.handle_response(response).await
1598 }
1599
1600 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1602 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1603 let response = self.client.get(&url).send().await?;
1604 let wrapper: ListSessionsResponse = self.handle_response(response).await?;
1605 Ok(wrapper.sessions)
1606 }
1607
1608 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1610 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1611 let response = self.client.get(&url).send().await?;
1612 self.handle_response(response).await
1613 }
1614
1615 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1639 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1640 let response = self.client.post(&url).json(&request).send().await?;
1641 self.handle_response(response).await
1642 }
1643
1644 pub async fn store_memories_batch(
1674 &self,
1675 request: BatchStoreMemoryRequest,
1676 ) -> Result<BatchStoreMemoryResponse> {
1677 let url = format!("{}/v1/memories/store/batch", self.base_url);
1678 let response = self.client.post(&url).json(&request).send().await?;
1679 self.handle_response(response).await
1680 }
1681
1682 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1702 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1703 let response = self.client.delete(&url).json(&request).send().await?;
1704 self.handle_response(response).await
1705 }
1706
1707 pub async fn import_memories(
1725 &self,
1726 data: serde_json::Value,
1727 format: &str,
1728 agent_id: Option<&str>,
1729 namespace: Option<&str>,
1730 ) -> Result<MemoryImportResponse> {
1731 let mut body = serde_json::json!({"data": data, "format": format});
1732 if let Some(aid) = agent_id {
1733 body["agent_id"] = serde_json::Value::String(aid.to_string());
1734 }
1735 if let Some(ns) = namespace {
1736 body["namespace"] = serde_json::Value::String(ns.to_string());
1737 }
1738 let url = format!("{}/v1/import", self.base_url);
1739 let response = self.client.post(&url).json(&body).send().await?;
1740 self.handle_response(response).await
1741 }
1742
1743 pub async fn export_memories(
1747 &self,
1748 format: &str,
1749 agent_id: Option<&str>,
1750 namespace: Option<&str>,
1751 limit: Option<u32>,
1752 ) -> Result<MemoryExportResponse> {
1753 let mut params = vec![("format", format.to_string())];
1754 if let Some(aid) = agent_id {
1755 params.push(("agent_id", aid.to_string()));
1756 }
1757 if let Some(ns) = namespace {
1758 params.push(("namespace", ns.to_string()));
1759 }
1760 if let Some(l) = limit {
1761 params.push(("limit", l.to_string()));
1762 }
1763 let url = format!("{}/v1/export", self.base_url);
1764 let response = self.client.get(&url).query(¶ms).send().await?;
1765 self.handle_response(response).await
1766 }
1767
1768 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1774 let url = format!("{}/v1/audit", self.base_url);
1775 let response = self.client.get(&url).query(&query).send().await?;
1776 self.handle_response(response).await
1777 }
1778
1779 pub async fn stream_audit_events(
1783 &self,
1784 agent_id: Option<&str>,
1785 event_type: Option<&str>,
1786 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1787 let mut params: Vec<(&str, String)> = Vec::new();
1788 if let Some(aid) = agent_id {
1789 params.push(("agent_id", aid.to_string()));
1790 }
1791 if let Some(et) = event_type {
1792 params.push(("event_type", et.to_string()));
1793 }
1794 let base = format!("{}/v1/audit/stream", self.base_url);
1795 let url = if params.is_empty() {
1796 base
1797 } else {
1798 let qs = params
1799 .iter()
1800 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1801 .collect::<Vec<_>>()
1802 .join("&");
1803 format!("{}?{}", base, qs)
1804 };
1805 self.stream_sse(url).await
1806 }
1807
1808 pub async fn export_audit(
1810 &self,
1811 format: &str,
1812 agent_id: Option<&str>,
1813 event_type: Option<&str>,
1814 from_ts: Option<u64>,
1815 to_ts: Option<u64>,
1816 ) -> Result<AuditExportResponse> {
1817 let mut body = serde_json::json!({"format": format});
1818 if let Some(aid) = agent_id {
1819 body["agent_id"] = serde_json::Value::String(aid.to_string());
1820 }
1821 if let Some(et) = event_type {
1822 body["event_type"] = serde_json::Value::String(et.to_string());
1823 }
1824 if let Some(f) = from_ts {
1825 body["from"] = serde_json::Value::Number(f.into());
1826 }
1827 if let Some(t) = to_ts {
1828 body["to"] = serde_json::Value::Number(t.into());
1829 }
1830 let url = format!("{}/v1/audit/export", self.base_url);
1831 let response = self.client.post(&url).json(&body).send().await?;
1832 self.handle_response(response).await
1833 }
1834
1835 pub async fn extract_text(
1844 &self,
1845 text: &str,
1846 namespace: Option<&str>,
1847 provider: Option<&str>,
1848 model: Option<&str>,
1849 ) -> Result<ExtractionResult> {
1850 let mut body = serde_json::json!({"text": text});
1851 if let Some(ns) = namespace {
1852 body["namespace"] = serde_json::Value::String(ns.to_string());
1853 }
1854 if let Some(p) = provider {
1855 body["provider"] = serde_json::Value::String(p.to_string());
1856 }
1857 if let Some(m) = model {
1858 body["model"] = serde_json::Value::String(m.to_string());
1859 }
1860 let url = format!("{}/v1/extract", self.base_url);
1861 let response = self.client.post(&url).json(&body).send().await?;
1862 self.handle_response(response).await
1863 }
1864
1865 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1867 let url = format!("{}/v1/extract/providers", self.base_url);
1868 let response = self.client.get(&url).send().await?;
1869 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1870 Ok(match result {
1871 ExtractProvidersResponse::List(v) => v,
1872 ExtractProvidersResponse::Object { providers } => providers,
1873 })
1874 }
1875
1876 pub async fn configure_namespace_extractor(
1878 &self,
1879 namespace: &str,
1880 provider: &str,
1881 model: Option<&str>,
1882 ) -> Result<serde_json::Value> {
1883 let mut body = serde_json::json!({"provider": provider});
1884 if let Some(m) = model {
1885 body["model"] = serde_json::Value::String(m.to_string());
1886 }
1887 let url = format!(
1888 "{}/v1/namespaces/{}/extractor",
1889 self.base_url,
1890 urlencoding::encode(namespace)
1891 );
1892 let response = self.client.patch(&url).json(&body).send().await?;
1893 self.handle_response(response).await
1894 }
1895
1896 pub async fn rotate_encryption_key(
1912 &self,
1913 new_key: &str,
1914 namespace: Option<&str>,
1915 ) -> Result<RotateEncryptionKeyResponse> {
1916 let body = RotateEncryptionKeyRequest {
1917 new_key: new_key.to_string(),
1918 namespace: namespace.map(|s| s.to_string()),
1919 };
1920 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1921 let response = self.client.post(&url).json(&body).send().await?;
1922 self.handle_response(response).await
1923 }
1924}