Skip to content

Python: feat(core): add tool concurrency groups and sequential execution order - #7881

Open
pratik wayase (PratikWayase) wants to merge 6 commits into
microsoft:mainfrom
PratikWayase:feat/serialize-same-message-function-calls
Open

Python: feat(core): add tool concurrency groups and sequential execution order#7881
pratik wayase (PratikWayase) wants to merge 6 commits into
microsoft:mainfrom
PratikWayase:feat/serialize-same-message-function-calls

Conversation

@PratikWayase

@PratikWayase pratik wayase (PratikWayase) commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

Currently, the framework executes all tool calls requested in a single assistant message concurrently. While this is a great default for independent calls (like parallel document lookups), models routinely emit dependent calls in one batch. Because the framework lacked a batch-wide execution control, dependent reads could race still-running writes, leading to "not found" errors and contradictory agent states.

This PR closes that gap by providing a declarative, framework-level boolean to control batch-wide execution order, preventing stateful tool race conditions without relying on fragile, tool-side asyncio.Lock workarounds.

Fixes #7386

Description & Review Guide

What are the major changes?

  • Batch-Wide Execution Control: Replaced the tool_execution_order string option with allow_concurrent_invocation: bool to mirror the .NET FunctionInvokingChatClient.AllowConcurrentInvocation API.
  • Location & Defaults: Defined allow_concurrent_invocation strictly within FunctionInvocationConfiguration (defaulting to True to preserve existing Python parallel behavior), rather than routing it through provider chat options.
  • Execution Loop Hardening: Simplified _try_execute_function_call_groups in _tools.py. When set to False, tools run one-by-one. If a call requests termination or fails, the loop immediately stops dequeuing subsequent calls and safely cancels any in-flight parallel siblings.
  • API Wiring Cleanup: Ensured allow_concurrent_invocation is safely popped from mutable_options and custom_args before being forwarded, preventing it from leaking as a tool runtime argument or provider request option.

What do you want reviewers to focus on?

Please review the execution logic in _try_execute_function_call_groups (_tools.py). Specifically, verify that:

  1. When allow_concurrent_invocation is False, the loop correctly breaks and stops dequeuing calls immediately if should_terminate is True.
  2. The except BaseException block correctly cancels in-flight sibling tasks and discards their results to preserve fail-closed middleware behavior.
  3. The configuration handling in FunctionInvocationLayer.get_response cleanly removes the key from mutable_options without leaking it to the underlying chat client.

Note: Per maintainer feedback, the per-tool concurrency_group parameter proposed in earlier iterations has been completely removed from this PR. Selective tool-level controls will be designed separately in #7914 to settle cross-SDK semantics before implementation.

Related Issue

Fixes #7386
Design for deferred per-tool controls: #7914

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open open PR for this issue.
  • This is not a Breaking Change. Default behavior remains allow_concurrent_invocation = True.

Copilot AI balanced review requested due to automatic review settings August 26, 2026 10:49
@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Aug 26, 2026
@github-actions github-actions Bot changed the title feat(core): add tool concurrency groups and sequential execution order Python: feat(core): add tool concurrency groups and sequential execution order Aug 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chetantoshniwal

Copy link
Copy Markdown
Contributor

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (5 commit(s)): e3efb1e57d81, b8b1f034fbdc, 7783ec2d9c37, fb4cb4f75eb1, e55413014eb4
Model: gpt-5.6-sol

Overview

The PR adds per-tool concurrency groups and a run-level sequential mode while preserving result order, per-call context propagation, and cancellation of in-flight group tasks. The new grouping loop has gaps around fail-closed middleware, middleware-requested termination, and approval replay ordering, and its configuration/key handling can silently violate the requested execution policy. These issues can start side-effecting calls after a policy stop, reverse dependent operations, or unexpectedly serialize or parallelize a batch.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
5 verified findings remained after source verification (1 high, 4 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/_tools.py

Comment thread python/packages/core/agent_framework/_tools.py Outdated
Comment thread python/packages/core/agent_framework/_tools.py Outdated
Comment thread python/packages/core/agent_framework/_tools.py Outdated
Comment thread python/packages/core/agent_framework/_tools.py Outdated
Comment thread python/packages/core/agent_framework/_tools.py Outdated
@eavanvalkenburg

Eduard van Valkenburg (eavanvalkenburg) commented Aug 27, 2026

Copy link
Copy Markdown
Member

pratik wayase (@PratikWayase) Thanks for working on this. I think we should narrow the scope of this PR to the batch-wide execution control and make two changes to that API:

  1. Mirror the .NET name: use allow_concurrent_invocation rather than tool_execution_order. This is a boolean policy in .NET (FunctionInvokingChatClient.AllowConcurrentInvocation), and using the same concept and name will make the SDKs easier to understand together.
  2. Define it in FunctionInvocationConfiguration, not chat options. allow_multiple_tool_calls is a provider request option controlling whether the model may emit multiple calls; allow_concurrent_invocation controls how the framework invokes calls after receiving them. The latter belongs to the function-invocation layer and should not need to be removed before forwarding options to the provider. Python should retain its existing parallel default, so the default value here should be True even though .NET defaults to sequential invocation.

I am not yet convinced that we have resolved enough of the design for per-tool concurrency_group controls to include them in this PR. There are open questions around MCP-discovered and provider-hosted tools, middleware failure and termination semantics, approval pause/replay ordering, cancellation guarantees, dynamic tools and MCP reloads, scheduler-key collisions, cross-run scope, and whether .NET and Python should expose the same capability simultaneously.

I opened #7914 to design the selective tool-level controls separately and tagged Roger Barreto (@rogerbarreto) there for input on whether we should address this in .NET and Python together. I suggest removing concurrency_group from this PR and using that issue to settle the cross-SDK API and semantics before implementation.

@PratikWayase

Copy link
Copy Markdown
Contributor Author

Hi Eduard van Valkenburg (@eavanvalkenburg), thanks for the guidance!

I've updated the PR based on your feedback

Renamed tool_execution_order to allow_concurrent_invocation: bool (defaulting to True) and kept it in FunctionInvocationConfiguration.
Completely removed concurrency_group from this PR as suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Feature]: No way to serialize (or order) same-message function calls — stateful tools with write→read dependencies race

4 participants