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