extract models manager and related ownership from core (#16508)

## Summary
- split `models-manager` out of `core` and add `ModelsManagerConfig`
plus `Config::to_models_manager_config()` so model metadata paths stop
depending on `core::Config`
- move login-owned/auth-owned code out of `core` into `codex-login`,
move model provider config into `codex-model-provider-info`, move API
bridge mapping into `codex-api`, move protocol-owned types/impls into
`codex-protocol`, and move response debug helpers into a dedicated
`response-debug-context` crate
- move feedback tag emission into `codex-feedback`, relocate tests to
the crates that now own the code, and keep broad temporary re-exports so
this PR avoids a giant import-only rewrite

## Major moves and decisions
- created `codex-models-manager` as the owner for model
cache/catalog/config/model info logic, including the new
`ModelsManagerConfig` struct
- created `codex-model-provider-info` as the owner for provider config
parsing/defaults and kept temporary `codex-login`/`codex-core`
re-exports for old import paths
- moved `api_bridge` error mapping + `CoreAuthProvider` into
`codex-api`, while `codex-login::api_bridge` temporarily re-exports
those symbols and keeps the `auth_provider_from_auth` wrapper
- moved `auth_env_telemetry` and `provider_auth` ownership to
`codex-login`
- moved `CodexErr` ownership to `codex-protocol::error`, plus
`StreamOutput`, `bytes_to_string_smart`, and network policy helpers to
protocol-owned modules
- created `codex-response-debug-context` for
`extract_response_debug_context`, `telemetry_transport_error_message`,
and related response-debug plumbing instead of leaving that behavior in
`core`
- moved `FeedbackRequestTags`, `emit_feedback_request_tags`, and
`emit_feedback_request_tags_with_auth_env` to `codex-feedback`
- deferred removal of temporary re-exports and the mechanical import
rewrites to a stacked follow-up PR so this PR stays reviewable

## Test moves
- moved auth refresh coverage from `core/tests/suite/auth_refresh.rs` to
`login/tests/suite/auth_refresh.rs`
- moved text encoding coverage from
`core/tests/suite/text_encoding_fix.rs` to
`protocol/src/exec_output_tests.rs`
- moved model info override coverage from
`core/tests/suite/model_info_overrides.rs` to
`models-manager/src/model_info_overrides_tests.rs`

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Ahmed Ibrahim
2026-04-02 23:00:02 -07:00
committed by GitHub
co-authored by Codex
parent 8cd7f20b48
commit 6fff9955f1
99 changed files with 1095 additions and 1137 deletions
@@ -292,7 +292,7 @@ pub(crate) async fn apply_requested_spawn_agent_model_overrides(
let selected_model_info = session
.services
.models_manager
.get_model_info(&selected_model_name, config)
.get_model_info(&selected_model_name, &config.to_models_manager_config())
.await;
config.model = Some(selected_model_name.clone());
@@ -4,6 +4,7 @@ use crate::error::SandboxErr;
use crate::exec::ExecCapturePolicy;
use crate::exec::ExecExpiration;
use crate::exec::ExecToolCallOutput;
use crate::exec::StreamOutput;
use crate::exec::is_likely_sandbox_denied;
use crate::guardian::GuardianApprovalRequest;
use crate::guardian::review_approval_request;
@@ -901,9 +902,9 @@ fn map_exec_result(
) -> Result<ExecToolCallOutput, ToolError> {
let output = ExecToolCallOutput {
exit_code: result.exit_code,
stdout: crate::exec::StreamOutput::new(result.stdout.clone()),
stderr: crate::exec::StreamOutput::new(result.stderr.clone()),
aggregated_output: crate::exec::StreamOutput::new(result.output.clone()),
stdout: StreamOutput::new(result.stdout.clone()),
stderr: StreamOutput::new(result.stderr.clone()),
aggregated_output: StreamOutput::new(result.output.clone()),
duration: result.duration,
timed_out: result.timed_out,
};
+13 -15
View File
@@ -1,8 +1,8 @@
use crate::config::test_config;
use crate::models_manager::manager::ModelsManager;
use crate::models_manager::model_info::with_config_overrides;
use crate::shell::Shell;
use crate::shell::ShellType;
use crate::test_support::construct_model_info_offline;
use crate::tools::ToolRouter;
use crate::tools::registry::tool_handler_key;
use crate::tools::router::ToolRouterParams;
@@ -14,7 +14,6 @@ use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::openai_models::ConfigShellToolType;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::openai_models::ModelsResponse;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_tools::ConfiguredToolSpec;
@@ -73,8 +72,7 @@ fn discoverable_connector(id: &str, name: &str, description: &str) -> Discoverab
fn search_capable_model_info() -> ModelInfo {
let config = test_config();
let mut model_info =
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let mut model_info = construct_model_info_offline("gpt-5-codex", &config);
model_info.supports_search_tool = true;
model_info
}
@@ -161,14 +159,14 @@ fn find_tool<'a>(tools: &'a [ConfiguredToolSpec], expected_name: &str) -> &'a Co
fn model_info_from_models_json(slug: &str) -> ModelInfo {
let config = test_config();
let response: ModelsResponse =
serde_json::from_str(include_str!("../../models.json")).expect("valid models.json");
let response = codex_models_manager::bundled_models_response()
.unwrap_or_else(|err| panic!("bundled models.json should parse: {err}"));
let model = response
.models
.into_iter()
.find(|candidate| candidate.slug == slug)
.unwrap_or_else(|| panic!("model slug {slug} is missing from models.json"));
with_config_overrides(model, &config)
with_config_overrides(model, &config.to_models_manager_config())
}
/// Builds the tool registry builder while collecting tool specs for later serialization.
@@ -214,7 +212,7 @@ fn model_provided_unified_exec_is_blocked_for_windows_sandboxed_policies() {
#[test]
fn get_memory_requires_feature_flag() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let model_info = construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.disable(Feature::MemoryTool);
let available_models = Vec::new();
@@ -506,7 +504,7 @@ fn test_gpt_5_1_codex_max_unified_exec_web_search() {
#[test]
fn test_build_specs_default_shell_present() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("o3", &config);
let model_info = construct_model_info_offline("o3", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
let available_models = Vec::new();
@@ -538,7 +536,7 @@ fn test_build_specs_default_shell_present() {
#[test]
fn shell_zsh_fork_prefers_shell_command_over_unified_exec() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("o3", &config);
let model_info = construct_model_info_offline("o3", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::ShellZshFork);
@@ -794,7 +792,7 @@ fn search_tool_registers_namespaced_app_tool_aliases() {
#[test]
fn test_mcp_tool_property_missing_type_defaults_to_string() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let model_info = construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
let available_models = Vec::new();
@@ -854,7 +852,7 @@ fn test_mcp_tool_property_missing_type_defaults_to_string() {
#[test]
fn test_mcp_tool_integer_normalized_to_number() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let model_info = construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
let available_models = Vec::new();
@@ -910,7 +908,7 @@ fn test_mcp_tool_integer_normalized_to_number() {
#[test]
fn test_mcp_tool_array_without_items_gets_default_string_items() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let model_info = construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::ApplyPatchFreeform);
@@ -970,7 +968,7 @@ fn test_mcp_tool_array_without_items_gets_default_string_items() {
#[test]
fn test_mcp_tool_anyof_defaults_to_string() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let model_info = construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
let available_models = Vec::new();
@@ -1028,7 +1026,7 @@ fn test_mcp_tool_anyof_defaults_to_string() {
#[test]
fn test_get_openai_tools_mcp_tools_with_additional_properties_schema() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let model_info = construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
let available_models = Vec::new();