diff --git a/code-rs/code-auto-drive-core/src/auto_coordinator.rs b/code-rs/code-auto-drive-core/src/auto_coordinator.rs index e81755d084d..738124e87f0 100644 --- a/code-rs/code-auto-drive-core/src/auto_coordinator.rs +++ b/code-rs/code-auto-drive-core/src/auto_coordinator.rs @@ -66,11 +66,11 @@ const MAX_QUEUED_CONVERSATION_UPDATES: usize = 24; const DEBUG_JSON_MAX_CHARS: usize = 1200; const CLI_PROMPT_MIN_CHARS: usize = 4; const CLI_PROMPT_MAX_CHARS: usize = 600; -const AUTO_DRIVE_CLI_MODEL_PRIMARY: &str = "gpt-5.3-codex"; -const AUTO_DRIVE_CLI_MODEL_SPARK: &str = "gpt-5.3-codex-spark"; +const AUTO_DRIVE_CLI_MODEL_PRIMARY: &str = "gpt-5.5"; +const AUTO_DRIVE_CLI_MODEL_FAST: &str = "gpt-5.4"; const AUTO_DRIVE_PRIMARY_ROUTING_DESCRIPTION: &str = "Hard planning and complex problem solving"; -const AUTO_DRIVE_SPARK_ROUTING_DESCRIPTION: &str = +const AUTO_DRIVE_FAST_ROUTING_DESCRIPTION: &str = "Fast implementation loops and failing-test iteration"; static HARD_LIMIT_TRIMMED_ITEMS_TOTAL: AtomicU64 = AtomicU64::new(0); @@ -80,7 +80,7 @@ static QUEUED_UPDATE_DROPS_TOTAL: AtomicU64 = AtomicU64::new(0); #[error("auto coordinator cancelled")] struct AutoCoordinatorCancelled; -pub const MODEL_SLUG: &str = "gpt-5.1"; +pub const MODEL_SLUG: &str = "gpt-5.5"; const USER_TURN_SCHEMA_NAME: &str = "auto_coordinator_user_turn"; const COORDINATOR_PROMPT: &str = include_str!("../../core/prompt_coordinator.md"); const TIMEBOXED_EXEC_COORDINATOR_GUIDANCE: &str = "SYSTEM: Time-boxed autonomous exec is enabled.\n\nYou are coordinating a non-interactive coding run. Optimize for verifier success, not exploration.\n\nContract-first and evidence-led:\n- In the first 3 minutes, force a concrete failing signal by running the most authoritative acceptance check available. Prefer (in order): `/tests/verify.sh` (read it, then run it) > a task-provided verifier script > the narrowest relevant test (e.g. `pytest -q /tests/test_outputs.py`).\n- Treat that check as the contract: exact paths, ports, formats, exit codes, and stated constraints.\n\nConverge with small diffs:\n- Ship a minimal first-pass fix quickly, then iterate against the same check.\n- Avoid speculative redesigns, broad refactors, or dependency churn unless forced by the contract.\n\nDelegate by outcomes only:\n- Each turn: give one short directive phrased as an outcome (\"make X pass\", \"produce file Y at path Z\", \"ensure binary at /path is executable\"). Let the CLI pick tactics.\n\nTime discipline:\n- Prefer cheap proofs before expensive steps (long builds/downloads/services).\n- Prefer local files and official libraries over scraping web UIs/config endpoints.\n\nFinish standard:\n- finish_success only with proof (acceptance check green + required artifacts present exactly where asserted).\n- Otherwise finish_failed with the last check, its output/error, what changed, and the single next verification step."; @@ -160,31 +160,27 @@ fn default_auto_drive_cli_routing_entries() -> Vec { description: AUTO_DRIVE_PRIMARY_ROUTING_DESCRIPTION.to_string(), }, AutoDriveCliRoutingEntry { - model: AUTO_DRIVE_CLI_MODEL_SPARK.to_string(), + model: AUTO_DRIVE_CLI_MODEL_FAST.to_string(), reasoning_levels: vec![ReasoningEffort::High], - description: AUTO_DRIVE_SPARK_ROUTING_DESCRIPTION.to_string(), + description: AUTO_DRIVE_FAST_ROUTING_DESCRIPTION.to_string(), }, ] } fn auto_drive_cli_routing_entries_for_auth( - auth_mode: Option, - supports_pro_only_models: bool, + _auth_mode: Option, + _supports_pro_only_models: bool, ) -> Vec { let mut entries = vec![AutoDriveCliRoutingEntry { model: AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string(), reasoning_levels: vec![ReasoningEffort::High, ReasoningEffort::XHigh], description: AUTO_DRIVE_PRIMARY_ROUTING_DESCRIPTION.to_string(), }]; - if auth_mode.is_some_and(code_app_server_protocol::AuthMode::is_chatgpt) - && supports_pro_only_models - { - entries.push(AutoDriveCliRoutingEntry { - model: AUTO_DRIVE_CLI_MODEL_SPARK.to_string(), - reasoning_levels: vec![ReasoningEffort::High], - description: AUTO_DRIVE_SPARK_ROUTING_DESCRIPTION.to_string(), - }); - } + entries.push(AutoDriveCliRoutingEntry { + model: AUTO_DRIVE_CLI_MODEL_FAST.to_string(), + reasoning_levels: vec![ReasoningEffort::High], + description: AUTO_DRIVE_FAST_ROUTING_DESCRIPTION.to_string(), + }); entries } @@ -268,9 +264,6 @@ fn resolve_auto_drive_cli_routing_entries( available_models: &[String], ) -> Vec { let mut entries = normalize_auto_drive_cli_routing_entries(&settings.model_routing_entries); - if !auth_mode.is_some_and(|mode| mode.is_chatgpt()) || !supports_pro_only_models { - entries.retain(|entry| !entry.model.eq_ignore_ascii_case(AUTO_DRIVE_CLI_MODEL_SPARK)); - } entries.retain(|entry| { available_models .iter() @@ -291,36 +284,6 @@ fn resolve_auto_drive_cli_routing_entries( entries } -fn spark_fallback_model(model: &str) -> Option<&'static str> { - if model.eq_ignore_ascii_case("gpt-5.3-codex-spark") { - Some("gpt-5.3-codex") - } else if model.eq_ignore_ascii_case("code-gpt-5.3-codex-spark") { - Some("code-gpt-5.3-codex") - } else { - None - } -} - -fn is_usage_limit_stream_error_message(message: &str) -> bool { - let lower = message.to_ascii_lowercase(); - lower.contains("usage limit") - || lower.contains("usage_limit_reached") - || lower.contains("usage_not_included") -} - -fn error_mentions_usage_limit(error: &anyhow::Error) -> bool { - error.chain().any(|cause| { - if let Some(code_err) = cause.downcast_ref::() { - return match code_err { - CodexErr::UsageLimitReached(_) | CodexErr::UsageNotIncluded => true, - CodexErr::Stream(message, _, _) => is_usage_limit_stream_error_message(message), - _ => false, - }; - } - false - }) -} - #[derive(Debug, Clone)] struct AutoTimeBudget { deadline: Instant, @@ -1183,7 +1146,7 @@ mod tests { "status_title": "Fixing tests", "status_sent_to_user": "Running clear failing-test loops.", "cli_milestone_instruction": "Take the failing test from red to green and report the passing evidence.", - "cli_model": "gpt-5.3-codex-spark", + "cli_model": "gpt-5.4", "cli_reasoning_effort": "high" }"#; @@ -1197,18 +1160,18 @@ mod tests { .expect("routing-enabled decision should parse"); let cli = decision.cli.expect("cli action expected"); - assert_eq!(cli.model_override.as_deref(), Some("gpt-5.3-codex-spark")); + assert_eq!(cli.model_override.as_deref(), Some(AUTO_DRIVE_CLI_MODEL_FAST)); assert_eq!(cli.reasoning_effort_override, Some(ReasoningEffort::High)); } #[test] - fn parse_decision_rejects_spark_when_not_allowed() { + fn parse_decision_rejects_fast_model_when_not_allowed() { let raw = r#"{ "finish_status": "continue", "status_title": "Fixing tests", "status_sent_to_user": "Running clear failing-test loops.", "cli_milestone_instruction": "Take the failing test from red to green and report the passing evidence.", - "cli_model": "gpt-5.3-codex-spark", + "cli_model": "gpt-5.4", "cli_reasoning_effort": "high" }"#; @@ -1223,7 +1186,7 @@ mod tests { }], }, ) - .expect_err("non-pro routing should reject spark"); + .expect_err("restricted routing should reject fast model"); assert!(err.to_string().contains("unsupported cli_model")); assert!(err.to_string().contains(AUTO_DRIVE_CLI_MODEL_PRIMARY)); @@ -1236,7 +1199,7 @@ mod tests { "status_title": "Fixing tests", "status_sent_to_user": "Running clear failing-test loops.", "cli_milestone_instruction": "Take the failing test from red to green and report the passing evidence.", - "cli_model": "gpt-5.3-codex", + "cli_model": "gpt-5.5", "cli_reasoning_effort": "xhigh" }"#; @@ -1259,25 +1222,25 @@ mod tests { } #[test] - fn auto_drive_cli_models_gates_spark_by_auth() { + fn auto_drive_cli_models_are_auth_independent() { let pro_models = auto_drive_cli_models_for_auth(Some(AuthMode::Chatgpt), true); assert!(pro_models.contains(&AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string())); - assert!(pro_models.contains(&AUTO_DRIVE_CLI_MODEL_SPARK.to_string())); + assert!(pro_models.contains(&AUTO_DRIVE_CLI_MODEL_FAST.to_string())); let non_pro_models = auto_drive_cli_models_for_auth(Some(AuthMode::Chatgpt), false); assert!(non_pro_models.contains(&AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string())); - assert!(!non_pro_models.contains(&AUTO_DRIVE_CLI_MODEL_SPARK.to_string())); + assert!(non_pro_models.contains(&AUTO_DRIVE_CLI_MODEL_FAST.to_string())); let api_key_models = auto_drive_cli_models_for_auth(Some(AuthMode::ApiKey), false); assert!(api_key_models.contains(&AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string())); - assert!(!api_key_models.contains(&AUTO_DRIVE_CLI_MODEL_SPARK.to_string())); + assert!(api_key_models.contains(&AUTO_DRIVE_CLI_MODEL_FAST.to_string())); } #[test] fn resolve_cli_routing_entries_falls_back_when_enabled_entries_missing() { let mut settings = AutoDriveSettings::default(); settings.model_routing_entries = vec![AutoDriveModelRoutingEntry { - model: "gpt-5.3-codex".to_string(), + model: AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string(), enabled: false, reasoning_levels: vec![ReasoningEffort::High], description: "disabled".to_string(), @@ -1285,7 +1248,7 @@ mod tests { let available_models = vec![ AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string(), - AUTO_DRIVE_CLI_MODEL_SPARK.to_string(), + AUTO_DRIVE_CLI_MODEL_FAST.to_string(), ]; let entries = resolve_auto_drive_cli_routing_entries( @@ -1296,7 +1259,7 @@ mod tests { ); assert!(entries.iter().any(|entry| entry.model == AUTO_DRIVE_CLI_MODEL_PRIMARY)); - assert!(entries.iter().any(|entry| entry.model == AUTO_DRIVE_CLI_MODEL_SPARK)); + assert!(entries.iter().any(|entry| entry.model == AUTO_DRIVE_CLI_MODEL_FAST)); } #[test] @@ -1310,7 +1273,7 @@ mod tests { description: String::new(), }, AutoDriveModelRoutingEntry { - model: "gpt-5.3-codex-experimental".to_string(), + model: "gpt-5.5-experimental".to_string(), enabled: true, reasoning_levels: vec![ReasoningEffort::High], description: String::new(), @@ -1584,30 +1547,6 @@ mod tests { } } - #[test] - fn usage_limit_stream_errors_are_detected() { - let err = anyhow!(CodexErr::Stream( - "[transport] Transport error: You've hit your usage limit. Try again in 5 days 47 minutes." - .to_string(), - None, - None, - )); - assert!(error_mentions_usage_limit(&err)); - } - - #[test] - fn spark_models_have_non_spark_fallback() { - assert_eq!( - spark_fallback_model("gpt-5.3-codex-spark"), - Some("gpt-5.3-codex") - ); - assert_eq!( - spark_fallback_model("code-gpt-5.3-codex-spark"), - Some("code-gpt-5.3-codex") - ); - assert!(spark_fallback_model("gpt-5.3-codex").is_none()); - } - #[test] fn push_unique_guidance_trims_and_dedupes() { let mut guidance = vec!["Keep CLI prompts short".to_string()]; @@ -3259,32 +3198,7 @@ fn request_decision_with_model( let cancel = cancel_token.clone(); let mut rate_limit_switch_state = RateLimitSwitchState::default(); let selected_model = Arc::new(Mutex::new(model_slug.to_string())); - let selected_model_for_retry = Arc::clone(&selected_model); - let mut did_usage_limit_model_fallback = false; let classify = |error: &anyhow::Error| { - if !did_usage_limit_model_fallback && error_mentions_usage_limit(error) { - let active_model = selected_model_for_retry - .lock() - .ok() - .map(|guard| guard.clone()) - .unwrap_or_else(|| model_slug.to_string()); - if let Some(fallback_model) = spark_fallback_model(&active_model) { - did_usage_limit_model_fallback = true; - if let Ok(mut guard) = selected_model_for_retry.lock() { - *guard = fallback_model.to_string(); - } - event_tx.send(AutoCoordinatorEvent::Action { - message: format!( - "Usage limit reached for {active_model}; retrying with {fallback_model}…" - ), - }); - return RetryDecision::RateLimited { - wait_until: Instant::now(), - reason: "usage limit reached; switched to non-spark model".to_string(), - }; - } - } - classify_model_error_with_auto_switch(client, &mut rate_limit_switch_state, event_tx, error) }; let options = RetryOptions::with_defaults(retry_max_elapsed(time_budget_deadline)); diff --git a/code-rs/core/src/agent_defaults.rs b/code-rs/core/src/agent_defaults.rs index 92b51b1c782..b7abb158212 100644 --- a/code-rs/core/src/agent_defaults.rs +++ b/code-rs/core/src/agent_defaults.rs @@ -101,15 +101,7 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[ description: "Default frontier model for complex coding, research, and real-world work.", enabled_by_default: true, aliases: &[ - "code-gpt-5.3-codex", - "code-gpt-5.3-codex-spark", - "code-gpt-5.3-spark", - "code-gpt-5.2-codex", "gpt-5.5", - "gpt-5.3-codex", - "gpt-5.3-codex-spark", - "codex-spark", - "gpt-5.2-codex", "code-gpt-5.1-codex-max", "code-gpt-5.1-codex", "code-gpt-5-codex", @@ -134,9 +126,7 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[ description: "Highest-capacity GPT option for tricky reasoning; use when correctness matters most.", enabled_by_default: true, aliases: &[ - "code-gpt-5.2", "gpt-5.4", - "gpt-5.2", "code-gpt-5.1", "code-gpt-5", "gpt-5.1", @@ -324,8 +314,8 @@ fn dynamic_code_agent_spec(model: ManifestModel) -> Option { let _display_name = leak_str(model.display_name); let aliases = leak_str_slice(vec![model_slug]); let model_args = leak_str_slice(vec!["--model", model_slug]); - let pro_only = matches!(track, CodeAgentTrack::CodexSpark); - let is_frontline = !matches!(track, CodeAgentTrack::Mini | CodeAgentTrack::CodexSpark); + let pro_only = false; + let is_frontline = !matches!(track, CodeAgentTrack::Mini); Some(AgentModelSpec { slug, @@ -348,7 +338,6 @@ enum CodeAgentTrack { Base, Mini, Codex, - CodexSpark, } fn code_agent_track(model: &str) -> Option { @@ -358,7 +347,7 @@ fn code_agent_track(model: &str) -> Option { } if canonical.contains("codex-spark") { - Some(CodeAgentTrack::CodexSpark) + None } else if canonical.contains("codex") { Some(CodeAgentTrack::Codex) } else if canonical.ends_with("-mini") { @@ -679,17 +668,6 @@ mod tests { let codex_direct = agent_model_spec("gpt-5.1-codex-max").expect("codex present"); assert_eq!(codex_direct.slug, "code-gpt-5.5"); - let codex_upgrade = agent_model_spec("gpt-5.2-codex").expect("upgrade alias present"); - assert_eq!(codex_upgrade.slug, "code-gpt-5.5"); - - let codex_slug_upgrade = - agent_model_spec("code-gpt-5.2-codex").expect("slug upgrade alias present"); - assert_eq!(codex_slug_upgrade.slug, "code-gpt-5.5"); - - let spark = - agent_model_spec("gpt-5.3-codex-spark").expect("spark alias for codex present"); - assert_eq!(spark.slug, "code-gpt-5.5"); - let mini = agent_model_spec("gpt-5.1-codex-mini").expect("mini alias present"); assert_eq!(mini.slug, "code-gpt-5.4-mini"); @@ -699,8 +677,10 @@ mod tests { let mid = agent_model_spec("gpt-5.1").expect("mid alias present"); assert_eq!(mid.slug, "code-gpt-5.4"); - let mid_upgrade = agent_model_spec("code-gpt-5.2").expect("mid upgrade alias present"); - assert_eq!(mid_upgrade.slug, "code-gpt-5.4"); + assert!(agent_model_spec("gpt-5.2-codex").is_none()); + assert!(agent_model_spec("code-gpt-5.2-codex").is_none()); + assert!(agent_model_spec("gpt-5.3-codex-spark").is_none()); + assert!(agent_model_spec("code-gpt-5.2").is_none()); } #[test] @@ -712,23 +692,20 @@ mod tests { .all(|spec| spec.slug != "code-gpt-5.3-codex" && spec.slug != "code-gpt-5.3-codex-spark") ); - - assert_eq!(agent_model_spec("code-gpt-5.3-codex").unwrap().slug, "code-gpt-5.5"); - assert_eq!(agent_model_spec("codex-spark").unwrap().slug, "code-gpt-5.5"); } #[test] - fn filter_agent_model_names_retains_retired_aliases_as_gpt_5_5() { + fn filter_agent_model_names_keeps_unknown_selectors_for_custom_config() { let filtered = filter_agent_model_names_for_auth( vec![ "code-gpt-5.5".to_string(), - "gpt-5.3-codex-spark".to_string(), + "custom-model".to_string(), ], Some(AuthMode::ApiKey), false, ); - assert_eq!(filtered, vec!["code-gpt-5.5", "gpt-5.3-codex-spark"]); + assert_eq!(filtered, vec!["code-gpt-5.5", "custom-model"]); } #[test] @@ -747,12 +724,11 @@ mod tests { #[test] fn dynamic_agent_specs_skip_older_manifest_models() { - let gpt_5_2 = agent_model_spec("gpt-5.2").expect("gpt-5.2 should resolve via upgrade alias"); - assert_eq!(gpt_5_2.slug, "code-gpt-5.4"); assert!( enabled_agent_model_specs() .iter() .all(|spec| spec.slug != "code-gpt-5.2") ); + assert!(agent_model_spec("gpt-5.2").is_none()); } } diff --git a/code-rs/core/src/codex/streaming.rs b/code-rs/core/src/codex/streaming.rs index 4a960c765fb..45d1431cb1c 100644 --- a/code-rs/core/src/codex/streaming.rs +++ b/code-rs/core/src/codex/streaming.rs @@ -1666,23 +1666,6 @@ fn is_context_overflow_stream_error(message: &str) -> bool { || lower.contains("too long"))) } -fn is_usage_limit_stream_error(message: &str) -> bool { - let lower = message.to_ascii_lowercase(); - lower.contains("usage limit") - || lower.contains("usage_limit_reached") - || lower.contains("usage_not_included") -} - -fn spark_fallback_model(model: &str) -> Option { - if model.eq_ignore_ascii_case("gpt-5.3-codex-spark") { - Some("gpt-5.3-codex".to_string()) - } else if model.eq_ignore_ascii_case("code-gpt-5.3-codex-spark") { - Some("code-gpt-5.3-codex".to_string()) - } else { - None - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] enum AutoContextPressureBand { @@ -2983,7 +2966,6 @@ async fn run_turn( // Ensure we only auto-compact once per turn to avoid loops let mut did_auto_compact = false; let mut did_context_model_fallback = false; - let mut did_usage_limit_model_fallback = false; let mut forced_model_override: Option = None; let mut fallback_metadata_warning_sent = false; // Attempt input starts as the provided input, and may be augmented with @@ -3228,29 +3210,6 @@ async fn run_turn( continue; } - if !did_usage_limit_model_fallback { - let active_model = prompt - .model_override - .clone() - .unwrap_or_else(|| tc.client.get_model()); - if let Some(fallback_model) = spark_fallback_model(&active_model) { - did_usage_limit_model_fallback = true; - forced_model_override = Some(fallback_model.clone()); - retries = 0; - sess.clear_scratchpad(); - attempt_input = input.clone(); - sess - .notify_stream_error( - &sub_id, - format!( - "Usage limit reached for {active_model}; retrying with {fallback_model}…" - ), - ) - .await; - continue; - } - } - let now = Utc::now(); let retry_after = limit_err .retry_after(now) @@ -3267,59 +3226,10 @@ async fn run_turn( continue; } Err(CodexErr::UsageNotIncluded) => { - if !did_usage_limit_model_fallback { - let active_model = prompt - .model_override - .clone() - .unwrap_or_else(|| tc.client.get_model()); - if let Some(fallback_model) = spark_fallback_model(&active_model) { - did_usage_limit_model_fallback = true; - forced_model_override = Some(fallback_model.clone()); - retries = 0; - sess.clear_scratchpad(); - attempt_input = input.clone(); - sess - .notify_stream_error( - &sub_id, - format!( - "Usage limit reached for {active_model}; retrying with {fallback_model}…" - ), - ) - .await; - continue; - } - } - return Err(CodexErr::UsageNotIncluded); } Err(CodexErr::QuotaExceeded) => return Err(CodexErr::QuotaExceeded), Err(e) => { - if let CodexErr::Stream(msg, _maybe_delay, _req_id) = &e - && is_usage_limit_stream_error(msg) - && !did_usage_limit_model_fallback - { - let active_model = prompt - .model_override - .clone() - .unwrap_or_else(|| tc.client.get_model()); - if let Some(fallback_model) = spark_fallback_model(&active_model) { - did_usage_limit_model_fallback = true; - forced_model_override = Some(fallback_model.clone()); - retries = 0; - sess.clear_scratchpad(); - attempt_input = input.clone(); - sess - .notify_stream_error( - &sub_id, - format!( - "Usage limit reached for {active_model}; retrying with {fallback_model}…" - ), - ) - .await; - continue; - } - } - if let CodexErr::Stream(msg, _maybe_delay, _req_id) = &e && is_context_overflow_stream_error(msg) { @@ -14260,7 +14170,6 @@ mod tests { save_image_generation_sidecar, ImageGenerationTurnMetadata, pending_items_not_already_recorded, - spark_fallback_model, TRUNCATION_MARKER, }; use crate::exec::{ExecToolCallOutput, StreamOutput}; @@ -14515,24 +14424,24 @@ mod tests { "o3", vec![ ContextFallbackCandidate { - model: "gpt-5.3-codex".to_string(), + model: "gpt-5.4".to_string(), context_window: Some(272_000), priority: 10, }, ContextFallbackCandidate { - model: "gpt-5.2-codex".to_string(), + model: "gpt-5.5".to_string(), context_window: Some(272_000), priority: 20, }, ], ); - assert_eq!(chosen.as_deref(), Some("gpt-5.2-codex")); + assert_eq!(chosen.as_deref(), Some("gpt-5.5")); } #[test] fn larger_context_fallback_skips_gpt_4_1_family() { let chosen = choose_larger_context_model_from_candidates( - "gpt-5.3-codex-spark", + "gpt-5.4", vec![ ContextFallbackCandidate { model: "gpt-4.1".to_string(), @@ -14540,21 +14449,13 @@ mod tests { priority: 100, }, ContextFallbackCandidate { - model: "gpt-5.2-codex".to_string(), + model: "gpt-5.5".to_string(), context_window: Some(400_000), priority: 10, }, ], ); - assert_eq!(chosen.as_deref(), Some("gpt-5.2-codex")); + assert_eq!(chosen.as_deref(), Some("gpt-5.5")); } - #[test] - fn spark_usage_limit_falls_back_to_non_spark_model() { - assert_eq!( - spark_fallback_model("gpt-5.3-codex-spark").as_deref(), - Some("gpt-5.3-codex") - ); - assert!(spark_fallback_model("gpt-5.3-codex").is_none()); - } } diff --git a/code-rs/core/src/config_types.rs b/code-rs/core/src/config_types.rs index a291484e891..21eabf08ef1 100644 --- a/code-rs/core/src/config_types.rs +++ b/code-rs/core/src/config_types.rs @@ -956,13 +956,13 @@ fn default_auto_drive_model_routing_reasoning_levels() -> Vec { pub fn default_auto_drive_model_routing_entries() -> Vec { vec![ AutoDriveModelRoutingEntry { - model: "gpt-5.3-codex".to_string(), + model: "gpt-5.5".to_string(), enabled: true, reasoning_levels: vec![ReasoningEffort::High, ReasoningEffort::XHigh], description: "Hard planning and complex problem solving".to_string(), }, AutoDriveModelRoutingEntry { - model: "gpt-5.3-codex-spark".to_string(), + model: "gpt-5.4".to_string(), enabled: true, reasoning_levels: vec![ReasoningEffort::High], description: "Fast implementation loops and failing-test iteration".to_string(), diff --git a/code-rs/core/src/reasoning.rs b/code-rs/core/src/reasoning.rs index ad24f129a58..b4135d7d640 100644 --- a/code-rs/core/src/reasoning.rs +++ b/code-rs/core/src/reasoning.rs @@ -24,7 +24,7 @@ const GPT5_1_EFFORTS: &[ReasoningEffort] = &[ ReasoningEffort::High, ]; -const GPT5_2_EFFORTS: &[ReasoningEffort] = &[ +const GPT5_FRONTIER_EFFORTS: &[ReasoningEffort] = &[ ReasoningEffort::Low, ReasoningEffort::Medium, ReasoningEffort::High, @@ -92,14 +92,12 @@ pub fn supported_reasoning_efforts_for_model(model: &str) -> &'static [Reasoning return GPT5_1_EFFORTS; } - if lower.starts_with("gpt-5.2") - || lower.starts_with("test-gpt-5.2") - || lower.starts_with("gpt-5.3") - || lower.starts_with("test-gpt-5.3") - || lower.starts_with("gpt-5.5") + if lower.starts_with("gpt-5.5") || lower.starts_with("test-gpt-5.5") + || lower.starts_with("gpt-5.4") + || lower.starts_with("test-gpt-5.4") { - return GPT5_2_EFFORTS; + return GPT5_FRONTIER_EFFORTS; } if lower.starts_with("gpt-5-codex-mini") { @@ -166,14 +164,14 @@ mod tests { } #[test] - fn gpt5_2_supports_xhigh() { - let clamped = clamp_reasoning_effort_for_model("gpt-5.2", ReasoningEffort::XHigh); + fn gpt5_5_supports_xhigh() { + let clamped = clamp_reasoning_effort_for_model("gpt-5.5", ReasoningEffort::XHigh); assert_eq!(clamped, ReasoningEffort::XHigh); } #[test] - fn gpt5_3_supports_xhigh() { - let clamped = clamp_reasoning_effort_for_model("gpt-5.3", ReasoningEffort::XHigh); + fn gpt5_4_supports_xhigh() { + let clamped = clamp_reasoning_effort_for_model("gpt-5.4", ReasoningEffort::XHigh); assert_eq!(clamped, ReasoningEffort::XHigh); } } diff --git a/docs/example-config.md b/docs/example-config.md index fd222e858cf..174f5e1c03c 100644 --- a/docs/example-config.md +++ b/docs/example-config.md @@ -37,7 +37,8 @@ model_provider = "openai" # Reasoning & Verbosity (Responses API capable models) ################################################################################ -# Reasoning effort: minimal | low | medium | high | xhigh (default: medium; xhigh on gpt-5.1-codex-max and gpt-5.2) +# Reasoning effort: minimal | low | medium | high | xhigh. +# Defaults to medium; xhigh is supported on gpt-5.5 and gpt-5.4. model_reasoning_effort = "medium" # Reasoning summary: auto | concise | detailed | none (default: auto)