1use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, Default)]
7pub struct WarmConfig {
8 pub text_encoder: super::encoding::TextEncoder,
9 pub ivf: IVFConfig,
10 pub nsg: NSGConfig,
11 pub shard: ShardConfig,
12 pub query: QueryConfig,
13 pub concepts: ConceptsConfig,
14 pub diffusion: DiffusionConfig,
15 pub security: SecurityConfig,
16 pub provenance: ProvenanceConfig,
17 pub privacy: PrivacyConfig,
18 pub meaning: MeaningConfig,
19 pub cognition: CognitionConfig,
20 pub hopfield: super::hopfield::HopfieldConfig,
21 pub embedding_space: Option<super::schema::EmbeddingSpace>,
22 pub document_checkpoint: DocumentCheckpointConfig,
23 #[cfg(feature = "local-embed")]
26 pub local_embed: Option<super::embed::EmbedOptions>,
27}
28
29#[derive(Clone, Debug)]
33pub struct DocumentCheckpointConfig {
34 pub enabled: bool,
36 pub min_journal_bytes: usize,
39}
40
41impl Default for DocumentCheckpointConfig {
42 fn default() -> Self {
43 Self {
44 enabled: true,
45 min_journal_bytes: 256 << 10,
46 }
47 }
48}
49
50#[derive(Clone, Debug)]
51pub struct MeaningConfig {
52 pub enabled: bool,
53 pub beta: f64,
54 pub algebraic_max_fanout: usize,
55 pub auto_decompose: bool,
56 pub max_hop_depth: usize,
57 pub max_rule_depth: usize,
58 pub idf_clip_factor: f64,
59}
60
61impl Default for MeaningConfig {
62 fn default() -> Self {
63 Self {
64 enabled: false,
65 beta: 24.0,
66 algebraic_max_fanout: 40,
67 auto_decompose: false,
68 max_hop_depth: 10,
69 max_rule_depth: 10,
70 idf_clip_factor: 3.0,
71 }
72 }
73}
74
75#[derive(Clone, Debug, Default)]
76pub struct SecurityConfig {
77 pub signing_enabled: bool,
78 pub key_path: Option<String>,
79 pub encryption_enabled: bool,
80 pub encryption_passphrase: Option<String>,
81 pub encryption_passphrase_env: Option<String>,
84 pub audit_enabled: bool,
85}
86
87#[derive(Clone, Debug, Default)]
88pub struct ProvenanceConfig {
89 pub enabled: bool,
90 pub key_path: Option<String>,
91 pub auto_sign: bool,
92 pub status_list_url: Option<String>,
93 #[cfg(feature = "provenance-scitt")]
94 pub scitt_endpoint: Option<String>,
95}
96
97#[derive(Clone, Debug)]
98pub struct PrivacyConfig {
99 pub dp_enabled: bool,
101 pub epsilon: f64,
104}
105
106impl Default for PrivacyConfig {
107 fn default() -> Self {
108 Self {
109 dp_enabled: false,
110 epsilon: 1.0,
111 }
112 }
113}
114
115#[derive(Clone, Debug)]
116pub struct QueryConfig {
117 pub component_similarity_threshold: f64,
118 pub component_max_neighbors: u32,
119}
120
121impl Default for QueryConfig {
122 fn default() -> Self {
123 Self {
124 component_similarity_threshold: 0.05,
125 component_max_neighbors: 20,
126 }
127 }
128}
129
130#[derive(Clone, Debug)]
131pub struct ConceptsConfig {
132 pub similarity_threshold: f64,
133 pub min_cluster_size: usize,
134}
135
136impl Default for ConceptsConfig {
137 fn default() -> Self {
138 Self {
139 similarity_threshold: 0.3,
140 min_cluster_size: 3,
141 }
142 }
143}
144
145#[derive(Clone, Debug)]
146pub struct DiffusionConfig {
147 pub steps: usize,
148 pub sigma_max: f64,
149 pub sigma_min: f64,
150 pub step_size: f64,
151 pub n_langevin: usize,
152}
153
154impl Default for DiffusionConfig {
155 fn default() -> Self {
156 Self {
157 steps: 10,
158 sigma_max: 0.5,
159 sigma_min: 0.01,
160 step_size: 0.1,
161 n_langevin: 5,
162 }
163 }
164}
165
166#[derive(Clone, Debug)]
167pub struct ShardConfig {
168 pub enabled: bool,
169 pub shard_count: usize,
170 pub auto_threshold: usize,
171 pub target_shard_size: usize,
172}
173
174impl Default for ShardConfig {
175 fn default() -> Self {
176 Self {
177 enabled: false,
178 shard_count: 0,
179 auto_threshold: 1_000_000,
180 target_shard_size: 250_000,
181 }
182 }
183}
184
185#[derive(Clone, Debug, Serialize, Deserialize)]
186pub struct NSGConfig {
187 pub max_degree: usize,
188 pub ef_construction: usize,
189 pub auto_threshold: usize,
190 pub seed: u64,
191}
192
193impl Default for NSGConfig {
194 fn default() -> Self {
195 Self {
196 max_degree: 32,
197 ef_construction: 128,
198 auto_threshold: 10_000,
199 seed: 42,
200 }
201 }
202}
203
204#[derive(Clone, Debug)]
205pub struct IVFConfig {
206 pub enabled: bool,
208 pub n_clusters: usize,
209 pub n_landmarks: usize,
210 pub d_reduced: usize,
211 pub n_probe: usize,
212 pub auto_threshold: usize,
213}
214
215impl Default for IVFConfig {
216 fn default() -> Self {
217 Self {
218 enabled: false,
219 n_clusters: 256,
220 n_landmarks: 1024,
221 d_reduced: 128,
222 n_probe: 8,
223 auto_threshold: 10_000,
224 }
225 }
226}
227
228#[derive(Clone, Debug)]
229pub struct CognitionConfig {
230 pub enabled: bool,
231 pub interval_secs: u64,
232 pub min_pattern_freq: usize,
233 pub min_abstraction_members: usize,
234 pub min_shared_relations: usize,
235 pub min_peer_coverage: f64,
236 pub hypothesis_beta: f64,
237 pub min_hypothesis_confidence: f64,
238 pub min_analogy_relations: usize,
239 pub governor_duplicate_threshold: f64,
240 pub governor_max_scan_size: usize,
241 pub governor_forget_unreferenced: bool,
242 pub refine_atoms: bool,
243}
244
245impl Default for CognitionConfig {
246 fn default() -> Self {
247 Self {
248 enabled: false,
249 interval_secs: 60,
250 min_pattern_freq: 3,
251 min_abstraction_members: 3,
252 min_shared_relations: 2,
253 min_peer_coverage: 0.5,
254 hypothesis_beta: 24.0,
255 min_hypothesis_confidence: 0.3,
256 min_analogy_relations: 2,
257 governor_duplicate_threshold: 0.95,
258 governor_max_scan_size: 1000,
259 governor_forget_unreferenced: false,
260 refine_atoms: false,
261 }
262 }
263}
264
265impl WarmConfig {
266 pub(crate) fn validate(&self) -> anyhow::Result<()> {
267 anyhow::ensure!(
268 self.text_encoder != super::encoding::TextEncoder::MultiscaleCappedUnionV1
269 || cfg!(feature = "experimental"),
270 "capped-union text encoding requires a build with the experimental feature"
271 );
272 anyhow::ensure!(
273 self.text_encoder != super::encoding::TextEncoder::LocalEmbedV1
274 || cfg!(feature = "local-embed"),
275 "the local-embed-v1 text encoder requires a build with the local-embed feature"
276 );
277 anyhow::ensure!(
278 cfg!(feature = "security")
279 || !(self.security.signing_enabled || self.security.encryption_enabled),
280 "signing/encryption requires a build with the security feature"
281 );
282 anyhow::ensure!(
283 cfg!(feature = "provenance") || !self.provenance.enabled,
284 "provenance requires a build with the provenance feature"
285 );
286 anyhow::ensure!(
287 self.privacy.epsilon.is_finite() && self.privacy.epsilon > 0.0,
288 "privacy epsilon must be finite and positive"
289 );
290 anyhow::ensure!(
291 self.shard.target_shard_size > 0 && self.shard.shard_count <= 1024,
292 "invalid shard configuration"
293 );
294 anyhow::ensure!(
295 self.meaning.beta.is_finite() && self.meaning.beta > 0.0,
296 "meaning beta must be finite and positive"
297 );
298 if let Some(space) = &self.embedding_space {
299 anyhow::ensure!(!space.model.is_empty() && !space.revision.is_empty() && (1..=65536).contains(&space.dimensions)
300 && space.metric == "cosine" && matches!(space.normalization.as_str(), "l2" | "none"),
301 "embedding space requires model, revision, dimensions 1..=65536, normalization l2/none and metric cosine");
302 }
303 Ok(())
304 }
305}