mirror of
https://github.com/openai/codex.git
synced 2026-11-08 20:40:33 +00:00
Keep active-turn model settings stable across updates (#38785)
## Why Thread settings can change while a turn is still active, including between sampling requests. Those updates should apply to the next turn instead of changing the model configuration partway through the current turn. ## What changed - Snapshot the model, reasoning settings, service tier, approval settings, and model-attributed telemetry in `StepContext`. - Build prompts, model requests, world state, tracing, and startup prewarms from that step-scoped snapshot. - Share immutable model metadata between turn and step contexts. ## Testing Added an integration test that pauses an active turn, updates its thread settings, and verifies that all requests in the active turn retain the original settings while the next turn uses the updates. GitOrigin-RevId: c51352218e463dcc9b7338566d16742292c49fbf
This commit is contained in:
committed by
copyberry
parent
c4941302c7
commit
00f6a8a60e
@@ -664,7 +664,7 @@ async fn shell_family_registers_visible_unified_exec_and_hidden_legacy_shell() {
|
||||
let plan = probe(|turn| {
|
||||
set_features(turn, &[Feature::ShellTool, Feature::UnifiedExec]);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ false);
|
||||
turn.model_info.shell_type = ConfigShellToolType::ShellCommand;
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::ShellCommand;
|
||||
})
|
||||
.await;
|
||||
|
||||
@@ -687,7 +687,7 @@ async fn login_shell_parameter_follows_selected_environment() {
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExec, tool_name == "exec_command");
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ false);
|
||||
turn.model_info.shell_type = ConfigShellToolType::ShellCommand;
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::ShellCommand;
|
||||
update_config(turn, |config| {
|
||||
config.permissions.allow_login_shell = !allow_login_shell;
|
||||
});
|
||||
@@ -744,7 +744,7 @@ async fn shell_command_is_not_registered_without_a_single_local_environment() {
|
||||
let remote_environment = probe(|turn| {
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExec, /*enabled*/ false);
|
||||
turn.model_info.shell_type = ConfigShellToolType::ShellCommand;
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::ShellCommand;
|
||||
|
||||
let TurnEnvironmentState::Ready(environment) = turn
|
||||
.environments
|
||||
@@ -769,7 +769,7 @@ async fn shell_command_is_not_registered_without_a_single_local_environment() {
|
||||
let multiple_local_environments = probe(|turn| {
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExec, /*enabled*/ false);
|
||||
turn.model_info.shell_type = ConfigShellToolType::ShellCommand;
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::ShellCommand;
|
||||
duplicate_primary_environment(turn);
|
||||
})
|
||||
.await;
|
||||
@@ -817,7 +817,7 @@ async fn shell_zsh_fork_stays_standalone_until_unified_exec_composition_is_enabl
|
||||
set_features(turn, &[Feature::ShellTool, Feature::UnifiedExec]);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExecZshFork, /*enabled*/ false);
|
||||
turn.model_info.shell_type = ConfigShellToolType::ShellCommand;
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::ShellCommand;
|
||||
})
|
||||
.await;
|
||||
|
||||
@@ -836,7 +836,7 @@ async fn shell_zsh_fork_stays_standalone_until_unified_exec_composition_is_enabl
|
||||
Feature::UnifiedExecZshFork,
|
||||
],
|
||||
);
|
||||
turn.model_info.shell_type = ConfigShellToolType::ShellCommand;
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::ShellCommand;
|
||||
})
|
||||
.await;
|
||||
|
||||
@@ -949,7 +949,8 @@ async fn environment_count_controls_environment_backed_tools() {
|
||||
turn.environments.environments.clear();
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ true);
|
||||
set_feature(turn, Feature::RequestPermissionsTool, /*enabled*/ true);
|
||||
turn.model_info.apply_patch_tool_type = Some(ApplyPatchToolType::Freeform);
|
||||
Arc::make_mut(&mut turn.model_info).apply_patch_tool_type =
|
||||
Some(ApplyPatchToolType::Freeform);
|
||||
})
|
||||
.await;
|
||||
no_environment.assert_visible_lacks(&[
|
||||
@@ -972,7 +973,8 @@ async fn environment_count_controls_environment_backed_tools() {
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExec, /*enabled*/ true);
|
||||
set_feature(turn, Feature::RequestPermissionsTool, /*enabled*/ true);
|
||||
turn.model_info.apply_patch_tool_type = Some(ApplyPatchToolType::Freeform);
|
||||
Arc::make_mut(&mut turn.model_info).apply_patch_tool_type =
|
||||
Some(ApplyPatchToolType::Freeform);
|
||||
})
|
||||
.await;
|
||||
multiple_environments.assert_visible_contains(&[
|
||||
@@ -1000,7 +1002,7 @@ async fn environment_count_controls_environment_backed_tools() {
|
||||
async fn environment_tools_follow_the_step_context() {
|
||||
let (_session, mut turn) = make_session_and_context().await;
|
||||
set_feature(&mut turn, Feature::UnifiedExec, /*enabled*/ true);
|
||||
turn.model_info.apply_patch_tool_type = Some(ApplyPatchToolType::Freeform);
|
||||
Arc::make_mut(&mut turn.model_info).apply_patch_tool_type = Some(ApplyPatchToolType::Freeform);
|
||||
|
||||
let environments = turn.environments.clone();
|
||||
turn.environments.environments.clear();
|
||||
@@ -1130,7 +1132,7 @@ async fn mcp_and_tool_search_follow_direct_and_deferred_tool_exposure() {
|
||||
|
||||
let missing_model_capability = probe_with(
|
||||
|turn| {
|
||||
turn.model_info.supports_search_tool = false;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = false;
|
||||
},
|
||||
searchable_mcp(),
|
||||
)
|
||||
@@ -1139,7 +1141,7 @@ async fn mcp_and_tool_search_follow_direct_and_deferred_tool_exposure() {
|
||||
|
||||
let missing_deferred_tools = probe(|turn| {
|
||||
set_feature(turn, Feature::Collab, /*enabled*/ false);
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
})
|
||||
.await;
|
||||
missing_deferred_tools.assert_visible_lacks(&["tool_search"]);
|
||||
@@ -1151,7 +1153,7 @@ async fn mcp_and_tool_search_follow_direct_and_deferred_tool_exposure() {
|
||||
|
||||
let bedrock_namespace_capability = probe_with(
|
||||
|turn| {
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
use_bedrock_provider(turn);
|
||||
},
|
||||
searchable_mcp(),
|
||||
@@ -1161,7 +1163,7 @@ async fn mcp_and_tool_search_follow_direct_and_deferred_tool_exposure() {
|
||||
|
||||
let enabled = probe_with(
|
||||
|turn| {
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
},
|
||||
searchable_mcp(),
|
||||
)
|
||||
@@ -1175,7 +1177,7 @@ async fn mcp_and_tool_search_follow_direct_and_deferred_tool_exposure() {
|
||||
let reserved_namespace = probe_with(
|
||||
|turn| {
|
||||
set_feature(turn, Feature::CodeMode, /*enabled*/ true);
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
},
|
||||
ToolPlanInputs {
|
||||
tool_runtimes: vec![
|
||||
@@ -1227,8 +1229,8 @@ async fn tool_namespaces_info_is_opt_in_and_tracks_mcp_exposure() {
|
||||
config.tool_registry.turn_metadata_includes_tool_info = enabled;
|
||||
});
|
||||
set_feature(turn, Feature::CodeMode, /*enabled*/ true);
|
||||
turn.model_info.supports_search_tool = true;
|
||||
turn.model_info.use_responses_lite = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).use_responses_lite = true;
|
||||
metadata_state = Some(Arc::clone(&turn.turn_metadata_state));
|
||||
},
|
||||
ToolPlanInputs {
|
||||
@@ -1302,7 +1304,7 @@ async fn strict_namespace_ownership_requires_tool_namespace_inventory_opt_in() {
|
||||
config.tool_registry.error_on_tool_collisions = true;
|
||||
config.tool_registry.turn_metadata_includes_tool_info = enabled;
|
||||
});
|
||||
turn.model_info.use_responses_lite = true;
|
||||
Arc::make_mut(&mut turn.model_info).use_responses_lite = true;
|
||||
let step_context = StepContext::for_test(Arc::new(turn));
|
||||
let mut registry = build_core_tool_registry(
|
||||
step_context.turn.as_ref(),
|
||||
@@ -1357,7 +1359,7 @@ async fn unified_tool_runtimes_preserve_source_order_and_collision_priority() {
|
||||
|turn| {
|
||||
set_features(turn, &[Feature::ShellTool, Feature::UnifiedExec]);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ false);
|
||||
turn.model_info.shell_type = ConfigShellToolType::ShellCommand;
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::ShellCommand;
|
||||
},
|
||||
ToolPlanInputs {
|
||||
tool_runtimes: vec![mcp_runtime(
|
||||
@@ -1553,7 +1555,7 @@ async fn strict_tool_collisions_reject_external_and_synthetic_duplicates() {
|
||||
if code_mode_enabled {
|
||||
set_feature(&mut turn, Feature::CodeMode, /*enabled*/ true);
|
||||
}
|
||||
turn.model_info.supports_search_tool = search_enabled;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = search_enabled;
|
||||
let turn = Arc::new(turn);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn));
|
||||
let mut registry = build_core_tool_registry(
|
||||
@@ -1711,8 +1713,8 @@ async fn code_mode_uses_the_first_normalized_tool_identity() {
|
||||
if code_mode_only {
|
||||
set_feature(turn, Feature::CodeModeOnly, /*enabled*/ true);
|
||||
}
|
||||
turn.model_info.supports_search_tool = true;
|
||||
turn.model_info.use_responses_lite = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).use_responses_lite = true;
|
||||
metadata_state = Some(Arc::clone(&turn.turn_metadata_state));
|
||||
},
|
||||
ToolPlanInputs {
|
||||
@@ -1795,7 +1797,7 @@ async fn code_mode_uses_the_first_normalized_tool_identity() {
|
||||
async fn deferred_extension_tools_are_discoverable_with_tool_search() {
|
||||
let plan = probe_with(
|
||||
|turn| {
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
},
|
||||
ToolPlanInputs {
|
||||
extension_tool_executors: vec![Arc::new(DeferredExtensionTool)],
|
||||
@@ -1815,7 +1817,7 @@ async fn tool_search_cache_rebuilds_when_deferred_sources_change() {
|
||||
let cache = ToolSearchHandlerCache::default();
|
||||
|
||||
let (_session, mut first_turn) = make_session_and_context().await;
|
||||
first_turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut first_turn.model_info).supports_search_tool = true;
|
||||
let first_turn = Arc::new(first_turn);
|
||||
let first_step_context = StepContext::for_test(Arc::clone(&first_turn));
|
||||
let mut first_registry = build_core_tool_registry(
|
||||
@@ -1836,7 +1838,7 @@ async fn tool_search_cache_rebuilds_when_deferred_sources_change() {
|
||||
let first_plan = ToolPlanProbe::from_router(first_router);
|
||||
|
||||
let (_session, mut second_turn) = make_session_and_context().await;
|
||||
second_turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut second_turn.model_info).supports_search_tool = true;
|
||||
let second_turn = Arc::new(second_turn);
|
||||
let second_step_context = StepContext::for_test(Arc::clone(&second_turn));
|
||||
let mut second_registry = build_core_tool_registry(
|
||||
@@ -1883,7 +1885,7 @@ async fn tool_search_cache_rebuilds_when_deferred_world_state_changes() {
|
||||
|
||||
for world_state_enabled in [false, true, false] {
|
||||
let (_session, mut turn) = make_session_and_context().await;
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
set_feature(
|
||||
&mut turn,
|
||||
Feature::DeferredToolWorldState,
|
||||
@@ -1996,7 +1998,7 @@ async fn request_plugin_install_requires_all_discovery_features() {
|
||||
async fn request_plugin_install_stays_visible_without_tool_search() {
|
||||
let plan = probe_with(
|
||||
|turn| {
|
||||
turn.model_info.supports_search_tool = false;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = false;
|
||||
set_features(
|
||||
turn,
|
||||
&[Feature::ToolSuggest, Feature::Apps, Feature::Plugins],
|
||||
@@ -2132,7 +2134,7 @@ async fn code_mode_only_exposes_configured_dynamic_namespace_directly() {
|
||||
let plan = probe_with(
|
||||
|turn| {
|
||||
set_features(turn, &[Feature::CodeMode, Feature::CodeModeOnly]);
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
update_config(turn, |config| {
|
||||
config.code_mode.direct_only_tool_namespaces = vec!["direct_only".to_string()];
|
||||
});
|
||||
@@ -2196,7 +2198,7 @@ async fn excluded_deferred_namespaces_do_not_enable_nested_tool_guidance() {
|
||||
|turn| {
|
||||
set_features(turn, &[Feature::CodeMode, Feature::CodeModeOnly]);
|
||||
set_feature(turn, Feature::Collab, /*enabled*/ false);
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
update_config(turn, |config| {
|
||||
config.code_mode.excluded_tool_namespaces = vec!["excluded".to_string()];
|
||||
});
|
||||
@@ -2450,7 +2452,7 @@ async fn multi_agent_v2_can_disable_wait_agent() {
|
||||
async fn tool_mode_selector_overrides_feature_flags() {
|
||||
let direct = probe(|turn| {
|
||||
set_features(turn, &[Feature::CodeMode, Feature::CodeModeOnly]);
|
||||
turn.model_info.tool_mode = Some(ToolMode::Direct);
|
||||
Arc::make_mut(&mut turn.model_info).tool_mode = Some(ToolMode::Direct);
|
||||
})
|
||||
.await;
|
||||
direct.assert_visible_lacks(&[
|
||||
@@ -2462,7 +2464,7 @@ async fn tool_mode_selector_overrides_feature_flags() {
|
||||
#[tokio::test]
|
||||
async fn v1_multi_agent_tools_defer_when_tool_search_available() {
|
||||
let plan = probe(|turn| {
|
||||
turn.model_info.supports_search_tool = true;
|
||||
Arc::make_mut(&mut turn.model_info).supports_search_tool = true;
|
||||
set_feature(turn, Feature::Collab, /*enabled*/ true);
|
||||
set_feature(turn, Feature::MultiAgentV2, /*enabled*/ false);
|
||||
})
|
||||
@@ -2605,8 +2607,8 @@ async fn multi_agent_v2_bedrock_workers_only_delegate_when_model_supports_v2() {
|
||||
config.multi_agent_v2.tool_namespace = Some("agents".to_string());
|
||||
});
|
||||
use_bedrock_provider(turn);
|
||||
turn.model_info.slug = model.to_string();
|
||||
turn.model_info.multi_agent_version = model_multi_agent_version;
|
||||
Arc::make_mut(&mut turn.model_info).slug = model.to_string();
|
||||
Arc::make_mut(&mut turn.model_info).multi_agent_version = model_multi_agent_version;
|
||||
turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
parent_thread_id: ThreadId::new(),
|
||||
depth: 1,
|
||||
@@ -2721,7 +2723,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
let image_generation = probe_with(
|
||||
|turn| {
|
||||
use_chatgpt_auth(turn);
|
||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
||||
Arc::make_mut(&mut turn.model_info).input_modalities = vec![InputModality::Image];
|
||||
},
|
||||
ToolPlanInputs {
|
||||
extension_tool_executors: vec![image_generation_tool.clone()],
|
||||
@@ -2735,7 +2737,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
|turn| {
|
||||
use_chatgpt_auth(turn);
|
||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ false);
|
||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
||||
Arc::make_mut(&mut turn.model_info).input_modalities = vec![InputModality::Image];
|
||||
},
|
||||
ToolPlanInputs {
|
||||
extension_tool_executors: vec![image_generation_tool.clone()],
|
||||
@@ -2748,7 +2750,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
let text_only_model = probe_with(
|
||||
|turn| {
|
||||
use_chatgpt_auth(turn);
|
||||
turn.model_info.input_modalities = vec![];
|
||||
Arc::make_mut(&mut turn.model_info).input_modalities = vec![];
|
||||
},
|
||||
ToolPlanInputs {
|
||||
extension_tool_executors: vec![image_generation_tool.clone()],
|
||||
@@ -2761,7 +2763,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
let unsupported_provider = probe_with(
|
||||
|turn| {
|
||||
use_bedrock_provider(turn);
|
||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
||||
Arc::make_mut(&mut turn.model_info).input_modalities = vec![InputModality::Image];
|
||||
},
|
||||
ToolPlanInputs {
|
||||
extension_tool_executors: vec![image_generation_tool],
|
||||
@@ -2773,7 +2775,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
|
||||
let live_web_search = probe(|turn| {
|
||||
set_web_search_mode(turn, WebSearchMode::Live);
|
||||
turn.model_info.web_search_tool_type = WebSearchToolType::TextAndImage;
|
||||
Arc::make_mut(&mut turn.model_info).web_search_tool_type = WebSearchToolType::TextAndImage;
|
||||
})
|
||||
.await;
|
||||
assert_eq!(
|
||||
@@ -2792,7 +2794,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
use_chatgpt_auth(turn);
|
||||
set_features(turn, &[Feature::CodeModeOnly, Feature::MultiAgentV2]);
|
||||
set_web_search_mode(turn, WebSearchMode::Live);
|
||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
||||
Arc::make_mut(&mut turn.model_info).input_modalities = vec![InputModality::Image];
|
||||
})
|
||||
.await;
|
||||
assert_eq!(
|
||||
@@ -2869,7 +2871,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
|
||||
let bedrock_cached_web_search = probe(|turn| {
|
||||
use_bedrock_provider(turn);
|
||||
turn.model_info.web_search_tool_type = WebSearchToolType::Text;
|
||||
Arc::make_mut(&mut turn.model_info).web_search_tool_type = WebSearchToolType::Text;
|
||||
})
|
||||
.await;
|
||||
assert_eq!(
|
||||
@@ -2888,7 +2890,7 @@ async fn hosted_web_search_and_standalone_image_generation_follow_runtime_gates(
|
||||
set_feature(turn, Feature::StandaloneWebSearch, /*enabled*/ true);
|
||||
set_web_search_mode(turn, WebSearchMode::Cached);
|
||||
use_bedrock_provider(turn);
|
||||
turn.model_info.web_search_tool_type = WebSearchToolType::Text;
|
||||
Arc::make_mut(&mut turn.model_info).web_search_tool_type = WebSearchToolType::Text;
|
||||
})
|
||||
.await;
|
||||
bedrock_with_standalone_web_search.assert_visible_contains(&["web_search"]);
|
||||
|
||||
Reference in New Issue
Block a user