-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.rs
More file actions
320 lines (284 loc) · 9.35 KB
/
Copy pathconfig.rs
File metadata and controls
320 lines (284 loc) · 9.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::env;
use std::fs;
/// Performance tuning configuration for CueMap engine
// Search configuration (Deprecated constants, mapped to TuningConfig now)
pub const MAX_DRIVER_SCAN: usize = 10000;
pub const MAX_SEARCH_DEPTH: usize = 5000;
// DashMap shard configuration (power of 2)
pub const DASHMAP_SHARD_COUNT: usize = 128;
#[derive(Clone, Debug, Default, PartialEq, clap::ValueEnum, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CueGenStrategy {
#[default]
Default, // Minimal expansion (WordNet / Synonyms only)
Glove, // Deep semantic expansion (GloVe + WordNet)
Ollama // Local Ollama with Mistral (+ WordNet)
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ServerConfig {
#[serde(default)]
pub server: ServerSettings,
#[serde(default)]
pub security: SecurityConfig,
#[serde(default)]
pub persistence: PersistenceConfig,
#[serde(default)]
pub jobs: JobsConfig,
#[serde(default)]
pub agent: AgentConfig,
#[serde(default)]
pub llm: LlmConfig,
#[serde(default)]
pub search: SearchConfig,
#[serde(default)]
pub tuning: TuningConfig,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
server: ServerSettings::default(),
security: SecurityConfig::default(),
persistence: PersistenceConfig::default(),
jobs: JobsConfig::default(),
agent: AgentConfig::default(),
llm: LlmConfig::default(),
search: SearchConfig::default(),
tuning: TuningConfig::default(),
}
}
}
pub fn get_base_dir() -> PathBuf {
let home = env::var("HOME").unwrap_or_else(|_| ".".to_string());
let path = PathBuf::from(home).join(".cuemap");
if !path.exists() {
let _ = fs::create_dir_all(&path);
}
path
}
impl ServerConfig {
pub fn load(config_path: Option<PathBuf>, profile: Option<String>) -> Result<Self, String> {
// 1. Start with defaults based on profile
let profile_name = profile.unwrap_or_else(|| "default".to_string());
let mut config = Self::default_for_profile(&profile_name);
// 2. Load from config file matching profile (or just global config)
let path = config_path.unwrap_or_else(|| {
get_base_dir().join("server_config.toml")
});
if path.exists() {
let content = fs::read_to_string(&path).map_err(|e| e.to_string())?;
let file_config: ServerConfig = toml::from_str(&content).map_err(|e| format!("Failed to parse config: {}", e))?;
// Merge file config onto defaults
// Note: This is a shallow merge implementation for simplicity.
// In a robust system, we'd use a crate like `config` to merge fields deeply.
// For now, we trust `toml` to deserialize partially if Option, but since we use structs with defaults,
// `toml::from_str` usually replaces the whole struct if present.
// To do proper layering without `config` crate is verbose.
// Simplified approach: Parsing the file gives us a full config with defaults filled in by serde if missing in file.
// So we just use the file config, but we need to ensure CLI args override it later.
config = file_config;
} else {
// info!("Config file not found at {:?}, using defaults", path);
}
// 3. Environment variables overrides (Manual mapping for key fields)
if let Ok(port) = env::var("CUEMAP_PORT") {
if let Ok(p) = port.parse() { config.server.port = p; }
}
if let Ok(key) = env::var("CUEMAP_SECRET_KEY") {
config.security.secret_key = Some(key);
}
if let Ok(key) = env::var("CUEMAP_MASTER_KEY") {
config.security.master_key = Some(key);
}
Ok(config)
}
fn default_for_profile(profile: &str) -> Self {
let mut config = Self::default();
match profile {
"read_only" => {
config.server.read_only = true;
config.persistence.enabled = false;
config.jobs.background_processing = false;
config.agent.enabled = false;
},
"live" => {
config.persistence.enabled = true;
config.jobs.background_processing = true;
config.jobs.consolidation_enabled = true;
},
"benchmark" => {
config.persistence.enabled = false;
config.jobs.background_processing = false;
config.server.log_level = "warn".to_string();
},
_ => {} // Default
}
config
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ServerSettings {
pub port: u16,
pub host: String,
pub data_dir: String,
pub assets_dir: Option<String>,
pub log_level: String,
pub read_only: bool,
}
impl Default for ServerSettings {
fn default() -> Self {
Self {
port: 8080,
host: "0.0.0.0".to_string(),
data_dir: get_base_dir().join("data").to_string_lossy().to_string(),
assets_dir: None,
log_level: "info".to_string(),
read_only: false,
}
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SecurityConfig {
pub require_auth: bool,
pub api_keys: Vec<String>,
pub master_key: Option<String>,
pub secret_key: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PersistenceConfig {
pub snapshot_interval_seconds: u64,
pub enabled: bool,
pub compress_snapshots: bool,
#[serde(default)]
pub cloud: CloudConfig,
}
impl Default for PersistenceConfig {
fn default() -> Self {
Self {
snapshot_interval_seconds: 60,
enabled: true,
compress_snapshots: true,
cloud: CloudConfig::default(),
}
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct CloudConfig {
pub provider: String, // "none", "s3", "gcs", "azure"
pub bucket: String,
pub region: String,
pub endpoint: Option<String>,
pub prefix: String,
pub auto_backup: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JobsConfig {
pub background_processing: bool,
pub consolidation_enabled: bool,
pub market_heatmap_interval_seconds: u64,
}
impl Default for JobsConfig {
fn default() -> Self {
Self {
background_processing: true,
consolidation_enabled: false,
market_heatmap_interval_seconds: 60,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AgentConfig {
pub enabled: bool,
pub watch_dir: Option<String>, // Deprecated in favor of project meta, but kept for global agent
pub throttle_ms: u64,
}
impl Default for AgentConfig {
fn default() -> Self {
Self {
enabled: false,
watch_dir: None,
throttle_ms: 100,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LlmConfig {
pub enabled: bool,
pub provider: String,
pub model: String,
pub url: String,
pub api_key: Option<String>,
}
impl Default for LlmConfig {
fn default() -> Self {
Self {
enabled: false,
provider: "ollama".to_string(),
model: "mistral".to_string(),
url: "http://localhost:11434".to_string(),
api_key: None,
}
}
}
// Helper to convert to existing structure if needed
impl LlmConfig {
pub fn to_legacy(&self) -> crate::llm::LlmConfig {
crate::llm::LlmConfig {
provider: self.provider.clone(),
model: self.model.clone(),
api_key: self.api_key.clone(),
ollama_url: self.url.clone(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SearchConfig {
pub max_scan_depth: usize,
pub dashmap_shards: usize,
pub cuegen_strategy: CueGenStrategy,
}
impl Default for SearchConfig {
fn default() -> Self {
Self {
max_scan_depth: 10000,
dashmap_shards: 128,
cuegen_strategy: CueGenStrategy::Default,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TuningConfig {
// Scoring
pub max_rec_weight: f64,
pub max_freq_weight: f64,
pub intersection_score_multiplier: f64,
pub salience_score_multiplier: f64,
// Search / Scan
pub idf_threshold_percent: f64,
pub idf_min_count: usize,
pub adaptive_scan_factor: usize,
pub adaptive_scan_max: usize,
// Expansion
pub expansion_threshold: f64,
pub expansion_limit: usize,
pub max_proposed_cues: usize,
}
impl Default for TuningConfig {
fn default() -> Self {
Self {
// Defaults matching previous hardcoded constants
max_rec_weight: 20.0,
max_freq_weight: 5.0,
intersection_score_multiplier: 100.0,
salience_score_multiplier: 10.0,
idf_threshold_percent: 0.1,
idf_min_count: 20,
adaptive_scan_factor: 100,
adaptive_scan_max: 2000,
expansion_threshold: 0.65,
expansion_limit: 3,
max_proposed_cues: 10,
}
}
}