1use std::num::NonZeroUsize;
5
6use crate::ProviderName;
7use crate::providers::{ProviderEntry, SttConfig, default_stt_language};
8use crate::root::Config;
9
10impl Config {
11 pub fn apply_env_overrides(&mut self) {
12 self.apply_env_overrides_core();
13 self.apply_env_overrides_security();
14 }
15
16 fn apply_env_overrides_core(&mut self) {
17 self.apply_env_overrides_core_1();
18 self.apply_env_overrides_core_1b();
19 self.apply_env_overrides_core_2();
20 self.apply_env_overrides_core_2b();
21 }
22
23 fn apply_env_overrides_core_1(&mut self) {
24 let has_provider_env = std::env::var("ZEPH_LLM_PROVIDER").is_ok()
25 || std::env::var("ZEPH_LLM_BASE_URL").is_ok()
26 || std::env::var("ZEPH_LLM_MODEL").is_ok();
27 if has_provider_env && self.llm.providers.is_empty() {
28 self.llm.providers.push(ProviderEntry::default());
29 }
30
31 if let Ok(v) = std::env::var("ZEPH_LLM_PROVIDER")
32 && let Some(entry) = self.llm.providers.first_mut()
33 {
34 if let Ok(kind) = serde_json::from_value(serde_json::Value::String(v.clone())) {
35 entry.provider_type = kind;
36 } else {
37 tracing::warn!("ignoring invalid ZEPH_LLM_PROVIDER value: {v}");
38 }
39 }
40 if let Ok(v) = std::env::var("ZEPH_LLM_BASE_URL")
41 && let Some(entry) = self.llm.providers.first_mut()
42 {
43 entry.base_url = Some(v);
44 }
45 if let Ok(v) = std::env::var("ZEPH_LLM_MODEL")
46 && let Some(entry) = self.llm.providers.first_mut()
47 {
48 entry.model = Some(v);
49 }
50 if let Ok(v) = std::env::var("ZEPH_LLM_EMBEDDING_MODEL") {
51 self.llm.embedding_model = v;
52 }
53 if let Ok(v) = std::env::var("ZEPH_SQLITE_PATH") {
54 self.memory.sqlite_path = v;
55 }
56 if let Ok(v) = std::env::var("ZEPH_QDRANT_URL") {
57 self.memory.qdrant_url = v;
58 }
59 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_ENABLED")
60 && let Ok(enabled) = v.parse::<bool>()
61 {
62 self.memory.semantic.enabled = enabled;
63 }
64 if let Ok(v) = std::env::var("ZEPH_MEMORY_RECALL_LIMIT")
65 && let Ok(limit) = v.parse::<usize>()
66 {
67 self.memory.semantic.recall_limit = limit;
68 }
69 if let Ok(v) = std::env::var("ZEPH_MEMORY_SUMMARIZATION_THRESHOLD")
70 && let Ok(threshold) = v.parse::<usize>()
71 {
72 self.memory.summarization_threshold = threshold;
73 }
74 if let Ok(v) = std::env::var("ZEPH_MEMORY_CONTEXT_BUDGET_TOKENS")
75 && let Ok(tokens) = v.parse::<usize>()
76 {
77 self.memory.context_budget_tokens = tokens;
78 }
79 if let Ok(v) = std::env::var("ZEPH_MEMORY_COMPACTION_THRESHOLD")
80 && let Ok(threshold) = v.parse::<f32>()
81 {
82 self.memory.hard_compaction_threshold = threshold;
83 }
84 if let Ok(v) = std::env::var("ZEPH_MEMORY_SOFT_COMPACTION_THRESHOLD")
85 && let Ok(threshold) = v.parse::<f32>()
86 {
87 self.memory.soft_compaction_threshold = threshold;
88 }
89 if let Ok(v) = std::env::var("ZEPH_MEMORY_COMPACTION_PRESERVE_TAIL")
90 && let Ok(tail) = v.parse::<usize>()
91 {
92 self.memory.compaction_preserve_tail = tail;
93 }
94 if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTO_BUDGET")
95 && let Ok(enabled) = v.parse::<bool>()
96 {
97 self.memory.auto_budget = enabled;
98 }
99 if let Ok(v) = std::env::var("ZEPH_MEMORY_PRUNE_PROTECT_TOKENS")
100 && let Ok(tokens) = v.parse::<usize>()
101 {
102 self.memory.prune_protect_tokens = tokens;
103 }
104 if let Ok(v) = std::env::var("ZEPH_MEMORY_VECTOR_BACKEND") {
105 match v.to_lowercase().as_str() {
106 "sqlite" => {
107 self.memory.vector_backend = crate::memory::VectorBackend::Sqlite;
108 }
109 "qdrant" => {
110 self.memory.vector_backend = crate::memory::VectorBackend::Qdrant;
111 }
112 _ => {}
113 }
114 }
115 }
116
117 fn apply_env_overrides_core_1b(&mut self) {
118 if let Ok(v) = std::env::var("ZEPH_SKILLS_MAX_ACTIVE")
119 && let Ok(n) = v.parse::<usize>()
120 && let Some(nz) = NonZeroUsize::new(n)
121 {
122 self.skills.max_active_skills = nz;
123 }
124 if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_ENABLED")
125 && let Ok(enabled) = v.parse::<bool>()
126 {
127 self.skills.learning.enabled = enabled;
128 }
129 if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_AUTO_ACTIVATE")
130 && let Ok(auto_activate) = v.parse::<bool>()
131 {
132 self.skills.learning.auto_activate = auto_activate;
133 }
134 if let Ok(v) = std::env::var("ZEPH_SKILLS_PROMPT_MODE") {
135 match v.to_lowercase().as_str() {
136 "full" => self.skills.prompt_mode = crate::features::SkillPromptMode::Full,
137 "compact" => self.skills.prompt_mode = crate::features::SkillPromptMode::Compact,
138 "auto" => self.skills.prompt_mode = crate::features::SkillPromptMode::Auto,
139 _ => {}
140 }
141 }
142 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_ENABLED")
143 && let Ok(enabled) = v.parse::<bool>()
144 {
145 self.memory.semantic.temporal_decay_enabled = enabled;
146 }
147 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_HALF_LIFE_DAYS")
148 && let Ok(days) = v.parse::<u32>()
149 {
150 self.memory.semantic.temporal_decay_half_life_days = days;
151 }
152 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_ENABLED")
153 && let Ok(enabled) = v.parse::<bool>()
154 {
155 self.memory.semantic.mmr_enabled = enabled;
156 }
157 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_LAMBDA")
158 && let Ok(lambda) = v.parse::<f32>()
159 {
160 self.memory.semantic.mmr_lambda = lambda;
161 }
162 if let Ok(v) = std::env::var("ZEPH_MEMORY_TOKEN_SAFETY_MARGIN")
163 && let Ok(margin) = v.parse::<f32>()
164 {
165 self.memory.token_safety_margin = margin.clamp(0.1, 10.0);
166 }
167 if let Ok(v) = std::env::var("ZEPH_TOOLS_SUMMARIZE_OUTPUT")
168 && let Ok(enabled) = v.parse::<bool>()
169 {
170 self.tools.summarize_output = enabled;
171 }
172 if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_COMMANDS") {
173 self.tools.shell.allowed_commands = v
174 .split(',')
175 .map(|s| s.trim().to_owned())
176 .filter(|s| !s.is_empty())
177 .collect();
178 }
179 if let Ok(v) = std::env::var("ZEPH_TOOLS_TIMEOUT")
180 && let Ok(secs) = v.parse::<u64>()
181 {
182 self.tools.shell.timeout = secs;
183 }
184 if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_TIMEOUT")
185 && let Ok(secs) = v.parse::<u64>()
186 {
187 self.tools.scrape.timeout = secs;
188 }
189 if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_MAX_BODY")
190 && let Ok(bytes) = v.parse::<usize>()
191 {
192 self.tools.scrape.max_body_bytes = bytes;
193 }
194 }
195
196 fn apply_env_overrides_core_2(&mut self) {
197 if let Ok(v) = std::env::var("ZEPH_INDEX_ENABLED")
198 && let Ok(enabled) = v.parse::<bool>()
199 {
200 self.index.enabled = enabled;
201 }
202 if let Ok(v) = std::env::var("ZEPH_INDEX_MAX_CHUNKS")
203 && let Ok(n) = v.parse::<usize>()
204 {
205 self.index.max_chunks = n;
206 }
207 if let Ok(v) = std::env::var("ZEPH_INDEX_SCORE_THRESHOLD")
208 && let Ok(t) = v.parse::<f32>()
209 {
210 self.index.score_threshold = t.clamp(0.0, 1.0);
211 }
212 if let Ok(v) = std::env::var("ZEPH_INDEX_BUDGET_RATIO")
213 && let Ok(r) = v.parse::<f32>()
214 {
215 self.index.budget_ratio = r.clamp(0.0, 1.0);
216 }
217 if let Ok(v) = std::env::var("ZEPH_INDEX_REPO_MAP_TOKENS")
218 && let Ok(n) = v.parse::<usize>()
219 {
220 self.index.repo_map_tokens = n;
221 }
222 if let Ok(v) = std::env::var("ZEPH_STT_PROVIDER") {
223 let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
224 provider: ProviderName::default(),
225 language: default_stt_language(),
226 });
227 stt.provider = ProviderName::new(v);
228 }
229 if let Ok(v) = std::env::var("ZEPH_STT_LANGUAGE") {
230 let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
231 provider: ProviderName::default(),
232 language: default_stt_language(),
233 });
234 stt.language = v;
235 }
236 if std::env::var("ZEPH_STT_MODEL").is_ok() {
237 tracing::warn!(
238 "env var ZEPH_STT_MODEL is no longer supported; set `stt_model` on the \
239 [[llm.providers]] entry instead"
240 );
241 }
242 if std::env::var("ZEPH_STT_BASE_URL").is_ok() {
243 tracing::warn!(
244 "env var ZEPH_STT_BASE_URL is no longer supported; set `base_url` on the \
245 [[llm.providers]] entry instead"
246 );
247 }
248 }
249
250 fn apply_env_overrides_core_2b(&mut self) {
251 if let Ok(v) = std::env::var("ZEPH_AUTO_UPDATE_CHECK")
252 && let Ok(enabled) = v.parse::<bool>()
253 {
254 self.agent.auto_update_check = enabled;
255 }
256 if let Ok(v) = std::env::var("ZEPH_A2A_ENABLED")
257 && let Ok(enabled) = v.parse::<bool>()
258 {
259 self.a2a.enabled = enabled;
260 }
261 if let Ok(v) = std::env::var("ZEPH_A2A_HOST") {
262 self.a2a.host = v;
263 }
264 if let Ok(v) = std::env::var("ZEPH_A2A_PORT")
265 && let Ok(port) = v.parse::<u16>()
266 {
267 self.a2a.port = port;
268 }
269 if let Ok(v) = std::env::var("ZEPH_A2A_PUBLIC_URL") {
270 self.a2a.public_url = v;
271 }
272 if let Ok(v) = std::env::var("ZEPH_A2A_RATE_LIMIT")
273 && let Ok(rate) = v.parse::<u32>()
274 {
275 self.a2a.rate_limit = rate;
276 }
277 if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_ASSISTANT")
278 && let Ok(enabled) = v.parse::<bool>()
279 {
280 self.memory.autosave_assistant = enabled;
281 }
282 if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_MIN_LENGTH")
283 && let Ok(len) = v.parse::<usize>()
284 {
285 self.memory.autosave_min_length = len;
286 }
287 if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_ENABLED")
288 && let Ok(enabled) = v.parse::<bool>()
289 {
290 self.llm.response_cache_enabled = enabled;
291 }
292 if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_TTL_SECS")
293 && let Ok(secs) = v.parse::<u64>()
294 {
295 self.llm.response_cache_ttl_secs = secs;
296 }
297 if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_ENABLED")
298 && let Ok(enabled) = v.parse::<bool>()
299 {
300 self.llm.semantic_cache_enabled = enabled;
301 }
302 if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_THRESHOLD")
303 && let Ok(threshold) = v.parse::<f32>()
304 {
305 self.llm.semantic_cache_threshold = threshold;
306 }
307 if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_MAX_CANDIDATES")
308 && let Ok(max) = v.parse::<u32>()
309 {
310 self.llm.semantic_cache_max_candidates = max;
311 }
312 if let Ok(v) = std::env::var("ZEPH_ACP_ENABLED")
313 && let Ok(enabled) = v.parse::<bool>()
314 {
315 self.acp.enabled = enabled;
316 }
317 if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_NAME") {
318 self.acp.agent_name = v;
319 }
320 if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_VERSION") {
321 self.acp.agent_version = v;
322 }
323 if let Ok(v) = std::env::var("ZEPH_ACP_MAX_SESSIONS")
324 && let Ok(n) = v.parse::<usize>()
325 {
326 self.acp.max_sessions = n;
327 }
328 if let Ok(v) = std::env::var("ZEPH_ACP_SESSION_IDLE_TIMEOUT_SECS")
329 && let Ok(secs) = v.parse::<u64>()
330 {
331 self.acp.session_idle_timeout_secs = secs;
332 }
333 if let Ok(v) = std::env::var("ZEPH_ACP_PERMISSION_FILE") {
334 self.acp.permission_file = Some(std::path::PathBuf::from(v));
335 }
336 if let Ok(v) = std::env::var("ZEPH_ACP_AUTH_TOKEN") {
337 self.acp.auth_token = Some(v);
338 }
339 if let Ok(v) = std::env::var("ZEPH_ACP_DISCOVERY_ENABLED")
340 && let Ok(enabled) = v.parse::<bool>()
341 {
342 self.acp.discovery_enabled = enabled;
343 }
344 if let Ok(v) = std::env::var("ZEPH_LOG_FILE") {
345 self.logging.file = v;
346 }
347 if let Ok(v) = std::env::var("ZEPH_LOG_LEVEL") {
348 self.logging.level = v;
349 }
350 }
351
352 fn apply_env_overrides_security(&mut self) {
353 if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_PATHS") {
354 self.tools.shell.allowed_paths = v
355 .split(',')
356 .map(|s| s.trim().to_owned())
357 .filter(|s| !s.is_empty())
358 .collect();
359 }
360 if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOW_NETWORK")
361 && let Ok(allow) = v.parse::<bool>()
362 {
363 self.tools.shell.allow_network = allow;
364 }
365 if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_ENABLED")
366 && let Ok(enabled) = v.parse::<bool>()
367 {
368 self.tools.audit.enabled = enabled;
369 }
370 if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_DESTINATION") {
371 self.tools.audit.destination = match v.as_str() {
372 "stdout" => crate::AuditDestination::Stdout,
373 "stderr" => crate::AuditDestination::Stderr,
374 path => crate::AuditDestination::File(std::path::PathBuf::from(path)),
375 };
376 }
377 if let Ok(v) = std::env::var("ZEPH_SECURITY_REDACT_SECRETS")
378 && let Ok(redact) = v.parse::<bool>()
379 {
380 self.security.redact_secrets = redact;
381 }
382 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM")
383 && let Ok(secs) = v.parse::<u64>()
384 {
385 self.timeouts.llm_seconds = secs;
386 }
387 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM_REQUEST")
388 && let Ok(secs) = v.parse::<u64>()
389 {
390 self.timeouts.llm_request_timeout_secs = secs;
391 }
392 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_EMBEDDING")
393 && let Ok(secs) = v.parse::<u64>()
394 {
395 self.timeouts.embedding_seconds = secs;
396 }
397 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_A2A")
398 && let Ok(secs) = v.parse::<u64>()
399 {
400 self.timeouts.a2a_seconds = secs;
401 }
402 if let Ok(v) = std::env::var("ZEPH_A2A_REQUIRE_TLS")
403 && let Ok(require) = v.parse::<bool>()
404 {
405 self.a2a.require_tls = require;
406 }
407 if let Ok(v) = std::env::var("ZEPH_A2A_SSRF_PROTECTION")
408 && let Ok(ssrf) = v.parse::<bool>()
409 {
410 self.a2a.ssrf_protection = ssrf;
411 }
412 if let Ok(v) = std::env::var("ZEPH_A2A_MAX_BODY_SIZE")
413 && let Ok(size) = v.parse::<usize>()
414 {
415 self.a2a.max_body_size = size;
416 }
417 }
418}