Split silent and warning lenient unwraps

Keep Lenient::into_valid as the silent projection helper and add into_valid_with_warning for runtime config consumption that should emit startup warnings.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Ahmed Ibrahim
2026-05-05 04:08:08 +03:00
parent e064d502ae
commit 2e1b882d19
9 changed files with 36 additions and 27 deletions

View File

@@ -512,25 +512,25 @@ impl From<ConfigToml> for UserSavedConfig {
Self {
approval_policy: config_toml
.approval_policy
.and_then(|value| value.into_valid("approval_policy", None)),
.and_then(|value| value.into_valid()),
sandbox_mode: config_toml
.sandbox_mode
.and_then(|value| value.into_valid("sandbox_mode", None)),
.and_then(|value| value.into_valid()),
sandbox_settings: config_toml.sandbox_workspace_write.map(From::from),
forced_chatgpt_workspace_id: config_toml.forced_chatgpt_workspace_id,
forced_login_method: config_toml
.forced_login_method
.and_then(|value| value.into_valid("forced_login_method", None)),
.and_then(|value| value.into_valid()),
model: config_toml.model,
model_reasoning_effort: config_toml
.model_reasoning_effort
.and_then(|value| value.into_valid("model_reasoning_effort", None)),
.and_then(|value| value.into_valid()),
model_reasoning_summary: config_toml
.model_reasoning_summary
.and_then(|value| value.into_valid("model_reasoning_summary", None)),
.and_then(|value| value.into_valid()),
model_verbosity: config_toml
.model_verbosity
.and_then(|value| value.into_valid("model_verbosity", None)),
.and_then(|value| value.into_valid()),
tools: config_toml.tools.map(From::from),
profile: config_toml.profile,
profiles,

View File

@@ -28,15 +28,24 @@ impl<T> Lenient<T> {
}
}
pub fn into_valid(self, field_path: &str, warnings: Option<&mut Vec<String>>) -> Option<T> {
pub fn into_valid(self) -> Option<T> {
match self {
Self::Valid(value) => Some(value),
Self::Invalid(_) => None,
}
}
pub fn into_valid_with_warning(
self,
field_path: &str,
warnings: &mut Vec<String>,
) -> Option<T> {
match self {
Self::Valid(value) => Some(value),
Self::Invalid(value) => {
if let Some(warnings) = warnings {
warnings.push(format!(
"Ignoring invalid config value at {field_path}: {value}"
));
}
warnings.push(format!(
"Ignoring invalid config value at {field_path}: {value}"
));
None
}
}

View File

@@ -81,16 +81,16 @@ impl From<ConfigProfile> for codex_app_server_protocol::Profile {
model_provider: config_profile.model_provider,
approval_policy: config_profile
.approval_policy
.and_then(|value| value.into_valid("approval_policy", None)),
.and_then(|value| value.into_valid()),
model_reasoning_effort: config_profile
.model_reasoning_effort
.and_then(|value| value.into_valid("model_reasoning_effort", None)),
.and_then(|value| value.into_valid()),
model_reasoning_summary: config_profile
.model_reasoning_summary
.and_then(|value| value.into_valid("model_reasoning_summary", None)),
.and_then(|value| value.into_valid()),
model_verbosity: config_profile
.model_verbosity
.and_then(|value| value.into_valid("model_verbosity", None)),
.and_then(|value| value.into_valid()),
chatgpt_base_url: config_profile.chatgpt_base_url,
}
}

View File

@@ -148,7 +148,7 @@ sandbox_mode = "make-it-so"
let mut enum_warnings = Vec::new();
let sandbox_mode = config_toml
.sandbox_mode
.and_then(|value| value.into_valid("sandbox_mode", Some(&mut enum_warnings)));
.and_then(|value| value.into_valid_with_warning("sandbox_mode", &mut enum_warnings));
let expected_config = toml::from_str::<TomlValue>(
r#"
model = "gpt-5-codex"

View File

@@ -3443,7 +3443,7 @@ async fn managed_config_overrides_oauth_store_mode() -> anyhow::Result<()> {
assert_eq!(
cfg.mcp_oauth_credentials_store
.clone()
.and_then(|value| value.into_valid("mcp_oauth_credentials_store", None)),
.and_then(|value| value.into_valid()),
Some(OAuthCredentialsStoreMode::Keyring),
);
@@ -4570,7 +4570,7 @@ async fn set_model_updates_defaults() -> anyhow::Result<()> {
assert_eq!(
parsed
.model_reasoning_effort
.and_then(|value| value.into_valid("model_reasoning_effort", None)),
.and_then(|value| value.into_valid()),
Some(ReasoningEffort::High)
);
@@ -4606,7 +4606,7 @@ model = "gpt-4.1"
assert_eq!(
parsed
.model_reasoning_effort
.and_then(|value| value.into_valid("model_reasoning_effort", None)),
.and_then(|value| value.into_valid()),
Some(ReasoningEffort::High)
);
assert_eq!(
@@ -4642,7 +4642,7 @@ async fn set_model_updates_profile() -> anyhow::Result<()> {
profile
.model_reasoning_effort
.clone()
.and_then(|value| value.into_valid("model_reasoning_effort", None)),
.and_then(|value| value.into_valid()),
Some(ReasoningEffort::Medium)
);
@@ -4685,7 +4685,7 @@ model = "gpt-5.4"
dev_profile
.model_reasoning_effort
.clone()
.and_then(|value| value.into_valid("model_reasoning_effort", None)),
.and_then(|value| value.into_valid()),
Some(ReasoningEffort::Medium)
);

View File

@@ -1678,7 +1678,7 @@ fn valid_lenient<T>(
field_path: &str,
warnings: &mut Vec<String>,
) -> Option<T> {
value.and_then(|value| value.into_valid(field_path, Some(warnings)))
value.and_then(|value| value.into_valid_with_warning(field_path, warnings))
}
fn valid_lenient_ref<T: Clone>(
@@ -1688,7 +1688,7 @@ fn valid_lenient_ref<T: Clone>(
) -> Option<T> {
value
.clone()
.and_then(|value| value.into_valid(field_path, Some(warnings)))
.and_then(|value| value.into_valid_with_warning(field_path, warnings))
}
fn valid_lenient_ref_silent<T: Clone>(value: &Option<Lenient<T>>) -> Option<T> {

View File

@@ -17,7 +17,7 @@ use tokio::io::AsyncWriteExt;
const TEST_TIMESTAMP: &str = "2025-01-01T00-00-00";
fn valid_lenient<T>(value: Option<Lenient<T>>) -> Option<T> {
value.and_then(|value| value.into_valid("personality", None))
value.and_then(|value| value.into_valid())
}
async fn read_config_toml(codex_home: &Path) -> io::Result<ConfigToml> {

View File

@@ -149,7 +149,7 @@ mod tests {
assert_eq!(
lock.model_reasoning_effort
.clone()
.and_then(|value| value.into_valid("model_reasoning_effort", None)),
.and_then(|value| value.into_valid()),
sc.collaboration_mode.reasoning_effort()
);
assert_eq!(lock.profile, None);

View File

@@ -23,7 +23,7 @@ use tokio::io::AsyncWriteExt;
const TEST_TIMESTAMP: &str = "2025-01-01T00-00-00";
fn valid_lenient<T>(value: Option<Lenient<T>>) -> Option<T> {
value.and_then(|value| value.into_valid("personality", None))
value.and_then(|value| value.into_valid())
}
async fn read_config_toml(codex_home: &Path) -> io::Result<ConfigToml> {