From f5d9939cda360d87637b893ce8472d7e58372d8b Mon Sep 17 00:00:00 2001 From: jif-oai Date: Tue, 18 Nov 2025 17:10:14 +0000 Subject: [PATCH] feat: enable parallel tool calls (#6796) --- codex-rs/core/src/codex.rs | 22 +++++++++++++++++-- codex-rs/core/src/features.rs | 8 +++++++ codex-rs/core/src/model_family.rs | 2 ++ .../core/templates/parallel/instructions.md | 14 ++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 codex-rs/core/templates/parallel/instructions.md diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index ecfefa792..64d06d057 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -1934,12 +1934,30 @@ async fn run_turn( .client .get_model_family() .supports_parallel_tool_calls; - let parallel_tool_calls = model_supports_parallel; + + // TODO(jif) revert once testing phase is done. + let parallel_tool_calls = model_supports_parallel + && sess + .state + .lock() + .await + .session_configuration + .features + .enabled(Feature::ParallelToolCalls); + let mut base_instructions = turn_context.base_instructions.clone(); + if parallel_tool_calls { + static INSTRUCTIONS: &str = include_str!("../templates/parallel/instructions.md"); + static INSERTION_SPOT: &str = "## Editing constraints"; + base_instructions + .as_mut() + .map(|base| base.replace(INSERTION_SPOT, INSTRUCTIONS)); + } + let prompt = Prompt { input, tools: router.specs(), parallel_tool_calls, - base_instructions_override: turn_context.base_instructions.clone(), + base_instructions_override: base_instructions, output_schema: turn_context.final_output_json_schema.clone(), }; diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index b11cd0f20..5f579defd 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -50,6 +50,8 @@ pub enum Feature { RemoteCompaction, /// Enable the default shell tool. ShellTool, + /// Allow model to call multiple tools in parallel (only for models supporting it). + ParallelToolCalls, } impl Feature { @@ -313,6 +315,12 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::Experimental, default_enabled: false, }, + FeatureSpec { + id: Feature::ParallelToolCalls, + key: "parallel", + stage: Stage::Experimental, + default_enabled: false, + }, FeatureSpec { id: Feature::ShellTool, key: "shell_tool", diff --git a/codex-rs/core/src/model_family.rs b/codex-rs/core/src/model_family.rs index db37119d1..150420fec 100644 --- a/codex-rs/core/src/model_family.rs +++ b/codex-rs/core/src/model_family.rs @@ -178,6 +178,7 @@ pub fn find_family_for_model(slug: &str) -> Option { base_instructions: GPT_5_CODEX_INSTRUCTIONS.to_string(), apply_patch_tool_type: Some(ApplyPatchToolType::Freeform), shell_type: if cfg!(windows) { ConfigShellToolType::ShellCommand } else { ConfigShellToolType::Default }, + supports_parallel_tool_calls: true, support_verbosity: false, ) } else if slug.starts_with("gpt-5.1") { @@ -189,6 +190,7 @@ pub fn find_family_for_model(slug: &str) -> Option { default_verbosity: Some(Verbosity::Low), base_instructions: GPT_5_1_INSTRUCTIONS.to_string(), default_reasoning_effort: Some(ReasoningEffort::Medium), + supports_parallel_tool_calls: true, ) } else if slug.starts_with("gpt-5") { model_family!( diff --git a/codex-rs/core/templates/parallel/instructions.md b/codex-rs/core/templates/parallel/instructions.md new file mode 100644 index 000000000..d690501af --- /dev/null +++ b/codex-rs/core/templates/parallel/instructions.md @@ -0,0 +1,14 @@ +## Exploration and reading files + +- **Think first.** Before any tool call, decide ALL files/resources you will need. +- **Batch everything.** If you need multiple files (even from different places), read them together. +- **multi_tool_use.parallel** Use `multi_tool_use.parallel` to parallelize tool calls and only this. +- **Only make sequential calls if you truly cannot know the next file without seeing a result first.** +- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise. + +**Additional notes**: +* Always maximize parallelism. Never read files one-by-one unless logically unavoidable. +* This concern every read/list/search operations including, but not only, `cat`, `rg`, `sed`, `ls`, `git show`, `nl`, `wc`, ... +* Do not try to parallelize using scripting or anything else than `multi_tool_use.parallel`. + +## Editing constraints \ No newline at end of file