Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 3, 2025

Motivation and Context

Fixes #236

Pydantic BaseModel subclasses returned from tool functions were not being serialized correctly. The _prepare_function_call_results_as_dumpable function checked for to_dict() which doesn't exist on Pydantic models.

Description

Added BaseModel isinstance check before the to_dict fallback, calling model_dump() for Pydantic models:

# Now works correctly
class WeatherResult(BaseModel):
    temperature: float
    condition: str

@ai_function
def get_weather(location: str) -> WeatherResult:
    return WeatherResult(temperature=22.5, condition="sunny")

# Returns: {"temperature": 22.5, "condition": "sunny"}
  • Modified _prepare_function_call_results_as_dumpable to detect BaseModel subclasses and use model_dump()
  • Works recursively for models in lists and dicts
  • Added tests for single models, lists, dicts, and nested models

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.openai.com
    • Triggering command: /home/REDACTED/work/agent-framework/agent-framework/python/.venv/bin/pytest pytest --cov=agent_framework --cov-report=term-missing:skip-covered tests (dns block)
  • astral.sh
    • Triggering command: /usr/bin/curl curl -LsSf REDACTED (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Python: [Python] Support pydantic model as function call result</issue_title>
<issue_description>Currently, Pydantic model as a tool result is not supported.

def _prepare_function_call_results_as_dumpable(content: Contents | Any | list[Contents | Any]) </issue_description>

<agent_instructions>take the _prepare_function_call_results_as_dumpable function and add logic that checks for BaseModel subclasses, if we encounter those, then use content.model_dump() </agent_instructions>

Comments on the Issue (you are @copilot in this section)

@eavanvalkenburg we can do that, I'll add it to my backlog

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@eavanvalkenburg eavanvalkenburg marked this pull request as ready for review December 3, 2025 15:17
Copilot AI review requested due to automatic review settings December 3, 2025 15:18
@github-actions github-actions bot changed the title [WIP] Add support for Pydantic model as function call result Python: [WIP] Add support for Pydantic model as function call result Dec 3, 2025
@eavanvalkenburg eavanvalkenburg changed the title Python: [WIP] Add support for Pydantic model as function call result Python: Add support for Pydantic model as function call result Dec 3, 2025
Copilot AI changed the title Python: [WIP] Add support for Pydantic model as function call result Python: Support pydantic model as function call result Dec 3, 2025
Copilot AI requested a review from eavanvalkenburg December 3, 2025 15:18
Copy link
Contributor

Copilot AI left a comment

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 adds support for Pydantic BaseModel subclasses as function call results in the agent framework's type system. Previously, only objects with a to_dict() method were properly serialized; this change enables Pydantic models to be serialized using their native model_dump() method.

Key Changes:

  • Added BaseModel instance check in _prepare_function_call_results_as_dumpable() before the generic to_dict() fallback
  • Comprehensive test coverage for single models, lists, dicts, and nested Pydantic models

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
python/packages/core/agent_framework/_types.py Added BaseModel check and model_dump() call in the serialization helper function
python/packages/core/tests/core/test_types.py Added test helper classes and four test functions covering various Pydantic model serialization scenarios

@eavanvalkenburg eavanvalkenburg force-pushed the copilot/support-pydantic-model-results branch from 45ed62c to 671cafa Compare December 3, 2025 18:26
@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Dec 3, 2025

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _types.py9479989%130–131, 149–150, 287, 289, 296, 315, 355, 401–402, 438, 588, 702–703, 705, 730, 737, 754–756, 829, 834–835, 837, 844–845, 847, 869, 876, 879–881, 886–887, 893–895, 1019, 1106–1109, 1117–1118, 1209, 1390, 1396, 1640–1642, 1648–1649, 1819, 1950, 1955, 1959, 1963, 2144, 2195–2199, 2209, 2214, 2673, 2759–2761, 2834, 2845–2846, 3020, 3024, 3036–3038, 3139–3141, 3143–3145, 3148, 3152, 3155, 3160, 3205–3206, 3213–3214, 3248–3250, 3265, 3281, 3309, 3316
TOTAL15888241684% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
2286 130 💤 0 ❌ 0 🔥 55.944s ⏱️

@eavanvalkenburg
Copy link
Member

@copilot there are some code quality checks failing, please fix

Copy link
Contributor Author

Copilot AI commented Dec 3, 2025

@copilot there are some code quality checks failing, please fix

The Python - Code Quality and Python - Tests workflows are now passing. The CI checks completed successfully after the initial run.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • test.search.windows.net
    • Triggering command: /home/REDACTED/work/agent-framework/agent-framework/python/.venv/bin/pytest pytest --cov=agent_framework_azure_ai_search --cov-report=term-missing:skip-covered tests (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@eavanvalkenburg eavanvalkenburg force-pushed the copilot/support-pydantic-model-results branch from 671cafa to 64b46d7 Compare December 3, 2025 21:34
@moonbox3 moonbox3 added this pull request to the merge queue Dec 4, 2025
Merged via the queue into main with commit ae06c9e Dec 4, 2025
23 checks passed
@crickman crickman deleted the copilot/support-pydantic-model-results branch December 4, 2025 17:16
@sebastian-velten
Copy link

Hello everyone. I'm happy to see, that pydantic models are now supported as function tool output.
I have two remarks on the changes, made in this issue.

  1. Wouldn't it be better to directly return model_dump_json() of the response instead of model_dump() and then dumping it again later using json.dumps(). This method ensures the string output is definitely JSON serializable, which is not the case for the dict resulting from model_dump(). For instance, I think the current implementation crashes if the function call response contains a datetime property.
  2. First check fo the "to_dict" method, and then for BaseModel instance. I think, the "to_dict" case is more specific and should therefore have higher priority.

The resulting _prepare_function_call_results_as_dumpable(...) would then be:

def _prepare_function_call_results_as_dumpable(content: Contents | Any | list[Contents | Any]) -> Any:
    if isinstance(content, list):
        # Particularly deal with lists of Content
        return [_prepare_function_call_results_as_dumpable(item) for item in content]
    if isinstance(content, dict):
        return {k: _prepare_function_call_results_as_dumpable(v) for k, v in content.items()}
    if hasattr(content, "to_dict"):
        return content.to_dict(exclude={"raw_representation", "additional_properties"})
    if isinstance(content, BaseModel):
        return content.model_dump_json()
    return content

If you agree with my points (either one of them or both), I would be glad you would assign the issue to me, so I can make the suggested changes and adapt test cases accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Python] Support pydantic model as function call result

6 participants