Skip to main content
Glama
pydantic

mcp-run-python

Official
by pydantic
test_agent_output_schemas.py22.2 kB
import pytest from inline_snapshot import snapshot from pydantic import BaseModel from pydantic_ai import ( Agent, BinaryImage, DeferredToolRequests, NativeOutput, PromptedOutput, StructuredDict, TextOutput, ToolOutput, ) pytestmark = pytest.mark.anyio class Bar(BaseModel): answer: str class Foo(BaseModel): a: list[Bar] b: int async def test_text_output_json_schema(): agent = Agent('test') assert agent.output_json_schema() == snapshot({'type': 'string'}) def func(x: str) -> str: return x # pragma: no cover agent = Agent('test', output_type=TextOutput(func)) assert agent.output_json_schema() == snapshot({'type': 'string'}) async def test_function_output_json_schema(): def func(x: int) -> int: return x # pragma: no cover agent = Agent('test', output_type=[func]) assert agent.output_json_schema() == snapshot({'type': 'integer'}) def func_no_return_type_hint(x: int): return x # pragma: no cover agent = Agent('test', output_type=[func_no_return_type_hint]) assert agent.output_json_schema() == snapshot({'type': 'string'}) async def test_auto_output_json_schema(): # one output agent = Agent('test', output_type=bool) assert agent.output_json_schema() == snapshot({'type': 'boolean'}) # multiple no str agent = Agent('test', output_type=bool | int) assert agent.output_json_schema() == snapshot({'anyOf': [{'type': 'boolean'}, {'type': 'integer'}]}) # multiple outputs agent = Agent('test', output_type=str | bool | Foo) assert agent.output_json_schema() == snapshot( { 'anyOf': [ {'type': 'string'}, {'type': 'boolean'}, { 'properties': { 'a': {'items': {'$ref': '#/$defs/Bar'}, 'title': 'A', 'type': 'array'}, 'b': {'title': 'B', 'type': 'integer'}, }, 'required': ['a', 'b'], 'title': 'Foo', 'type': 'object', }, ], '$defs': { 'Bar': { 'properties': {'answer': {'title': 'Answer', 'type': 'string'}}, 'required': ['answer'], 'title': 'Bar', 'type': 'object', } }, } ) async def test_tool_output_json_schema(): # one output agent = Agent( 'test', output_type=[ToolOutput(bool)], ) assert agent.output_json_schema() == snapshot({'type': 'boolean'}) # multiple outputs agent = Agent( 'test', output_type=[ToolOutput(str), ToolOutput(bool), ToolOutput(Foo)], ) assert agent.output_json_schema() == snapshot( { 'anyOf': [ {'type': 'string'}, {'type': 'boolean'}, { 'properties': { 'a': {'items': {'$ref': '#/$defs/Bar'}, 'title': 'A', 'type': 'array'}, 'b': {'title': 'B', 'type': 'integer'}, }, 'required': ['a', 'b'], 'title': 'Foo', 'type': 'object', }, ], '$defs': { 'Bar': { 'properties': {'answer': {'title': 'Answer', 'type': 'string'}}, 'required': ['answer'], 'title': 'Bar', 'type': 'object', } }, } ) # multiple duplicate output types agent = Agent( 'test', output_type=[ToolOutput(bool), ToolOutput(bool), ToolOutput(bool)], ) assert agent.output_json_schema() == snapshot({'type': 'boolean'}) async def test_native_output_json_schema(): agent = Agent( 'test', output_type=NativeOutput([bool]), ) assert agent.output_json_schema() == snapshot({'type': 'boolean'}) agent = Agent( 'test', output_type=NativeOutput([bool, Foo]), ) assert agent.output_json_schema() == snapshot( { 'anyOf': [ {'type': 'boolean'}, { 'properties': { 'a': {'items': {'$ref': '#/$defs/Bar'}, 'title': 'A', 'type': 'array'}, 'b': {'title': 'B', 'type': 'integer'}, }, 'required': ['a', 'b'], 'title': 'Foo', 'type': 'object', }, ], '$defs': { 'Bar': { 'properties': {'answer': {'title': 'Answer', 'type': 'string'}}, 'required': ['answer'], 'title': 'Bar', 'type': 'object', } }, } ) async def test_prompted_output_json_schema(): agent = Agent( 'test', output_type=PromptedOutput([bool]), ) assert agent.output_json_schema() == snapshot({'type': 'boolean'}) agent = Agent( 'test', output_type=PromptedOutput([bool, Foo]), ) assert agent.output_json_schema() == snapshot( { 'anyOf': [ {'type': 'boolean'}, { 'properties': { 'a': {'items': {'$ref': '#/$defs/Bar'}, 'title': 'A', 'type': 'array'}, 'b': {'title': 'B', 'type': 'integer'}, }, 'required': ['a', 'b'], 'title': 'Foo', 'type': 'object', }, ], '$defs': { 'Bar': { 'properties': {'answer': {'title': 'Answer', 'type': 'string'}}, 'required': ['answer'], 'title': 'Bar', 'type': 'object', } }, } ) async def test_custom_output_json_schema(): HumanDict = StructuredDict( { 'type': 'object', 'properties': {'name': {'type': 'string'}, 'age': {'type': 'integer'}}, 'required': ['name', 'age'], }, name='Human', description='A human with a name and age', ) agent = Agent('test', output_type=HumanDict) assert agent.output_json_schema() == snapshot( { 'description': 'A human with a name and age', 'type': 'object', 'properties': {'name': {'type': 'string'}, 'age': {'type': 'integer'}}, 'title': 'Human', 'required': ['name', 'age'], } ) async def test_image_output_json_schema(): # one output agent = Agent('test', output_type=BinaryImage) assert agent.output_json_schema() == snapshot( { 'properties': { 'data': {'format': 'binary', 'title': 'Data', 'type': 'string'}, 'media_type': { 'anyOf': [ { 'enum': ['audio/wav', 'audio/mpeg', 'audio/ogg', 'audio/flac', 'audio/aiff', 'audio/aac'], 'type': 'string', }, {'enum': ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], 'type': 'string'}, { 'enum': [ 'application/pdf', 'text/plain', 'text/csv', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/html', 'text/markdown', 'application/msword', 'application/vnd.ms-excel', ], 'type': 'string', }, {'type': 'string'}, ], 'title': 'Media Type', }, 'vendor_metadata': { 'anyOf': [{'additionalProperties': True, 'type': 'object'}, {'type': 'null'}], 'default': None, 'title': 'Vendor Metadata', }, 'kind': {'const': 'binary', 'default': 'binary', 'title': 'Kind', 'type': 'string'}, 'identifier': { 'description': """\ Identifier for the binary content, such as a unique ID. This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument, and the tool can look up the file in question by iterating over the message history and finding the matching `BinaryContent`. This identifier is only automatically passed to the model when the `BinaryContent` is returned by a tool. If you're passing the `BinaryContent` as a user message, it's up to you to include a separate text part with the identifier, e.g. "This is file <identifier>:" preceding the `BinaryContent`. It's also included in inline-text delimiters for providers that require inlining text documents, so the model can distinguish multiple files.\ """, 'readOnly': True, 'title': 'Identifier', 'type': 'string', }, }, 'required': ['data', 'media_type', 'identifier'], 'title': 'BinaryImage', 'type': 'object', } ) # multiple outputs agent = Agent('test', output_type=str | bool | BinaryImage) assert agent.output_json_schema() == snapshot( { 'anyOf': [ {'type': 'string'}, {'type': 'boolean'}, { 'properties': { 'data': {'format': 'binary', 'title': 'Data', 'type': 'string'}, 'media_type': { 'anyOf': [ { 'enum': [ 'audio/wav', 'audio/mpeg', 'audio/ogg', 'audio/flac', 'audio/aiff', 'audio/aac', ], 'type': 'string', }, {'enum': ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], 'type': 'string'}, { 'enum': [ 'application/pdf', 'text/plain', 'text/csv', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/html', 'text/markdown', 'application/msword', 'application/vnd.ms-excel', ], 'type': 'string', }, {'type': 'string'}, ], 'title': 'Media Type', }, 'vendor_metadata': { 'anyOf': [{'additionalProperties': True, 'type': 'object'}, {'type': 'null'}], 'default': None, 'title': 'Vendor Metadata', }, 'kind': {'const': 'binary', 'default': 'binary', 'title': 'Kind', 'type': 'string'}, 'identifier': { 'description': """\ Identifier for the binary content, such as a unique ID. This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument, and the tool can look up the file in question by iterating over the message history and finding the matching `BinaryContent`. This identifier is only automatically passed to the model when the `BinaryContent` is returned by a tool. If you're passing the `BinaryContent` as a user message, it's up to you to include a separate text part with the identifier, e.g. "This is file <identifier>:" preceding the `BinaryContent`. It's also included in inline-text delimiters for providers that require inlining text documents, so the model can distinguish multiple files.\ """, 'readOnly': True, 'title': 'Identifier', 'type': 'string', }, }, 'required': ['data', 'media_type', 'identifier'], 'title': 'BinaryImage', 'type': 'object', }, ] } ) async def test_override_output_json_schema(): agent = Agent('test') assert agent.output_json_schema() == snapshot({'type': 'string'}) output_type = [ToolOutput(bool)] assert agent.output_json_schema(output_type=output_type) == snapshot({'type': 'boolean'}) async def test_deferred_output_json_schema(): agent = Agent('test', output_type=[str, DeferredToolRequests]) assert agent.output_json_schema() == snapshot( { 'anyOf': [ {'type': 'string'}, { 'properties': { 'calls': {'items': {'$ref': '#/$defs/ToolCallPart'}, 'title': 'Calls', 'type': 'array'}, 'approvals': {'items': {'$ref': '#/$defs/ToolCallPart'}, 'title': 'Approvals', 'type': 'array'}, 'metadata': { 'additionalProperties': {'additionalProperties': True, 'type': 'object'}, 'title': 'Metadata', 'type': 'object', }, }, 'title': 'DeferredToolRequests', 'type': 'object', }, ], '$defs': { 'ToolCallPart': { 'description': 'A tool call from a model.', 'properties': { 'tool_name': {'title': 'Tool Name', 'type': 'string'}, 'args': { 'anyOf': [ {'type': 'string'}, {'additionalProperties': True, 'type': 'object'}, {'type': 'null'}, ], 'default': None, 'title': 'Args', }, 'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'}, 'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'default': None, 'title': 'Id'}, 'provider_details': { 'anyOf': [{'additionalProperties': True, 'type': 'object'}, {'type': 'null'}], 'default': None, 'title': 'Provider Details', }, 'part_kind': { 'const': 'tool-call', 'default': 'tool-call', 'title': 'Part Kind', 'type': 'string', }, }, 'required': ['tool_name'], 'title': 'ToolCallPart', 'type': 'object', } }, } ) # special case of only BinaryImage and DeferredToolRequests agent = Agent('test', output_type=[BinaryImage, DeferredToolRequests]) assert agent.output_json_schema() == snapshot( { 'anyOf': [ { 'properties': { 'data': {'format': 'binary', 'title': 'Data', 'type': 'string'}, 'media_type': { 'anyOf': [ { 'enum': [ 'audio/wav', 'audio/mpeg', 'audio/ogg', 'audio/flac', 'audio/aiff', 'audio/aac', ], 'type': 'string', }, {'enum': ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], 'type': 'string'}, { 'enum': [ 'application/pdf', 'text/plain', 'text/csv', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/html', 'text/markdown', 'application/msword', 'application/vnd.ms-excel', ], 'type': 'string', }, {'type': 'string'}, ], 'title': 'Media Type', }, 'vendor_metadata': { 'anyOf': [{'additionalProperties': True, 'type': 'object'}, {'type': 'null'}], 'default': None, 'title': 'Vendor Metadata', }, 'kind': {'const': 'binary', 'default': 'binary', 'title': 'Kind', 'type': 'string'}, 'identifier': { 'description': """\ Identifier for the binary content, such as a unique ID. This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument, and the tool can look up the file in question by iterating over the message history and finding the matching `BinaryContent`. This identifier is only automatically passed to the model when the `BinaryContent` is returned by a tool. If you're passing the `BinaryContent` as a user message, it's up to you to include a separate text part with the identifier, e.g. "This is file <identifier>:" preceding the `BinaryContent`. It's also included in inline-text delimiters for providers that require inlining text documents, so the model can distinguish multiple files.\ """, 'readOnly': True, 'title': 'Identifier', 'type': 'string', }, }, 'required': ['data', 'media_type', 'identifier'], 'title': 'BinaryImage', 'type': 'object', }, { 'properties': { 'calls': {'items': {'$ref': '#/$defs/ToolCallPart'}, 'title': 'Calls', 'type': 'array'}, 'approvals': {'items': {'$ref': '#/$defs/ToolCallPart'}, 'title': 'Approvals', 'type': 'array'}, 'metadata': { 'additionalProperties': {'additionalProperties': True, 'type': 'object'}, 'title': 'Metadata', 'type': 'object', }, }, 'title': 'DeferredToolRequests', 'type': 'object', }, ], '$defs': { 'ToolCallPart': { 'description': 'A tool call from a model.', 'properties': { 'tool_name': {'title': 'Tool Name', 'type': 'string'}, 'args': { 'anyOf': [ {'type': 'string'}, {'additionalProperties': True, 'type': 'object'}, {'type': 'null'}, ], 'default': None, 'title': 'Args', }, 'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'}, 'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'default': None, 'title': 'Id'}, 'provider_details': { 'anyOf': [{'additionalProperties': True, 'type': 'object'}, {'type': 'null'}], 'default': None, 'title': 'Provider Details', }, 'part_kind': { 'const': 'tool-call', 'default': 'tool-call', 'title': 'Part Kind', 'type': 'string', }, }, 'required': ['tool_name'], 'title': 'ToolCallPart', 'type': 'object', } }, } )

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pydantic/pydantic-ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server