Skip to main content

dakera_client/
knowledge.rs

1//! Knowledge graph operations for the Dakera client.
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Result;
6use crate::DakeraClient;
7
8// ============================================================================
9// Knowledge Graph Types
10// ============================================================================
11
12/// Request to build a knowledge graph
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct KnowledgeGraphRequest {
15    pub agent_id: String,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub memory_id: Option<String>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub depth: Option<u32>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub min_similarity: Option<f32>,
22}
23
24/// A node in the knowledge graph
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct KnowledgeNode {
27    pub id: String,
28    pub content: String,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub memory_type: Option<String>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub importance: Option<f32>,
33    #[serde(default)]
34    pub metadata: serde_json::Value,
35}
36
37/// An edge in the knowledge graph
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct KnowledgeEdge {
40    pub source: String,
41    pub target: String,
42    pub similarity: f32,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub relationship: Option<String>,
45}
46
47/// Response from knowledge graph operations
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct KnowledgeGraphResponse {
50    pub nodes: Vec<KnowledgeNode>,
51    pub edges: Vec<KnowledgeEdge>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub clusters: Option<Vec<Vec<String>>>,
54}
55
56/// Request to build a full knowledge graph
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct FullKnowledgeGraphRequest {
59    pub agent_id: String,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub max_nodes: Option<u32>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub min_similarity: Option<f32>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub cluster_threshold: Option<f32>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub max_edges_per_node: Option<u32>,
68}
69
70/// Request to summarize memories
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct SummarizeRequest {
73    pub agent_id: String,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub memory_ids: Option<Vec<String>>,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub target_type: Option<String>,
78    #[serde(default)]
79    pub dry_run: bool,
80}
81
82/// Response from summarization
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct SummarizeResponse {
85    pub summary: String,
86    pub source_count: usize,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub new_memory_id: Option<String>,
89}
90
91/// Request to deduplicate memories
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct DeduplicateRequest {
94    pub agent_id: String,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub threshold: Option<f32>,
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub memory_type: Option<String>,
99    #[serde(default)]
100    pub dry_run: bool,
101}
102
103/// Response from deduplication
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct DeduplicateResponse {
106    pub duplicates_found: usize,
107    pub removed_count: usize,
108    pub groups: Vec<Vec<String>>,
109}
110
111// ============================================================================
112// Cross-Agent Network Types (DASH-A)
113// ============================================================================
114
115/// Request to build a cross-agent knowledge network
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct CrossAgentNetworkRequest {
118    /// Agent IDs to include; `None` means all agents.
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub agent_ids: Option<Vec<String>>,
121    /// Minimum cosine similarity for cross-agent edges (default 0.3).
122    pub min_similarity: f32,
123    /// Maximum memory nodes returned per agent (default 50).
124    pub max_nodes_per_agent: usize,
125    /// Minimum importance score for included nodes (default 0.0).
126    pub min_importance: f32,
127    /// Maximum cross-agent edges in the response (default 200).
128    pub max_cross_edges: usize,
129}
130
131impl Default for CrossAgentNetworkRequest {
132    fn default() -> Self {
133        Self {
134            agent_ids: None,
135            min_similarity: 0.3,
136            max_nodes_per_agent: 50,
137            min_importance: 0.0,
138            max_cross_edges: 200,
139        }
140    }
141}
142
143/// Summary information about a single agent in the network
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct AgentNetworkInfo {
146    pub agent_id: String,
147    pub memory_count: usize,
148    pub avg_importance: f32,
149}
150
151/// A memory node in the cross-agent network graph
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct AgentNetworkNode {
154    pub id: String,
155    pub agent_id: String,
156    pub content: String,
157    pub importance: f32,
158    pub tags: Vec<String>,
159    pub memory_type: String,
160    /// Unix milliseconds.
161    pub created_at: u64,
162}
163
164/// A cross-agent similarity edge between two memory nodes
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct AgentNetworkEdge {
167    pub source: String,
168    pub target: String,
169    pub source_agent: String,
170    pub target_agent: String,
171    pub similarity: f32,
172}
173
174/// Aggregate statistics for the cross-agent network
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct AgentNetworkStats {
177    pub total_agents: usize,
178    pub total_nodes: usize,
179    pub total_cross_edges: usize,
180    pub density: f32,
181}
182
183/// Response from the cross-agent network endpoint
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct CrossAgentNetworkResponse {
186    pub agents: Vec<AgentNetworkInfo>,
187    pub nodes: Vec<AgentNetworkNode>,
188    pub edges: Vec<AgentNetworkEdge>,
189    pub stats: AgentNetworkStats,
190    /// Total number of memory nodes in the network (added in server v0.6.2).
191    #[serde(default)]
192    pub node_count: usize,
193}
194
195// ============================================================================
196// Knowledge Graph Client Methods
197// ============================================================================
198
199impl DakeraClient {
200    /// Build a knowledge graph from a seed memory
201    pub async fn knowledge_graph(
202        &self,
203        request: KnowledgeGraphRequest,
204    ) -> Result<KnowledgeGraphResponse> {
205        let url = format!("{}/v1/knowledge/graph", self.base_url);
206        let response = self.client.post(&url).json(&request).send().await?;
207        self.handle_response(response).await
208    }
209
210    /// Build a full knowledge graph for an agent
211    pub async fn full_knowledge_graph(
212        &self,
213        request: FullKnowledgeGraphRequest,
214    ) -> Result<KnowledgeGraphResponse> {
215        let url = format!("{}/v1/knowledge/graph/full", self.base_url);
216        let response = self.client.post(&url).json(&request).send().await?;
217        self.handle_response(response).await
218    }
219
220    /// Summarize memories
221    pub async fn summarize(&self, request: SummarizeRequest) -> Result<SummarizeResponse> {
222        let url = format!("{}/v1/knowledge/summarize", self.base_url);
223        let response = self.client.post(&url).json(&request).send().await?;
224        self.handle_response(response).await
225    }
226
227    /// Deduplicate memories
228    pub async fn deduplicate(&self, request: DeduplicateRequest) -> Result<DeduplicateResponse> {
229        let url = format!("{}/v1/knowledge/deduplicate", self.base_url);
230        let response = self.client.post(&url).json(&request).send().await?;
231        self.handle_response(response).await
232    }
233
234    /// Build a cross-agent knowledge network (DASH-A).
235    ///
236    /// Calls `POST /v1/knowledge/network/cross-agent` (Admin scope) and returns
237    /// a graph of memory nodes and cross-agent similarity edges.
238    pub async fn cross_agent_network(
239        &self,
240        request: CrossAgentNetworkRequest,
241    ) -> Result<CrossAgentNetworkResponse> {
242        let url = format!("{}/v1/knowledge/network/cross-agent", self.base_url);
243        let response = self.client.post(&url).json(&request).send().await?;
244        self.handle_response(response).await
245    }
246}