Skip to content

Python: Drop post-limit streamed function calls#7334

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

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.
@scarab-systems
scarab-systems marked this pull request as ready for review July 26, 2026 16:55
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

2 participants