Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/anthropic/lib/_parse/_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ def transform_schema(
strict_schema["anyOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in one_of]
elif is_list(all_of):
strict_schema["allOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in all_of]
else:
if type_ is None:
raise ValueError("Schema must have a 'type', 'anyOf', 'oneOf', or 'allOf' field.")

elif type_ is not None:
strict_schema["type"] = type_
# else: a typeless schema with no combinators — e.g. an empty `{}`, which pydantic
# emits for an `Any`-typed field or the items of an untyped `list`/`List[Any]`. This
# is a valid, unconstrained ("any") schema, so leave it untyped rather than raising.

enum = json_schema.pop("enum", None)
if is_list(enum):
Expand Down
53 changes: 53 additions & 0 deletions tests/lib/_parse/test_transform.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from copy import deepcopy
from typing import Any, List

import pytest
import pydantic
from inline_snapshot import snapshot

from anthropic.lib._parse._transform import transform_schema
Expand Down Expand Up @@ -145,6 +147,57 @@ def test_array_schema():
)


def test_empty_schema():
# pydantic emits a bare `{}` for an `Any`-typed field; an empty schema is a
# valid, unconstrained ("any") schema and must not raise.
assert transform_schema({}) == snapshot({})


def test_array_with_untyped_items():
# `list` / `List[Any]` produces `{"type": "array", "items": {}}`.
schema: dict[str, Any] = {"type": "array", "items": {}}
result = transform_schema(schema)
assert result == snapshot({"type": "array", "items": {}})


def test_object_with_any_property():
schema: dict[str, Any] = {"type": "object", "properties": {"x": {}}, "required": ["x"], "title": "M"}
result = transform_schema(schema)
assert result == snapshot(
{
"type": "object",
"title": "M",
"properties": {"x": {}},
"additionalProperties": False,
"required": ["x"],
}
)


def test_model_with_untyped_list_and_any_field():
# Regression: previously raised ValueError("Schema must have a 'type', ...") because
# the untyped `list` items / `Any` field transform to an empty `{}` sub-schema.
class Result(pydantic.BaseModel):
summary: str
tags: List[Any]
extra: Any

result = transform_schema(Result)
assert result == snapshot(
{
"type": "object",
"title": "Result",
"properties": {
"summary": {"type": "string", "title": "Summary"},
"tags": {"type": "array", "title": "Tags", "items": {}},
"extra": {"title": "Extra"},
},
"additionalProperties": False,
"required": ["summary", "tags", "extra"],
}
)


def test_string_schema_with_format_and_default():
schema = {
"type": "string",
Expand Down