1use velesdb_core::DistanceMetric as CoreDistanceMetric;
6use velesdb_core::FusionStrategy as CoreFusionStrategy;
7
8#[derive(Debug, thiserror::Error, uniffi::Error)]
14pub enum VelesError {
15 #[error("[{code}] Database error: {message}")]
22 Database {
23 message: String,
24 code: String,
25 recoverable: bool,
26 },
27
28 #[error("Collection error: {message}")]
30 Collection { message: String },
31
32 #[error("Dimension mismatch: expected {expected}, got {actual}")]
34 DimensionMismatch { expected: u32, actual: u32 },
35}
36
37impl VelesError {
38 #[must_use]
44 pub fn database(message: String) -> Self {
45 VelesError::Database {
46 message,
47 code: String::new(),
48 recoverable: true,
49 }
50 }
51}
52
53impl From<velesdb_core::Error> for VelesError {
54 fn from(err: velesdb_core::Error) -> Self {
55 let code = err.code().to_string();
56 let recoverable = err.is_recoverable();
57 match err {
58 velesdb_core::Error::DimensionMismatch { expected, actual } =>
59 {
60 #[allow(clippy::cast_possible_truncation)]
61 VelesError::DimensionMismatch {
62 expected: expected as u32,
63 actual: actual as u32,
64 }
65 }
66 velesdb_core::Error::CollectionNotFound(name) => VelesError::Collection {
67 message: format!("Collection not found: {name}"),
68 },
69 velesdb_core::Error::CollectionExists(name) => VelesError::Collection {
70 message: format!("Collection already exists: {name}"),
71 },
72 other => VelesError::Database {
73 message: other.to_string(),
74 code,
75 recoverable,
76 },
77 }
78 }
79}
80
81#[derive(Debug, Clone, Copy, uniffi::Enum)]
87pub enum DistanceMetric {
88 Cosine,
90 Euclidean,
92 DotProduct,
94 Hamming,
96 Jaccard,
98}
99
100impl From<DistanceMetric> for CoreDistanceMetric {
101 fn from(metric: DistanceMetric) -> Self {
102 match metric {
103 DistanceMetric::Cosine => CoreDistanceMetric::Cosine,
104 DistanceMetric::Euclidean => CoreDistanceMetric::Euclidean,
105 DistanceMetric::DotProduct => CoreDistanceMetric::DotProduct,
106 DistanceMetric::Hamming => CoreDistanceMetric::Hamming,
107 DistanceMetric::Jaccard => CoreDistanceMetric::Jaccard,
108 }
109 }
110}
111
112#[derive(Debug, Clone, Copy, uniffi::Enum)]
114pub enum StorageMode {
115 Full,
117 Sq8,
119 Binary,
121 ProductQuantization,
123 Rabitq,
125}
126
127impl From<StorageMode> for velesdb_core::StorageMode {
128 fn from(mode: StorageMode) -> Self {
129 match mode {
130 StorageMode::Full => velesdb_core::StorageMode::Full,
131 StorageMode::Sq8 => velesdb_core::StorageMode::SQ8,
132 StorageMode::Binary => velesdb_core::StorageMode::Binary,
133 StorageMode::ProductQuantization => velesdb_core::StorageMode::ProductQuantization,
134 StorageMode::Rabitq => velesdb_core::StorageMode::RaBitQ,
135 }
136 }
137}
138
139#[derive(Debug, Clone, Default, uniffi::Enum)]
145pub enum SearchQuality {
146 Fast,
148 #[default]
150 Balanced,
151 Accurate,
153 Perfect,
155 Custom {
157 ef: u32,
159 },
160 Adaptive {
162 min_ef: u32,
164 max_ef: u32,
166 },
167 AutoTune,
169}
170
171impl From<SearchQuality> for velesdb_core::SearchQuality {
172 fn from(quality: SearchQuality) -> Self {
173 match quality {
174 SearchQuality::Fast => velesdb_core::SearchQuality::Fast,
175 SearchQuality::Balanced => velesdb_core::SearchQuality::Balanced,
176 SearchQuality::Accurate => velesdb_core::SearchQuality::Accurate,
177 SearchQuality::Perfect => velesdb_core::SearchQuality::Perfect,
178 SearchQuality::Custom { ef } => {
179 velesdb_core::SearchQuality::Custom(usize::try_from(ef).unwrap_or(usize::MAX))
180 }
181 SearchQuality::Adaptive { min_ef, max_ef } => velesdb_core::SearchQuality::Adaptive {
182 min_ef: usize::try_from(min_ef).unwrap_or(usize::MAX),
183 max_ef: usize::try_from(max_ef).unwrap_or(usize::MAX),
184 },
185 SearchQuality::AutoTune => velesdb_core::SearchQuality::AutoTune,
186 }
187 }
188}
189
190#[derive(Debug, Clone, uniffi::Enum)]
192pub enum FusionStrategy {
193 Average,
195 Maximum,
197 Rrf {
199 k: u32,
201 },
202 Weighted {
204 avg_weight: f32,
206 max_weight: f32,
208 hit_weight: f32,
210 },
211 RelativeScore {
213 dense_weight: f32,
215 sparse_weight: f32,
217 },
218}
219
220impl From<FusionStrategy> for CoreFusionStrategy {
221 fn from(strategy: FusionStrategy) -> Self {
222 match strategy {
223 FusionStrategy::Average => CoreFusionStrategy::Average,
224 FusionStrategy::Maximum => CoreFusionStrategy::Maximum,
225 FusionStrategy::Rrf { k } => CoreFusionStrategy::RRF { k },
226 FusionStrategy::Weighted {
227 avg_weight,
228 max_weight,
229 hit_weight,
230 } => CoreFusionStrategy::Weighted {
231 avg_weight,
232 max_weight,
233 hit_weight,
234 },
235 FusionStrategy::RelativeScore {
236 dense_weight,
237 sparse_weight,
238 } => CoreFusionStrategy::RelativeScore {
239 dense_weight,
240 sparse_weight,
241 },
242 }
243 }
244}
245
246impl Default for FusionStrategy {
247 fn default() -> Self {
248 Self::Rrf { k: 60 }
249 }
250}
251
252#[derive(Debug, Clone, uniffi::Record)]
261pub struct VelesSparseVector {
262 pub indices: Vec<u32>,
264 pub values: Vec<f32>,
266}
267
268#[derive(Debug, Clone, uniffi::Record)]
270pub struct PqTrainConfig {
271 pub m: u32,
273 pub k: u32,
275 pub opq: bool,
277}
278
279#[derive(Debug, Clone, uniffi::Record)]
281pub struct SearchResult {
282 pub id: u64,
284 pub score: f32,
286 pub payload: Option<String>,
288}
289
290#[derive(Debug, Clone, uniffi::Record)]
292pub struct VelesPoint {
293 pub id: u64,
295 pub vector: Vec<f32>,
297 pub payload: Option<String>,
299}
300
301#[derive(Debug, Clone, uniffi::Record)]
303pub struct IndividualSearchRequest {
304 pub vector: Vec<f32>,
306 pub top_k: u32,
308 pub filter: Option<String>,
310}
311
312#[derive(Debug, Clone, uniffi::Record)]
314pub struct MobileCollectionStats {
315 pub total_points: u64,
317 pub payload_size_bytes: u64,
319 pub row_count: u64,
321 pub deleted_count: u64,
323 pub avg_row_size_bytes: u64,
325 pub total_size_bytes: u64,
327 pub field_stats_count: u32,
329 pub column_stats_count: u32,
331 pub index_stats_count: u32,
333}
334
335impl From<velesdb_core::collection::stats::CollectionStats> for MobileCollectionStats {
336 fn from(stats: velesdb_core::collection::stats::CollectionStats) -> Self {
337 Self {
338 total_points: stats.total_points,
339 payload_size_bytes: stats.payload_size_bytes,
340 row_count: stats.row_count,
341 deleted_count: stats.deleted_count,
342 avg_row_size_bytes: stats.avg_row_size_bytes,
343 total_size_bytes: stats.total_size_bytes,
344 field_stats_count: u32::try_from(stats.field_stats.len()).unwrap_or(u32::MAX),
345 column_stats_count: u32::try_from(stats.column_stats.len()).unwrap_or(u32::MAX),
346 index_stats_count: u32::try_from(stats.index_stats.len()).unwrap_or(u32::MAX),
347 }
348 }
349}
350
351#[derive(Debug, Clone, uniffi::Record)]
358pub struct MobileCollectionDiagnostics {
359 pub has_vectors: bool,
361 pub search_ready: bool,
363 pub dimension_configured: bool,
365 pub point_count: u64,
367 pub index_health: String,
369 pub index_health_detail: Option<String>,
371}
372
373impl From<velesdb_core::collection::CollectionDiagnostics> for MobileCollectionDiagnostics {
374 fn from(diag: velesdb_core::collection::CollectionDiagnostics) -> Self {
375 use velesdb_core::collection::IndexHealth;
376 let (index_health, index_health_detail) = match diag.index_health {
377 IndexHealth::Healthy => ("healthy".to_string(), None),
378 IndexHealth::Empty => ("empty".to_string(), None),
379 IndexHealth::NeedsRebuild(reason) => ("needs_rebuild".to_string(), Some(reason)),
380 _ => ("unknown".to_string(), None),
381 };
382 Self {
383 has_vectors: diag.has_vectors,
384 search_ready: diag.search_ready,
385 dimension_configured: diag.dimension_configured,
386 point_count: u64::try_from(diag.point_count).unwrap_or(u64::MAX),
387 index_health,
388 index_health_detail,
389 }
390 }
391}
392
393#[derive(Debug, Clone, uniffi::Record)]
395pub struct MobileIndexInfo {
396 pub label: String,
398 pub property: String,
400 pub index_type: String,
402 pub cardinality: u64,
404 pub memory_bytes: u64,
406}
407
408impl From<velesdb_core::IndexInfo> for MobileIndexInfo {
409 fn from(value: velesdb_core::IndexInfo) -> Self {
410 Self {
411 label: value.label,
412 property: value.property,
413 index_type: value.index_type,
414 cardinality: u64::try_from(value.cardinality).unwrap_or(u64::MAX),
415 memory_bytes: u64::try_from(value.memory_bytes).unwrap_or(u64::MAX),
416 }
417 }
418}
419
420#[derive(Debug, Clone, uniffi::Record)]
422pub struct MobileQueryLimits {
423 pub max_depth: u32,
425 pub max_cardinality: u64,
427 pub memory_limit_bytes: u64,
429 pub timeout_ms: u64,
431 pub rate_limit_qps: u32,
433 pub circuit_failure_threshold: u32,
435 pub circuit_recovery_seconds: u64,
437}
438
439impl From<velesdb_core::guardrails::QueryLimits> for MobileQueryLimits {
440 fn from(v: velesdb_core::guardrails::QueryLimits) -> Self {
441 Self {
442 max_depth: v.max_depth,
443 max_cardinality: u64::try_from(v.max_cardinality).unwrap_or(u64::MAX),
444 memory_limit_bytes: u64::try_from(v.memory_limit_bytes).unwrap_or(u64::MAX),
445 timeout_ms: v.timeout_ms,
446 rate_limit_qps: v.rate_limit_qps,
447 circuit_failure_threshold: v.circuit_failure_threshold,
448 circuit_recovery_seconds: v.circuit_recovery_seconds,
449 }
450 }
451}
452
453impl From<MobileQueryLimits> for velesdb_core::guardrails::QueryLimits {
454 fn from(v: MobileQueryLimits) -> Self {
455 Self {
456 max_depth: v.max_depth,
457 max_cardinality: usize::try_from(v.max_cardinality).unwrap_or(usize::MAX),
458 memory_limit_bytes: usize::try_from(v.memory_limit_bytes).unwrap_or(usize::MAX),
459 timeout_ms: v.timeout_ms,
460 rate_limit_qps: v.rate_limit_qps,
461 circuit_failure_threshold: v.circuit_failure_threshold,
462 circuit_recovery_seconds: v.circuit_recovery_seconds,
463 }
464 }
465}
466
467#[derive(Debug, Clone, uniffi::Record)]
470pub struct MobileDeferredIndexerConfig {
471 pub enabled: bool,
473 pub merge_threshold: u64,
475 pub max_buffer_age_ms: u64,
479}
480
481impl From<MobileDeferredIndexerConfig>
482 for velesdb_core::collection::streaming::DeferredIndexerConfig
483{
484 fn from(v: MobileDeferredIndexerConfig) -> Self {
485 Self {
486 enabled: v.enabled,
487 merge_threshold: usize::try_from(v.merge_threshold).unwrap_or(usize::MAX),
488 max_buffer_age_ms: v.max_buffer_age_ms,
489 }
490 }
491}
492
493#[derive(Debug, Clone, uniffi::Record)]
496pub struct MobileAsyncIndexBuilderConfig {
497 pub merge_threshold: u64,
499 pub segment_count: Option<u32>,
503}
504
505impl From<MobileAsyncIndexBuilderConfig>
506 for velesdb_core::collection::streaming::AsyncIndexBuilderConfig
507{
508 fn from(v: MobileAsyncIndexBuilderConfig) -> Self {
509 Self {
510 merge_threshold: usize::try_from(v.merge_threshold).unwrap_or(usize::MAX),
511 segment_count: v.segment_count.map(|s| s as usize),
512 }
513 }
514}
515
516#[derive(Debug, Clone, uniffi::Record)]
523pub struct MobileStreamingConfig {
524 pub buffer_size: u64,
526 pub batch_size: u64,
528 pub flush_interval_ms: u64,
530}
531
532#[derive(Debug, Clone, uniffi::Record)]
538pub struct MobileAdvancedConfig {
539 pub pq_rescore_oversampling: Option<u32>,
541 pub deferred_indexing: Option<MobileDeferredIndexerConfig>,
543 pub async_index_builder: Option<MobileAsyncIndexBuilderConfig>,
545}
546
547#[cfg(test)]
548#[path = "types_tests.rs"]
549mod error_tests;