Skip to main content

converge_knowledge/grpc/
client.rs

1//! gRPC client for the knowledge base.
2
3use super::knowledge_service_client::KnowledgeServiceClient as ProtoClient;
4use super::*;
5use crate::error::{Error, Result};
6
7use tonic::transport::Channel;
8
9/// gRPC client for interacting with the knowledge base server.
10pub struct KnowledgeClient {
11    client: ProtoClient<Channel>,
12}
13
14impl KnowledgeClient {
15    /// Connect to a knowledge base server.
16    pub async fn connect(addr: impl Into<String>) -> Result<Self> {
17        let addr = addr.into();
18        let client = ProtoClient::connect(addr)
19            .await
20            .map_err(|e| Error::storage(format!("Failed to connect: {}", e)))?;
21
22        Ok(Self { client })
23    }
24
25    /// Add a new knowledge entry.
26    pub async fn add_entry(
27        &mut self,
28        title: impl Into<String>,
29        content: impl Into<String>,
30    ) -> Result<String> {
31        let request = AddEntryRequest {
32            title: title.into(),
33            content: content.into(),
34            category: None,
35            tags: Vec::new(),
36            source: None,
37            metadata: std::collections::HashMap::new(),
38        };
39
40        let response = self
41            .client
42            .add_entry(request)
43            .await
44            .map_err(|e| Error::storage(e.to_string()))?
45            .into_inner();
46
47        if response.success {
48            Ok(response.id)
49        } else {
50            Err(Error::storage(
51                response
52                    .error
53                    .unwrap_or_else(|| "Unknown error".to_string()),
54            ))
55        }
56    }
57
58    /// Add entry with full options.
59    pub async fn add_entry_full(&mut self, request: AddEntryRequest) -> Result<String> {
60        let response = self
61            .client
62            .add_entry(request)
63            .await
64            .map_err(|e| Error::storage(e.to_string()))?
65            .into_inner();
66
67        if response.success {
68            Ok(response.id)
69        } else {
70            Err(Error::storage(
71                response
72                    .error
73                    .unwrap_or_else(|| "Unknown error".to_string()),
74            ))
75        }
76    }
77
78    /// Add multiple entries in batch.
79    pub async fn add_entries(&mut self, entries: Vec<AddEntryRequest>) -> Result<Vec<String>> {
80        let request = AddEntriesRequest { entries };
81
82        let response = self
83            .client
84            .add_entries(request)
85            .await
86            .map_err(|e| Error::storage(e.to_string()))?
87            .into_inner();
88
89        if response.success {
90            Ok(response.ids)
91        } else {
92            Err(Error::storage(
93                response
94                    .error
95                    .unwrap_or_else(|| "Unknown error".to_string()),
96            ))
97        }
98    }
99
100    /// Get an entry by ID.
101    pub async fn get_entry(&mut self, id: impl Into<String>) -> Result<Option<KnowledgeEntry>> {
102        let request = GetEntryRequest { id: id.into() };
103
104        let response = self
105            .client
106            .get_entry(request)
107            .await
108            .map_err(|e| Error::storage(e.to_string()))?
109            .into_inner();
110
111        if response.found {
112            Ok(response.entry)
113        } else {
114            Ok(None)
115        }
116    }
117
118    /// Update an existing entry.
119    pub async fn update_entry(&mut self, request: UpdateEntryRequest) -> Result<()> {
120        let response = self
121            .client
122            .update_entry(request)
123            .await
124            .map_err(|e| Error::storage(e.to_string()))?
125            .into_inner();
126
127        if response.success {
128            Ok(())
129        } else {
130            Err(Error::storage(
131                response
132                    .error
133                    .unwrap_or_else(|| "Unknown error".to_string()),
134            ))
135        }
136    }
137
138    /// Delete an entry.
139    pub async fn delete_entry(&mut self, id: impl Into<String>) -> Result<()> {
140        let request = DeleteEntryRequest { id: id.into() };
141
142        let response = self
143            .client
144            .delete_entry(request)
145            .await
146            .map_err(|e| Error::storage(e.to_string()))?
147            .into_inner();
148
149        if response.success {
150            Ok(())
151        } else {
152            Err(Error::storage(
153                response
154                    .error
155                    .unwrap_or_else(|| "Unknown error".to_string()),
156            ))
157        }
158    }
159
160    /// Search for similar entries.
161    pub async fn search(
162        &mut self,
163        query: impl Into<String>,
164        limit: u32,
165    ) -> Result<Vec<SearchResult>> {
166        let request = SearchRequest {
167            query: query.into(),
168            limit,
169            min_similarity: 0.0,
170            category: None,
171            tags: Vec::new(),
172            use_learning: true,
173            include_related: false,
174            diversity: 0.0,
175            hybrid: false,
176            keyword_weight: 0.3,
177        };
178
179        let response = self
180            .client
181            .search(request)
182            .await
183            .map_err(|e| Error::storage(e.to_string()))?
184            .into_inner();
185
186        Ok(response.results)
187    }
188
189    /// Search with full options.
190    pub async fn search_full(&mut self, request: SearchRequest) -> Result<SearchResponse> {
191        let response = self
192            .client
193            .search(request)
194            .await
195            .map_err(|e| Error::storage(e.to_string()))?
196            .into_inner();
197
198        Ok(response)
199    }
200
201    /// Record feedback on a search result.
202    pub async fn record_feedback(
203        &mut self,
204        entry_id: impl Into<String>,
205        positive: bool,
206    ) -> Result<()> {
207        let request = FeedbackRequest {
208            entry_id: entry_id.into(),
209            positive,
210            query_context: None,
211        };
212
213        let response = self
214            .client
215            .record_feedback(request)
216            .await
217            .map_err(|e| Error::storage(e.to_string()))?
218            .into_inner();
219
220        if response.success {
221            Ok(())
222        } else {
223            Err(Error::storage("Failed to record feedback"))
224        }
225    }
226
227    /// Get related entries.
228    pub async fn get_related(
229        &mut self,
230        id: impl Into<String>,
231        limit: u32,
232    ) -> Result<Vec<KnowledgeEntry>> {
233        let request = GetRelatedRequest {
234            id: id.into(),
235            limit,
236        };
237
238        let response = self
239            .client
240            .get_related(request)
241            .await
242            .map_err(|e| Error::storage(e.to_string()))?
243            .into_inner();
244
245        Ok(response.entries)
246    }
247
248    /// Link two entries as related.
249    pub async fn link_entries(
250        &mut self,
251        id1: impl Into<String>,
252        id2: impl Into<String>,
253    ) -> Result<()> {
254        let request = LinkEntriesRequest {
255            id1: id1.into(),
256            id2: id2.into(),
257        };
258
259        let response = self
260            .client
261            .link_entries(request)
262            .await
263            .map_err(|e| Error::storage(e.to_string()))?
264            .into_inner();
265
266        if response.success {
267            Ok(())
268        } else {
269            Err(Error::storage(
270                response
271                    .error
272                    .unwrap_or_else(|| "Unknown error".to_string()),
273            ))
274        }
275    }
276
277    /// Get knowledge base statistics.
278    pub async fn get_stats(&mut self) -> Result<GetStatsResponse> {
279        let response = self
280            .client
281            .get_stats(GetStatsRequest {})
282            .await
283            .map_err(|e| Error::storage(e.to_string()))?
284            .into_inner();
285
286        Ok(response)
287    }
288
289    /// Health check.
290    pub async fn health(&mut self) -> Result<HealthResponse> {
291        let response = self
292            .client
293            .health(HealthRequest {})
294            .await
295            .map_err(|e| Error::storage(e.to_string()))?
296            .into_inner();
297
298        Ok(response)
299    }
300}