Skip to content

Python: Drop post-limit streamed function calls - #7334

Closed
scarab-systems wants to merge 2 commits into
microsoft:mainfrom
scarab-systems:scarab-systems/function-call-limit-streaming-orphans
Closed

Python: Drop post-limit streamed function calls#7334
scarab-systems wants to merge 2 commits into
microsoft:mainfrom
scarab-systems:scarab-systems/function-call-limit-streaming-orphans

Conversation

@scarab-systems

@scarab-systems scarab-systems commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

Fixes #7045.

When max_function_calls is reached, Agent Framework asks the provider for a final response with tool_choice="none". If a streaming provider still returns a function call on that final turn, forwarding it exposes a tool call that the framework will not execute, so transports such as AG-UI can be left waiting for a matching tool result.

Disclosure: This PR was prepared with AI assistance under human direction and review.

Description & Review Guide

  • What are the major changes?
    • Drop function_call content from post-limit streaming updates before forwarding them to callers.
    • Strip function_call content from limit fallback responses before deciding whether fallback text is needed.
    • Add regression coverage for a streaming provider returning another function_call after max_function_calls is exhausted.
  • What is the impact of these changes?
    • Clients no longer receive a post-limit function call that Agent Framework will not execute, while the existing fallback text behavior remains unchanged when no visible final content is available.
  • What do you want reviewers to focus on?
    • Whether dropping the disobedient post-limit call is the right scope for the max-function-call limit path, compared with executing an over-limit call or emitting a synthetic tool result.

Related Issue

Fixes #7045

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 (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) -- a workflow keeps the label and title prefix in sync automatically.

Filter function_call content from streaming updates once max_function_calls has forced tool_choice=none, so a provider-disobeyed post-limit call is not forwarded without a matching result.

Keep existing fallback text behavior when no visible final content remains, and strip function_call content from limit fallback responses before returning them.

Add a regression test that simulates a streaming provider returning another function_call after the call budget is exhausted.

Validation: uv run pytest packages/core/tests/core/test_function_invocation_logic.py -q -k 'streaming_max_function_calls_drops_post_limit_function_call_update'; uv run pytest packages/core/tests/core/test_function_invocation_logic.py -q -k 'max_function_calls or max_iterations'; uv run pytest packages/core/tests/core/test_function_invocation_logic.py -q; uv run poe check -P core.
Copilot AI review requested due to automatic review settings July 26, 2026 16:29
@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Jul 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.

Pull request overview

This PR fixes a streaming edge case in the Python Agent Framework function-invocation limit path: when max_function_calls is reached and the framework requests a final tool_choice="none" response, any provider that still streams function_call content could cause clients (e.g., AG-UI) to observe tool-call starts without a corresponding tool result.

Changes:

  • Strip function_call content from post-limit streaming updates before forwarding them to callers.
  • Strip function_call content from finalized responses in the max-function-calls fallback path before deciding whether to synthesize fallback text.
  • Add a regression test covering a streaming provider emitting another function_call after max_function_calls is exhausted.

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 Filters post-limit streamed tool-call content and ensures finalized responses don’t retain over-limit function_call items.
python/packages/core/tests/core/test_function_invocation_logic.py Adds coverage to ensure post-limit streamed function_call updates are not forwarded and no orphaned tool calls appear.

Comment thread python/packages/core/agent_framework/_tools.py Outdated
Keep post-limit streaming updates when function_call content is stripped but the chunk still carries meaningful metadata, such as finish_reason, continuation_token, ids, provider metadata, or raw provider representation.

Add regression coverage for a provider chunk that includes both a disallowed post-limit function_call and finish/provider metadata.

Validation: uv run pytest packages/core/tests/core/test_function_invocation_logic.py -q -k 'streaming_max_function_calls_drops_post_limit_function_call_update or streaming_max_function_calls_preserves_post_limit_update_metadata'; uv run pytest packages/core/tests/core/test_function_invocation_logic.py -q -k 'max_function_calls or max_iterations'; uv run poe check -P core; SDS workspace-execution pytest-function-limit-copilot-review.
@eavanvalkenburg

Copy link
Copy Markdown
Member

@scarab-systems, thank you for identifying and fixing the post-limit streaming orphan. The consolidated draft PR #7345 incorporates that approach and extends it across both max_function_calls and max_iterations, including native approval requests while preserving metadata-only updates. Closing this PR in favor of the consolidated work.

scarab-systems added a commit to scarab-systems/scarab-field-lab that referenced this pull request Jul 27, 2026
Record microsoft/agent-framework#7334 as closed without merge after maintainer consolidation into #7345.

Keep the record public-facing: issue and PR links, diagnostic finding, validation commands, review status, changed files, and explicit non-claims. Merged contribution totals are unchanged.

Verification: git diff --staged --check; public leakage scan for local paths, SDS internals, and private run wording; indexed case path check.
@scarab-systems
scarab-systems deleted the scarab-systems/function-call-limit-streaming-orphans branch July 27, 2026 13:59
@scarab-systems

Copy link
Copy Markdown
Contributor Author

Woohoo! Thank you for the clear update, and for carrying the fix forward into the consolidated PR. I appreciate the broader treatment across max_function_calls, max_iterations, approval requests, and metadata-only stream updates. Glad the original patch helped identify the right direction.

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

3 participants