-
Notifications
You must be signed in to change notification settings - Fork 394
feat: support special types for structured outputs #1076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
feat: support special types for structured outputs #1076
Conversation
There was a problem hiding this 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 special types (like Literal, Union, etc.) in structured outputs by introducing a sophisticated overload pattern. The solution works around current typing limitations (until PEP 747 is supported) by providing three separate overloads for each parsing/streaming method: one for None | Omit, one for class types type[T] that unwraps to instance T, and one for special types that pass through as-is.
Key Changes
- Introduced three overloads for
parse()andstream()methods (both sync and async) to handle different output format types - Changed
ParseMessageCreateParamsBase.output_formatfromtype[ResponseFormatT]toResponseFormatTto accept special types - Updated snapshot test utilities to handle single responses more gracefully
- Added comprehensive test coverage for special types using
Literal
Reviewed Changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/anthropic/resources/beta/messages/messages.py | Added three overloads each for parse() and stream() methods (sync and async) to support None, class types, and special types |
| src/anthropic/types/beta/message_create_params.py | Changed output_format type from type[ResponseFormatT] to ResponseFormatT to accommodate special types |
| tests/lib/_parse/test_beta_messages.py | Added new test file with tests for both regular class types and special types (Literal) |
| tests/lib/snapshots.py | Modified snapshot handling to wrap single responses in a list instead of asserting they must be lists |
| tests/lib/_parse/init.py | Added empty init file for new test module |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <[email protected]>
|
Could you check if we could do something similar to python/mypy#19227 (comment) where we only support special types for Pyright? It'd be ideal if we could avoid using overloads here imo |
|
I tried it, to be honest, and it doesn't work. # This should be before the generic overload, so if the type is a class type, we unwrap it.
# Otherwise, something like Union[str, int] would incorrectly resolve to type[str] | type[int].
@overload
def parse(output_format: type[T]) -> T: ... # for classes
@overload
def parse(output_format: T) -> T: ... # for special types |
| extra_query: Query | None = None, | ||
| extra_body: Body | None = None, | ||
| timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, | ||
| ) -> ParsedBetaMessage[ResponseFormatT]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need a fallback overload for the "you're passing any supported thing" case like we have for streaming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in the streaming case we do it so we can annotate cases when the boolean is not statically known, so we just say it's either a streaming or non-streaming response, not just because we need to have an 'any supported thing' case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah do we not need that same thing here? e.g. if you're writing a wrapper function, you would get type errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah do we not need that same thing here?
I think this is a slightly different case, so no.
e.g. if you're writing a wrapper function, you would get type errors?
I'm not quite following this. Can you explain the use case?
36c82f7 to
2eb9415
Compare
|
@RobertCraigie @karpetrosyan is anthropic participating at all in this conversations? I don't understand their level of participation. |
Fixes typing so it also works with special types like
Literal,Union, etc. Currently, we usetype[T]and returnTto unwrap the class type, telling the type checker that we return the instance, not the type. When special types are passed, the type checker fails because special types are not subclasses of type, and there is nothing to unwrap in this case.PEP 747 would solve this problem by providing a way to annotate these types. Until then, this workaround can be used: