1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct LlmConfig {
9 pub provider: String,
11 pub model: String,
13 pub api_key_env: String,
15 pub base_url: Option<String>,
17 pub timeout_secs: u64,
19 pub max_tokens: u32,
21 pub temperature: f32,
23}
24
25impl Default for LlmConfig {
26 fn default() -> Self {
27 Self {
28 provider: "openai".to_string(),
29 model: "gpt-4o-mini".to_string(),
30 api_key_env: "OPENAI_API_KEY".to_string(),
31 base_url: None,
32 timeout_secs: 60,
33 max_tokens: 4096,
34 temperature: 0.3,
35 }
36 }
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct AgentConfig {
42 pub enabled: bool,
44 pub namespace: String,
46 pub agent_type: String,
48 pub inbox_dir: String,
50 pub scan_interval_secs: u64,
52 pub consolidation_interval_mins: u64,
54 pub consolidation_batch_size: usize,
56 pub query_context_limit: usize,
58}
59
60impl Default for AgentConfig {
61 fn default() -> Self {
62 Self {
63 enabled: false,
64 namespace: "nexus-agent".to_string(),
65 agent_type: std::env::var("NEXUS_AGENT_TYPE")
66 .unwrap_or_else(|_| "nexus-agent".to_string()),
67 inbox_dir: "./inbox".to_string(),
68 scan_interval_secs: 5,
69 consolidation_interval_mins: 30,
70 consolidation_batch_size: 10,
71 query_context_limit: 50,
72 }
73 }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct CognitionConfig {
79 pub auto_runtime_enabled: bool,
81 pub derive_enabled: bool,
83 pub digest_enabled: bool,
85 pub reflect_enabled: bool,
87 pub activity_distill_enabled: bool,
89 pub dream_on_session_end: bool,
91 pub checkpoint_flush_enabled: bool,
93 pub runtime_idle_timeout_secs: u64,
95 pub max_job_batch: usize,
97 pub lease_ttl_secs: u64,
99 pub representation_max_items: usize,
101 pub digest_short_target_tokens: usize,
103 pub digest_long_target_tokens: usize,
105 pub direct_enrichment_timeout_secs: u64,
107 pub activity_distill_min_events: usize,
109 pub activity_distill_max_events: usize,
111 pub include_raw_by_default: bool,
113 pub session_end_dream_timeout_secs: u64,
115 pub retry_buffer_drain_limit: usize,
117 pub contradiction_belief_revision_enabled: bool,
119 pub contradiction_confidence_penalty: f32,
121 pub memory_decay_enabled: bool,
123 pub memory_decay_age_days: u64,
125 pub memory_decay_access_boost_days: u64,
127 pub adaptive_dream_enabled: bool,
129 pub adaptive_dream_min_interval_secs: u64,
131 pub adaptive_dream_max_interval_secs: u64,
133}
134
135impl Default for CognitionConfig {
136 fn default() -> Self {
137 Self {
138 auto_runtime_enabled: true,
139 derive_enabled: true,
140 digest_enabled: true,
141 reflect_enabled: true,
142 activity_distill_enabled: true,
143 dream_on_session_end: true,
144 checkpoint_flush_enabled: true,
145 runtime_idle_timeout_secs: 900,
146 max_job_batch: 8,
147 lease_ttl_secs: 120,
148 representation_max_items: 24,
149 digest_short_target_tokens: 600,
150 digest_long_target_tokens: 1800,
151 direct_enrichment_timeout_secs: 8,
152 activity_distill_min_events: 8,
153 activity_distill_max_events: 60,
154 include_raw_by_default: false,
155 session_end_dream_timeout_secs: 8,
156 retry_buffer_drain_limit: 8,
157 contradiction_belief_revision_enabled: true,
158 contradiction_confidence_penalty: 0.15,
159 memory_decay_enabled: true,
160 memory_decay_age_days: 90,
161 memory_decay_access_boost_days: 30,
162 adaptive_dream_enabled: true,
163 adaptive_dream_min_interval_secs: 60,
164 adaptive_dream_max_interval_secs: 1800,
165 }
166 }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct DreamTriggerConfig {
172 pub nap_on_session_end: bool,
174 pub nap_idle_timeout_secs: u64,
176 pub dream_memory_threshold: usize,
178 pub deep_dream_cooldown_hours: u64,
180 pub deep_dream_inactivity_mins: u64,
182}
183
184impl Default for DreamTriggerConfig {
185 fn default() -> Self {
186 Self {
187 nap_on_session_end: true,
188 nap_idle_timeout_secs: 600,
189 dream_memory_threshold: 20,
190 deep_dream_cooldown_hours: 24,
191 deep_dream_inactivity_mins: 30,
192 }
193 }
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
198#[serde(default)]
199pub struct CognitiveSystemConfig {
200 pub enabled: bool,
201 pub bootstrap_mode: String,
203 pub dream_triggers: DreamTriggerConfig,
205 pub hot_cache_max_entries: usize,
207 pub context_allocation_pct: f32,
209 pub mid_session_rescore_enabled: bool,
211 pub rescore_turn_interval: u32,
213 pub rescore_drift_threshold: f32,
215 pub similarity_threshold: f32,
217 pub subconscious_mode: String,
219}
220
221impl Default for CognitiveSystemConfig {
222 fn default() -> Self {
223 Self {
224 enabled: true,
225 bootstrap_mode: "silent".to_string(),
226 dream_triggers: DreamTriggerConfig::default(),
227 hot_cache_max_entries: 20,
228 context_allocation_pct: 0.10,
229 mid_session_rescore_enabled: true,
230 rescore_turn_interval: 5,
231 rescore_drift_threshold: 0.70,
232 similarity_threshold: 0.85,
233 subconscious_mode: "whisper".to_string(),
234 }
235 }
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize, Default)]
240pub struct Config {
241 pub database: DatabaseConfig,
243
244 pub server: ServerConfig,
246
247 pub embedding: EmbeddingConfig,
249
250 pub sync: SyncConfig,
252
253 pub llm: LlmConfig,
255
256 pub agent: AgentConfig,
258
259 pub cognition: CognitionConfig,
261
262 pub cognitive_system: CognitiveSystemConfig,
264}
265
266impl Config {
267 pub fn from_env() -> crate::Result<Self> {
269 let mut config = Self::default();
270
271 if let Ok(path) = std::env::var("NEXUS_DATABASE_PATH") {
272 config.database.path = PathBuf::from(path);
273 }
274
275 if let Ok(host) = std::env::var("NEXUS_HOST") {
276 config.server.host = host;
277 }
278
279 if let Ok(port) = std::env::var("NEXUS_PORT") {
280 config.server.port = port.parse().unwrap_or(8768);
281 }
282
283 if let Ok(enabled) = std::env::var("NEXUS_EMBEDDINGS_ENABLED") {
284 config.embedding.enabled = enabled.parse().unwrap_or(false);
285 }
286
287 if let Ok(backend) = std::env::var("NEXUS_EMBEDDING_BACKEND") {
288 config.embedding.backend = backend;
289 }
290
291 if let Ok(provider) = std::env::var("NEXUS_EMBEDDING_PROVIDER") {
292 config.embedding.provider = provider;
293 }
294
295 if let Ok(model) = std::env::var("NEXUS_EMBEDDING_MODEL") {
296 config.embedding.model = model;
297 }
298
299 if let Ok(key_env) = std::env::var("NEXUS_EMBEDDING_API_KEY_ENV") {
300 config.embedding.api_key_env = key_env;
301 }
302
303 if let Ok(base_url) = std::env::var("NEXUS_EMBEDDING_BASE_URL") {
304 config.embedding.base_url = Some(base_url);
305 }
306
307 if let Ok(dimension) = std::env::var("NEXUS_EMBEDDING_DIMENSION") {
308 config.embedding.dimension = dimension
309 .parse()
310 .unwrap_or(EmbeddingConfig::default().dimension);
311 }
312
313 if let Ok(timeout) = std::env::var("NEXUS_EMBEDDING_TIMEOUT_SECS") {
314 config.embedding.timeout_secs = timeout
315 .parse()
316 .unwrap_or(EmbeddingConfig::default().timeout_secs);
317 }
318
319 if let Ok(model_path) = std::env::var("NEXUS_EMBEDDING_MODEL_PATH") {
320 config.embedding.local_model_path = Some(model_path);
321 }
322
323 if let Ok(tokenizer_path) = std::env::var("NEXUS_TOKENIZER_PATH") {
324 config.embedding.local_tokenizer_path = Some(tokenizer_path);
325 }
326
327 if let Ok(policy) = std::env::var("NEXUS_SYNC_POLICY") {
328 config.sync.policy = policy;
329 }
330
331 if let Ok(provider) = std::env::var("NEXUS_LLM_PROVIDER") {
333 config.llm.provider = provider;
334 }
335 if let Ok(model) = std::env::var("NEXUS_LLM_MODEL") {
336 config.llm.model = model;
337 }
338 if let Ok(key_env) = std::env::var("NEXUS_LLM_API_KEY_ENV") {
339 config.llm.api_key_env = key_env;
340 }
341 if let Ok(base_url) = std::env::var("NEXUS_LLM_BASE_URL") {
342 config.llm.base_url = Some(base_url);
343 }
344
345 if let Ok(enabled) = std::env::var("NEXUS_AGENT_ENABLED") {
347 config.agent.enabled = enabled.parse().unwrap_or(false);
348 }
349 if let Ok(namespace) = std::env::var("NEXUS_AGENT_NAMESPACE") {
350 config.agent.namespace = namespace;
351 }
352 if let Ok(inbox) = std::env::var("NEXUS_AGENT_INBOX_DIR") {
353 config.agent.inbox_dir = inbox;
354 }
355 if let Ok(interval) = std::env::var("NEXUS_AGENT_CONSOLIDATION_INTERVAL_MINS") {
356 config.agent.consolidation_interval_mins = interval
357 .parse()
358 .unwrap_or(AgentConfig::default().consolidation_interval_mins);
359 } else if let Ok(interval) = std::env::var("NEXUS_AGENT_CONSOLIDATION_INTERVAL") {
360 config.agent.consolidation_interval_mins = interval
362 .parse()
363 .unwrap_or(AgentConfig::default().consolidation_interval_mins);
364 }
365 if let Ok(interval) = std::env::var("NEXUS_AGENT_SCAN_INTERVAL_SECS") {
366 config.agent.scan_interval_secs = interval
367 .parse()
368 .unwrap_or(AgentConfig::default().scan_interval_secs);
369 } else if let Ok(interval) = std::env::var("NEXUS_AGENT_SCAN_INTERVAL") {
370 config.agent.scan_interval_secs = interval
372 .parse()
373 .unwrap_or(AgentConfig::default().scan_interval_secs);
374 }
375
376 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_AUTO_RUNTIME_ENABLED") {
377 config.cognition.auto_runtime_enabled = enabled.parse().unwrap_or(true);
378 }
379 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_DERIVE_ENABLED") {
380 config.cognition.derive_enabled = enabled.parse().unwrap_or(true);
381 }
382 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_DIGEST_ENABLED") {
383 config.cognition.digest_enabled = enabled.parse().unwrap_or(true);
384 }
385 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_REFLECT_ENABLED") {
386 config.cognition.reflect_enabled = enabled.parse().unwrap_or(true);
387 }
388 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_ACTIVITY_DISTILL_ENABLED") {
389 config.cognition.activity_distill_enabled = enabled.parse().unwrap_or(true);
390 }
391 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_DREAM_ON_SESSION_END") {
392 config.cognition.dream_on_session_end = enabled.parse().unwrap_or(true);
393 }
394 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_CHECKPOINT_FLUSH_ENABLED") {
395 config.cognition.checkpoint_flush_enabled = enabled.parse().unwrap_or(true);
396 }
397 if let Ok(timeout) = std::env::var("NEXUS_COGNITION_RUNTIME_IDLE_TIMEOUT_SECS") {
398 config.cognition.runtime_idle_timeout_secs = timeout
399 .parse()
400 .unwrap_or(CognitionConfig::default().runtime_idle_timeout_secs);
401 }
402 if let Ok(batch) = std::env::var("NEXUS_COGNITION_MAX_JOB_BATCH") {
403 config.cognition.max_job_batch = batch
404 .parse()
405 .unwrap_or(CognitionConfig::default().max_job_batch);
406 }
407 if let Ok(ttl) = std::env::var("NEXUS_COGNITION_LEASE_TTL_SECS") {
408 config.cognition.lease_ttl_secs = ttl
409 .parse()
410 .unwrap_or(CognitionConfig::default().lease_ttl_secs);
411 }
412 if let Ok(items) = std::env::var("NEXUS_COGNITION_REPRESENTATION_MAX_ITEMS") {
413 config.cognition.representation_max_items = items
414 .parse()
415 .unwrap_or(CognitionConfig::default().representation_max_items);
416 }
417 if let Ok(tokens) = std::env::var("NEXUS_COGNITION_DIGEST_SHORT_TARGET_TOKENS") {
418 config.cognition.digest_short_target_tokens = tokens
419 .parse()
420 .unwrap_or(CognitionConfig::default().digest_short_target_tokens);
421 }
422 if let Ok(tokens) = std::env::var("NEXUS_COGNITION_DIGEST_LONG_TARGET_TOKENS") {
423 config.cognition.digest_long_target_tokens = tokens
424 .parse()
425 .unwrap_or(CognitionConfig::default().digest_long_target_tokens);
426 }
427 if let Ok(timeout) = std::env::var("NEXUS_COGNITION_DIRECT_ENRICHMENT_TIMEOUT_SECS") {
428 config.cognition.direct_enrichment_timeout_secs = timeout
429 .parse()
430 .unwrap_or(CognitionConfig::default().direct_enrichment_timeout_secs);
431 }
432 if let Ok(events) = std::env::var("NEXUS_COGNITION_ACTIVITY_DISTILL_MIN_EVENTS") {
433 config.cognition.activity_distill_min_events = events
434 .parse()
435 .unwrap_or(CognitionConfig::default().activity_distill_min_events);
436 }
437 if let Ok(events) = std::env::var("NEXUS_COGNITION_ACTIVITY_DISTILL_MAX_EVENTS") {
438 config.cognition.activity_distill_max_events = events
439 .parse()
440 .unwrap_or(CognitionConfig::default().activity_distill_max_events);
441 }
442 if let Ok(include_raw) = std::env::var("NEXUS_COGNITION_INCLUDE_RAW_BY_DEFAULT") {
443 config.cognition.include_raw_by_default = include_raw.parse().unwrap_or(false);
444 }
445 if let Ok(timeout) = std::env::var("NEXUS_COGNITION_SESSION_END_DREAM_TIMEOUT_SECS") {
446 config.cognition.session_end_dream_timeout_secs = timeout
447 .parse()
448 .unwrap_or(CognitionConfig::default().session_end_dream_timeout_secs);
449 }
450 if let Ok(limit) = std::env::var("NEXUS_COGNITION_RETRY_BUFFER_DRAIN_LIMIT") {
451 config.cognition.retry_buffer_drain_limit = limit
452 .parse()
453 .unwrap_or(CognitionConfig::default().retry_buffer_drain_limit);
454 }
455 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_CONTRADICTION_BELIEF_REVISION_ENABLED")
456 {
457 config.cognition.contradiction_belief_revision_enabled =
458 enabled.parse().unwrap_or(true);
459 }
460 if let Ok(penalty) = std::env::var("NEXUS_COGNITION_CONTRADICTION_CONFIDENCE_PENALTY") {
461 config.cognition.contradiction_confidence_penalty = penalty
462 .parse()
463 .unwrap_or(CognitionConfig::default().contradiction_confidence_penalty);
464 }
465 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_MEMORY_DECAY_ENABLED") {
466 config.cognition.memory_decay_enabled = enabled.parse().unwrap_or(true);
467 }
468 if let Ok(days) = std::env::var("NEXUS_COGNITION_MEMORY_DECAY_AGE_DAYS") {
469 config.cognition.memory_decay_age_days = days
470 .parse()
471 .unwrap_or(CognitionConfig::default().memory_decay_age_days);
472 }
473 if let Ok(days) = std::env::var("NEXUS_COGNITION_MEMORY_DECAY_ACCESS_BOOST_DAYS") {
474 config.cognition.memory_decay_access_boost_days = days
475 .parse()
476 .unwrap_or(CognitionConfig::default().memory_decay_access_boost_days);
477 }
478 if let Ok(enabled) = std::env::var("NEXUS_COGNITION_ADAPTIVE_DREAM_ENABLED") {
479 config.cognition.adaptive_dream_enabled = enabled.parse().unwrap_or(true);
480 }
481 if let Ok(secs) = std::env::var("NEXUS_COGNITION_ADAPTIVE_DREAM_MIN_INTERVAL_SECS") {
482 config.cognition.adaptive_dream_min_interval_secs = secs
483 .parse()
484 .unwrap_or(CognitionConfig::default().adaptive_dream_min_interval_secs);
485 }
486 if let Ok(secs) = std::env::var("NEXUS_COGNITION_ADAPTIVE_DREAM_MAX_INTERVAL_SECS") {
487 config.cognition.adaptive_dream_max_interval_secs = secs
488 .parse()
489 .unwrap_or(CognitionConfig::default().adaptive_dream_max_interval_secs);
490 }
491
492 if let Ok(enabled) = std::env::var("NEXUS_COGNITIVE_ENABLED") {
494 config.cognitive_system.enabled = enabled.parse().unwrap_or(true);
495 }
496 if let Ok(mode) = std::env::var("NEXUS_BOOTSTRAP_MODE") {
497 config.cognitive_system.bootstrap_mode = mode;
498 }
499 if let Ok(max) = std::env::var("NEXUS_HOT_CACHE_MAX") {
500 config.cognitive_system.hot_cache_max_entries = max
501 .parse()
502 .unwrap_or(CognitiveSystemConfig::default().hot_cache_max_entries);
503 }
504 if let Ok(pct) = std::env::var("NEXUS_CONTEXT_ALLOCATION_PCT") {
505 config.cognitive_system.context_allocation_pct = pct
506 .parse::<f32>()
507 .ok()
508 .filter(|&v| (0.0..=1.0).contains(&v))
509 .unwrap_or(CognitiveSystemConfig::default().context_allocation_pct);
510 }
511 if let Ok(enabled) = std::env::var("NEXUS_RESCORE_ENABLED") {
512 config.cognitive_system.mid_session_rescore_enabled = enabled.parse().unwrap_or(true);
513 }
514 if let Ok(interval) = std::env::var("NEXUS_RESCORE_TURN_INTERVAL") {
515 config.cognitive_system.rescore_turn_interval = interval
516 .parse()
517 .unwrap_or(CognitiveSystemConfig::default().rescore_turn_interval);
518 }
519 if let Ok(threshold) = std::env::var("NEXUS_RESCORE_DRIFT_THRESHOLD") {
520 config.cognitive_system.rescore_drift_threshold = threshold
521 .parse()
522 .ok()
523 .filter(|&v: &f32| (0.0..=1.0).contains(&v))
524 .unwrap_or(CognitiveSystemConfig::default().rescore_drift_threshold);
525 }
526 if let Ok(mode) = std::env::var("NEXUS_SUBCONSCIOUS_MODE") {
527 config.cognitive_system.subconscious_mode = mode;
528 }
529 if let Ok(threshold) = std::env::var("NEXUS_DREAM_THRESHOLD") {
530 config
531 .cognitive_system
532 .dream_triggers
533 .dream_memory_threshold = threshold
534 .parse()
535 .unwrap_or(DreamTriggerConfig::default().dream_memory_threshold);
536 }
537 if let Ok(hours) = std::env::var("NEXUS_DEEP_DREAM_COOLDOWN_HOURS") {
538 config
539 .cognitive_system
540 .dream_triggers
541 .deep_dream_cooldown_hours = hours
542 .parse()
543 .unwrap_or(DreamTriggerConfig::default().deep_dream_cooldown_hours);
544 }
545
546 if let Ok(mins) = std::env::var("NEXUS_DEEP_DREAM_INACTIVITY_MINS") {
547 config
548 .cognitive_system
549 .dream_triggers
550 .deep_dream_inactivity_mins = mins
551 .parse()
552 .unwrap_or(DreamTriggerConfig::default().deep_dream_inactivity_mins);
553 }
554 if let Ok(threshold) = std::env::var("NEXUS_SIMILARITY_THRESHOLD") {
555 config.cognitive_system.similarity_threshold = threshold
556 .parse()
557 .ok()
558 .filter(|&v: &f32| (0.0..=1.0).contains(&v))
559 .unwrap_or(CognitiveSystemConfig::default().similarity_threshold);
560 }
561
562 Ok(config)
563 }
564
565 pub fn database_url(&self) -> String {
567 format!("sqlite:{}", self.database.path.display())
568 }
569}
570
571#[derive(Debug, Clone, Serialize, Deserialize)]
573pub struct DatabaseConfig {
574 pub path: PathBuf,
576
577 pub foreign_keys: bool,
579
580 pub pool_size: u32,
582}
583
584impl Default for DatabaseConfig {
585 fn default() -> Self {
586 let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
587 let base_path = PathBuf::from(home).join(".nexus");
588
589 Self {
590 path: base_path.join("nexus.db"),
591 foreign_keys: true,
592 pool_size: 5,
593 }
594 }
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct ServerConfig {
600 pub host: String,
602
603 pub port: u16,
605
606 pub web_port: u16,
608
609 pub transport: String,
611}
612
613impl Default for ServerConfig {
614 fn default() -> Self {
615 Self {
616 host: "127.0.0.1".to_string(),
617 port: 8768,
618 web_port: 8768,
619 transport: "stdio".to_string(),
620 }
621 }
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
626pub struct EmbeddingConfig {
627 pub enabled: bool,
629
630 pub backend: String,
632
633 pub provider: String,
636
637 pub model: String,
639
640 pub api_key_env: String,
642
643 pub base_url: Option<String>,
645
646 pub dimension: usize,
648
649 pub timeout_secs: u64,
651
652 pub local_model_path: Option<String>,
654
655 pub local_tokenizer_path: Option<String>,
657}
658
659impl Default for EmbeddingConfig {
660 fn default() -> Self {
661 Self {
662 enabled: false,
663 backend: "local".to_string(),
664 provider: "local".to_string(),
665 model: "all-MiniLM-L6-v2".to_string(),
666 api_key_env: "OPENAI_API_KEY".to_string(),
667 base_url: None,
668 dimension: 384,
669 timeout_secs: 60,
670 local_model_path: Some("models/all-MiniLM-L6-v2.onnx".to_string()),
671 local_tokenizer_path: Some("models/all-MiniLM-L6-v2-tokenizer".to_string()),
672 }
673 }
674}
675
676#[derive(Debug, Clone, Serialize, Deserialize)]
678pub struct SyncConfig {
679 pub policy: String,
681
682 pub interval_secs: u64,
684}
685
686impl Default for SyncConfig {
687 fn default() -> Self {
688 Self {
689 policy: "manual".to_string(),
690 interval_secs: 300,
691 }
692 }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698 use serial_test::serial;
699
700 #[test]
701 fn test_default_config() {
702 let config = Config::default();
703 assert!(!config.embedding.enabled);
704 assert_eq!(config.embedding.backend, "local");
705 assert_eq!(config.embedding.provider, "local");
706 assert_eq!(config.embedding.dimension, 384);
707 assert_eq!(config.server.port, 8768);
708 }
709
710 #[test]
711 fn test_database_url() {
712 let config = Config::default();
713 let url = config.database_url();
714 assert!(url.starts_with("sqlite:"));
715 }
716
717 #[test]
718 fn test_cognition_config_defaults() {
719 let config = Config::default();
720 assert!(config.cognition.derive_enabled);
721 assert!(config.cognition.digest_enabled);
722 assert!(config.cognition.reflect_enabled);
723 assert!(config.cognition.activity_distill_enabled);
724 assert_eq!(config.cognition.representation_max_items, 24);
725 assert!(!config.cognition.include_raw_by_default);
726 assert!(config.cognition.contradiction_belief_revision_enabled);
727 assert!((config.cognition.contradiction_confidence_penalty - 0.15).abs() < f32::EPSILON);
728 assert!(config.cognition.memory_decay_enabled);
729 assert_eq!(config.cognition.memory_decay_age_days, 90);
730 assert!(config.cognition.adaptive_dream_enabled);
731 assert_eq!(config.cognition.adaptive_dream_min_interval_secs, 60);
732 assert_eq!(config.cognition.adaptive_dream_max_interval_secs, 1800);
733 }
734
735 #[test]
736 #[serial]
737 fn test_cognition_config_from_env() {
738 std::env::set_var("NEXUS_COGNITION_DERIVE_ENABLED", "false");
739 std::env::set_var("NEXUS_COGNITION_MAX_JOB_BATCH", "16");
740 std::env::set_var("NEXUS_COGNITION_REPRESENTATION_MAX_ITEMS", "42");
741 std::env::set_var("NEXUS_COGNITION_INCLUDE_RAW_BY_DEFAULT", "true");
742
743 let config = Config::from_env().expect("config from env");
744 assert!(!config.cognition.derive_enabled);
745 assert_eq!(config.cognition.max_job_batch, 16);
746 assert_eq!(config.cognition.representation_max_items, 42);
747 assert!(config.cognition.include_raw_by_default);
748
749 std::env::remove_var("NEXUS_COGNITION_DERIVE_ENABLED");
750 std::env::remove_var("NEXUS_COGNITION_MAX_JOB_BATCH");
751 std::env::remove_var("NEXUS_COGNITION_REPRESENTATION_MAX_ITEMS");
752 std::env::remove_var("NEXUS_COGNITION_INCLUDE_RAW_BY_DEFAULT");
753 }
754
755 #[test]
756 fn test_cognitive_system_config_defaults() {
757 let config = Config::default();
758 assert!(config.cognitive_system.enabled);
759 assert_eq!(config.cognitive_system.bootstrap_mode, "silent");
760 assert_eq!(config.cognitive_system.hot_cache_max_entries, 20);
761 assert!((config.cognitive_system.context_allocation_pct - 0.10).abs() < f32::EPSILON);
762 assert!(config.cognitive_system.dream_triggers.nap_on_session_end);
763 assert_eq!(
764 config
765 .cognitive_system
766 .dream_triggers
767 .dream_memory_threshold,
768 20
769 );
770 }
771
772 #[test]
773 #[serial]
774 fn test_cognitive_system_config_from_env() {
775 std::env::set_var("NEXUS_COGNITIVE_ENABLED", "false");
776 std::env::set_var("NEXUS_BOOTSTRAP_MODE", "chatty");
777 std::env::set_var("NEXUS_HOT_CACHE_MAX", "50");
778 std::env::set_var("NEXUS_DREAM_THRESHOLD", "10");
779
780 let config = Config::from_env().expect("config from env");
781 assert!(!config.cognitive_system.enabled);
782 assert_eq!(config.cognitive_system.bootstrap_mode, "chatty");
783 assert_eq!(config.cognitive_system.hot_cache_max_entries, 50);
784 assert_eq!(
785 config
786 .cognitive_system
787 .dream_triggers
788 .dream_memory_threshold,
789 10
790 );
791
792 std::env::remove_var("NEXUS_COGNITIVE_ENABLED");
793 std::env::remove_var("NEXUS_BOOTSTRAP_MODE");
794 std::env::remove_var("NEXUS_HOT_CACHE_MAX");
795 std::env::remove_var("NEXUS_DREAM_THRESHOLD");
796 }
797
798 #[test]
799 #[serial]
800 fn test_embedding_config_from_env() {
801 std::env::set_var("NEXUS_EMBEDDINGS_ENABLED", "true");
802 std::env::set_var("NEXUS_EMBEDDING_BACKEND", "openai-compatible");
803 std::env::set_var("NEXUS_EMBEDDING_PROVIDER", "inherit");
804 std::env::set_var("NEXUS_EMBEDDING_MODEL", "text-embedding-004");
805 std::env::set_var("NEXUS_EMBEDDING_API_KEY_ENV", "GEMINI_API_KEY");
806 std::env::set_var(
807 "NEXUS_EMBEDDING_BASE_URL",
808 "https://generativelanguage.googleapis.com/v1beta/openai",
809 );
810 std::env::set_var("NEXUS_EMBEDDING_TIMEOUT_SECS", "45");
811
812 let config = Config::from_env().expect("config from env");
813 assert!(config.embedding.enabled);
814 assert_eq!(config.embedding.backend, "openai-compatible");
815 assert_eq!(config.embedding.provider, "inherit");
816 assert_eq!(config.embedding.model, "text-embedding-004");
817 assert_eq!(config.embedding.api_key_env, "GEMINI_API_KEY");
818 assert_eq!(
819 config.embedding.base_url.as_deref(),
820 Some("https://generativelanguage.googleapis.com/v1beta/openai")
821 );
822 assert_eq!(config.embedding.timeout_secs, 45);
823
824 std::env::remove_var("NEXUS_EMBEDDINGS_ENABLED");
825 std::env::remove_var("NEXUS_EMBEDDING_BACKEND");
826 std::env::remove_var("NEXUS_EMBEDDING_PROVIDER");
827 std::env::remove_var("NEXUS_EMBEDDING_MODEL");
828 std::env::remove_var("NEXUS_EMBEDDING_API_KEY_ENV");
829 std::env::remove_var("NEXUS_EMBEDDING_BASE_URL");
830 std::env::remove_var("NEXUS_EMBEDDING_TIMEOUT_SECS");
831 }
832}