From 33dc93e4d2913ba940213ede693b84ebaf80b3f6 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Tue, 3 Feb 2026 18:05:02 +0000 Subject: [PATCH] Enable parallel shell tools (#10505) Summary - mark the shell-related tools as supporting parallel tool calls so exec_command, shell_command, etc. can run concurrently - update expectations in tool parallelism tests to reflect the new parallel behavior - drop the unused serial duration helper from the suite Testing - Not run (not requested) --- codex-rs/core/src/tools/spec.rs | 19 ++++++++++++++----- codex-rs/core/tests/suite/tool_parallelism.rs | 15 ++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index bd37e8bcc..c0fd1d022 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -1258,13 +1258,19 @@ pub(crate) fn build_specs( match &config.shell_type { ConfigShellToolType::Default => { - builder.push_spec(create_shell_tool(config.request_rule_enabled)); + builder.push_spec_with_parallel_support( + create_shell_tool(config.request_rule_enabled), + true, + ); } ConfigShellToolType::Local => { - builder.push_spec(ToolSpec::LocalShell {}); + builder.push_spec_with_parallel_support(ToolSpec::LocalShell {}, true); } ConfigShellToolType::UnifiedExec => { - builder.push_spec(create_exec_command_tool(config.request_rule_enabled)); + builder.push_spec_with_parallel_support( + create_exec_command_tool(config.request_rule_enabled), + true, + ); builder.push_spec(create_write_stdin_tool()); builder.register_handler("exec_command", unified_exec_handler.clone()); builder.register_handler("write_stdin", unified_exec_handler); @@ -1273,7 +1279,10 @@ pub(crate) fn build_specs( // Do nothing. } ConfigShellToolType::ShellCommand => { - builder.push_spec(create_shell_command_tool(config.request_rule_enabled)); + builder.push_spec_with_parallel_support( + create_shell_command_tool(config.request_rule_enabled), + true, + ); } } @@ -1982,7 +1991,7 @@ mod tests { }); let (tools, _) = build_specs(&tools_config, None, &[]).build(); - assert!(!find_tool(&tools, "exec_command").supports_parallel_tool_calls); + assert!(find_tool(&tools, "exec_command").supports_parallel_tool_calls); assert!(!find_tool(&tools, "write_stdin").supports_parallel_tool_calls); assert!(find_tool(&tools, "grep_files").supports_parallel_tool_calls); assert!(find_tool(&tools, "list_dir").supports_parallel_tool_calls); diff --git a/codex-rs/core/tests/suite/tool_parallelism.rs b/codex-rs/core/tests/suite/tool_parallelism.rs index 0e03bbc26..955b2f7ec 100644 --- a/codex-rs/core/tests/suite/tool_parallelism.rs +++ b/codex-rs/core/tests/suite/tool_parallelism.rs @@ -77,13 +77,6 @@ fn assert_parallel_duration(actual: Duration) { ); } -fn assert_serial_duration(actual: Duration) { - assert!( - actual >= Duration::from_millis(500), - "expected serial execution to take longer, got {actual:?}" - ); -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn read_file_tools_run_in_parallel() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); @@ -147,7 +140,7 @@ async fn read_file_tools_run_in_parallel() -> anyhow::Result<()> { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn non_parallel_tools_run_serially() -> anyhow::Result<()> { +async fn shell_tools_run_in_parallel() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; @@ -174,13 +167,13 @@ async fn non_parallel_tools_run_serially() -> anyhow::Result<()> { mount_sse_sequence(&server, vec![first_response, second_response]).await; let duration = run_turn_and_measure(&test, "run shell_command twice").await?; - assert_serial_duration(duration); + assert_parallel_duration(duration); Ok(()) } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn mixed_tools_fall_back_to_serial() -> anyhow::Result<()> { +async fn mixed_parallel_tools_run_in_parallel() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; @@ -208,7 +201,7 @@ async fn mixed_tools_fall_back_to_serial() -> anyhow::Result<()> { mount_sse_sequence(&server, vec![first_response, second_response]).await; let duration = run_turn_and_measure(&test, "mix tools").await?; - assert_serial_duration(duration); + assert_parallel_duration(duration); Ok(()) }