[codex] Inline tool mode resolution

This commit is contained in:
Ahmed Ibrahim
2026-05-29 00:40:33 -07:00
parent b138a8a992
commit c1713608ac
7 changed files with 54 additions and 96 deletions

View File

@@ -60,7 +60,6 @@ pub use codex_mcp::SandboxState;
mod mcp_openai_file;
mod mcp_tool_call;
pub(crate) mod mention_syntax;
mod tool_mode;
pub(crate) mod utils;
pub use mention_syntax::PLUGIN_TEXT_MENTION_SIGIL;
pub use mention_syntax::TOOL_MENTION_SIGIL;

View File

@@ -1,5 +1,4 @@
use super::*;
use crate::tool_mode::resolve_tool_mode;
use std::sync::atomic::AtomicBool;
/// Spawn a review thread using the given prompt.
@@ -48,7 +47,15 @@ pub(super) async fn spawn_review_thread(
let mut per_turn_config = (*config).clone();
per_turn_config.model = Some(model.clone());
per_turn_config.features = review_features.clone();
let tool_mode = resolve_tool_mode(&model_info, &per_turn_config.features);
let tool_mode = model_info.tool_mode.unwrap_or_else(|| {
if per_turn_config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if per_turn_config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
if let Err(err) = per_turn_config.web_search_mode.set(review_web_search_mode) {
let fallback_value = per_turn_config.web_search_mode.value();
tracing::warn!(

View File

@@ -2,7 +2,6 @@ use super::*;
use crate::SkillLoadOutcome;
use crate::config::GhostSnapshotConfig;
use crate::environment_selection::ResolvedTurnEnvironments;
use crate::tool_mode::resolve_tool_mode;
use codex_model_provider::SharedModelProvider;
use codex_model_provider::create_model_provider;
use codex_protocol::SessionId;
@@ -165,14 +164,6 @@ impl TurnContext {
self.goal_tools_supported && self.features.get().enabled(Feature::Goals)
}
pub(crate) fn code_mode_enabled(&self) -> bool {
matches!(self.tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly)
}
pub(crate) fn code_mode_only_enabled(&self) -> bool {
self.tool_mode == ToolMode::CodeModeOnly
}
pub(crate) async fn with_model(
&self,
model: String,
@@ -183,7 +174,15 @@ impl TurnContext {
let model_info = models_manager
.get_model_info(model.as_str(), &config.to_models_manager_config())
.await;
let tool_mode = resolve_tool_mode(&model_info, &config.features);
let tool_mode = model_info.tool_mode.unwrap_or_else(|| {
if config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
let truncation_policy = model_info.truncation_policy.into();
let supported_reasoning_levels = model_info
.supported_reasoning_levels
@@ -488,7 +487,15 @@ impl Session {
);
let mut per_turn_config = per_turn_config;
let tool_mode = resolve_tool_mode(&model_info, &per_turn_config.features);
let tool_mode = model_info.tool_mode.unwrap_or_else(|| {
if per_turn_config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if per_turn_config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
per_turn_config.service_tier = get_service_tier(
per_turn_config.service_tier,
per_turn_config.features.enabled(Feature::FastMode),

View File

@@ -1,60 +0,0 @@
use crate::config::ManagedFeatures;
use codex_features::Feature;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::openai_models::ToolMode;
pub(crate) fn resolve_tool_mode(model_info: &ModelInfo, features: &ManagedFeatures) -> ToolMode {
model_info
.tool_mode
.unwrap_or_else(|| tool_mode_from_features(features))
}
fn tool_mode_from_features(features: &ManagedFeatures) -> ToolMode {
if features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
}
#[cfg(test)]
mod tests {
use super::*;
use codex_features::Features;
fn features(enabled: &[Feature]) -> ManagedFeatures {
let mut features = Features::default();
for feature in enabled {
features.enable(*feature);
}
features.into()
}
fn model_info(tool_mode: Option<ToolMode>) -> ModelInfo {
let mut model_info = codex_models_manager::model_info::model_info_from_slug("test-model");
model_info.tool_mode = tool_mode;
model_info
}
#[test]
fn omitted_selector_follows_feature_flags() {
let tool_mode = resolve_tool_mode(
&model_info(/*tool_mode*/ None),
&features(&[Feature::CodeModeOnly]),
);
assert_eq!(tool_mode, ToolMode::CodeModeOnly);
}
#[test]
fn explicit_selector_overrides_feature_flags() {
let tool_mode = resolve_tool_mode(
&model_info(Some(ToolMode::Direct)),
&features(&[Feature::CodeModeOnly]),
);
assert_eq!(tool_mode, ToolMode::Direct);
}
}

View File

@@ -30,6 +30,7 @@ use crate::tools::parallel::ToolCallRuntime;
use crate::tools::router::ToolCall;
use crate::tools::router::ToolCallSource;
use crate::unified_exec::resolve_max_tokens;
use codex_protocol::openai_models::ToolMode;
use codex_tools::ToolName;
use codex_utils_output_truncation::TruncationPolicy;
use codex_utils_output_truncation::formatted_truncate_text_content_items_with_policy;
@@ -90,7 +91,7 @@ impl CodeModeService {
router: Arc<ToolRouter>,
tracker: SharedTurnDiffTracker,
) -> Option<codex_code_mode::CodeModeTurnWorker> {
if !turn.code_mode_enabled() {
if !matches!(turn.tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) {
return None;
}

View File

@@ -60,6 +60,7 @@ use codex_mcp::ToolInfo;
use codex_protocol::dynamic_tools::DynamicToolSpec;
use codex_protocol::openai_models::ConfigShellToolType;
use codex_protocol::openai_models::InputModality;
use codex_protocol::openai_models::ToolMode;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
use codex_tools::DiscoverableTool;
@@ -230,8 +231,10 @@ fn spec_for_model_request(
exposure: ToolExposure,
spec: ToolSpec,
) -> ToolSpec {
if code_mode_enabled(turn_context)
&& exposure != ToolExposure::DirectModelOnly
if matches!(
turn_context.tool_mode,
ToolMode::CodeMode | ToolMode::CodeModeOnly
) && exposure != ToolExposure::DirectModelOnly
&& codex_code_mode::is_code_mode_nested_tool(spec.name())
{
codex_tools::augment_tool_spec_for_code_mode(spec)
@@ -282,14 +285,6 @@ fn namespace_tools_enabled(turn_context: &TurnContext) -> bool {
turn_context.provider.capabilities().namespace_tools
}
fn code_mode_enabled(turn_context: &TurnContext) -> bool {
turn_context.code_mode_enabled()
}
fn code_mode_only_enabled(turn_context: &TurnContext) -> bool {
turn_context.code_mode_only_enabled()
}
fn multi_agent_v2_enabled(turn_context: &TurnContext) -> bool {
turn_context.features.get().enabled(Feature::MultiAgentV2)
}
@@ -398,7 +393,7 @@ fn is_hidden_by_code_mode_only(
tool_name: &ToolName,
exposure: ToolExposure,
) -> bool {
code_mode_only_enabled(turn_context)
turn_context.tool_mode == ToolMode::CodeModeOnly
&& exposure != ToolExposure::DirectModelOnly
&& codex_code_mode::is_code_mode_nested_tool(&codex_tools::code_mode_name_for_tool_name(
tool_name,
@@ -410,7 +405,10 @@ fn build_code_mode_executors(
executors: &[Arc<dyn CoreToolRuntime>],
deferred_tools_available: bool,
) -> Vec<Arc<dyn CoreToolRuntime>> {
if !code_mode_enabled(turn_context) {
if !matches!(
turn_context.tool_mode,
ToolMode::CodeMode | ToolMode::CodeModeOnly
) {
return vec![];
}
@@ -444,7 +442,7 @@ fn build_code_mode_executors(
create_code_mode_tool(
&enabled_tools,
&namespace_descriptions,
code_mode_only_enabled(turn_context),
turn_context.tool_mode == ToolMode::CodeModeOnly,
deferred_tools_available,
),
code_mode_nested_tool_specs,
@@ -847,7 +845,10 @@ fn append_extension_tool_executors(
.iter()
.map(|executor| executor.tool_name())
.collect::<HashSet<_>>();
if code_mode_enabled(turn_context) {
if matches!(
turn_context.tool_mode,
ToolMode::CodeMode | ToolMode::CodeModeOnly
) {
reserved_tool_names.insert(ToolName::plain(codex_code_mode::PUBLIC_TOOL_NAME));
reserved_tool_names.insert(ToolName::plain(codex_code_mode::WAIT_TOOL_NAME));
}

View File

@@ -32,7 +32,6 @@ use serde_json::json;
use crate::session::tests::make_session_and_context;
use crate::session::turn_context::TurnContext;
use crate::tool_mode::resolve_tool_mode;
use crate::tools::handlers::multi_agents_spec::MULTI_AGENT_V1_NAMESPACE;
use crate::tools::router::ToolRouter;
use crate::tools::router::ToolRouterParams;
@@ -217,7 +216,15 @@ fn set_feature(turn: &mut TurnContext, feature: Feature, enabled: bool) {
.expect("test feature should be disableable in config");
}
turn.config = Arc::new(config);
resolve_tool_mode_for_turn(turn);
turn.tool_mode = turn.model_info.tool_mode.unwrap_or_else(|| {
if turn.config.features.enabled(Feature::CodeModeOnly) {
ToolMode::CodeModeOnly
} else if turn.config.features.enabled(Feature::CodeMode) {
ToolMode::CodeMode
} else {
ToolMode::Direct
}
});
}
fn set_features(turn: &mut TurnContext, features: &[Feature]) {
@@ -232,10 +239,6 @@ fn update_config(turn: &mut TurnContext, update: impl FnOnce(&mut crate::config:
turn.config = Arc::new(config);
}
fn resolve_tool_mode_for_turn(turn: &mut TurnContext) {
turn.tool_mode = resolve_tool_mode(&turn.model_info, &turn.config.features);
}
fn set_web_search_mode(turn: &mut TurnContext, mode: WebSearchMode) {
update_config(turn, |config| {
config
@@ -809,7 +812,7 @@ 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);
resolve_tool_mode_for_turn(turn);
turn.tool_mode = ToolMode::Direct;
})
.await;
direct.assert_visible_lacks(&[