Python: Drop post-limit streamed function calls - #7334
Conversation
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.
There was a problem hiding this comment.
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_callcontent from post-limit streaming updates before forwarding them to callers. - Strip
function_callcontent 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_callaftermax_function_callsis 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. |
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, 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. |
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.
|
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. |
Motivation & Context
Fixes #7045.
When
max_function_callsis reached, Agent Framework asks the provider for a final response withtool_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
function_callcontent from post-limit streaming updates before forwarding them to callers.function_callcontent from limit fallback responses before deciding whether fallback text is needed.function_callaftermax_function_callsis exhausted.Related Issue
Fixes #7045
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) -- a workflow keeps the label and title prefix in sync automatically.