Skip to content

Python: Stream results for tools executed during approval resolution#7259

Closed
kartikmadan11 wants to merge 1 commit into
microsoft:mainfrom
kartikmadan11:fix/7241-stream-approval-resolution-results
Closed

Python: Stream results for tools executed during approval resolution#7259
kartikmadan11 wants to merge 1 commit into
microsoft:mainfrom
kartikmadan11:fix/7241-stream-approval-resolution-results

Conversation

@kartikmadan11

Copy link
Copy Markdown
Contributor

Motivation & Context

Tools executed while resolving an approval did not stream their result, so streaming consumers never observed it. This is the follow-on issue found while implementing #7043 / PR #7091: once provider-injected tools are deferred and executed in-run (instead of being rejected), their function_result was consumed inside agent.run and never surfaced, so the AG-UI transport emitted no TOOL_CALL_RESULT for them, unlike statically executed approvals.

Fixes #7241

Description & Review Guide

What are the major changes?

  • The approval-resolution branch of _process_function_requests (_tools.py) executes the approved calls and splices results into the message history, but returned update_role=None, so the streaming loop never yielded them. It now returns the executed function_result items with update_role="tool".
  • The first _process_function_requests call site in the streaming loop had no yield gate (unlike the second call site). Added the matching gate so these results stream like any other tool result. The existing AG-UI emitter then produces exactly one TOOL_CALL_RESULT, with correct ordering and no transport changes.

What is the impact of these changes?

  • Tools executed during approval resolution (notably deferred provider-injected tools) now stream their result like normal tool calls. The non-streaming path is unaffected: it does not consume update_role and already carries the result via the spliced messages. No public API changes.

What do you want reviewers to focus on?

  • Whether the single yield at the first call site can double-emit. It cannot: the result is spliced into prepped_messages and fed to the next model call, which does not echo tool results back as content; the second _process_function_requests call operates on the model response, not the already-executed approval. The added test asserts exactly one streamed function_result per call.

Related Issue

Fixes #7241

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 PR for this issue.
  • This is not a breaking change.

Tools executed while resolving approvals went through the prepped_messages
branch of _process_function_requests, which spliced their results into the
message history but returned update_role=None, so the streaming loop never
yielded them. Consumers such as the AG-UI transport therefore never observed
the result: for a deferred provider-injected tool the side effect fired but no
result update was streamed, unlike a statically executed approval.

Return the executed function_result items with update_role="tool" and add the
matching yield gate after the first _process_function_requests call so these
results stream like any other tool result. The non-streaming path is unaffected
(it does not consume update_role and already carries the result via the spliced
messages).

Add a streaming regression test asserting an approved tool's result is streamed.

Fixes microsoft#7241.
Copilot AI review requested due to automatic review settings July 22, 2026 12:57
@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Jul 22, 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.

Pull request overview

This PR fixes a streaming gap in the Python function-invocation layer where tool calls executed during approval resolution were executed and spliced into message history, but their function_result was not emitted as a streamed update—so streaming consumers (notably the AG-UI transport) never observed a TOOL_CALL_RESULT.

Changes:

  • In _process_function_requests’ approval-resolution (prepped_messages) branch, return executed function_result items via update_role="tool" + function_call_results.
  • In the streaming loop, add a yield gate after the first _process_function_requests call so those approval-executed tool results are actually streamed.
  • Add a regression test asserting exactly one streamed function_result appears after resuming with an approval response.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
python/packages/core/agent_framework/_tools.py Surfaces approval-executed tool results to the stream and yields them during streaming approval resolution.
python/packages/core/tests/core/test_function_invocation_logic.py Adds a streaming regression test to ensure approved tool results are streamed exactly once.

Comment on lines +2784 to +2790
# Emit results for tools executed while resolving approvals (e.g. deferred
# provider-injected tools) so streaming consumers see them like normal tool results.
if role := approval_result.get("update_role"):
yield ChatResponseUpdate(
contents=approval_result.get("function_call_results") or [],
role=role,
)
@kartikmadan11

Copy link
Copy Markdown
Contributor Author

Closing as a duplicate of #7243, which was opened first and takes the same approach (return the executed results with update_role="tool" and add the missing yield gate at the first call site). Deferring to that PR.

@kartikmadan11
kartikmadan11 deleted the fix/7241-stream-approval-resolution-results branch July 22, 2026 13:09
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: [Bug]: Tool results from approval-resolution execution are never streamed, so provider-injected approvals emit no TOOL_CALL_RESULT

2 participants