fix(config): preserve explicit custom provider against legacy PROVIDER override
This commit is contained in:
parent
d6dca4b890
commit
9381e4451a
3 changed files with 67 additions and 4 deletions
|
|
@ -20,6 +20,19 @@ Schema export command:
|
||||||
| `default_model` | `anthropic/claude-sonnet-4-6` | model routed through selected provider |
|
| `default_model` | `anthropic/claude-sonnet-4-6` | model routed through selected provider |
|
||||||
| `default_temperature` | `0.7` | model temperature |
|
| `default_temperature` | `0.7` | model temperature |
|
||||||
|
|
||||||
|
## Environment Provider Overrides
|
||||||
|
|
||||||
|
Provider selection can also be controlled by environment variables. Precedence is:
|
||||||
|
|
||||||
|
1. `ZEROCLAW_PROVIDER` (explicit override, always wins when non-empty)
|
||||||
|
2. `PROVIDER` (legacy fallback, only applied when config provider is unset or still `openrouter`)
|
||||||
|
3. `default_provider` in `config.toml`
|
||||||
|
|
||||||
|
Operational note for container users:
|
||||||
|
|
||||||
|
- If your `config.toml` sets an explicit custom provider like `custom:https://.../v1`, a default `PROVIDER=openrouter` from Docker/container env will no longer replace it.
|
||||||
|
- Use `ZEROCLAW_PROVIDER` when you intentionally want runtime env to override a non-default configured provider.
|
||||||
|
|
||||||
## `[agent]`
|
## `[agent]`
|
||||||
|
|
||||||
| Key | Default | Purpose |
|
| Key | Default | Purpose |
|
||||||
|
|
|
||||||
|
|
@ -2767,13 +2767,23 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider: ZEROCLAW_PROVIDER or PROVIDER
|
// Provider override precedence:
|
||||||
if let Ok(provider) =
|
// 1) ZEROCLAW_PROVIDER always wins when set.
|
||||||
std::env::var("ZEROCLAW_PROVIDER").or_else(|_| std::env::var("PROVIDER"))
|
// 2) Legacy PROVIDER is only honored when config still uses the
|
||||||
{
|
// default provider (openrouter) or provider is unset. This prevents
|
||||||
|
// container defaults from overriding explicit custom providers.
|
||||||
|
if let Ok(provider) = std::env::var("ZEROCLAW_PROVIDER") {
|
||||||
if !provider.is_empty() {
|
if !provider.is_empty() {
|
||||||
self.default_provider = Some(provider);
|
self.default_provider = Some(provider);
|
||||||
}
|
}
|
||||||
|
} else if let Ok(provider) = std::env::var("PROVIDER") {
|
||||||
|
let should_apply_legacy_provider = self.default_provider.as_deref().map_or(
|
||||||
|
true,
|
||||||
|
|configured| configured.trim().eq_ignore_ascii_case("openrouter"),
|
||||||
|
);
|
||||||
|
if should_apply_legacy_provider && !provider.is_empty() {
|
||||||
|
self.default_provider = Some(provider);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Model: ZEROCLAW_MODEL or MODEL
|
// Model: ZEROCLAW_MODEL or MODEL
|
||||||
|
|
@ -4353,6 +4363,42 @@ default_temperature = 0.7
|
||||||
std::env::remove_var("PROVIDER");
|
std::env::remove_var("PROVIDER");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn env_override_provider_fallback_does_not_replace_non_default_provider() {
|
||||||
|
let _env_guard = env_override_test_guard();
|
||||||
|
let mut config = Config {
|
||||||
|
default_provider: Some("custom:https://proxy.example.com/v1".to_string()),
|
||||||
|
..Config::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
std::env::remove_var("ZEROCLAW_PROVIDER");
|
||||||
|
std::env::set_var("PROVIDER", "openrouter");
|
||||||
|
config.apply_env_overrides();
|
||||||
|
assert_eq!(
|
||||||
|
config.default_provider.as_deref(),
|
||||||
|
Some("custom:https://proxy.example.com/v1")
|
||||||
|
);
|
||||||
|
|
||||||
|
std::env::remove_var("PROVIDER");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn env_override_zero_claw_provider_overrides_non_default_provider() {
|
||||||
|
let _env_guard = env_override_test_guard();
|
||||||
|
let mut config = Config {
|
||||||
|
default_provider: Some("custom:https://proxy.example.com/v1".to_string()),
|
||||||
|
..Config::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
std::env::set_var("ZEROCLAW_PROVIDER", "openrouter");
|
||||||
|
std::env::set_var("PROVIDER", "anthropic");
|
||||||
|
config.apply_env_overrides();
|
||||||
|
assert_eq!(config.default_provider.as_deref(), Some("openrouter"));
|
||||||
|
|
||||||
|
std::env::remove_var("ZEROCLAW_PROVIDER");
|
||||||
|
std::env::remove_var("PROVIDER");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn env_override_glm_api_key_for_regional_aliases() {
|
fn env_override_glm_api_key_for_regional_aliases() {
|
||||||
let _env_guard = env_override_test_guard();
|
let _env_guard = env_override_test_guard();
|
||||||
|
|
|
||||||
|
|
@ -1194,6 +1194,8 @@ mod tests {
|
||||||
idempotency_store: Arc::new(IdempotencyStore::new(Duration::from_secs(300), 1000)),
|
idempotency_store: Arc::new(IdempotencyStore::new(Duration::from_secs(300), 1000)),
|
||||||
whatsapp: None,
|
whatsapp: None,
|
||||||
whatsapp_app_secret: None,
|
whatsapp_app_secret: None,
|
||||||
|
linq: None,
|
||||||
|
linq_signing_secret: None,
|
||||||
observer: Arc::new(crate::observability::NoopObserver),
|
observer: Arc::new(crate::observability::NoopObserver),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1235,6 +1237,8 @@ mod tests {
|
||||||
idempotency_store: Arc::new(IdempotencyStore::new(Duration::from_secs(300), 1000)),
|
idempotency_store: Arc::new(IdempotencyStore::new(Duration::from_secs(300), 1000)),
|
||||||
whatsapp: None,
|
whatsapp: None,
|
||||||
whatsapp_app_secret: None,
|
whatsapp_app_secret: None,
|
||||||
|
linq: None,
|
||||||
|
linq_signing_secret: None,
|
||||||
observer,
|
observer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue