Skip to content

Conversation

@g-eoj
Copy link
Contributor

@g-eoj g-eoj commented Nov 17, 2025

This PR adds a method to get the json schema for agent output.

It aims to support all output types, including special ones:

  • text only
  • BinaryImage
  • DeferredToolRequests

Simple example:

agent = Agent('test') agent.output_json_schema()
{'type': 'string'}

Override agent output types:

agent = Agent('test') agent.output_json_schema(output_type=bool)
{'type': 'bool'}

Closes #3225

@g-eoj
Copy link
Contributor Author

g-eoj commented Nov 17, 2025

@DouweM is there a source of truth I can reference for what the schema output should be for tests? I think I will get stuck figuring this out so any guidance will be appreciated.

Edit: I added snapshots of expected output to the tests. Please review them carefully as I'm guessing what they are supposed to be.

@g-eoj g-eoj marked this pull request as ready for review November 17, 2025 21:23
@g-eoj g-eoj changed the title Add output_json_schema property to Agents Add output_json_schema method to Agent class Nov 19, 2025
@g-eoj
Copy link
Contributor Author

g-eoj commented Nov 21, 2025

@DouweM thanks for the reviews so far.

Fixed: The PR has an issue where it always adds str to the output schema if the AutoOutputSchema is selected. However I think this is due to a bug not caused by the PR. I think it is because

text_processor=processor,
will always set allows_text to True. This doesn't seem right but I'll wait for confirmation.

@g-eoj g-eoj requested a review from DouweM November 21, 2025 23:38
@g-eoj g-eoj marked this pull request as draft November 21, 2025 23:45
@g-eoj g-eoj marked this pull request as ready for review November 22, 2025 00:43
@g-eoj g-eoj marked this pull request as draft November 22, 2025 01:50
@g-eoj g-eoj requested a review from DouweM December 2, 2025 18:22
@g-eoj g-eoj requested a review from DouweM December 3, 2025 21:37
@g-eoj
Copy link
Contributor Author

g-eoj commented Dec 4, 2025

@DouweM ready for review

json_schemas: list[JsonSchema] = []
for output_spec in flat_output_type:
json_schema = TypeAdapter(output_spec).json_schema(mode='serialization')
if json_schema not in json_schemas:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe json_schemas can be a set so that it's automatically de-duped

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think I can do that because dictionaries are mutable.

elif isinstance(output_spec, _output.ToolOutput):
flat_output_type += _flatten_output_spec(output_spec.output)
elif inspect.isfunction(output_spec) or inspect.ismethod(output_spec):
return_annotation = inspect.signature(output_spec).return_annotation
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if there is no return annotation on the output function?

Copy link
Collaborator

Choose a reason for hiding this comment

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

In function_schema we use a helper method for this:

type_hints = _typing_extra.get_function_type_hints(function)

I think its return value will have a 'return' key. Can we use it here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done - if there is no key, I set the return type to string.

raise ValueError('`BinaryImage` must be have a media type that starts with "image/"') # pragma: no cover

@classmethod
def __get_pydantic_json_schema__(
Copy link
Collaborator

Choose a reason for hiding this comment

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

On second thought, let's leave these changes out for now. Once we get to #3122, we'll also need to think about the schemas for BinaryContent, FileUrl etc, so I'd rather tackle this in one go than instead of giving BinaryImage special treatment now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reverted all changes to BinaryImage, hopefully that is okay.

@g-eoj g-eoj requested a review from DouweM December 10, 2025 21:50
@g-eoj
Copy link
Contributor Author

g-eoj commented Dec 10, 2025

@DouweM ready for review


def flatten_output_type(
output_type: OutputSpec[OutputDataT],
) -> list[OutputSpec[OutputDataT] | type[str]]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would this be list[OutputDataT], without the OutputSpec? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

_flatten_output_spec returns OutputSpec sequences, so out of the box it doesn't work (I gave it a try, type checkers not pleased). So I think I would have to cast flat_output_type or write my own version of _flatten_output_spec to achieve the effect of having list[OutputDataT] as the return.

return outputs_flat


def flatten_output_type(
Copy link
Collaborator

Choose a reason for hiding this comment

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

types_from_output_spec?

@DouweM DouweM enabled auto-merge (squash) December 11, 2025 22:36
@DouweM
Copy link
Collaborator

DouweM commented Dec 11, 2025

@g-eoj Thanks Joe, looks great! 🤞🏻 that CI goes green

@DouweM DouweM merged commit 5ba691d into pydantic:main Dec 11, 2025
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

2 participants