diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index f4248f30a..a3c990cdb 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -46,10 +46,10 @@ use crate::default_client::build_reqwest_client; use crate::error::CodexErr; use crate::error::Result; use crate::flags::CODEX_RS_SSE_FIXTURE; -use crate::model_family::ModelFamily; use crate::model_provider_info::ModelProviderInfo; use crate::model_provider_info::WireApi; use crate::openai_model_info::get_model_info; +use crate::openai_models::model_family::ModelFamily; use crate::tools::spec::create_tools_json_for_chat_completions_api; use crate::tools::spec::create_tools_json_for_responses_api; diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index a249ca6fc..f6152e55d 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -1,6 +1,6 @@ use crate::client_common::tools::ToolSpec; use crate::error::Result; -use crate::model_family::ModelFamily; +use crate::openai_models::model_family::ModelFamily; pub use codex_api::common::ResponseEvent; use codex_apply_patch::APPLY_PATCH_TOOL_INSTRUCTIONS; use codex_protocol::models::ResponseItem; @@ -252,7 +252,7 @@ impl Stream for ResponseStream { #[cfg(test)] mod tests { - use crate::model_family::find_family_for_model; + use crate::openai_models::model_family::find_family_for_model; use codex_api::ResponsesApiRequest; use codex_api::common::OpenAiVerbosity; use codex_api::common::TextControls; @@ -309,7 +309,7 @@ mod tests { }, ]; for test_case in test_cases { - let model_family = find_family_for_model(test_case.slug).expect("known model slug"); + let model_family = find_family_for_model(test_case.slug); let expected = if test_case.expects_apply_patch_instructions { format!( "{}\n{}", diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 885a4cdf7..ba7c69eb9 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -14,6 +14,7 @@ use crate::compact_remote::run_inline_remote_auto_compact_task; use crate::features::Feature; use crate::features::Features; use crate::function_tool::FunctionCallError; +use crate::openai_models::models_manager::ModelsManager; use crate::parse_command::parse_command; use crate::parse_turn_item; use crate::response_processing::process_items; @@ -74,7 +75,6 @@ use crate::error::Result as CodexResult; use crate::exec::StreamOutput; use crate::mcp::auth::compute_auth_statuses; use crate::mcp_connection_manager::McpConnectionManager; -use crate::model_family::find_family_for_model; use crate::openai_model_info::get_model_info; use crate::project_doc::get_user_instructions; use crate::protocol::AgentMessageContentDeltaEvent; @@ -163,6 +163,7 @@ impl Codex { pub async fn spawn( config: Config, auth_manager: Arc, + models_manager: Arc, conversation_history: InitialHistory, session_source: SessionSource, ) -> CodexResult { @@ -200,6 +201,7 @@ impl Codex { session_configuration, config.clone(), auth_manager.clone(), + models_manager.clone(), tx_event.clone(), conversation_history, session_source_clone, @@ -394,6 +396,7 @@ pub(crate) struct SessionSettingsUpdate { impl Session { fn make_turn_context( auth_manager: Option>, + models_manager: &ModelsManager, otel_event_manager: &OtelEventManager, provider: ModelProviderInfo, session_configuration: &SessionConfiguration, @@ -402,8 +405,7 @@ impl Session { ) -> TurnContext { let config = session_configuration.original_config_do_not_use.clone(); let features = &config.features; - let model_family = find_family_for_model(&session_configuration.model) - .unwrap_or_else(|| config.model_family.clone()); + let model_family = models_manager.construct_model_family(&session_configuration.model); let mut per_turn_config = (*config).clone(); per_turn_config.model = session_configuration.model.clone(); per_turn_config.model_family = model_family.clone(); @@ -459,6 +461,7 @@ impl Session { session_configuration: SessionConfiguration, config: Arc, auth_manager: Arc, + models_manager: Arc, tx_event: Sender, initial_history: InitialHistory, session_source: SessionSource, @@ -570,6 +573,7 @@ impl Session { show_raw_agent_reasoning: config.show_raw_agent_reasoning, auth_manager: Arc::clone(&auth_manager), otel_event_manager, + models_manager: Arc::clone(&models_manager), tool_approvals: Mutex::new(ApprovalStore::default()), }; @@ -756,6 +760,7 @@ impl Session { let mut turn_context: TurnContext = Self::make_turn_context( Some(Arc::clone(&self.services.auth_manager)), + &self.services.models_manager, &self.services.otel_event_manager, session_configuration.provider.clone(), &session_configuration, @@ -1824,8 +1829,7 @@ async fn spawn_review_thread( resolved: crate::review_prompts::ResolvedReviewRequest, ) { let model = config.review_model.clone(); - let review_model_family = find_family_for_model(&model) - .unwrap_or_else(|| parent_turn_context.client.get_model_family()); + let review_model_family = sess.services.models_manager.construct_model_family(&model); // For reviews, disable web_search and view_image regardless of global settings. let mut review_features = sess.features.clone(); review_features @@ -2083,13 +2087,13 @@ async fn run_turn( let mut base_instructions = turn_context.base_instructions.clone(); if parallel_tool_calls { static INSTRUCTIONS: &str = include_str!("../templates/parallel/instructions.md"); - if let Some(family) = - find_family_for_model(&sess.state.lock().await.session_configuration.model) - { - let mut new_instructions = base_instructions.unwrap_or(family.base_instructions); - new_instructions.push_str(INSTRUCTIONS); - base_instructions = Some(new_instructions); - } + let family = sess + .services + .models_manager + .construct_model_family(&sess.state.lock().await.session_configuration.model); + let mut new_instructions = base_instructions.unwrap_or(family.base_instructions); + new_instructions.push_str(INSTRUCTIONS); + base_instructions = Some(new_instructions); } let prompt = Prompt { input, @@ -2751,6 +2755,7 @@ mod tests { false, config.cli_auth_credentials_store_mode, ); + let models_manager = Arc::new(ModelsManager::new(auth_manager.get_auth_mode())); let session_configuration = SessionConfiguration { provider: config.model_provider.clone(), @@ -2781,11 +2786,13 @@ mod tests { show_raw_agent_reasoning: config.show_raw_agent_reasoning, auth_manager: Arc::clone(&auth_manager), otel_event_manager: otel_event_manager.clone(), + models_manager: models_manager.clone(), tool_approvals: Mutex::new(ApprovalStore::default()), }; let turn_context = Session::make_turn_context( Some(Arc::clone(&auth_manager)), + &models_manager, &otel_event_manager, session_configuration.provider.clone(), &session_configuration, @@ -2829,6 +2836,7 @@ mod tests { false, config.cli_auth_credentials_store_mode, ); + let models_manager = Arc::new(ModelsManager::new(auth_manager.get_auth_mode())); let session_configuration = SessionConfiguration { provider: config.model_provider.clone(), @@ -2859,11 +2867,13 @@ mod tests { show_raw_agent_reasoning: config.show_raw_agent_reasoning, auth_manager: Arc::clone(&auth_manager), otel_event_manager: otel_event_manager.clone(), + models_manager: models_manager.clone(), tool_approvals: Mutex::new(ApprovalStore::default()), }; let turn_context = Arc::new(Session::make_turn_context( Some(Arc::clone(&auth_manager)), + &models_manager, &otel_event_manager, session_configuration.provider.clone(), &session_configuration, diff --git a/codex-rs/core/src/codex_delegate.rs b/codex-rs/core/src/codex_delegate.rs index 796331d1e..b6e4c88f3 100644 --- a/codex-rs/core/src/codex_delegate.rs +++ b/codex-rs/core/src/codex_delegate.rs @@ -25,6 +25,7 @@ use crate::codex::Session; use crate::codex::TurnContext; use crate::config::Config; use crate::error::CodexErr; +use crate::openai_models::models_manager::ModelsManager; use codex_protocol::protocol::InitialHistory; /// Start an interactive sub-Codex conversation and return IO channels. @@ -35,6 +36,7 @@ use codex_protocol::protocol::InitialHistory; pub(crate) async fn run_codex_conversation_interactive( config: Config, auth_manager: Arc, + models_manager: Arc, parent_session: Arc, parent_ctx: Arc, cancel_token: CancellationToken, @@ -46,6 +48,7 @@ pub(crate) async fn run_codex_conversation_interactive( let CodexSpawnOk { codex, .. } = Codex::spawn( config, auth_manager, + models_manager, initial_history.unwrap_or(InitialHistory::New), SessionSource::SubAgent(SubAgentSource::Review), ) @@ -88,9 +91,11 @@ pub(crate) async fn run_codex_conversation_interactive( /// Convenience wrapper for one-time use with an initial prompt. /// /// Internally calls the interactive variant, then immediately submits the provided input. +#[allow(clippy::too_many_arguments)] pub(crate) async fn run_codex_conversation_one_shot( config: Config, auth_manager: Arc, + models_manager: Arc, input: Vec, parent_session: Arc, parent_ctx: Arc, @@ -103,6 +108,7 @@ pub(crate) async fn run_codex_conversation_one_shot( let io = run_codex_conversation_interactive( config, auth_manager, + models_manager, parent_session, parent_ctx, child_cancel.clone(), diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index dccf0556f..185584115 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -22,14 +22,13 @@ use crate::features::FeatureOverrides; use crate::features::Features; use crate::features::FeaturesToml; use crate::git_info::resolve_root_git_project_for_trust; -use crate::model_family::ModelFamily; -use crate::model_family::derive_default_model_family; -use crate::model_family::find_family_for_model; use crate::model_provider_info::LMSTUDIO_OSS_PROVIDER_ID; use crate::model_provider_info::ModelProviderInfo; use crate::model_provider_info::OLLAMA_OSS_PROVIDER_ID; use crate::model_provider_info::built_in_model_providers; use crate::openai_model_info::get_model_info; +use crate::openai_models::model_family::ModelFamily; +use crate::openai_models::model_family::find_family_for_model; use crate::project_doc::DEFAULT_PROJECT_DOC_FILENAME; use crate::project_doc::LOCAL_PROJECT_DOC_FILENAME; use crate::protocol::AskForApproval; @@ -82,6 +81,7 @@ pub struct Config { /// Model used specifically for review sessions. Defaults to "gpt-5.1-codex-max". pub review_model: String, + // todo(aibrahim): remove this field pub model_family: ModelFamily, /// Size of the context window for the model, in tokens. @@ -1109,8 +1109,7 @@ impl Config { .or(cfg.model) .unwrap_or_else(default_model); - let mut model_family = - find_family_for_model(&model).unwrap_or_else(|| derive_default_model_family(&model)); + let mut model_family = find_family_for_model(&model); if let Some(supports_reasoning_summaries) = cfg.model_supports_reasoning_summaries { model_family.supports_reasoning_summaries = supports_reasoning_summaries; @@ -2955,7 +2954,7 @@ model_verbosity = "high" Config { model: "o3".to_string(), review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(), - model_family: find_family_for_model("o3").expect("known model slug"), + model_family: find_family_for_model("o3"), model_context_window: Some(200_000), model_auto_compact_token_limit: Some(180_000), model_provider_id: "openai".to_string(), @@ -3029,7 +3028,7 @@ model_verbosity = "high" let expected_gpt3_profile_config = Config { model: "gpt-3.5-turbo".to_string(), review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(), - model_family: find_family_for_model("gpt-3.5-turbo").expect("known model slug"), + model_family: find_family_for_model("gpt-3.5-turbo"), model_context_window: Some(16_385), model_auto_compact_token_limit: Some(14_746), model_provider_id: "openai-chat-completions".to_string(), @@ -3118,7 +3117,7 @@ model_verbosity = "high" let expected_zdr_profile_config = Config { model: "o3".to_string(), review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(), - model_family: find_family_for_model("o3").expect("known model slug"), + model_family: find_family_for_model("o3"), model_context_window: Some(200_000), model_auto_compact_token_limit: Some(180_000), model_provider_id: "openai".to_string(), @@ -3193,7 +3192,7 @@ model_verbosity = "high" let expected_gpt5_profile_config = Config { model: "gpt-5.1".to_string(), review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(), - model_family: find_family_for_model("gpt-5.1").expect("known model slug"), + model_family: find_family_for_model("gpt-5.1"), model_context_window: Some(272_000), model_auto_compact_token_limit: Some(244_800), model_provider_id: "openai".to_string(), diff --git a/codex-rs/core/src/conversation_manager.rs b/codex-rs/core/src/conversation_manager.rs index f41e5b597..22f73dfe1 100644 --- a/codex-rs/core/src/conversation_manager.rs +++ b/codex-rs/core/src/conversation_manager.rs @@ -65,14 +65,19 @@ impl ConversationManager { } pub async fn new_conversation(&self, config: Config) -> CodexResult { - self.spawn_conversation(config, self.auth_manager.clone()) - .await + self.spawn_conversation( + config, + self.auth_manager.clone(), + self.models_manager.clone(), + ) + .await } async fn spawn_conversation( &self, config: Config, auth_manager: Arc, + models_manager: Arc, ) -> CodexResult { let CodexSpawnOk { codex, @@ -80,6 +85,7 @@ impl ConversationManager { } = Codex::spawn( config, auth_manager, + models_manager, InitialHistory::New, self.session_source.clone(), ) @@ -156,6 +162,7 @@ impl ConversationManager { } = Codex::spawn( config, auth_manager, + self.models_manager.clone(), initial_history, self.session_source.clone(), ) @@ -193,7 +200,14 @@ impl ConversationManager { let CodexSpawnOk { codex, conversation_id, - } = Codex::spawn(config, auth_manager, history, self.session_source.clone()).await?; + } = Codex::spawn( + config, + auth_manager, + self.models_manager.clone(), + history, + self.session_source.clone(), + ) + .await?; self.finalize_spawn(codex, conversation_id).await } diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index d32366476..39dc224dd 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -67,7 +67,6 @@ pub use conversation_manager::NewConversation; pub use auth::AuthManager; pub use auth::CodexAuth; pub mod default_client; -pub mod model_family; mod openai_model_info; pub mod project_doc; mod rollout; diff --git a/codex-rs/core/src/openai_model_info.rs b/codex-rs/core/src/openai_model_info.rs index 96f3ed77c..4ee7d7187 100644 --- a/codex-rs/core/src/openai_model_info.rs +++ b/codex-rs/core/src/openai_model_info.rs @@ -1,4 +1,4 @@ -use crate::model_family::ModelFamily; +use crate::openai_models::model_family::ModelFamily; // Shared constants for commonly used window/token sizes. pub(crate) const CONTEXT_WINDOW_272K: i64 = 272_000; diff --git a/codex-rs/core/src/openai_models/mod.rs b/codex-rs/core/src/openai_models/mod.rs index 13ee2e060..e7a8beddb 100644 --- a/codex-rs/core/src/openai_models/mod.rs +++ b/codex-rs/core/src/openai_models/mod.rs @@ -1,2 +1,3 @@ +pub mod model_family; pub mod model_presets; pub mod models_manager; diff --git a/codex-rs/core/src/model_family.rs b/codex-rs/core/src/openai_models/model_family.rs similarity index 95% rename from codex-rs/core/src/model_family.rs rename to codex-rs/core/src/openai_models/model_family.rs index 0417f13b1..1580ab137 100644 --- a/codex-rs/core/src/model_family.rs +++ b/codex-rs/core/src/openai_models/model_family.rs @@ -8,11 +8,11 @@ use crate::truncate::TruncationPolicy; /// The `instructions` field in the payload sent to a model should always start /// with this content. -const BASE_INSTRUCTIONS: &str = include_str!("../prompt.md"); +const BASE_INSTRUCTIONS: &str = include_str!("../../prompt.md"); -const GPT_5_CODEX_INSTRUCTIONS: &str = include_str!("../gpt_5_codex_prompt.md"); -const GPT_5_1_INSTRUCTIONS: &str = include_str!("../gpt_5_1_prompt.md"); -const GPT_5_1_CODEX_MAX_INSTRUCTIONS: &str = include_str!("../gpt-5.1-codex-max_prompt.md"); +const GPT_5_CODEX_INSTRUCTIONS: &str = include_str!("../../gpt_5_codex_prompt.md"); +const GPT_5_1_INSTRUCTIONS: &str = include_str!("../../gpt_5_1_prompt.md"); +const GPT_5_1_CODEX_MAX_INSTRUCTIONS: &str = include_str!("../../gpt-5.1-codex-max_prompt.md"); /// A model family is a group of models that share certain characteristics. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -100,13 +100,14 @@ macro_rules! model_family { $( mf.$key = $value; )* - Some(mf) + mf }}; } +// todo(aibrahim): remove this function /// Returns a `ModelFamily` for the given model slug, or `None` if the slug /// does not match any known model family. -pub fn find_family_for_model(slug: &str) -> Option { +pub fn find_family_for_model(slug: &str) -> ModelFamily { if slug.starts_with("o3") { model_family!( slug, "o3", @@ -238,11 +239,11 @@ pub fn find_family_for_model(slug: &str) -> Option { truncation_policy: TruncationPolicy::Bytes(10_000), ) } else { - None + derive_default_model_family(slug) } } -pub fn derive_default_model_family(model: &str) -> ModelFamily { +fn derive_default_model_family(model: &str) -> ModelFamily { ModelFamily { slug: model.to_string(), family: model.to_string(), diff --git a/codex-rs/core/src/openai_models/models_manager.rs b/codex-rs/core/src/openai_models/models_manager.rs index 1d57f1e69..c6f936542 100644 --- a/codex-rs/core/src/openai_models/models_manager.rs +++ b/codex-rs/core/src/openai_models/models_manager.rs @@ -2,6 +2,8 @@ use codex_app_server_protocol::AuthMode; use codex_protocol::openai_models::ModelPreset; use tokio::sync::RwLock; +use crate::openai_models::model_family::ModelFamily; +use crate::openai_models::model_family::find_family_for_model; use crate::openai_models::model_presets::builtin_model_presets; pub struct ModelsManager { @@ -23,4 +25,8 @@ impl ModelsManager { let models = builtin_model_presets(self.auth_mode); *self.available_models.write().await = models; } + + pub fn construct_model_family(&self, model: &str) -> ModelFamily { + find_family_for_model(model) + } } diff --git a/codex-rs/core/src/state/service.rs b/codex-rs/core/src/state/service.rs index 287fb73d2..a35720a9b 100644 --- a/codex-rs/core/src/state/service.rs +++ b/codex-rs/core/src/state/service.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use crate::AuthManager; use crate::RolloutRecorder; use crate::mcp_connection_manager::McpConnectionManager; +use crate::openai_models::models_manager::ModelsManager; use crate::tools::sandboxing::ApprovalStore; use crate::unified_exec::UnifiedExecSessionManager; use crate::user_notification::UserNotifier; @@ -20,6 +21,7 @@ pub(crate) struct SessionServices { pub(crate) user_shell: crate::shell::Shell, pub(crate) show_raw_agent_reasoning: bool, pub(crate) auth_manager: Arc, + pub(crate) models_manager: Arc, pub(crate) otel_event_manager: OtelEventManager, pub(crate) tool_approvals: Mutex, } diff --git a/codex-rs/core/src/tasks/mod.rs b/codex-rs/core/src/tasks/mod.rs index 9bda02c34..fa5433ef5 100644 --- a/codex-rs/core/src/tasks/mod.rs +++ b/codex-rs/core/src/tasks/mod.rs @@ -19,6 +19,7 @@ use tracing::warn; use crate::AuthManager; use crate::codex::Session; use crate::codex::TurnContext; +use crate::openai_models::models_manager::ModelsManager; use crate::protocol::EventMsg; use crate::protocol::TaskCompleteEvent; use crate::protocol::TurnAbortReason; @@ -55,6 +56,10 @@ impl SessionTaskContext { pub(crate) fn auth_manager(&self) -> Arc { Arc::clone(&self.session.services.auth_manager) } + + pub(crate) fn models_manager(&self) -> Arc { + Arc::clone(&self.session.services.models_manager) + } } /// Async task that drives a [`Session`] turn. diff --git a/codex-rs/core/src/tasks/review.rs b/codex-rs/core/src/tasks/review.rs index a6ec840a8..738d33c52 100644 --- a/codex-rs/core/src/tasks/review.rs +++ b/codex-rs/core/src/tasks/review.rs @@ -93,6 +93,7 @@ async fn start_review_conversation( (run_codex_conversation_one_shot( sub_agent_config, session.auth_manager(), + session.models_manager(), input, session.clone_session(), ctx.clone(), diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index d07c605e5..2c3aa2d44 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -2,7 +2,7 @@ use crate::client_common::tools::ResponsesApiTool; use crate::client_common::tools::ToolSpec; use crate::features::Feature; use crate::features::Features; -use crate::model_family::ModelFamily; +use crate::openai_models::model_family::ModelFamily; use crate::tools::handlers::PLAN_TOOL; use crate::tools::handlers::apply_patch::ApplyPatchToolType; use crate::tools::handlers::apply_patch::create_apply_patch_freeform_tool; @@ -1118,7 +1118,7 @@ pub(crate) fn build_specs( #[cfg(test)] mod tests { use crate::client_common::tools::FreeformTool; - use crate::model_family::find_family_for_model; + use crate::openai_models::model_family::find_family_for_model; use crate::tools::registry::ConfiguredToolSpec; use mcp_types::ToolInputSchema; use pretty_assertions::assert_eq; @@ -1213,8 +1213,7 @@ mod tests { #[test] fn test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search() { - let model_family = find_family_for_model("gpt-5-codex") - .expect("gpt-5-codex should be a valid model family"); + let model_family = find_family_for_model("gpt-5-codex"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); features.enable(Feature::WebSearchRequest); @@ -1273,8 +1272,7 @@ mod tests { } fn assert_model_tools(model_family: &str, features: &Features, expected_tools: &[&str]) { - let model_family = find_family_for_model(model_family) - .unwrap_or_else(|| panic!("{model_family} should be a valid model family")); + let model_family = find_family_for_model(model_family); let config = ToolsConfig::new(&ToolsConfigParams { model_family: &model_family, features, @@ -1466,7 +1464,7 @@ mod tests { #[test] fn test_build_specs_default_shell_present() { - let model_family = find_family_for_model("o3").expect("o3 should be a valid model family"); + let model_family = find_family_for_model("o3"); let mut features = Features::with_defaults(); features.enable(Feature::WebSearchRequest); features.enable(Feature::UnifiedExec); @@ -1487,8 +1485,7 @@ mod tests { #[test] #[ignore] fn test_parallel_support_flags() { - let model_family = find_family_for_model("gpt-5-codex") - .expect("codex-mini-latest should be a valid model family"); + let model_family = find_family_for_model("gpt-5-codex"); let mut features = Features::with_defaults(); features.disable(Feature::ViewImageTool); features.enable(Feature::UnifiedExec); @@ -1507,8 +1504,7 @@ mod tests { #[test] fn test_test_model_family_includes_sync_tool() { - let model_family = find_family_for_model("test-gpt-5-codex") - .expect("test-gpt-5-codex should be a valid model family"); + let model_family = find_family_for_model("test-gpt-5-codex"); let mut features = Features::with_defaults(); features.disable(Feature::ViewImageTool); let config = ToolsConfig::new(&ToolsConfigParams { @@ -1537,7 +1533,7 @@ mod tests { #[test] fn test_build_specs_mcp_tools_converted() { - let model_family = find_family_for_model("o3").expect("o3 should be a valid model family"); + let model_family = find_family_for_model("o3"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); features.enable(Feature::WebSearchRequest); @@ -1631,7 +1627,7 @@ mod tests { #[test] fn test_build_specs_mcp_tools_sorted_by_name() { - let model_family = find_family_for_model("o3").expect("o3 should be a valid model family"); + let model_family = find_family_for_model("o3"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); let config = ToolsConfig::new(&ToolsConfigParams { @@ -1706,8 +1702,7 @@ mod tests { #[test] fn test_mcp_tool_property_missing_type_defaults_to_string() { - let model_family = find_family_for_model("gpt-5-codex") - .expect("gpt-5-codex should be a valid model family"); + let model_family = find_family_for_model("gpt-5-codex"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); features.enable(Feature::WebSearchRequest); @@ -1763,8 +1758,7 @@ mod tests { #[test] fn test_mcp_tool_integer_normalized_to_number() { - let model_family = find_family_for_model("gpt-5-codex") - .expect("gpt-5-codex should be a valid model family"); + let model_family = find_family_for_model("gpt-5-codex"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); features.enable(Feature::WebSearchRequest); @@ -1816,8 +1810,7 @@ mod tests { #[test] fn test_mcp_tool_array_without_items_gets_default_string_items() { - let model_family = find_family_for_model("gpt-5-codex") - .expect("gpt-5-codex should be a valid model family"); + let model_family = find_family_for_model("gpt-5-codex"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); features.enable(Feature::WebSearchRequest); @@ -1873,8 +1866,7 @@ mod tests { #[test] fn test_mcp_tool_anyof_defaults_to_string() { - let model_family = find_family_for_model("gpt-5-codex") - .expect("gpt-5-codex should be a valid model family"); + let model_family = find_family_for_model("gpt-5-codex"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); features.enable(Feature::WebSearchRequest); @@ -1985,8 +1977,7 @@ Examples of valid command strings: #[test] fn test_get_openai_tools_mcp_tools_with_additional_properties_schema() { - let model_family = find_family_for_model("gpt-5-codex") - .expect("gpt-5-codex should be a valid model family"); + let model_family = find_family_for_model("gpt-5-codex"); let mut features = Features::with_defaults(); features.enable(Feature::UnifiedExec); features.enable(Feature::WebSearchRequest); diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 1c5919214..aff2ab60d 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -11,7 +11,6 @@ use codex_core::ModelProviderInfo; use codex_core::built_in_model_providers; use codex_core::config::Config; use codex_core::features::Feature; -use codex_core::model_family::find_family_for_model; use codex_core::protocol::AskForApproval; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; @@ -71,7 +70,6 @@ impl TestCodexBuilder { let new_model = model.to_string(); self.with_config(move |config| { config.model = new_model.clone(); - config.model_family = find_family_for_model(&new_model).expect("model family"); }) } diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index e0e06757b..e5d3d7e61 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -16,7 +16,7 @@ use codex_core::auth::AuthCredentialsStoreMode; use codex_core::built_in_model_providers; use codex_core::error::CodexErr; use codex_core::features::Feature; -use codex_core::model_family::find_family_for_model; +use codex_core::openai_models::model_family::find_family_for_model; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; use codex_core::protocol::SessionSource; @@ -1378,8 +1378,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res let TestCodex { codex, .. } = test_codex() .with_config(|config| { config.model = "gpt-5.1".to_string(); - config.model_family = - find_family_for_model("gpt-5.1").expect("known gpt-5.1 model family"); + config.model_family = find_family_for_model("gpt-5.1"); config.model_context_window = Some(272_000); }) .build(&server) diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index 0c908e35b..219f29e2f 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -1,7 +1,7 @@ #![allow(clippy::unwrap_used)] use codex_core::features::Feature; -use codex_core::model_family::find_family_for_model; +use codex_core::openai_models::model_family::find_family_for_model; use codex_core::protocol::AskForApproval; use codex_core::protocol::ENVIRONMENT_CONTEXT_OPEN_TAG; use codex_core::protocol::EventMsg; @@ -74,7 +74,6 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> { config.features.disable(Feature::ApplyPatchFreeform); config.model = "codex-mini-latest".to_string(); config.model_family = find_family_for_model("codex-mini-latest") - .expect("model family for codex-mini-latest"); }) .build(&server) .await?; diff --git a/codex-rs/core/tests/suite/shell_serialization.rs b/codex-rs/core/tests/suite/shell_serialization.rs index 77c835b32..5dbdda4fc 100644 --- a/codex-rs/core/tests/suite/shell_serialization.rs +++ b/codex-rs/core/tests/suite/shell_serialization.rs @@ -4,7 +4,7 @@ use anyhow::Result; use codex_core::config::Config; use codex_core::features::Feature; -use codex_core::model_family::find_family_for_model; +use codex_core::openai_models::model_family::find_family_for_model; use codex_core::protocol::SandboxPolicy; use core_test_support::assert_regex_match; use core_test_support::responses::ev_assistant_message; @@ -46,13 +46,12 @@ fn configure_shell_command_model(output_type: ShellModelOutput, config: &mut Con return; } - if let Some(shell_command_family) = find_family_for_model("test-gpt-5-codex") { - if config.model_family.shell_type == shell_command_family.shell_type { - return; - } - config.model = shell_command_family.slug.clone(); - config.model_family = shell_command_family; + let shell_command_family = find_family_for_model("test-gpt-5-codex"); + if config.model_family.shell_type == shell_command_family.shell_type { + return; } + config.model = shell_command_family.slug.clone(); + config.model_family = shell_command_family; } fn shell_responses( diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 2367bbd58..ff5fe9a54 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -26,7 +26,7 @@ use codex_core::ConversationManager; use codex_core::config::Config; use codex_core::config::edit::ConfigEditsBuilder; use codex_core::features::Feature; -use codex_core::model_family::find_family_for_model; +use codex_core::openai_models::model_family::find_family_for_model; use codex_core::openai_models::model_presets::HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG; use codex_core::openai_models::model_presets::HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG; use codex_core::openai_models::models_manager::ModelsManager; @@ -162,9 +162,7 @@ async fn handle_model_migration_prompt_if_needed( migration_config: migration_config_key.to_string(), }); config.model = target_model.to_string(); - if let Some(family) = find_family_for_model(&target_model) { - config.model_family = family; - } + config.model_family = find_family_for_model(&target_model); let mapped_effort = if let Some(reasoning_effort_mapping) = reasoning_effort_mapping && let Some(reasoning_effort) = config.model_reasoning_effort @@ -683,9 +681,8 @@ impl App { AppEvent::UpdateModel(model) => { self.chat_widget.set_model(&model); self.config.model = model.clone(); - if let Some(family) = find_family_for_model(&model) { - self.config.model_family = family; - } + let family = find_family_for_model(&model); + self.config.model_family = family; } AppEvent::OpenReasoningPopup { model } => { self.chat_widget.open_reasoning_popup(model);