Skip to main content

zeph_config/
env.rs

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