Skip to content

Python: Preserve null arguments during tool invocation - #5944

Closed
Shubham Singh (shusingh) wants to merge 6 commits into
microsoft:mainfrom
shusingh:fix-null-tool-arguments
Closed

Python: Preserve null arguments during tool invocation#5944
Shubham Singh (shusingh) wants to merge 6 commits into
microsoft:mainfrom
shusingh:fix-null-tool-arguments

Conversation

@shusingh

Copy link
Copy Markdown

Summary

  • Preserve explicitly provided null values when dumping validated tool arguments
  • Apply the same behavior to direct FunctionTool.invoke() and automatic function calling
  • Add regression coverage for required nullable tool parameters

Fixes #5934

Tests

  • python -m pytest tests/core/test_tools.py::test_tool_invoke_preserves_explicit_null_for_required_nullable_argument tests/core/test_function_invocation_logic.py::test_auto_function_calling_preserves_explicit_null_arguments -q
  • python -m pytest tests/core/test_tools.py tests/core/test_function_invocation_logic.py -q
  • python -m ruff check agent_framework/_tools.py tests/core/test_tools.py tests/core/test_function_invocation_logic.py

Copilot AI review requested due to automatic review settings May 19, 2026 01:57
@moonbox3 Evan Mattson (moonbox3) added the python Usage: [Issues, PRs], Target: Python label May 19, 2026
@github-actions github-actions Bot changed the title Preserve null arguments during tool invocation Python: Preserve null arguments during tool invocation May 19, 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 updates Python core tool invocation to preserve explicitly supplied null values through Pydantic argument validation, addressing required nullable tool parameters during direct and automatic invocation.

Changes:

  • Adds a helper for dumping Pydantic argument models while restoring explicit top-level None values.
  • Applies the helper in FunctionTool.invoke() and automatic function calling.
  • Adds regression tests for direct and automatic nullable required arguments.

Reviewed changes

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

File Description
python/packages/core/agent_framework/_tools.py Updates argument dumping in tool invocation paths to preserve explicit None values.
python/packages/core/tests/core/test_tools.py Adds direct FunctionTool.invoke() regression coverage.
python/packages/core/tests/core/test_function_invocation_logic.py Adds automatic function calling regression coverage.

Comment thread python/packages/core/agent_framework/_tools.py
@shusingh

Copy link
Copy Markdown
Author

Updated the PR to handle nested explicit null values as well. The argument dump helper now recursively restores explicitly provided nulls in nested Pydantic models, mappings, and lists/tuples, and I added regression coverage for both direct tool invocation and automatic function calling with nested nullable arguments.

@moonbox3

Evan Mattson (moonbox3) commented May 22, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _tools.py11828392%215, 264–265, 442, 444, 457, 482–484, 492, 510, 524, 531, 538, 561, 563, 570, 578, 707, 741–743, 746–748, 750, 756, 807–809, 834, 860, 864, 902–904, 908, 930, 1081, 1093, 1100–1103, 1124, 1132, 1146–1148, 1498, 1590, 1618, 1640, 1648, 1737, 1744–1745, 1804, 1808, 1854, 1915–1916, 1927, 1999, 2013, 2016, 2029, 2032, 2055, 2062, 2072, 2076, 2153, 2206, 2228, 2284, 2363, 2560, 2626–2627, 2793–2794, 2885
TOTAL44517534787% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
8819 33 💤 0 ❌ 0 🔥 2m 20s ⏱️

@shusingh

Copy link
Copy Markdown
Author

Pushed a follow-up that keeps the explicit-null helper behavior unchanged while making the mapping and sequence narrowing explicit for Pyright. Locally verified the touched file with Pyright 1.1.408 and reran the focused nullable-argument regression tests.

@moonbox3

Copy link
Copy Markdown
Contributor

Shubham Singh (@shusingh) please fix the failing CI/CD checks.

@shusingh

Copy link
Copy Markdown
Author

Fixed the failing type checks by changing the helper narrowing from Any container casts to concrete object container casts, which keeps Pyright from seeing unknown element types while avoiding Mypy redundant-cast errors. Locally verified the touched file with Pyright 1.1.408, Mypy on �gent_framework/_tools.py, and the focused nullable-argument regression tests.

Comment thread python/packages/core/agent_framework/_tools.py
@shusingh

Copy link
Copy Markdown
Author

Hi! Just checking in on this PR. The review feedback has been addressed, and it’s ready for another look when you have a chance. I’m happy to update the branch or make any further adjustments. Thanks!

@shusingh

Copy link
Copy Markdown
Author

Hi again! Just following up on this PR when you have a chance. It should be ready for review, and I’m happy to update the branch or address anything else needed. Thanks!

…ents

# Conflicts:
#	python/packages/core/tests/core/test_function_invocation_logic.py
"""Dump a model without dropping fields that were explicitly set to None."""
# Pydantic's exclude_none removes both default None values and explicit null arguments.
# Restore only fields present in model_fields_set so omitted optional fields stay omitted.
dumped = model.model_dump(exclude_none=True)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is also a exclude_defaults keyword on model_dump could that be used instead? I am not a fan of this extra function doing a bunch of dict manipulation.

@shusingh Shubham Singh (shusingh) Jul 18, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. exclude_defaults compares values, so an explicit null on a None-default field would still be dropped. exclude_unset keys off model_fields_set instead, keeping anything explicitly provided (nested included). Replaced the helper with a plain model_dump(exclude_unset=True); tests and type checks pass.

@moonbox3

Copy link
Copy Markdown
Contributor

Please re-open when ready to address comments and move forward. Thanks.

@shusingh

Copy link
Copy Markdown
Author

Evan Mattson (@moonbox3) Apologies for the delay, I was traveling and missed the last round of feedback. It's addressed now: the custom dict-manipulation helper is gone, replaced with a plain model_dump(exclude_unset=True) per the review discussion, which simplifies the change to a net removal of code. GitHub isn't letting me reopen the PR from my side, so could you reopen it when you get a chance? Happy to make any further adjustments. Thanks!

@shusingh

Copy link
Copy Markdown
Author

Hi Evan Mattson (@moonbox3), a gentle follow-up on this one. The latest revision addresses the review comments: the custom dict-manipulation helper is gone, replaced with a plain model_dump(exclude_unset=True), so the change is now a net removal of code. As I mentioned above, GitHub will not let me reopen from my side; would you be able to reopen it when you have a moment? Happy to make any further adjustments. Thank you kindly!

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]: Auto function calling removes null arguments

5 participants