Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/common/models_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo {
input_modalities: default_input_modalities(),
used_fallback_model_metadata: false,
supports_search_tool: false,
tool_mode: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions codex-rs/codex-api/tests/models_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ async fn models_client_hits_models_endpoint() {
input_modalities: default_input_modalities(),
used_fallback_model_metadata: false,
supports_search_tool: false,
tool_mode: None,
}],
};

Expand Down
11 changes: 11 additions & 0 deletions codex-rs/core/src/session/review.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use codex_protocol::openai_models::ToolMode;
use std::sync::atomic::AtomicBool;

/// Spawn a review thread using the given prompt.
Expand Down Expand Up @@ -47,6 +48,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 = 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!(
Expand Down Expand Up @@ -96,6 +106,7 @@ pub(super) async fn spawn_review_thread(
config: per_turn_config,
auth_manager: auth_manager_for_context,
model_info: model_info.clone(),
tool_mode,
session_telemetry: session_telemetry_for_context,
provider: provider_for_context,
reasoning_effort,
Expand Down
22 changes: 22 additions & 0 deletions codex-rs/core/src/session/turn_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use codex_model_provider::SharedModelProvider;
use codex_model_provider::create_model_provider;
use codex_protocol::SessionId;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::openai_models::ToolMode;
use codex_protocol::protocol::ThreadSource;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
Expand Down Expand Up @@ -55,6 +56,7 @@ pub struct TurnContext {
pub config: Arc<Config>,
pub(crate) auth_manager: Option<Arc<AuthManager>>,
pub(crate) model_info: ModelInfo,
pub(crate) tool_mode: ToolMode,
pub(crate) session_telemetry: SessionTelemetry,
pub(crate) provider: SharedModelProvider,
pub(crate) reasoning_effort: Option<ReasoningEffortConfig>,
Expand Down Expand Up @@ -172,6 +174,15 @@ impl TurnContext {
let model_info = models_manager
.get_model_info(model.as_str(), &config.to_models_manager_config())
.await;
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
Expand Down Expand Up @@ -212,6 +223,7 @@ impl TurnContext {
config: Arc::new(config),
auth_manager: self.auth_manager.clone(),
model_info: model_info.clone(),
tool_mode,
session_telemetry: self
.session_telemetry
.clone()
Expand Down Expand Up @@ -475,6 +487,15 @@ impl Session {
);

let mut per_turn_config = per_turn_config;
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),
Expand All @@ -501,6 +522,7 @@ impl Session {
config: per_turn_config.clone(),
auth_manager: auth_manager_for_context,
model_info: model_info.clone(),
tool_mode,
session_telemetry: session_telemetry_for_context,
provider: provider_for_context,
reasoning_effort,
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/code_mode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +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_features::Feature;
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;
Expand Down Expand Up @@ -91,7 +91,7 @@ impl CodeModeService {
router: Arc<ToolRouter>,
tracker: SharedTurnDiffTracker,
) -> Option<codex_code_mode::CodeModeTurnWorker> {
if !turn.features.enabled(Feature::CodeMode) {
if !matches!(turn.tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) {
return None;
}

Expand Down
29 changes: 15 additions & 14 deletions codex-rs/core/src/tools/spec_plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.features.get().enabled(Feature::CodeMode)
}

fn code_mode_only_enabled(turn_context: &TurnContext) -> bool {
code_mode_enabled(turn_context) && turn_context.features.get().enabled(Feature::CodeModeOnly)
}

fn multi_agent_v2_enabled(turn_context: &TurnContext) -> bool {
turn_context.features.get().enabled(Feature::MultiAgentV2)
}
Expand Down Expand Up @@ -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,
Expand All @@ -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![];
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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));
}
Expand Down
24 changes: 24 additions & 0 deletions codex-rs/core/src/tools/spec_plan_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use codex_protocol::dynamic_tools::DynamicToolSpec;
use codex_protocol::openai_models::ApplyPatchToolType;
use codex_protocol::openai_models::ConfigShellToolType;
use codex_protocol::openai_models::InputModality;
use codex_protocol::openai_models::ToolMode;
use codex_protocol::openai_models::WebSearchToolType;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
Expand Down Expand Up @@ -215,6 +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);
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]) {
Expand Down Expand Up @@ -797,6 +807,20 @@ async fn multi_agent_feature_selects_one_agent_tool_family() {
);
}

#[tokio::test]
async fn tool_mode_selector_overrides_feature_flags() {
Comment thread
aibrahim-oai marked this conversation as resolved.
let direct = probe(|turn| {
set_features(turn, &[Feature::CodeMode, Feature::CodeModeOnly]);
turn.model_info.tool_mode = Some(ToolMode::Direct);
turn.tool_mode = ToolMode::Direct;
})
.await;
direct.assert_visible_lacks(&[
codex_code_mode::PUBLIC_TOOL_NAME,
codex_code_mode::WAIT_TOOL_NAME,
]);
}

#[tokio::test]
async fn v1_multi_agent_tools_defer_when_tool_search_available() {
let plan = probe(|turn| {
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/tests/suite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ mod json_result;
mod live_cli;
mod mcp_turn_metadata;
mod model_overrides;
mod model_runtime_selectors;
mod model_switching;
mod model_visible_layout;
mod models_cache_ttl;
Expand Down
Loading
Loading