diff --git a/codex-rs/app-server/tests/suite/v2/model_list.rs b/codex-rs/app-server/tests/suite/v2/model_list.rs index 8b17185f44..3c895fb7c4 100644 --- a/codex-rs/app-server/tests/suite/v2/model_list.rs +++ b/codex-rs/app-server/tests/suite/v2/model_list.rs @@ -45,6 +45,29 @@ async fn list_models_returns_all_models_with_large_limit() -> Result<()> { } = to_response::(response)?; let expected_models = vec![ + Model { + id: "codex-auto".to_string(), + model: "codex-auto".to_string(), + display_name: "codex-auto".to_string(), + description: "Automatically chooses the best Codex model configuration for your task." + .to_string(), + supported_reasoning_efforts: vec![ + ReasoningEffortOption { + reasoning_effort: ReasoningEffort::Low, + description: "Works faster".to_string(), + }, + ReasoningEffortOption { + reasoning_effort: ReasoningEffort::Medium, + description: "Balances speed with intelligence".to_string(), + }, + ReasoningEffortOption { + reasoning_effort: ReasoningEffort::High, + description: "Works longer for harder tasks".to_string(), + }, + ], + default_reasoning_effort: ReasoningEffort::Medium, + is_default: true, + }, Model { id: "gpt-5.1-codex".to_string(), model: "gpt-5.1-codex".to_string(), @@ -66,7 +89,7 @@ async fn list_models_returns_all_models_with_large_limit() -> Result<()> { }, ], default_reasoning_effort: ReasoningEffort::Medium, - is_default: true, + is_default: false, }, Model { id: "gpt-5.1-codex-mini".to_string(), @@ -147,7 +170,7 @@ async fn list_models_pagination_works() -> Result<()> { } = to_response::(first_response)?; assert_eq!(first_items.len(), 1); - assert_eq!(first_items[0].id, "gpt-5.1-codex"); + assert_eq!(first_items[0].id, "codex-auto"); let next_cursor = first_cursor.ok_or_else(|| anyhow!("cursor for second page"))?; let second_request = mcp @@ -169,7 +192,7 @@ async fn list_models_pagination_works() -> Result<()> { } = to_response::(second_response)?; assert_eq!(second_items.len(), 1); - assert_eq!(second_items[0].id, "gpt-5.1-codex-mini"); + assert_eq!(second_items[0].id, "gpt-5.1-codex"); let third_cursor = second_cursor.ok_or_else(|| anyhow!("cursor for third page"))?; let third_request = mcp @@ -191,8 +214,30 @@ async fn list_models_pagination_works() -> Result<()> { } = to_response::(third_response)?; assert_eq!(third_items.len(), 1); - assert_eq!(third_items[0].id, "gpt-5.1"); - assert!(third_cursor.is_none()); + assert_eq!(third_items[0].id, "gpt-5.1-codex-mini"); + let fourth_cursor = third_cursor.ok_or_else(|| anyhow!("cursor for fourth page"))?; + + let fourth_request = mcp + .send_list_models_request(ModelListParams { + limit: Some(1), + cursor: Some(fourth_cursor.clone()), + }) + .await?; + + let fourth_response: JSONRPCResponse = timeout( + DEFAULT_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(fourth_request)), + ) + .await??; + + let ModelListResponse { + data: fourth_items, + next_cursor: fourth_cursor, + } = to_response::(fourth_response)?; + + assert_eq!(fourth_items.len(), 1); + assert_eq!(fourth_items[0].id, "gpt-5.1"); + assert!(fourth_cursor.is_none()); Ok(()) } diff --git a/codex-rs/common/src/model_presets.rs b/codex-rs/common/src/model_presets.rs index 29da9969b2..81932277a2 100644 --- a/codex-rs/common/src/model_presets.rs +++ b/codex-rs/common/src/model_presets.rs @@ -245,11 +245,15 @@ static PRESETS: Lazy> = Lazy::new(|| { ] }); -pub fn builtin_model_presets(_auth_mode: Option) -> Vec { - // leave auth mode for later use +pub fn builtin_model_presets(auth_mode: Option) -> Vec { PRESETS .iter() .filter(|preset| preset.upgrade.is_none()) + .filter(|preset| match auth_mode { + // `codex-auto` is only available when using ChatGPT-style auth. + Some(AuthMode::ApiKey) => preset.id != "codex-auto", + _ => true, + }) .cloned() .collect() } @@ -276,4 +280,31 @@ mod tests { let default_models = PRESETS.iter().filter(|preset| preset.is_default).count(); assert!(default_models == 1); } + + #[test] + fn codex_auto_is_included_for_non_api_auth() { + let presets_no_auth = builtin_model_presets(None); + assert!( + presets_no_auth + .iter() + .any(|preset| preset.id == "codex-auto") + ); + + let presets_chatgpt = builtin_model_presets(Some(AuthMode::ChatGPT)); + assert!( + presets_chatgpt + .iter() + .any(|preset| preset.id == "codex-auto") + ); + } + + #[test] + fn codex_auto_is_excluded_for_api_key_auth() { + let presets_api_key = builtin_model_presets(Some(AuthMode::ApiKey)); + assert!( + !presets_api_key + .iter() + .any(|preset| preset.id == "codex-auto") + ); + } } diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index c858ea20fd..b768df25ac 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -194,6 +194,18 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> { "view_image", ], ), + ( + "codex-auto", + vec![ + "shell", + "list_mcp_resources", + "list_mcp_resource_templates", + "read_mcp_resource", + "update_plan", + "apply_patch", + "view_image", + ], + ), ( "gpt-5.1-codex", vec![ diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap index 060d1f82a9..0d6c000940 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap @@ -4,9 +4,9 @@ expression: popup --- Select Reasoning Level for gpt-5.1-codex - 1. Low Fastest responses with limited reasoning - 2. Medium (default) Dynamically adjusts reasoning based on the task -› 3. High (current) Maximizes reasoning depth for complex or ambiguous + 1. low Fastest responses with limited reasoning + 2. medium (default) Dynamically adjusts reasoning based on the task +› 3. high (current) Maximizes reasoning depth for complex or ambiguous problems ⚠ High reasoning effort can quickly consume Plus plan rate limits. diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_selection_popup.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_selection_popup.snap index 001558ef05..6cfce48b89 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_selection_popup.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_selection_popup.snap @@ -1,15 +1,13 @@ --- source: tui/src/chatwidget/tests.rs -assertion_line: 1440 expression: popup --- - Select Model - Quickly pick Codex Auto or open the legacy list for more options. + Select Model and Effort + Access legacy models by running codex -m or in your config.toml -› 1. Fast Works faster - 2. Balanced (default) Balances speed with intelligence - 3. Thorough Works longer for harder tasks - 4. All models Choose and configure what model and reasoning level - to use +› 1. gpt-5.1-codex Optimized for codex. + 2. gpt-5.1-codex-mini Optimized for codex. Cheaper, faster, but less + capable. + 3. gpt-5.1 Broad world knowledge with strong general reasoning. - Press enter to apply selection, or esc to dismiss. + Press enter to select reasoning effort, or esc to dismiss. diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index b416aa5158..446e591e3d 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -1440,6 +1440,21 @@ fn model_selection_popup_snapshot() { assert_snapshot!("model_selection_popup", popup); } +#[test] +fn model_selection_popup_chatgpt_auth_snapshot() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(); + + chat.auth_manager = + AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing()); + chat.config.model = "gpt-5.1-codex".to_string(); + chat.open_model_popup(); + + let popup = render_bottom_popup(&chat, 80); + insta::with_settings!({ snapshot_suffix => "chatgpt_auth" }, { + assert_snapshot!("model_selection_popup", popup); + }); +} + #[test] fn featured_model_popup_hides_default_label_when_option_is_current() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual();