rskit_vectorstore/
config.rs1use serde::{Deserialize, Serialize};
4
5use rskit_errors::{AppError, AppResult, ErrorCode};
6
7use crate::SimilarityMetric;
8
9pub const DEFAULT_MAX_SEARCH_LIMIT: usize = 1_000;
11pub const DEFAULT_MAX_VECTOR_DIMENSIONS: usize = 32_768;
13pub const DEFAULT_MAX_PAYLOAD_FIELDS: usize = 128;
15pub const DEFAULT_MAX_PAYLOAD_BYTES: usize = 64 * 1024;
17pub const DEFAULT_MAX_FILTER_CONDITIONS: usize = 32;
19
20#[derive(Debug, Clone, Deserialize, Serialize)]
22pub struct VectorStoreConfig {
23 #[serde(default = "default_backend")]
25 pub backend: String,
26 #[serde(default)]
28 pub memory: MemoryVectorStoreConfig,
29 #[serde(default)]
31 pub limits: VectorStoreLimits,
32}
33
34impl Default for VectorStoreConfig {
35 fn default() -> Self {
36 Self {
37 backend: default_backend(),
38 memory: MemoryVectorStoreConfig::default(),
39 limits: VectorStoreLimits::default(),
40 }
41 }
42}
43
44#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
46#[non_exhaustive]
47pub struct VectorStoreLimits {
48 #[serde(default = "default_max_search_limit")]
50 pub max_search_limit: usize,
51 #[serde(default = "default_max_vector_dimensions")]
53 pub max_vector_dimensions: usize,
54 #[serde(default = "default_max_payload_fields")]
56 pub max_payload_fields: usize,
57 #[serde(default = "default_max_payload_bytes")]
59 pub max_payload_bytes: usize,
60 #[serde(default = "default_max_filter_conditions")]
62 pub max_filter_conditions: usize,
63}
64
65impl VectorStoreLimits {
66 #[must_use]
68 pub const fn new() -> Self {
69 Self {
70 max_search_limit: DEFAULT_MAX_SEARCH_LIMIT,
71 max_vector_dimensions: DEFAULT_MAX_VECTOR_DIMENSIONS,
72 max_payload_fields: DEFAULT_MAX_PAYLOAD_FIELDS,
73 max_payload_bytes: DEFAULT_MAX_PAYLOAD_BYTES,
74 max_filter_conditions: DEFAULT_MAX_FILTER_CONDITIONS,
75 }
76 }
77
78 #[must_use]
80 pub const fn with_max_search_limit(mut self, max_search_limit: usize) -> Self {
81 self.max_search_limit = max_search_limit;
82 self
83 }
84
85 #[must_use]
87 pub const fn with_max_vector_dimensions(mut self, max_vector_dimensions: usize) -> Self {
88 self.max_vector_dimensions = max_vector_dimensions;
89 self
90 }
91
92 #[must_use]
94 pub const fn with_max_payload_fields(mut self, max_payload_fields: usize) -> Self {
95 self.max_payload_fields = max_payload_fields;
96 self
97 }
98
99 #[must_use]
101 pub const fn with_max_payload_bytes(mut self, max_payload_bytes: usize) -> Self {
102 self.max_payload_bytes = max_payload_bytes;
103 self
104 }
105
106 #[must_use]
108 pub const fn with_max_filter_conditions(mut self, max_filter_conditions: usize) -> Self {
109 self.max_filter_conditions = max_filter_conditions;
110 self
111 }
112
113 pub fn validate_dimensions(&self, dimensions: usize) -> AppResult<()> {
115 if self.max_vector_dimensions == 0 {
116 return Err(AppError::new(
117 ErrorCode::InvalidInput,
118 "max_vector_dimensions must be at least 1",
119 ));
120 }
121 if dimensions == 0 || dimensions > self.max_vector_dimensions {
122 return Err(AppError::new(
123 ErrorCode::InvalidInput,
124 format!(
125 "vector dimensions must be between 1 and {}",
126 self.max_vector_dimensions
127 ),
128 ));
129 }
130 Ok(())
131 }
132
133 pub fn validate_search_limit(&self, limit: usize) -> AppResult<()> {
135 if self.max_search_limit == 0 {
136 return Err(AppError::new(
137 ErrorCode::InvalidInput,
138 "max_search_limit must be at least 1",
139 ));
140 }
141 if limit == 0 || limit > self.max_search_limit {
142 return Err(AppError::new(
143 ErrorCode::InvalidInput,
144 format!(
145 "vector search limit must be between 1 and {}",
146 self.max_search_limit
147 ),
148 ));
149 }
150 Ok(())
151 }
152}
153
154impl Default for VectorStoreLimits {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160#[derive(Debug, Clone, Deserialize, Serialize)]
162pub struct MemoryVectorStoreConfig {
163 #[serde(default)]
165 pub metric: SimilarityMetric,
166}
167
168impl Default for MemoryVectorStoreConfig {
169 fn default() -> Self {
170 Self {
171 metric: SimilarityMetric::Cosine,
172 }
173 }
174}
175
176fn default_backend() -> String {
177 "memory".to_owned()
178}
179
180const fn default_max_search_limit() -> usize {
181 DEFAULT_MAX_SEARCH_LIMIT
182}
183
184const fn default_max_vector_dimensions() -> usize {
185 DEFAULT_MAX_VECTOR_DIMENSIONS
186}
187
188const fn default_max_payload_fields() -> usize {
189 DEFAULT_MAX_PAYLOAD_FIELDS
190}
191
192const fn default_max_payload_bytes() -> usize {
193 DEFAULT_MAX_PAYLOAD_BYTES
194}
195
196const fn default_max_filter_conditions() -> usize {
197 DEFAULT_MAX_FILTER_CONDITIONS
198}
199
200#[cfg(test)]
201mod tests {
202 use rskit_errors::ErrorCode;
203
204 use super::*;
205
206 #[test]
207 fn validate_dimensions_rejects_zero_configured_max_with_clear_error() {
208 let err = VectorStoreLimits::new()
209 .with_max_vector_dimensions(0)
210 .validate_dimensions(1)
211 .unwrap_err();
212
213 assert_eq!(err.code(), ErrorCode::InvalidInput);
214 assert!(err.message().contains("max_vector_dimensions"));
215 assert!(!err.message().contains("between 1 and 0"));
216 }
217
218 #[test]
219 fn validate_search_limit_rejects_zero_configured_max_with_clear_error() {
220 let err = VectorStoreLimits::new()
221 .with_max_search_limit(0)
222 .validate_search_limit(1)
223 .unwrap_err();
224
225 assert_eq!(err.code(), ErrorCode::InvalidInput);
226 assert!(err.message().contains("max_search_limit"));
227 assert!(!err.message().contains("between 1 and 0"));
228 }
229}