mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
Finalize model-specific tool plans in ToolRouter (#41195)
## Why Planning tools for a candidate model must not change the selected model or its Responses Lite tool inventory. Otherwise, preparing a fallback can overwrite metadata before the current model's request is sent. ## What changed - Build tool registries and visible specs from an explicit `ModelInfo`. - Store the finalized tool mode, code-mode names, namespace inventory, and terminal and child-management capabilities on `ToolRouter`. - Publish namespace inventory only after selecting the step's router, and use that router's mode when dispatching and recording tool calls. ## Testing Added coverage for candidate-model isolation, fallback compaction metadata, code-mode worker selection, normalized tool names, and router capabilities. GitOrigin-RevId: 56df2de8f42c7e93a4c4dcd117720e40a0cca3db
This commit is contained in:
1 parent
8aea62b2d8
commit
0182ff3480
13 files changed
+532
-127
No files matched your search
@@ -3398,6 +3398,8 @@ impl Session {
|
||||
let tool_router = turn::built_tools(
|
||||
self.as_ref(),
|
||||
turn_context.as_ref(),
|
||||
// TODO(CDXENT-441): use the step scoped model
|
||||
turn_context.model_info(),
|
||||
&environments,
|
||||
&mcp,
|
||||
&extension_data,
|
||||
@@ -3405,6 +3407,21 @@ impl Session {
|
||||
)
|
||||
.or_cancel(cancellation_token)
|
||||
.await??;
|
||||
// Publish inventory after planning rather than during finalization, so constructing
|
||||
// additional candidate plans cannot overwrite turn-wide metadata.
|
||||
if turn_context
|
||||
.config
|
||||
.tool_registry
|
||||
.turn_metadata_includes_tool_info
|
||||
&& turn_context.model_info().use_responses_lite
|
||||
{
|
||||
turn_context.turn_metadata_state.set_tool_namespaces_info(
|
||||
tool_router
|
||||
.tool_namespaces_info()
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
Ok(Arc::new(StepContext {
|
||||
settings,
|
||||
token_budget,
|
||||
|
||||
@@ -70,6 +70,7 @@ use codex_protocol::models::ImageDetail;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::SandboxEnforcement;
|
||||
use codex_protocol::openai_models::ModelServiceTier;
|
||||
use codex_protocol::openai_models::ToolMode;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
@@ -257,6 +258,10 @@ impl StepContext {
|
||||
tool_router: Arc::new(ToolRouter::from_parts(
|
||||
ToolRegistry::empty_for_test(),
|
||||
Vec::new(),
|
||||
ToolMode::Direct,
|
||||
BTreeMap::new(),
|
||||
/*tool_namespaces_info*/ None,
|
||||
&[],
|
||||
)),
|
||||
loaded_agents_md: None,
|
||||
})
|
||||
@@ -877,6 +882,7 @@ pub(crate) fn tool_registry_for_test_step(
|
||||
) -> (ToolRegistry, Vec<ToolSpec>) {
|
||||
let mut registry = crate::tools::spec_plan::build_core_tool_registry(
|
||||
step_context.turn.as_ref(),
|
||||
step_context.turn.model_info(),
|
||||
&step_context.environments,
|
||||
step_context.mcp.as_ref(),
|
||||
/*tool_suggest_candidates*/ None,
|
||||
@@ -884,6 +890,7 @@ pub(crate) fn tool_registry_for_test_step(
|
||||
);
|
||||
let hosted_specs = crate::tools::spec_plan::append_source_tools(
|
||||
step_context.turn.as_ref(),
|
||||
step_context.turn.model_info(),
|
||||
&mut registry,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
@@ -897,6 +904,7 @@ fn test_tool_runtime(session: Arc<Session>, turn_context: Arc<TurnContext>) -> T
|
||||
let (registry, hosted_specs) = tool_registry_for_test_step(step_context.as_ref());
|
||||
let router = Arc::new(ToolRouter::from_registry(
|
||||
step_context.turn.as_ref(),
|
||||
step_context.turn.model_info(),
|
||||
registry,
|
||||
hosted_specs,
|
||||
&Default::default(),
|
||||
@@ -11612,6 +11620,7 @@ async fn fatal_tool_error_stops_turn_and_reports_error() {
|
||||
let (registry, hosted_specs) = tool_registry_for_test_step(step_context.as_ref());
|
||||
let router = ToolRouter::from_registry(
|
||||
step_context.turn.as_ref(),
|
||||
step_context.turn.model_info(),
|
||||
registry,
|
||||
hosted_specs,
|
||||
&Default::default(),
|
||||
|
||||
@@ -1509,13 +1509,14 @@ pub(crate) async fn prepare_tool_recommendations(
|
||||
skip_all,
|
||||
fields(
|
||||
turn_id = %turn_context.sub_id,
|
||||
model = %turn_context.model_info().slug,
|
||||
model = %model_info.slug,
|
||||
apps_enabled = turn_context.apps_enabled()
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn built_tools(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
model_info: &codex_protocol::openai_models::ModelInfo,
|
||||
environments: &TurnEnvironmentSnapshot,
|
||||
mcp: &Arc<codex_mcp::McpBinding>,
|
||||
step_store: &ExtensionData,
|
||||
@@ -1584,6 +1585,7 @@ pub(crate) async fn built_tools(
|
||||
Ok(Arc::new(build_tool_router(
|
||||
sess,
|
||||
turn_context,
|
||||
model_info,
|
||||
environments,
|
||||
mcp,
|
||||
apps_enabled,
|
||||
|
||||
@@ -1046,14 +1046,14 @@ impl Session {
|
||||
|
||||
if !tc.code_mode_available
|
||||
&& matches!(
|
||||
crate::tools::requested_tool_mode(tc),
|
||||
crate::tools::requested_tool_mode(tc, tc.model_info()),
|
||||
codex_protocol::openai_models::ToolMode::CodeMode
|
||||
| codex_protocol::openai_models::ToolMode::CodeModeOnly
|
||||
)
|
||||
&& let Some(message) = self
|
||||
.services
|
||||
.code_mode_service
|
||||
.take_unavailable_warning(crate::tools::effective_tool_mode(tc))
|
||||
.take_unavailable_warning(crate::tools::effective_tool_mode(tc, tc.model_info()))
|
||||
{
|
||||
self.send_event(tc, EventMsg::Warning(WarningEvent { message }))
|
||||
.await;
|
||||
|
||||
Reference in new issue
Block a user