From 3a53bdfd8d14dae9746e7876b56bae55a5d6c796 Mon Sep 17 00:00:00 2001 From: Andrei Hasna Date: Sat, 1 Aug 2026 21:00:47 +0300 Subject: [PATCH 1/2] fix: validate configured model reasoning effort Fixes #428. Agent: Silvanus --- codex-rs/core/src/session/mod.rs | 30 ++++++++++++ codex-rs/core/src/session/tests.rs | 79 ++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index ec5634ec3..ca47b23fc 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -463,6 +463,34 @@ pub(crate) fn resolve_multi_agent_version( }) } +fn validate_model_reasoning_effort( + model_info: &ModelInfo, + configured_effort: Option<&ReasoningEffortConfig>, +) -> Result<(), String> { + let Some(configured_effort) = configured_effort else { + return Ok(()); + }; + if model_info.supported_reasoning_levels.is_empty() + || model_info + .supported_reasoning_levels + .iter() + .any(|preset| &preset.effort == configured_effort) + { + return Ok(()); + } + + let supported_efforts = model_info + .supported_reasoning_levels + .iter() + .map(|preset| preset.effort.to_string()) + .collect::>() + .join(", "); + Err(format!( + "model_reasoning_effort `{configured_effort}` is not supported for model `{}`. Supported values: {supported_efforts}", + model_info.slug + )) +} + pub(crate) const INITIAL_SUBMIT_ID: &str = ""; pub(crate) const SUBMISSION_CHANNEL_CAPACITY: usize = 512; const CYBER_VERIFY_URL: &str = "https://chatgpt.com/cyber"; @@ -595,6 +623,8 @@ impl Codex { let model_info = models_manager .get_model_info(model.as_str(), &models_manager_config) .await; + validate_model_reasoning_effort(&model_info, config.model_reasoning_effort.as_ref()) + .map_err(CodexErr::InvalidRequest)?; let multi_agent_version = resolve_multi_agent_version(&conversation_history, inherited_multi_agent_version); config diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 37269652a..96f3327c5 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -53,6 +53,7 @@ use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::PermissionProfile; use codex_protocol::models::SandboxEnforcement; use codex_protocol::openai_models::ModelServiceTier; +use codex_protocol::openai_models::ReasoningEffortPreset; use codex_protocol::permissions::FileSystemAccessMode; use codex_protocol::permissions::FileSystemPath; use codex_protocol::permissions::FileSystemSandboxEntry; @@ -4442,6 +4443,84 @@ fn model_with_default_service_tier(default_service_tier: Option<&str>) -> ModelI model_info } +fn model_with_supported_reasoning_levels( + supported_reasoning_levels: Vec, +) -> ModelInfo { + let mut model_info = model_info::model_info_from_slug("gpt-5.4"); + model_info.supported_reasoning_levels = supported_reasoning_levels + .into_iter() + .map(|effort| ReasoningEffortPreset { + description: format!("{effort} reasoning"), + effort, + }) + .collect(); + model_info +} + +#[test] +fn model_reasoning_effort_rejects_unadvertised_custom_value() { + let model_info = model_with_supported_reasoning_levels(vec![ + ReasoningEffortConfig::Low, + ReasoningEffortConfig::High, + ]); + + assert_eq!( + validate_model_reasoning_effort( + &model_info, + Some(&ReasoningEffortConfig::Custom("unknown".to_string())), + ), + Err( + "model_reasoning_effort `unknown` is not supported for model `gpt-5.4`. Supported values: low, high" + .to_string() + ) + ); +} + +#[test] +fn model_reasoning_effort_accepts_ultra_alias_when_max_is_advertised() { + let model_info = model_with_supported_reasoning_levels(vec![ReasoningEffortConfig::Custom( + "max".to_string(), + )]); + let ultra = "ultra" + .parse::() + .expect("ultra should parse as the max alias"); + + assert_eq!( + validate_model_reasoning_effort(&model_info, Some(&ultra)), + Ok(()) + ); +} + +#[test] +fn model_reasoning_effort_rejects_ultra_alias_when_max_is_not_advertised() { + let model_info = + model_with_supported_reasoning_levels(vec![ReasoningEffortConfig::XHigh]); + let ultra = "ultra" + .parse::() + .expect("ultra should parse as the max alias"); + + assert_eq!( + validate_model_reasoning_effort(&model_info, Some(&ultra)), + Err( + "model_reasoning_effort `max` is not supported for model `gpt-5.4`. Supported values: xhigh" + .to_string() + ) + ); +} + +#[test] +fn model_reasoning_effort_allows_custom_value_without_model_metadata() { + let model_info = model_with_supported_reasoning_levels(Vec::new()); + + assert_eq!( + validate_model_reasoning_effort( + &model_info, + Some(&ReasoningEffortConfig::Custom("provider-defined".to_string())), + ), + Ok(()) + ); +} + #[test] fn get_service_tier_does_not_use_model_default_when_absent_and_fast_mode_enabled() { let model_info = model_with_default_service_tier(Some(ServiceTier::Fast.request_value())); From 56f7abdab6fd092392b453feebace8640b0a67dc Mon Sep 17 00:00:00 2001 From: Andrei Hasna Date: Sat, 1 Aug 2026 21:08:37 +0300 Subject: [PATCH 2/2] fix: format reasoning effort tests Remediates PR #473 review formatting blocker. Agent: Silvanus --- codex-rs/core/src/session/tests.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 96f3327c5..712a398a3 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -4493,8 +4493,7 @@ fn model_reasoning_effort_accepts_ultra_alias_when_max_is_advertised() { #[test] fn model_reasoning_effort_rejects_ultra_alias_when_max_is_not_advertised() { - let model_info = - model_with_supported_reasoning_levels(vec![ReasoningEffortConfig::XHigh]); + let model_info = model_with_supported_reasoning_levels(vec![ReasoningEffortConfig::XHigh]); let ultra = "ultra" .parse::() .expect("ultra should parse as the max alias"); @@ -4515,7 +4514,9 @@ fn model_reasoning_effort_allows_custom_value_without_model_metadata() { assert_eq!( validate_model_reasoning_effort( &model_info, - Some(&ReasoningEffortConfig::Custom("provider-defined".to_string())), + Some(&ReasoningEffortConfig::Custom( + "provider-defined".to_string() + )), ), Ok(()) );