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)]
22pub enum MemoryType {
23 #[default]
24 Episodic,
25 Semantic,
26 Procedural,
27 Working,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct StoreMemoryRequest {
33 pub agent_id: String,
34 pub content: String,
35 #[serde(default)]
36 pub memory_type: MemoryType,
37 #[serde(default = "default_importance")]
38 pub importance: f32,
39 #[serde(default)]
40 pub tags: Vec<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub session_id: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub metadata: Option<serde_json::Value>,
45 #[serde(skip_serializing_if = "Option::is_none")]
48 pub ttl_seconds: Option<u64>,
49 #[serde(skip_serializing_if = "Option::is_none")]
53 pub expires_at: Option<u64>,
54}
55
56fn default_importance() -> f32 {
57 0.5
58}
59
60impl StoreMemoryRequest {
61 pub fn new(agent_id: impl Into<String>, content: impl Into<String>) -> Self {
63 Self {
64 agent_id: agent_id.into(),
65 content: content.into(),
66 memory_type: MemoryType::default(),
67 importance: 0.5,
68 tags: Vec::new(),
69 session_id: None,
70 metadata: None,
71 ttl_seconds: None,
72 expires_at: None,
73 }
74 }
75
76 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
78 self.memory_type = memory_type;
79 self
80 }
81
82 pub fn with_importance(mut self, importance: f32) -> Self {
84 self.importance = importance.clamp(0.0, 1.0);
85 self
86 }
87
88 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
90 self.tags = tags;
91 self
92 }
93
94 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
96 self.session_id = Some(session_id.into());
97 self
98 }
99
100 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
102 self.metadata = Some(metadata);
103 self
104 }
105
106 pub fn with_ttl(mut self, ttl_seconds: u64) -> Self {
109 self.ttl_seconds = Some(ttl_seconds);
110 self
111 }
112
113 pub fn with_expires_at(mut self, expires_at: u64) -> Self {
116 self.expires_at = Some(expires_at);
117 self
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct StoreMemoryResponse {
124 pub memory_id: String,
125 pub agent_id: String,
126 pub namespace: String,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct RecallRequest {
132 pub agent_id: String,
133 pub query: String,
134 #[serde(default = "default_top_k")]
135 pub top_k: usize,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub memory_type: Option<MemoryType>,
138 #[serde(default)]
139 pub min_importance: f32,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub session_id: Option<String>,
142 #[serde(default)]
143 pub tags: Vec<String>,
144 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
147 pub include_associated: bool,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub associated_memories_cap: Option<u32>,
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub associated_memories_depth: Option<u8>,
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub associated_memories_min_weight: Option<f32>,
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub since: Option<String>,
160 #[serde(skip_serializing_if = "Option::is_none")]
162 pub until: Option<String>,
163}
164
165fn default_top_k() -> usize {
166 5
167}
168
169impl RecallRequest {
170 pub fn new(agent_id: impl Into<String>, query: impl Into<String>) -> Self {
172 Self {
173 agent_id: agent_id.into(),
174 query: query.into(),
175 top_k: 5,
176 memory_type: None,
177 min_importance: 0.0,
178 session_id: None,
179 tags: Vec::new(),
180 include_associated: false,
181 associated_memories_cap: None,
182 associated_memories_depth: None,
183 associated_memories_min_weight: None,
184 since: None,
185 until: None,
186 }
187 }
188
189 pub fn with_top_k(mut self, top_k: usize) -> Self {
191 self.top_k = top_k;
192 self
193 }
194
195 pub fn with_type(mut self, memory_type: MemoryType) -> Self {
197 self.memory_type = Some(memory_type);
198 self
199 }
200
201 pub fn with_min_importance(mut self, min: f32) -> Self {
203 self.min_importance = min;
204 self
205 }
206
207 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
209 self.session_id = Some(session_id.into());
210 self
211 }
212
213 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
215 self.tags = tags;
216 self
217 }
218
219 pub fn with_associated(mut self) -> Self {
221 self.include_associated = true;
222 self
223 }
224
225 pub fn with_associated_cap(mut self, cap: u32) -> Self {
227 self.include_associated = true;
228 self.associated_memories_cap = Some(cap);
229 self
230 }
231
232 pub fn with_since(mut self, since: impl Into<String>) -> Self {
234 self.since = Some(since.into());
235 self
236 }
237
238 pub fn with_until(mut self, until: impl Into<String>) -> Self {
240 self.until = Some(until.into());
241 self
242 }
243
244 pub fn with_associated_depth(mut self, depth: u8) -> Self {
246 self.include_associated = true;
247 self.associated_memories_depth = Some(depth);
248 self
249 }
250
251 pub fn with_associated_min_weight(mut self, weight: f32) -> Self {
253 self.associated_memories_min_weight = Some(weight);
254 self
255 }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
260pub struct RecalledMemory {
261 pub id: String,
262 pub content: String,
263 pub memory_type: MemoryType,
264 pub importance: f32,
265 pub score: f32,
266 #[serde(default)]
267 pub tags: Vec<String>,
268 #[serde(skip_serializing_if = "Option::is_none")]
269 pub session_id: Option<String>,
270 #[serde(skip_serializing_if = "Option::is_none")]
271 pub metadata: Option<serde_json::Value>,
272 pub created_at: u64,
273 pub last_accessed_at: u64,
274 pub access_count: u32,
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub depth: Option<u8>,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct RecallResponse {
283 pub memories: Vec<RecalledMemory>,
284 pub total_found: usize,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub associated_memories: Option<Vec<RecalledMemory>>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct ForgetRequest {
293 pub agent_id: String,
294 #[serde(default)]
295 pub memory_ids: Vec<String>,
296 #[serde(default)]
297 pub tags: Vec<String>,
298 #[serde(skip_serializing_if = "Option::is_none")]
299 pub session_id: Option<String>,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub before_timestamp: Option<u64>,
302}
303
304impl ForgetRequest {
305 pub fn by_ids(agent_id: impl Into<String>, ids: Vec<String>) -> Self {
307 Self {
308 agent_id: agent_id.into(),
309 memory_ids: ids,
310 tags: Vec::new(),
311 session_id: None,
312 before_timestamp: None,
313 }
314 }
315
316 pub fn by_tags(agent_id: impl Into<String>, tags: Vec<String>) -> Self {
318 Self {
319 agent_id: agent_id.into(),
320 memory_ids: Vec::new(),
321 tags,
322 session_id: None,
323 before_timestamp: None,
324 }
325 }
326
327 pub fn by_session(agent_id: impl Into<String>, session_id: impl Into<String>) -> Self {
329 Self {
330 agent_id: agent_id.into(),
331 memory_ids: Vec::new(),
332 tags: Vec::new(),
333 session_id: Some(session_id.into()),
334 before_timestamp: None,
335 }
336 }
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct ForgetResponse {
342 pub deleted_count: u64,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct SessionStartRequest {
348 pub agent_id: String,
349 #[serde(skip_serializing_if = "Option::is_none")]
350 pub metadata: Option<serde_json::Value>,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct Session {
356 pub id: String,
357 pub agent_id: String,
358 pub started_at: u64,
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub ended_at: Option<u64>,
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub summary: Option<String>,
363 #[serde(skip_serializing_if = "Option::is_none")]
364 pub metadata: Option<serde_json::Value>,
365 #[serde(default)]
367 pub memory_count: usize,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct SessionEndRequest {
373 #[serde(skip_serializing_if = "Option::is_none")]
374 pub summary: Option<String>,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
379pub struct SessionStartResponse {
380 pub session: Session,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct SessionEndResponse {
386 pub session: Session,
387 pub memory_count: usize,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct UpdateMemoryRequest {
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub content: Option<String>,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub metadata: Option<serde_json::Value>,
397 #[serde(skip_serializing_if = "Option::is_none")]
398 pub memory_type: Option<MemoryType>,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
403pub struct UpdateImportanceRequest {
404 pub memory_ids: Vec<String>,
405 pub importance: f32,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize, Default)]
410pub struct ConsolidationConfig {
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub algorithm: Option<String>,
414 #[serde(skip_serializing_if = "Option::is_none")]
416 pub min_samples: Option<u32>,
417 #[serde(skip_serializing_if = "Option::is_none")]
419 pub eps: Option<f32>,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct ConsolidationLogEntry {
425 pub step: String,
426 pub memories_before: usize,
427 pub memories_after: usize,
428 pub duration_ms: f64,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize, Default)]
433pub struct ConsolidateRequest {
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub memory_type: Option<String>,
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub threshold: Option<f32>,
438 #[serde(default)]
439 pub dry_run: bool,
440 #[serde(skip_serializing_if = "Option::is_none")]
442 pub config: Option<ConsolidationConfig>,
443}
444
445#[derive(Debug, Clone, Serialize, Deserialize)]
447pub struct ConsolidateResponse {
448 pub consolidated_count: usize,
449 pub removed_count: usize,
450 pub new_memories: Vec<String>,
451 #[serde(default, skip_serializing_if = "Vec::is_empty")]
453 pub log: Vec<ConsolidationLogEntry>,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct MemoryImportResponse {
463 pub imported_count: usize,
464 pub skipped_count: usize,
465 #[serde(default)]
466 pub errors: Vec<String>,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct MemoryExportResponse {
472 pub data: Vec<serde_json::Value>,
473 pub format: String,
474 pub count: usize,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct AuditEvent {
484 pub id: String,
485 pub event_type: String,
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub agent_id: Option<String>,
488 #[serde(skip_serializing_if = "Option::is_none")]
489 pub namespace: Option<String>,
490 pub timestamp: u64,
491 #[serde(default)]
492 pub details: serde_json::Value,
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize)]
497pub struct AuditListResponse {
498 pub events: Vec<AuditEvent>,
499 pub total: usize,
500 #[serde(skip_serializing_if = "Option::is_none")]
501 pub cursor: Option<String>,
502}
503
504#[derive(Debug, Clone, Serialize, Deserialize)]
506pub struct AuditExportResponse {
507 pub data: String,
508 pub format: String,
509 pub count: usize,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize, Default)]
514pub struct AuditQuery {
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub agent_id: Option<String>,
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub event_type: Option<String>,
519 #[serde(skip_serializing_if = "Option::is_none")]
520 pub from: Option<u64>,
521 #[serde(skip_serializing_if = "Option::is_none")]
522 pub to: Option<u64>,
523 #[serde(skip_serializing_if = "Option::is_none")]
524 pub limit: Option<u32>,
525 #[serde(skip_serializing_if = "Option::is_none")]
526 pub cursor: Option<String>,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize)]
535pub struct ExtractionResult {
536 pub entities: Vec<serde_json::Value>,
537 pub provider: String,
538 #[serde(skip_serializing_if = "Option::is_none")]
539 pub model: Option<String>,
540 pub duration_ms: f64,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct ExtractionProviderInfo {
546 pub name: String,
547 pub available: bool,
548 #[serde(default)]
549 pub models: Vec<String>,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
554#[serde(untagged)]
555pub enum ExtractProvidersResponse {
556 List(Vec<ExtractionProviderInfo>),
557 Object {
558 providers: Vec<ExtractionProviderInfo>,
559 },
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize)]
568pub struct RotateEncryptionKeyRequest {
569 pub new_key: String,
571 #[serde(skip_serializing_if = "Option::is_none")]
573 pub namespace: Option<String>,
574}
575
576#[derive(Debug, Clone, Serialize, Deserialize)]
578pub struct RotateEncryptionKeyResponse {
579 pub rotated: usize,
580 pub skipped: usize,
581 #[serde(default)]
582 pub namespaces: Vec<String>,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct FeedbackRequest {
588 pub memory_id: String,
589 pub feedback: String,
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub relevance_score: Option<f32>,
592}
593
594#[derive(Debug, Clone, Serialize, Deserialize)]
596pub struct LegacyFeedbackResponse {
597 pub status: String,
598 pub updated_importance: Option<f32>,
599}
600
601#[derive(Debug, Clone, Serialize, Deserialize, Default)]
610pub struct BatchMemoryFilter {
611 #[serde(skip_serializing_if = "Option::is_none")]
613 pub tags: Option<Vec<String>>,
614 #[serde(skip_serializing_if = "Option::is_none")]
616 pub min_importance: Option<f32>,
617 #[serde(skip_serializing_if = "Option::is_none")]
619 pub max_importance: Option<f32>,
620 #[serde(skip_serializing_if = "Option::is_none")]
622 pub created_after: Option<u64>,
623 #[serde(skip_serializing_if = "Option::is_none")]
625 pub created_before: Option<u64>,
626 #[serde(skip_serializing_if = "Option::is_none")]
628 pub memory_type: Option<MemoryType>,
629 #[serde(skip_serializing_if = "Option::is_none")]
631 pub session_id: Option<String>,
632}
633
634impl BatchMemoryFilter {
635 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
637 self.tags = Some(tags);
638 self
639 }
640
641 pub fn with_min_importance(mut self, min: f32) -> Self {
643 self.min_importance = Some(min);
644 self
645 }
646
647 pub fn with_max_importance(mut self, max: f32) -> Self {
649 self.max_importance = Some(max);
650 self
651 }
652
653 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
655 self.session_id = Some(session_id.into());
656 self
657 }
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct BatchRecallRequest {
663 pub agent_id: String,
665 #[serde(default)]
667 pub filter: BatchMemoryFilter,
668 #[serde(default = "default_batch_limit")]
670 pub limit: usize,
671}
672
673fn default_batch_limit() -> usize {
674 100
675}
676
677impl BatchRecallRequest {
678 pub fn new(agent_id: impl Into<String>) -> Self {
680 Self {
681 agent_id: agent_id.into(),
682 filter: BatchMemoryFilter::default(),
683 limit: 100,
684 }
685 }
686
687 pub fn with_filter(mut self, filter: BatchMemoryFilter) -> Self {
689 self.filter = filter;
690 self
691 }
692
693 pub fn with_limit(mut self, limit: usize) -> Self {
695 self.limit = limit;
696 self
697 }
698}
699
700#[derive(Debug, Clone, Serialize, Deserialize)]
702pub struct BatchRecallResponse {
703 pub memories: Vec<RecalledMemory>,
704 pub total: usize,
706 pub filtered: usize,
708}
709
710#[derive(Debug, Clone, Serialize, Deserialize)]
712pub struct BatchForgetRequest {
713 pub agent_id: String,
715 pub filter: BatchMemoryFilter,
717}
718
719impl BatchForgetRequest {
720 pub fn new(agent_id: impl Into<String>, filter: BatchMemoryFilter) -> Self {
722 Self {
723 agent_id: agent_id.into(),
724 filter,
725 }
726 }
727}
728
729#[derive(Debug, Clone, Serialize, Deserialize)]
731pub struct BatchForgetResponse {
732 pub deleted_count: usize,
733}
734
735impl DakeraClient {
740 pub async fn store_memory(&self, request: StoreMemoryRequest) -> Result<StoreMemoryResponse> {
764 let url = format!("{}/v1/memory/store", self.base_url);
765 let response = self.client.post(&url).json(&request).send().await?;
766 self.handle_response(response).await
767 }
768
769 pub async fn recall(&self, request: RecallRequest) -> Result<RecallResponse> {
790 let url = format!("{}/v1/memory/recall", self.base_url);
791 let response = self.client.post(&url).json(&request).send().await?;
792 self.handle_response(response).await
793 }
794
795 pub async fn recall_simple(
797 &self,
798 agent_id: &str,
799 query: &str,
800 top_k: usize,
801 ) -> Result<RecallResponse> {
802 self.recall(RecallRequest::new(agent_id, query).with_top_k(top_k))
803 .await
804 }
805
806 pub async fn get_memory(&self, memory_id: &str) -> Result<RecalledMemory> {
808 let url = format!("{}/v1/memory/get/{}", self.base_url, memory_id);
809 let response = self.client.get(&url).send().await?;
810 self.handle_response(response).await
811 }
812
813 pub async fn forget(&self, request: ForgetRequest) -> Result<ForgetResponse> {
815 let url = format!("{}/v1/memory/forget", self.base_url);
816 let response = self.client.post(&url).json(&request).send().await?;
817 self.handle_response(response).await
818 }
819
820 pub async fn search_memories(&self, request: RecallRequest) -> Result<RecallResponse> {
822 let url = format!("{}/v1/memory/search", self.base_url);
823 let response = self.client.post(&url).json(&request).send().await?;
824 self.handle_response(response).await
825 }
826
827 pub async fn update_memory(
829 &self,
830 agent_id: &str,
831 memory_id: &str,
832 request: UpdateMemoryRequest,
833 ) -> Result<StoreMemoryResponse> {
834 let url = format!(
835 "{}/v1/agents/{}/memories/{}",
836 self.base_url, agent_id, memory_id
837 );
838 let response = self.client.put(&url).json(&request).send().await?;
839 self.handle_response(response).await
840 }
841
842 pub async fn update_importance(
844 &self,
845 agent_id: &str,
846 request: UpdateImportanceRequest,
847 ) -> Result<serde_json::Value> {
848 let url = format!(
849 "{}/v1/agents/{}/memories/importance",
850 self.base_url, agent_id
851 );
852 let response = self.client.put(&url).json(&request).send().await?;
853 self.handle_response(response).await
854 }
855
856 pub async fn consolidate(
858 &self,
859 agent_id: &str,
860 request: ConsolidateRequest,
861 ) -> Result<ConsolidateResponse> {
862 let url = format!(
863 "{}/v1/agents/{}/memories/consolidate",
864 self.base_url, agent_id
865 );
866 let response = self.client.post(&url).json(&request).send().await?;
867 self.handle_response(response).await
868 }
869
870 pub async fn memory_feedback(
872 &self,
873 agent_id: &str,
874 request: FeedbackRequest,
875 ) -> Result<LegacyFeedbackResponse> {
876 let url = format!("{}/v1/agents/{}/memories/feedback", self.base_url, agent_id);
877 let response = self.client.post(&url).json(&request).send().await?;
878 self.handle_response(response).await
879 }
880
881 pub async fn feedback_memory(
901 &self,
902 memory_id: &str,
903 agent_id: &str,
904 signal: FeedbackSignal,
905 ) -> Result<FeedbackResponse> {
906 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
907 let body = MemoryFeedbackBody {
908 agent_id: agent_id.to_string(),
909 signal,
910 };
911 let response = self.client.post(&url).json(&body).send().await?;
912 self.handle_response(response).await
913 }
914
915 pub async fn get_memory_feedback_history(
917 &self,
918 memory_id: &str,
919 ) -> Result<FeedbackHistoryResponse> {
920 let url = format!("{}/v1/memories/{}/feedback", self.base_url, memory_id);
921 let response = self.client.get(&url).send().await?;
922 self.handle_response(response).await
923 }
924
925 pub async fn get_agent_feedback_summary(&self, agent_id: &str) -> Result<AgentFeedbackSummary> {
927 let url = format!("{}/v1/agents/{}/feedback/summary", self.base_url, agent_id);
928 let response = self.client.get(&url).send().await?;
929 self.handle_response(response).await
930 }
931
932 pub async fn patch_memory_importance(
939 &self,
940 memory_id: &str,
941 agent_id: &str,
942 importance: f32,
943 ) -> Result<FeedbackResponse> {
944 let url = format!("{}/v1/memories/{}/importance", self.base_url, memory_id);
945 let body = MemoryImportancePatch {
946 agent_id: agent_id.to_string(),
947 importance,
948 };
949 let response = self.client.patch(&url).json(&body).send().await?;
950 self.handle_response(response).await
951 }
952
953 pub async fn get_feedback_health(&self, agent_id: &str) -> Result<FeedbackHealthResponse> {
958 let url = format!("{}/v1/feedback/health?agent_id={}", self.base_url, agent_id);
959 let response = self.client.get(&url).send().await?;
960 self.handle_response(response).await
961 }
962
963 pub async fn memory_graph(
984 &self,
985 memory_id: &str,
986 options: GraphOptions,
987 ) -> Result<MemoryGraph> {
988 let mut url = format!("{}/v1/memories/{}/graph", self.base_url, memory_id);
989 let depth = options.depth.unwrap_or(1);
990 url.push_str(&format!("?depth={}", depth));
991 if let Some(types) = &options.types {
992 let type_strs: Vec<String> = types
993 .iter()
994 .map(|t| {
995 serde_json::to_value(t)
996 .unwrap()
997 .as_str()
998 .unwrap_or("")
999 .to_string()
1000 })
1001 .collect();
1002 if !type_strs.is_empty() {
1003 url.push_str(&format!("&types={}", type_strs.join(",")));
1004 }
1005 }
1006 let response = self.client.get(&url).send().await?;
1007 self.handle_response(response).await
1008 }
1009
1010 pub async fn memory_path(&self, source_id: &str, target_id: &str) -> Result<GraphPath> {
1023 let url = format!(
1024 "{}/v1/memories/{}/path?target={}",
1025 self.base_url,
1026 source_id,
1027 urlencoding::encode(target_id)
1028 );
1029 let response = self.client.get(&url).send().await?;
1030 self.handle_response(response).await
1031 }
1032
1033 pub async fn memory_link(
1046 &self,
1047 source_id: &str,
1048 target_id: &str,
1049 edge_type: EdgeType,
1050 ) -> Result<GraphLinkResponse> {
1051 let url = format!("{}/v1/memories/{}/links", self.base_url, source_id);
1052 let request = GraphLinkRequest {
1053 target_id: target_id.to_string(),
1054 edge_type,
1055 };
1056 let response = self.client.post(&url).json(&request).send().await?;
1057 self.handle_response(response).await
1058 }
1059
1060 pub async fn agent_graph_export(&self, agent_id: &str, format: &str) -> Result<GraphExport> {
1068 let url = format!(
1069 "{}/v1/agents/{}/graph/export?format={}",
1070 self.base_url, agent_id, format
1071 );
1072 let response = self.client.get(&url).send().await?;
1073 self.handle_response(response).await
1074 }
1075
1076 pub async fn start_session(&self, agent_id: &str) -> Result<Session> {
1082 let url = format!("{}/v1/sessions/start", self.base_url);
1083 let request = SessionStartRequest {
1084 agent_id: agent_id.to_string(),
1085 metadata: None,
1086 };
1087 let response = self.client.post(&url).json(&request).send().await?;
1088 let resp: SessionStartResponse = self.handle_response(response).await?;
1089 Ok(resp.session)
1090 }
1091
1092 pub async fn start_session_with_metadata(
1094 &self,
1095 agent_id: &str,
1096 metadata: serde_json::Value,
1097 ) -> Result<Session> {
1098 let url = format!("{}/v1/sessions/start", self.base_url);
1099 let request = SessionStartRequest {
1100 agent_id: agent_id.to_string(),
1101 metadata: Some(metadata),
1102 };
1103 let response = self.client.post(&url).json(&request).send().await?;
1104 let resp: SessionStartResponse = self.handle_response(response).await?;
1105 Ok(resp.session)
1106 }
1107
1108 pub async fn end_session(
1111 &self,
1112 session_id: &str,
1113 summary: Option<String>,
1114 ) -> Result<SessionEndResponse> {
1115 let url = format!("{}/v1/sessions/{}/end", self.base_url, session_id);
1116 let request = SessionEndRequest { summary };
1117 let response = self.client.post(&url).json(&request).send().await?;
1118 self.handle_response(response).await
1119 }
1120
1121 pub async fn get_session(&self, session_id: &str) -> Result<Session> {
1123 let url = format!("{}/v1/sessions/{}", self.base_url, session_id);
1124 let response = self.client.get(&url).send().await?;
1125 self.handle_response(response).await
1126 }
1127
1128 pub async fn list_sessions(&self, agent_id: &str) -> Result<Vec<Session>> {
1130 let url = format!("{}/v1/sessions?agent_id={}", self.base_url, agent_id);
1131 let response = self.client.get(&url).send().await?;
1132 self.handle_response(response).await
1133 }
1134
1135 pub async fn session_memories(&self, session_id: &str) -> Result<RecallResponse> {
1137 let url = format!("{}/v1/sessions/{}/memories", self.base_url, session_id);
1138 let response = self.client.get(&url).send().await?;
1139 self.handle_response(response).await
1140 }
1141
1142 pub async fn batch_recall(&self, request: BatchRecallRequest) -> Result<BatchRecallResponse> {
1166 let url = format!("{}/v1/memories/recall/batch", self.base_url);
1167 let response = self.client.post(&url).json(&request).send().await?;
1168 self.handle_response(response).await
1169 }
1170
1171 pub async fn batch_forget(&self, request: BatchForgetRequest) -> Result<BatchForgetResponse> {
1191 let url = format!("{}/v1/memories/forget/batch", self.base_url);
1192 let response = self.client.delete(&url).json(&request).send().await?;
1193 self.handle_response(response).await
1194 }
1195
1196 pub async fn import_memories(
1214 &self,
1215 data: serde_json::Value,
1216 format: &str,
1217 agent_id: Option<&str>,
1218 namespace: Option<&str>,
1219 ) -> Result<MemoryImportResponse> {
1220 let mut body = serde_json::json!({"data": data, "format": format});
1221 if let Some(aid) = agent_id {
1222 body["agent_id"] = serde_json::Value::String(aid.to_string());
1223 }
1224 if let Some(ns) = namespace {
1225 body["namespace"] = serde_json::Value::String(ns.to_string());
1226 }
1227 let url = format!("{}/v1/import", self.base_url);
1228 let response = self.client.post(&url).json(&body).send().await?;
1229 self.handle_response(response).await
1230 }
1231
1232 pub async fn export_memories(
1236 &self,
1237 format: &str,
1238 agent_id: Option<&str>,
1239 namespace: Option<&str>,
1240 limit: Option<u32>,
1241 ) -> Result<MemoryExportResponse> {
1242 let mut params = vec![("format", format.to_string())];
1243 if let Some(aid) = agent_id {
1244 params.push(("agent_id", aid.to_string()));
1245 }
1246 if let Some(ns) = namespace {
1247 params.push(("namespace", ns.to_string()));
1248 }
1249 if let Some(l) = limit {
1250 params.push(("limit", l.to_string()));
1251 }
1252 let url = format!("{}/v1/export", self.base_url);
1253 let response = self.client.get(&url).query(¶ms).send().await?;
1254 self.handle_response(response).await
1255 }
1256
1257 pub async fn list_audit_events(&self, query: AuditQuery) -> Result<AuditListResponse> {
1263 let url = format!("{}/v1/audit", self.base_url);
1264 let response = self.client.get(&url).query(&query).send().await?;
1265 self.handle_response(response).await
1266 }
1267
1268 pub async fn stream_audit_events(
1272 &self,
1273 agent_id: Option<&str>,
1274 event_type: Option<&str>,
1275 ) -> Result<tokio::sync::mpsc::Receiver<Result<crate::events::DakeraEvent>>> {
1276 let mut params: Vec<(&str, String)> = Vec::new();
1277 if let Some(aid) = agent_id {
1278 params.push(("agent_id", aid.to_string()));
1279 }
1280 if let Some(et) = event_type {
1281 params.push(("event_type", et.to_string()));
1282 }
1283 let base = format!("{}/v1/audit/stream", self.base_url);
1284 let url = if params.is_empty() {
1285 base
1286 } else {
1287 let qs = params
1288 .iter()
1289 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1290 .collect::<Vec<_>>()
1291 .join("&");
1292 format!("{}?{}", base, qs)
1293 };
1294 self.stream_sse(url).await
1295 }
1296
1297 pub async fn export_audit(
1299 &self,
1300 format: &str,
1301 agent_id: Option<&str>,
1302 event_type: Option<&str>,
1303 from_ts: Option<u64>,
1304 to_ts: Option<u64>,
1305 ) -> Result<AuditExportResponse> {
1306 let mut body = serde_json::json!({"format": format});
1307 if let Some(aid) = agent_id {
1308 body["agent_id"] = serde_json::Value::String(aid.to_string());
1309 }
1310 if let Some(et) = event_type {
1311 body["event_type"] = serde_json::Value::String(et.to_string());
1312 }
1313 if let Some(f) = from_ts {
1314 body["from"] = serde_json::Value::Number(f.into());
1315 }
1316 if let Some(t) = to_ts {
1317 body["to"] = serde_json::Value::Number(t.into());
1318 }
1319 let url = format!("{}/v1/audit/export", self.base_url);
1320 let response = self.client.post(&url).json(&body).send().await?;
1321 self.handle_response(response).await
1322 }
1323
1324 pub async fn extract_text(
1333 &self,
1334 text: &str,
1335 namespace: Option<&str>,
1336 provider: Option<&str>,
1337 model: Option<&str>,
1338 ) -> Result<ExtractionResult> {
1339 let mut body = serde_json::json!({"text": text});
1340 if let Some(ns) = namespace {
1341 body["namespace"] = serde_json::Value::String(ns.to_string());
1342 }
1343 if let Some(p) = provider {
1344 body["provider"] = serde_json::Value::String(p.to_string());
1345 }
1346 if let Some(m) = model {
1347 body["model"] = serde_json::Value::String(m.to_string());
1348 }
1349 let url = format!("{}/v1/extract", self.base_url);
1350 let response = self.client.post(&url).json(&body).send().await?;
1351 self.handle_response(response).await
1352 }
1353
1354 pub async fn list_extract_providers(&self) -> Result<Vec<ExtractionProviderInfo>> {
1356 let url = format!("{}/v1/extract/providers", self.base_url);
1357 let response = self.client.get(&url).send().await?;
1358 let result: ExtractProvidersResponse = self.handle_response(response).await?;
1359 Ok(match result {
1360 ExtractProvidersResponse::List(v) => v,
1361 ExtractProvidersResponse::Object { providers } => providers,
1362 })
1363 }
1364
1365 pub async fn configure_namespace_extractor(
1367 &self,
1368 namespace: &str,
1369 provider: &str,
1370 model: Option<&str>,
1371 ) -> Result<serde_json::Value> {
1372 let mut body = serde_json::json!({"provider": provider});
1373 if let Some(m) = model {
1374 body["model"] = serde_json::Value::String(m.to_string());
1375 }
1376 let url = format!(
1377 "{}/v1/namespaces/{}/extractor",
1378 self.base_url,
1379 urlencoding::encode(namespace)
1380 );
1381 let response = self.client.patch(&url).json(&body).send().await?;
1382 self.handle_response(response).await
1383 }
1384
1385 pub async fn rotate_encryption_key(
1401 &self,
1402 new_key: &str,
1403 namespace: Option<&str>,
1404 ) -> Result<RotateEncryptionKeyResponse> {
1405 let body = RotateEncryptionKeyRequest {
1406 new_key: new_key.to_string(),
1407 namespace: namespace.map(|s| s.to_string()),
1408 };
1409 let url = format!("{}/v1/admin/encryption/rotate-key", self.base_url);
1410 let response = self.client.post(&url).json(&body).send().await?;
1411 self.handle_response(response).await
1412 }
1413}