1use crate::error::{VectorizerError, Result};
4use crate::models::*;
5use crate::transport::{Transport, Protocol};
6use crate::http_transport::HttpTransport;
7
8#[cfg(feature = "umicp")]
9use crate::umicp_transport::UmicpTransport;
10
11use serde_json;
12use std::sync::Arc;
13
14pub struct ClientConfig {
16 pub base_url: Option<String>,
18 pub connection_string: Option<String>,
20 pub protocol: Option<Protocol>,
22 pub api_key: Option<String>,
24 pub timeout_secs: Option<u64>,
26 #[cfg(feature = "umicp")]
28 pub umicp: Option<UmicpConfig>,
29}
30
31#[cfg(feature = "umicp")]
32pub struct UmicpConfig {
34 pub host: String,
35 pub port: u16,
36}
37
38impl Default for ClientConfig {
39 fn default() -> Self {
40 Self {
41 base_url: Some("http://localhost:15002".to_string()),
42 connection_string: None,
43 protocol: None,
44 api_key: None,
45 timeout_secs: Some(30),
46 #[cfg(feature = "umicp")]
47 umicp: None,
48 }
49 }
50}
51
52pub struct VectorizerClient {
54 transport: Arc<dyn Transport>,
55 protocol: Protocol,
56 base_url: String,
57}
58
59impl VectorizerClient {
60 pub fn base_url(&self) -> &str {
62 &self.base_url
63 }
64
65 pub fn new(config: ClientConfig) -> Result<Self> {
67 let timeout_secs = config.timeout_secs.unwrap_or(30);
68
69 let (transport, protocol, base_url): (Arc<dyn Transport>, Protocol, String) = if let Some(conn_str) = config.connection_string {
71 let (proto, host, port) = crate::transport::parse_connection_string(&conn_str)?;
73
74 match proto {
75 Protocol::Http => {
76 let transport = HttpTransport::new(&host, config.api_key.as_deref(), timeout_secs)?;
77 (Arc::new(transport), Protocol::Http, host.clone())
78 },
79 #[cfg(feature = "umicp")]
80 Protocol::Umicp => {
81 let port = port.unwrap_or(15003);
82 let transport = UmicpTransport::new(&host, port, config.api_key.as_deref(), timeout_secs)?;
83 let base_url = format!("umicp://{}:{}", host, port);
84 (Arc::new(transport), Protocol::Umicp, base_url)
85 },
86 }
87 } else {
88 let proto = config.protocol.unwrap_or(Protocol::Http);
90
91 match proto {
92 Protocol::Http => {
93 let base_url = config.base_url.unwrap_or_else(|| "http://localhost:15002".to_string());
94 let transport = HttpTransport::new(&base_url, config.api_key.as_deref(), timeout_secs)?;
95 (Arc::new(transport), Protocol::Http, base_url.clone())
96 },
97 #[cfg(feature = "umicp")]
98 Protocol::Umicp => {
99 #[cfg(feature = "umicp")]
100 {
101 let umicp_config = config.umicp.ok_or_else(|| {
102 VectorizerError::configuration("UMICP configuration is required when using UMICP protocol")
103 })?;
104
105 let transport = UmicpTransport::new(
106 &umicp_config.host,
107 umicp_config.port,
108 config.api_key.as_deref(),
109 timeout_secs,
110 )?;
111 let base_url = format!("umicp://{}:{}", umicp_config.host, umicp_config.port);
112 (Arc::new(transport), Protocol::Umicp, base_url)
113 }
114 #[cfg(not(feature = "umicp"))]
115 {
116 return Err(VectorizerError::configuration(
117 "UMICP feature is not enabled. Enable it with --features umicp"
118 ));
119 }
120 },
121 }
122 };
123
124 Ok(Self { transport, protocol, base_url })
125 }
126
127 pub fn new_default() -> Result<Self> {
129 Self::new(ClientConfig::default())
130 }
131
132 pub fn new_with_url(base_url: &str) -> Result<Self> {
134 Self::new(ClientConfig {
135 base_url: Some(base_url.to_string()),
136 ..Default::default()
137 })
138 }
139
140 pub fn new_with_api_key(base_url: &str, api_key: &str) -> Result<Self> {
142 Self::new(ClientConfig {
143 base_url: Some(base_url.to_string()),
144 api_key: Some(api_key.to_string()),
145 ..Default::default()
146 })
147 }
148
149 pub fn from_connection_string(connection_string: &str, api_key: Option<&str>) -> Result<Self> {
151 Self::new(ClientConfig {
152 connection_string: Some(connection_string.to_string()),
153 api_key: api_key.map(|s| s.to_string()),
154 ..Default::default()
155 })
156 }
157
158 pub fn protocol(&self) -> Protocol {
160 self.protocol
161 }
162
163 pub async fn health_check(&self) -> Result<HealthStatus> {
165 let response = self.make_request("GET", "/health", None).await?;
166 let health: HealthStatus = serde_json::from_str(&response)
167 .map_err(|e| VectorizerError::server(format!("Failed to parse health check response: {}", e)))?;
168 Ok(health)
169 }
170
171 pub async fn list_collections(&self) -> Result<Vec<CollectionInfo>> {
173 let response = self.make_request("GET", "/collections", None).await?;
174 let collections_response: CollectionsResponse = serde_json::from_str(&response)
175 .map_err(|e| VectorizerError::server(format!("Failed to parse collections response: {}", e)))?;
176 Ok(collections_response.collections)
177 }
178
179 pub async fn search_vectors(
181 &self,
182 collection: &str,
183 query: &str,
184 limit: Option<usize>,
185 score_threshold: Option<f32>,
186 ) -> Result<SearchResponse> {
187 let mut payload = serde_json::Map::new();
188 payload.insert("query".to_string(), serde_json::Value::String(query.to_string()));
189 payload.insert("limit".to_string(), serde_json::Value::Number(limit.unwrap_or(10).into()));
190
191 if let Some(threshold) = score_threshold {
192 payload.insert("score_threshold".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(threshold as f64).unwrap()));
193 }
194
195 let response = self.make_request("POST", &format!("/collections/{}/search/text", collection), Some(serde_json::Value::Object(payload))).await?;
196 let search_response: SearchResponse = serde_json::from_str(&response)
197 .map_err(|e| VectorizerError::server(format!("Failed to parse search response: {}", e)))?;
198 Ok(search_response)
199 }
200
201 pub async fn intelligent_search(&self, request: IntelligentSearchRequest) -> Result<IntelligentSearchResponse> {
205 let response = self.make_request("POST", "/intelligent_search", Some(serde_json::to_value(request).unwrap())).await?;
206 let search_response: IntelligentSearchResponse = serde_json::from_str(&response)
207 .map_err(|e| VectorizerError::server(format!("Failed to parse intelligent search response: {}", e)))?;
208 Ok(search_response)
209 }
210
211 pub async fn semantic_search(&self, request: SemanticSearchRequest) -> Result<SemanticSearchResponse> {
213 let response = self.make_request("POST", "/semantic_search", Some(serde_json::to_value(request).unwrap())).await?;
214 let search_response: SemanticSearchResponse = serde_json::from_str(&response)
215 .map_err(|e| VectorizerError::server(format!("Failed to parse semantic search response: {}", e)))?;
216 Ok(search_response)
217 }
218
219 pub async fn contextual_search(&self, request: ContextualSearchRequest) -> Result<ContextualSearchResponse> {
221 let response = self.make_request("POST", "/contextual_search", Some(serde_json::to_value(request).unwrap())).await?;
222 let search_response: ContextualSearchResponse = serde_json::from_str(&response)
223 .map_err(|e| VectorizerError::server(format!("Failed to parse contextual search response: {}", e)))?;
224 Ok(search_response)
225 }
226
227 pub async fn multi_collection_search(&self, request: MultiCollectionSearchRequest) -> Result<MultiCollectionSearchResponse> {
229 let response = self.make_request("POST", "/multi_collection_search", Some(serde_json::to_value(request).unwrap())).await?;
230 let search_response: MultiCollectionSearchResponse = serde_json::from_str(&response)
231 .map_err(|e| VectorizerError::server(format!("Failed to parse multi-collection search response: {}", e)))?;
232 Ok(search_response)
233 }
234
235 pub async fn create_collection(
237 &self,
238 name: &str,
239 dimension: usize,
240 metric: Option<SimilarityMetric>,
241 ) -> Result<CollectionInfo> {
242 let mut payload = serde_json::Map::new();
243 payload.insert("name".to_string(), serde_json::Value::String(name.to_string()));
244 payload.insert("dimension".to_string(), serde_json::Value::Number(dimension.into()));
245 payload.insert("metric".to_string(), serde_json::Value::String(format!("{:?}", metric.unwrap_or_default()).to_lowercase()));
246
247 let response = self.make_request("POST", "/collections", Some(serde_json::Value::Object(payload))).await?;
248 let create_response: CreateCollectionResponse = serde_json::from_str(&response)
249 .map_err(|e| VectorizerError::server(format!("Failed to parse create collection response: {}", e)))?;
250
251 let info = CollectionInfo {
253 name: create_response.collection,
254 dimension: dimension,
255 metric: format!("{:?}", metric.unwrap_or_default()).to_lowercase(),
256 vector_count: 0,
257 document_count: 0,
258 created_at: "".to_string(),
259 updated_at: "".to_string(),
260 indexing_status: crate::models::IndexingStatus {
261 status: "created".to_string(),
262 progress: 0.0,
263 total_documents: 0,
264 processed_documents: 0,
265 vector_count: 0,
266 estimated_time_remaining: None,
267 last_updated: "".to_string(),
268 },
269 };
270 Ok(info)
271 }
272
273 pub async fn insert_texts(
275 &self,
276 collection: &str,
277 texts: Vec<BatchTextRequest>,
278 ) -> Result<BatchResponse> {
279 let payload = serde_json::json!({
280 "texts": texts
281 });
282
283 let response = self.make_request("POST", &format!("/collections/{}/documents", collection), Some(serde_json::to_value(payload)?)).await?;
284 let batch_response: BatchResponse = serde_json::from_str(&response)
285 .map_err(|e| VectorizerError::server(format!("Failed to parse insert texts response: {}", e)))?;
286 Ok(batch_response)
287 }
288
289 pub async fn delete_collection(&self, name: &str) -> Result<()> {
291 self.make_request("DELETE", &format!("/collections/{}", name), None).await?;
292 Ok(())
293 }
294
295 pub async fn get_vector(&self, collection: &str, vector_id: &str) -> Result<Vector> {
297 let response = self.make_request("GET", &format!("/collections/{}/vectors/{}", collection, vector_id), None).await?;
298 let vector: Vector = serde_json::from_str(&response)
299 .map_err(|e| VectorizerError::server(format!("Failed to parse get vector response: {}", e)))?;
300 Ok(vector)
301 }
302
303 pub async fn get_collection_info(&self, collection: &str) -> Result<CollectionInfo> {
305 let response = self.make_request("GET", &format!("/collections/{}", collection), None).await?;
306 let info: CollectionInfo = serde_json::from_str(&response)
307 .map_err(|e| VectorizerError::server(format!("Failed to parse collection info: {}", e)))?;
308 Ok(info)
309 }
310
311 pub async fn embed_text(&self, text: &str, model: Option<&str>) -> Result<EmbeddingResponse> {
313 let mut payload = serde_json::Map::new();
314 payload.insert("text".to_string(), serde_json::Value::String(text.to_string()));
315
316 if let Some(model) = model {
317 payload.insert("model".to_string(), serde_json::Value::String(model.to_string()));
318 }
319
320 let response = self.make_request("POST", "/embed", Some(serde_json::Value::Object(payload))).await?;
321 let embedding_response: EmbeddingResponse = serde_json::from_str(&response)
322 .map_err(|e| VectorizerError::server(format!("Failed to parse embedding response: {}", e)))?;
323 Ok(embedding_response)
324 }
325
326 pub async fn discover(
332 &self,
333 query: &str,
334 include_collections: Option<Vec<String>>,
335 exclude_collections: Option<Vec<String>>,
336 max_bullets: Option<usize>,
337 broad_k: Option<usize>,
338 focus_k: Option<usize>,
339 ) -> Result<serde_json::Value> {
340 if query.trim().is_empty() {
342 return Err(VectorizerError::validation("Query cannot be empty"));
343 }
344
345 if let Some(max) = max_bullets {
347 if max == 0 {
348 return Err(VectorizerError::validation("max_bullets must be greater than 0"));
349 }
350 }
351
352 let mut payload = serde_json::Map::new();
353 payload.insert("query".to_string(), serde_json::Value::String(query.to_string()));
354
355 if let Some(inc) = include_collections {
356 payload.insert("include_collections".to_string(), serde_json::to_value(inc).unwrap());
357 }
358 if let Some(exc) = exclude_collections {
359 payload.insert("exclude_collections".to_string(), serde_json::to_value(exc).unwrap());
360 }
361 if let Some(max) = max_bullets {
362 payload.insert("max_bullets".to_string(), serde_json::Value::Number(max.into()));
363 }
364 if let Some(k) = broad_k {
365 payload.insert("broad_k".to_string(), serde_json::Value::Number(k.into()));
366 }
367 if let Some(k) = focus_k {
368 payload.insert("focus_k".to_string(), serde_json::Value::Number(k.into()));
369 }
370
371 let response = self.make_request("POST", "/discover", Some(serde_json::Value::Object(payload))).await?;
372 let result: serde_json::Value = serde_json::from_str(&response)
373 .map_err(|e| VectorizerError::server(format!("Failed to parse discover response: {}", e)))?;
374 Ok(result)
375 }
376
377 pub async fn filter_collections(
379 &self,
380 query: &str,
381 include: Option<Vec<String>>,
382 exclude: Option<Vec<String>>,
383 ) -> Result<serde_json::Value> {
384 if query.trim().is_empty() {
386 return Err(VectorizerError::validation("Query cannot be empty"));
387 }
388
389 let mut payload = serde_json::Map::new();
390 payload.insert("query".to_string(), serde_json::Value::String(query.to_string()));
391
392 if let Some(inc) = include {
393 payload.insert("include".to_string(), serde_json::to_value(inc).unwrap());
394 }
395 if let Some(exc) = exclude {
396 payload.insert("exclude".to_string(), serde_json::to_value(exc).unwrap());
397 }
398
399 let response = self.make_request("POST", "/discovery/filter_collections", Some(serde_json::Value::Object(payload))).await?;
400 let result: serde_json::Value = serde_json::from_str(&response)
401 .map_err(|e| VectorizerError::server(format!("Failed to parse filter response: {}", e)))?;
402 Ok(result)
403 }
404
405 pub async fn score_collections(
407 &self,
408 query: &str,
409 name_match_weight: Option<f32>,
410 term_boost_weight: Option<f32>,
411 signal_boost_weight: Option<f32>,
412 ) -> Result<serde_json::Value> {
413 if let Some(w) = name_match_weight {
415 if w < 0.0 || w > 1.0 {
416 return Err(VectorizerError::validation("name_match_weight must be between 0.0 and 1.0"));
417 }
418 }
419 if let Some(w) = term_boost_weight {
420 if w < 0.0 || w > 1.0 {
421 return Err(VectorizerError::validation("term_boost_weight must be between 0.0 and 1.0"));
422 }
423 }
424 if let Some(w) = signal_boost_weight {
425 if w < 0.0 || w > 1.0 {
426 return Err(VectorizerError::validation("signal_boost_weight must be between 0.0 and 1.0"));
427 }
428 }
429
430 let mut payload = serde_json::Map::new();
431 payload.insert("query".to_string(), serde_json::Value::String(query.to_string()));
432
433 if let Some(w) = name_match_weight {
434 payload.insert("name_match_weight".to_string(), serde_json::json!(w));
435 }
436 if let Some(w) = term_boost_weight {
437 payload.insert("term_boost_weight".to_string(), serde_json::json!(w));
438 }
439 if let Some(w) = signal_boost_weight {
440 payload.insert("signal_boost_weight".to_string(), serde_json::json!(w));
441 }
442
443 let response = self.make_request("POST", "/discovery/score_collections", Some(serde_json::Value::Object(payload))).await?;
444 let result: serde_json::Value = serde_json::from_str(&response)
445 .map_err(|e| VectorizerError::server(format!("Failed to parse score response: {}", e)))?;
446 Ok(result)
447 }
448
449 pub async fn expand_queries(
451 &self,
452 query: &str,
453 max_expansions: Option<usize>,
454 include_definition: Option<bool>,
455 include_features: Option<bool>,
456 include_architecture: Option<bool>,
457 ) -> Result<serde_json::Value> {
458 let mut payload = serde_json::Map::new();
459 payload.insert("query".to_string(), serde_json::Value::String(query.to_string()));
460
461 if let Some(max) = max_expansions {
462 payload.insert("max_expansions".to_string(), serde_json::Value::Number(max.into()));
463 }
464 if let Some(def) = include_definition {
465 payload.insert("include_definition".to_string(), serde_json::Value::Bool(def));
466 }
467 if let Some(feat) = include_features {
468 payload.insert("include_features".to_string(), serde_json::Value::Bool(feat));
469 }
470 if let Some(arch) = include_architecture {
471 payload.insert("include_architecture".to_string(), serde_json::Value::Bool(arch));
472 }
473
474 let response = self.make_request("POST", "/discovery/expand_queries", Some(serde_json::Value::Object(payload))).await?;
475 let result: serde_json::Value = serde_json::from_str(&response)
476 .map_err(|e| VectorizerError::server(format!("Failed to parse expand response: {}", e)))?;
477 Ok(result)
478 }
479
480 pub async fn get_file_content(
486 &self,
487 collection: &str,
488 file_path: &str,
489 max_size_kb: Option<usize>,
490 ) -> Result<serde_json::Value> {
491 let mut payload = serde_json::Map::new();
492 payload.insert("collection".to_string(), serde_json::Value::String(collection.to_string()));
493 payload.insert("file_path".to_string(), serde_json::Value::String(file_path.to_string()));
494
495 if let Some(max) = max_size_kb {
496 payload.insert("max_size_kb".to_string(), serde_json::Value::Number(max.into()));
497 }
498
499 let response = self.make_request("POST", "/file/content", Some(serde_json::Value::Object(payload))).await?;
500 let result: serde_json::Value = serde_json::from_str(&response)
501 .map_err(|e| VectorizerError::server(format!("Failed to parse file content response: {}", e)))?;
502 Ok(result)
503 }
504
505 pub async fn list_files_in_collection(
507 &self,
508 collection: &str,
509 filter_by_type: Option<Vec<String>>,
510 min_chunks: Option<usize>,
511 max_results: Option<usize>,
512 sort_by: Option<&str>,
513 ) -> Result<serde_json::Value> {
514 let mut payload = serde_json::Map::new();
515 payload.insert("collection".to_string(), serde_json::Value::String(collection.to_string()));
516
517 if let Some(types) = filter_by_type {
518 payload.insert("filter_by_type".to_string(), serde_json::to_value(types).unwrap());
519 }
520 if let Some(min) = min_chunks {
521 payload.insert("min_chunks".to_string(), serde_json::Value::Number(min.into()));
522 }
523 if let Some(max) = max_results {
524 payload.insert("max_results".to_string(), serde_json::Value::Number(max.into()));
525 }
526 if let Some(sort) = sort_by {
527 payload.insert("sort_by".to_string(), serde_json::Value::String(sort.to_string()));
528 }
529
530 let response = self.make_request("POST", "/file/list", Some(serde_json::Value::Object(payload))).await?;
531 let result: serde_json::Value = serde_json::from_str(&response)
532 .map_err(|e| VectorizerError::server(format!("Failed to parse list files response: {}", e)))?;
533 Ok(result)
534 }
535
536 pub async fn get_file_summary(
538 &self,
539 collection: &str,
540 file_path: &str,
541 summary_type: Option<&str>,
542 max_sentences: Option<usize>,
543 ) -> Result<serde_json::Value> {
544 let mut payload = serde_json::Map::new();
545 payload.insert("collection".to_string(), serde_json::Value::String(collection.to_string()));
546 payload.insert("file_path".to_string(), serde_json::Value::String(file_path.to_string()));
547
548 if let Some(stype) = summary_type {
549 payload.insert("summary_type".to_string(), serde_json::Value::String(stype.to_string()));
550 }
551 if let Some(max) = max_sentences {
552 payload.insert("max_sentences".to_string(), serde_json::Value::Number(max.into()));
553 }
554
555 let response = self.make_request("POST", "/file/summary", Some(serde_json::Value::Object(payload))).await?;
556 let result: serde_json::Value = serde_json::from_str(&response)
557 .map_err(|e| VectorizerError::server(format!("Failed to parse file summary response: {}", e)))?;
558 Ok(result)
559 }
560
561 pub async fn get_file_chunks_ordered(
563 &self,
564 collection: &str,
565 file_path: &str,
566 start_chunk: Option<usize>,
567 limit: Option<usize>,
568 include_context: Option<bool>,
569 ) -> Result<serde_json::Value> {
570 let mut payload = serde_json::Map::new();
571 payload.insert("collection".to_string(), serde_json::Value::String(collection.to_string()));
572 payload.insert("file_path".to_string(), serde_json::Value::String(file_path.to_string()));
573
574 if let Some(start) = start_chunk {
575 payload.insert("start_chunk".to_string(), serde_json::Value::Number(start.into()));
576 }
577 if let Some(lim) = limit {
578 payload.insert("limit".to_string(), serde_json::Value::Number(lim.into()));
579 }
580 if let Some(ctx) = include_context {
581 payload.insert("include_context".to_string(), serde_json::Value::Bool(ctx));
582 }
583
584 let response = self.make_request("POST", "/file/chunks", Some(serde_json::Value::Object(payload))).await?;
585 let result: serde_json::Value = serde_json::from_str(&response)
586 .map_err(|e| VectorizerError::server(format!("Failed to parse chunks response: {}", e)))?;
587 Ok(result)
588 }
589
590 pub async fn get_project_outline(
592 &self,
593 collection: &str,
594 max_depth: Option<usize>,
595 include_summaries: Option<bool>,
596 highlight_key_files: Option<bool>,
597 ) -> Result<serde_json::Value> {
598 let mut payload = serde_json::Map::new();
599 payload.insert("collection".to_string(), serde_json::Value::String(collection.to_string()));
600
601 if let Some(depth) = max_depth {
602 payload.insert("max_depth".to_string(), serde_json::Value::Number(depth.into()));
603 }
604 if let Some(summ) = include_summaries {
605 payload.insert("include_summaries".to_string(), serde_json::Value::Bool(summ));
606 }
607 if let Some(highlight) = highlight_key_files {
608 payload.insert("highlight_key_files".to_string(), serde_json::Value::Bool(highlight));
609 }
610
611 let response = self.make_request("POST", "/file/outline", Some(serde_json::Value::Object(payload))).await?;
612 let result: serde_json::Value = serde_json::from_str(&response)
613 .map_err(|e| VectorizerError::server(format!("Failed to parse outline response: {}", e)))?;
614 Ok(result)
615 }
616
617 pub async fn get_related_files(
619 &self,
620 collection: &str,
621 file_path: &str,
622 limit: Option<usize>,
623 similarity_threshold: Option<f32>,
624 include_reason: Option<bool>,
625 ) -> Result<serde_json::Value> {
626 let mut payload = serde_json::Map::new();
627 payload.insert("collection".to_string(), serde_json::Value::String(collection.to_string()));
628 payload.insert("file_path".to_string(), serde_json::Value::String(file_path.to_string()));
629
630 if let Some(lim) = limit {
631 payload.insert("limit".to_string(), serde_json::Value::Number(lim.into()));
632 }
633 if let Some(thresh) = similarity_threshold {
634 payload.insert("similarity_threshold".to_string(), serde_json::json!(thresh));
635 }
636 if let Some(reason) = include_reason {
637 payload.insert("include_reason".to_string(), serde_json::Value::Bool(reason));
638 }
639
640 let response = self.make_request("POST", "/file/related", Some(serde_json::Value::Object(payload))).await?;
641 let result: serde_json::Value = serde_json::from_str(&response)
642 .map_err(|e| VectorizerError::server(format!("Failed to parse related files response: {}", e)))?;
643 Ok(result)
644 }
645
646 pub async fn search_by_file_type(
648 &self,
649 collection: &str,
650 query: &str,
651 file_types: Vec<String>,
652 limit: Option<usize>,
653 return_full_files: Option<bool>,
654 ) -> Result<serde_json::Value> {
655 if file_types.is_empty() {
657 return Err(VectorizerError::validation("file_types cannot be empty"));
658 }
659
660 let mut payload = serde_json::Map::new();
661 payload.insert("collection".to_string(), serde_json::Value::String(collection.to_string()));
662 payload.insert("query".to_string(), serde_json::Value::String(query.to_string()));
663 payload.insert("file_types".to_string(), serde_json::to_value(file_types).unwrap());
664
665 if let Some(lim) = limit {
666 payload.insert("limit".to_string(), serde_json::Value::Number(lim.into()));
667 }
668 if let Some(full) = return_full_files {
669 payload.insert("return_full_files".to_string(), serde_json::Value::Bool(full));
670 }
671
672 let response = self.make_request("POST", "/file/search_by_type", Some(serde_json::Value::Object(payload))).await?;
673 let result: serde_json::Value = serde_json::from_str(&response)
674 .map_err(|e| VectorizerError::server(format!("Failed to parse search by type response: {}", e)))?;
675 Ok(result)
676 }
677
678 async fn make_request(
680 &self,
681 method: &str,
682 endpoint: &str,
683 payload: Option<serde_json::Value>,
684 ) -> Result<String> {
685 match method {
686 "GET" => self.transport.get(endpoint).await,
687 "POST" => self.transport.post(endpoint, payload.as_ref()).await,
688 "PUT" => self.transport.put(endpoint, payload.as_ref()).await,
689 "DELETE" => self.transport.delete(endpoint).await,
690 _ => Err(VectorizerError::configuration(format!("Unsupported method: {}", method))),
691 }
692 }
693}