Skip to content

fix(structured outputs): correct schema coercion for inline ref expansion #2025

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

Merged
merged 2 commits into from
Jan 17, 2025
Merged
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
3 changes: 3 additions & 0 deletions src/openai/lib/_pydantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ def _ensure_strict_json_schema(
# properties from the json schema take priority over the ones on the `$ref`
json_schema.update({**resolved, **json_schema})
json_schema.pop("$ref")
# Since the schema expanded from `$ref` might not have `additionalProperties: false` applied,
# we call `_ensure_strict_json_schema` again to fix the inlined schema and ensure it's valid.
return _ensure_strict_json_schema(json_schema, path=path, root=root)

return json_schema

Expand Down
174 changes: 174 additions & 0 deletions tests/lib/test_pydantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import openai
from openai._compat import PYDANTIC_V2
from openai.lib._pydantic import to_strict_json_schema

from .schema_types.query import Query

Expand Down Expand Up @@ -235,3 +236,176 @@ def test_enums() -> None:
},
}
)


class Star(BaseModel):
name: str = Field(description="The name of the star.")


class Galaxy(BaseModel):
name: str = Field(description="The name of the galaxy.")
largest_star: Star = Field(description="The largest star in the galaxy.")


class Universe(BaseModel):
name: str = Field(description="The name of the universe.")
galaxy: Galaxy = Field(description="A galaxy in the universe.")


def test_nested_inline_ref_expansion() -> None:
if PYDANTIC_V2:
assert to_strict_json_schema(Universe) == snapshot(
{
"title": "Universe",
"type": "object",
"$defs": {
"Star": {
"title": "Star",
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "The name of the star.",
}
},
"required": ["name"],
"additionalProperties": False,
},
"Galaxy": {
"title": "Galaxy",
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "The name of the galaxy.",
},
"largest_star": {
"title": "Star",
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "The name of the star.",
}
},
"required": ["name"],
"description": "The largest star in the galaxy.",
"additionalProperties": False,
},
},
"required": ["name", "largest_star"],
"additionalProperties": False,
},
},
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "The name of the universe.",
},
"galaxy": {
"title": "Galaxy",
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "The name of the galaxy.",
},
"largest_star": {
"title": "Star",
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "The name of the star.",
}
},
"required": ["name"],
"description": "The largest star in the galaxy.",
"additionalProperties": False,
},
},
"required": ["name", "largest_star"],
"description": "A galaxy in the universe.",
"additionalProperties": False,
},
},
"required": ["name", "galaxy"],
"additionalProperties": False,
}
)
else:
assert to_strict_json_schema(Universe) == snapshot(
{
"title": "Universe",
"type": "object",
"definitions": {
"Star": {
"title": "Star",
"type": "object",
"properties": {
"name": {"title": "Name", "description": "The name of the star.", "type": "string"}
},
"required": ["name"],
"additionalProperties": False,
},
"Galaxy": {
"title": "Galaxy",
"type": "object",
"properties": {
"name": {"title": "Name", "description": "The name of the galaxy.", "type": "string"},
"largest_star": {
"title": "Largest Star",
"description": "The largest star in the galaxy.",
"type": "object",
"properties": {
"name": {"title": "Name", "description": "The name of the star.", "type": "string"}
},
"required": ["name"],
"additionalProperties": False,
},
},
"required": ["name", "largest_star"],
"additionalProperties": False,
},
},
"properties": {
"name": {
"title": "Name",
"description": "The name of the universe.",
"type": "string",
},
"galaxy": {
"title": "Galaxy",
"description": "A galaxy in the universe.",
"type": "object",
"properties": {
"name": {
"title": "Name",
"description": "The name of the galaxy.",
"type": "string",
},
"largest_star": {
"title": "Largest Star",
"description": "The largest star in the galaxy.",
"type": "object",
"properties": {
"name": {"title": "Name", "description": "The name of the star.", "type": "string"}
},
"required": ["name"],
"additionalProperties": False,
},
},
"required": ["name", "largest_star"],
"additionalProperties": False,
},
},
"required": ["name", "galaxy"],
"additionalProperties": False,
}
)
Loading