diff --git a/src/anthropic/resources/beta/messages/messages.py b/src/anthropic/resources/beta/messages/messages.py index ed10bd7a..fcc90065 100644 --- a/src/anthropic/resources/beta/messages/messages.py +++ b/src/anthropic/resources/beta/messages/messages.py @@ -1033,6 +1033,9 @@ def create( stream_cls=Stream[BetaRawMessageStreamEvent], ) + # This overload should be listed first to prioritize it over generic overload + # so calls with Omit() resolve to ParsedBetaMessage[None] not ParsedBetaMessage[Omit] + @overload def parse( self, *, @@ -1043,7 +1046,7 @@ def parse( context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, + output_format: None | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, @@ -1061,7 +1064,100 @@ def parse( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ParsedBetaMessage[ResponseFormatT]: + ) -> ParsedBetaMessage[None]: ... + + @overload + def parse( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + output_format: type[ResponseFormatT], + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedBetaMessage[ResponseFormatT]: ... + + @overload + def parse( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + output_format: ResponseFormatT, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedBetaMessage[ResponseFormatT]: ... + + def parse( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + # TODO: Use TypeForm[ResponseFormatT] instead when PEP 747 is supported by mypy + output_format: None | type[ResponseFormatT] | ResponseFormatT | Omit = omit, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedBetaMessage[ResponseFormatT] | ParsedBetaMessage[None]: if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) @@ -1338,6 +1434,9 @@ def tool_runner( max_iterations=max_iterations if is_given(max_iterations) else None, ) + # This overload should be listed first to prioritize it over generic overload + # so calls with Omit() resolve to ParsedBetaMessage[None] not ParsedBetaMessage[Omit] + @overload def stream( self, *, @@ -1348,7 +1447,94 @@ def stream( context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, + output_format: None | Omit = omit, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BetaMessageStreamManager[None]: ... + @overload + def stream( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + output_format: type[ResponseFormatT], + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BetaMessageStreamManager[ResponseFormatT]: ... + @overload + def stream( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + output_format: ResponseFormatT, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BetaMessageStreamManager[ResponseFormatT]: ... + def stream( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + # TODO: Use TypeForm[ResponseFormatT] instead when PEP 747 is supported by mypy + output_format: None | type[ResponseFormatT] | ResponseFormatT | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, @@ -2646,6 +2832,9 @@ async def create( stream_cls=AsyncStream[BetaRawMessageStreamEvent], ) + # This overload should be listed first to prioritize it over generic overload + # so calls with Omit() resolve to ParsedBetaMessage[None] not ParsedBetaMessage[Omit] + @overload async def parse( self, *, @@ -2656,7 +2845,98 @@ async def parse( context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, + output_format: None | Omit = omit, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedBetaMessage[None]: ... + @overload + async def parse( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + output_format: type[ResponseFormatT], + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedBetaMessage[ResponseFormatT]: ... + @overload + async def parse( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + output_format: ResponseFormatT, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedBetaMessage[ResponseFormatT]: ... + + async def parse( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + metadata: BetaMetadataParam | Omit = omit, + # TODO: Use TypeForm[ResponseFormatT] instead when PEP 747 is supported by mypy + output_format: None | type[ResponseFormatT] | ResponseFormatT | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, @@ -2950,6 +3230,9 @@ def tool_runner( max_iterations=max_iterations if is_given(max_iterations) else None, ) + # This overload should be listed first to prioritize it over generic overload + # so calls with Omit() resolve to ParsedBetaMessage[None] not ParsedBetaMessage[Omit] + @overload def stream( self, *, @@ -2957,7 +3240,95 @@ def stream( messages: Iterable[BetaMessageParam], model: ModelParam, metadata: BetaMetadataParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, + output_format: None | Omit = omit, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BetaAsyncMessageStreamManager[None]: ... + @overload + def stream( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + metadata: BetaMetadataParam | Omit = omit, + output_format: type[ResponseFormatT], + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BetaAsyncMessageStreamManager[ResponseFormatT]: ... + @overload + def stream( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + metadata: BetaMetadataParam | Omit = omit, + output_format: ResponseFormatT, + container: Optional[message_create_params.Container] | Omit = omit, + context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, + mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, + service_tier: Literal["auto", "standard_only"] | Omit = omit, + stop_sequences: SequenceNotStr[str] | Omit = omit, + system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, + temperature: float | Omit = omit, + thinking: BetaThinkingConfigParam | Omit = omit, + tool_choice: BetaToolChoiceParam | Omit = omit, + tools: Iterable[BetaToolUnionParam] | Omit = omit, + top_k: int | Omit = omit, + top_p: float | Omit = omit, + betas: List[AnthropicBetaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BetaAsyncMessageStreamManager[ResponseFormatT]: ... + + def stream( + self, + *, + max_tokens: int, + messages: Iterable[BetaMessageParam], + model: ModelParam, + metadata: BetaMetadataParam | Omit = omit, + # TODO: Use TypeForm[ResponseFormatT] instead when PEP 747 is supported by mypy + output_format: None | type[ResponseFormatT] | ResponseFormatT | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, diff --git a/src/anthropic/types/beta/message_create_params.py b/src/anthropic/types/beta/message_create_params.py index 1202c91b..9ada27f0 100644 --- a/src/anthropic/types/beta/message_create_params.py +++ b/src/anthropic/types/beta/message_create_params.py @@ -302,7 +302,7 @@ class MessageCreateParamsBase(TypedDict, total=False): class ParseMessageCreateParamsBase(MessageCreateParamsBase, Generic[ResponseFormatT]): - output_format: type[ResponseFormatT] # type: ignore[misc] + output_format: ResponseFormatT # type: ignore[misc] class OutputFormat(TypedDict, total=False): diff --git a/tests/lib/_parse/__init__.py b/tests/lib/_parse/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/lib/_parse/test_beta_messages.py b/tests/lib/_parse/test_beta_messages.py new file mode 100644 index 00000000..edd107c7 --- /dev/null +++ b/tests/lib/_parse/test_beta_messages.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +from typing import Literal + +import pytest +from respx import MockRouter +from pydantic import BaseModel +from inline_snapshot import snapshot + +from anthropic import AsyncAnthropic, _compat + +from ..utils import print_obj +from ..snapshots import make_async_snapshot_request + + +@pytest.mark.skipif(_compat.PYDANTIC_V1, reason="tool runner not supported with pydantic v1") +class TestAsyncMessages: + async def test_simple_parse( + self, async_client: AsyncAnthropic, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch + ) -> None: + class ContactInfo(BaseModel): + name: str + email: str + plan_interest: str + demo_requested: bool + + resp = await make_async_snapshot_request( + lambda cl: cl.beta.messages.parse( + model="claude-sonnet-4-5", + messages=[ + { + "role": "user", + "content": ( + "Extract the key information from this email: John Smith (john@example.com) is " + "interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm." + ), + } + ], + max_tokens=1024, + output_format=ContactInfo, + ), + content_snapshot=snapshot( + '{"model": "claude-sonnet-4-5-20250929", "id": "msg_01PpBVKyHCmDmjk57fYhRsMk", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "{\\"name\\": \\"John Smith\\", \\"email\\": \\"john@example.com\\", \\"plan_interest\\": \\"Enterprise\\", \\"demo_requested\\": true}"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 303, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 35, "service_tier": "standard"}}' + ), + respx_mock=respx_mock, + mock_client=async_client, + path="/v1/messages?beta=true", + ) + assert print_obj(resp, monkeypatch) == snapshot( + "ParsedBetaMessage(container=None, content=[ParsedBetaTextBlock(citations=None, parsed_output=ContactInfo(demo_requested=True, email='john@example.com', name='John Smith', plan_interest='Enterprise'), text='{\"name\": \"John Smith\", \"email\": \"john@example.com\", \"plan_interest\": \"Enterprise\", \"demo_requested\": true}', type='text')], context_management=None, id='msg_01PpBVKyHCmDmjk57fYhRsMk', model='claude-sonnet-4-5-20250929', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage(cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, input_tokens=303, output_tokens=35, server_tool_use=None, service_tier='standard'))\n" + ) + + async def test_special_types( + self, async_client: AsyncAnthropic, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch + ) -> None: + resp = await make_async_snapshot_request( + lambda cl: cl.beta.messages.parse( + model="claude-sonnet-4-5", + messages=[ + { + "role": "user", + "content": "Which programming language is better for data science, Python or JavaScript?", + } + ], + max_tokens=1024, + output_format=Literal["Python", "Typescript"], + ), + content_snapshot=snapshot( + '{"model": "claude-sonnet-4-5-20250929", "id": "msg_015CQWZ6XgLgvZpALgDXQQR5", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "\\"Python\\""}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 129, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 6, "service_tier": "standard"}}' + ), + respx_mock=respx_mock, + mock_client=async_client, + path="/v1/messages?beta=true", + ) + assert print_obj(resp, monkeypatch) == snapshot( + "ParsedBetaMessage(container=None, content=[ParsedBetaTextBlock(citations=None, parsed_output='Python', text='\"Python\"', type='text')], context_management=None, id='msg_015CQWZ6XgLgvZpALgDXQQR5', model='claude-sonnet-4-5-20250929', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage(cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, input_tokens=129, output_tokens=6, server_tool_use=None, service_tier='standard'))\n" + ) diff --git a/tests/lib/snapshots.py b/tests/lib/snapshots.py index 510fceb0..7b3158d4 100644 --- a/tests/lib/snapshots.py +++ b/tests/lib/snapshots.py @@ -161,7 +161,8 @@ async def _on_response(response: httpx.Response) -> None: ) else: responses = get_snapshot_value(content_snapshot) - assert is_list(responses) + if not is_list(responses): + responses = [responses] curr = 0