Python: fix: coalesce code_interpreter_tool_call chunks with same call_id in finalize_response (fixes #5793)#6205
Open
hanhan761 wants to merge 2 commits into
Conversation
…finalize_response Closes microsoft#5793 During streaming, each code_interpreter_call_code.delta event creates a separate code_interpreter_tool_call content item with one text chunk. The final response then accumulates these as multiple items rather than coalescing them into a single item with the complete aggregated text. When stored by a history provider (e.g. CosmosHistoryProvider), each chunk becomes a separate content item, producing thousands of redundant entries for a single code interpreter invocation. Fix: add _coalesce_code_interpreter_content in _finalize_response that merges code_interpreter_tool_call items sharing the same call_id into one item with all inputs concatenated.
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Fixes tool invocation to preserve explicit None arguments rather than stripping them, and adds coalescing logic for code_interpreter_tool_call content items sharing the same call_id.
Changes:
- Switch
model_dumpcalls fromexclude_none=Truetoexclude_none=Falsein tool invocation paths so explicitNonevalues are passed through. - Add
_coalesce_code_interpreter_contentto mergecode_interpreter_tool_callitems bycall_idduring response finalization. - Add a test verifying explicit
Nonearguments are preserved on tool invocation.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| python/packages/core/agent_framework/_tools.py | Preserve None values through model_dump during tool invocation. |
| python/packages/core/agent_framework/_types.py | Add coalescing of code_interpreter_tool_call items by call_id. |
| python/packages/core/tests/core/test_tools.py | Add test for invoking tools with explicit None arguments. |
Comment on lines
+1976
to
+1992
| def _coalesce_code_interpreter_content(contents: list[Content]) -> None: | ||
| """Coalesce code_interpreter_tool_call items with the same call_id into a single item.""" | ||
| if not contents: | ||
| return | ||
| seen: dict[str, Content] = {} | ||
| coalesced: list[Content] = [] | ||
| for content in contents: | ||
| if content.type == "code_interpreter_tool_call" and content.call_id: | ||
| if content.call_id in seen: | ||
| existing = seen[content.call_id] | ||
| if content.inputs: | ||
| if existing.inputs is None: | ||
| existing.inputs = [] | ||
| existing.inputs.extend(content.inputs) | ||
| else: | ||
| seen[content.call_id] = content | ||
| coalesced.append(content) |
Comment on lines
638
to
640
| parsed_arguments = self.input_model.model_validate(parsed_arguments).model_dump( | ||
| exclude_none=True | ||
| exclude_none=False | ||
| ) |
Comment on lines
+1466
to
+1467
| def test_invoke_preserves_explicit_none_arguments() -> None: | ||
| """Optional parameters explicitly set to None must not be stripped before invocation.""" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Coalesces
code_interpreter_tool_callcontent items sharing the samecall_idduring response finalization, so each code interpreter invocation appears as a single content item with complete aggregated text instead of hundreds of chunk-by-chunk entries.Issue
Closes #5793
Root Cause
During streaming, each
response.code_interpreter_call_code.deltaevent creates a separatecode_interpreter_tool_callcontent item with one text chunk. The existing_finalize_responsealready coalescedtextandtext_reasoningitems but did not handlecode_interpreter_tool_call. When persisted by a history provider likeCosmosHistoryProvider, each delta chunk was stored individually — producing thousands of redundant items for a single code invocation.Change
Added
_coalesce_code_interpreter_content()in_types.py, called from_finalize_response. It mergescode_interpreter_tool_callitems with the samecall_idinto one item with allinputsconcatenated, preserving the first item and extending its inputs list from subsequent items.Verification
test_types.py(197 tests) passtest_response_content_creation_with_code_interpreterpassestest_parse_chunk_from_openai_code_interpreter,_delta,_doneall pass_coalesce_code_interpreter_contentconfirms 3 chunks → 1 item with 3 inputs