From a9b45b66da00ca991008b3b81d8b96ebbf61f911 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:37:09 +0000 Subject: [PATCH 1/2] Support NativeOutput in FunctionModel by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add default ModelProfile with JSON schema and object output support when no profile is provided to FunctionModel - Update test_native_output to no longer pass custom profile since it's now the default - This enables testing native output pipeline in agents using FunctionModel Fixes #2839 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Douwe Maan --- .../pydantic_ai/models/function.py | 8 +++- test_native_output_verification.py | 41 +++++++++++++++++++ tests/test_agent.py | 2 +- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test_native_output_verification.py diff --git a/pydantic_ai_slim/pydantic_ai/models/function.py b/pydantic_ai_slim/pydantic_ai/models/function.py index e098e986fb..591c37970d 100644 --- a/pydantic_ai_slim/pydantic_ai/models/function.py +++ b/pydantic_ai_slim/pydantic_ai/models/function.py @@ -31,7 +31,7 @@ UserContent, UserPromptPart, ) -from ..profiles import ModelProfileSpec +from ..profiles import ModelProfile, ModelProfileSpec from ..settings import ModelSettings from ..tools import ToolDefinition from . import Model, ModelRequestParameters, StreamedResponse @@ -111,6 +111,12 @@ def __init__( stream_function_name = self.stream_function.__name__ if self.stream_function is not None else '' self._model_name = model_name or f'function:{function_name}:{stream_function_name}' + # Use a default profile that supports JSON schema and object output if none provided + if profile is None: + profile = ModelProfile( + supports_json_schema_output=True, + supports_json_object_output=True, + ) super().__init__(settings=settings, profile=profile) async def request( diff --git a/test_native_output_verification.py b/test_native_output_verification.py new file mode 100644 index 0000000000..0a9776f0fa --- /dev/null +++ b/test_native_output_verification.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +"""Test script to verify that FunctionModel now supports NativeOutput by default.""" + +from pydantic import BaseModel +from pydantic_ai import Agent +from pydantic_ai.models.function import FunctionModel, AgentInfo +from pydantic_ai.messages import ModelMessage, ModelResponse, TextPart +from pydantic_ai.output import NativeOutput + + +class TestOutput(BaseModel): + message: str + value: int + + +def simple_function(messages: list[ModelMessage], _info: AgentInfo) -> ModelResponse: + return ModelResponse(parts=[TextPart(content='{"message": "Hello World", "value": 42}')]) + + +def main(): + # Create a FunctionModel without specifying a profile + # This should now work with NativeOutput due to the default profile + model = FunctionModel(simple_function) + + # Create an agent with NativeOutput + agent = Agent(model, output_type=NativeOutput(TestOutput)) + + # This should work without errors now + result = agent.run_sync("Test") + + print(f"Success! Result: {result.output}") + print(f"Result type: {type(result.output)}") + assert isinstance(result.output, TestOutput) + assert result.output.message == "Hello World" + assert result.output.value == 42 + + print("✅ FunctionModel now supports NativeOutput by default!") + + +if __name__ == "__main__": + main() diff --git a/tests/test_agent.py b/tests/test_agent.py index 415b242d0d..61ba806969 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -1557,7 +1557,7 @@ def return_city_location(messages: list[ModelMessage], _info: AgentInfo) -> Mode text = '{"city": "Mexico City", "country": "Mexico"}' return ModelResponse(parts=[TextPart(content=text)]) - m = FunctionModel(return_city_location, profile=ModelProfile(supports_json_schema_output=True)) + m = FunctionModel(return_city_location) class CityLocation(BaseModel): city: str From d1ec212ed5ec2044a092a74cfd48177b9fde4894 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:40:31 +0000 Subject: [PATCH 2/2] Remove redundant test file The functionality is already covered by the modified test in test_agent.py Co-authored-by: Douwe Maan --- test_native_output_verification.py | 41 ------------------------------ 1 file changed, 41 deletions(-) delete mode 100644 test_native_output_verification.py diff --git a/test_native_output_verification.py b/test_native_output_verification.py deleted file mode 100644 index 0a9776f0fa..0000000000 --- a/test_native_output_verification.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -"""Test script to verify that FunctionModel now supports NativeOutput by default.""" - -from pydantic import BaseModel -from pydantic_ai import Agent -from pydantic_ai.models.function import FunctionModel, AgentInfo -from pydantic_ai.messages import ModelMessage, ModelResponse, TextPart -from pydantic_ai.output import NativeOutput - - -class TestOutput(BaseModel): - message: str - value: int - - -def simple_function(messages: list[ModelMessage], _info: AgentInfo) -> ModelResponse: - return ModelResponse(parts=[TextPart(content='{"message": "Hello World", "value": 42}')]) - - -def main(): - # Create a FunctionModel without specifying a profile - # This should now work with NativeOutput due to the default profile - model = FunctionModel(simple_function) - - # Create an agent with NativeOutput - agent = Agent(model, output_type=NativeOutput(TestOutput)) - - # This should work without errors now - result = agent.run_sync("Test") - - print(f"Success! Result: {result.output}") - print(f"Result type: {type(result.output)}") - assert isinstance(result.output, TestOutput) - assert result.output.message == "Hello World" - assert result.output.value == 42 - - print("✅ FunctionModel now supports NativeOutput by default!") - - -if __name__ == "__main__": - main()