From 2f97d52481dc5796e3d7ff962f95cd54f75bb18f Mon Sep 17 00:00:00 2001 From: David Montague <35119617+dmontagu@users.noreply.github.com> Date: Wed, 16 Apr 2025 19:00:10 -0600 Subject: [PATCH 1/2] Retain defaults in non-strict openai schemas --- pydantic_ai_slim/pydantic_ai/models/openai.py | 10 +++++++++- tests/models/test_openai.py | 11 +++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pydantic_ai_slim/pydantic_ai/models/openai.py b/pydantic_ai_slim/pydantic_ai/models/openai.py index dc1026f9f6..d6a76de807 100644 --- a/pydantic_ai_slim/pydantic_ai/models/openai.py +++ b/pydantic_ai_slim/pydantic_ai/models/openai.py @@ -992,10 +992,18 @@ def walk(self) -> JsonSchema: def transform(self, schema: JsonSchema) -> JsonSchema: # noqa C901 # Remove unnecessary keys schema.pop('title', None) - schema.pop('default', None) schema.pop('$schema', None) schema.pop('discriminator', None) + default = schema.get('default', _sentinel) + if default is not _sentinel: + # the "default" keyword is not allowed in strict mode, but including it makes some Ollama models behave + # better, so we keep it around when not strict + if self.strict is True: + schema.pop('default', None) + elif self.strict is None: + self.is_strict_compatible = False + if schema_ref := schema.get('$ref'): if schema_ref == self.root_ref: schema['$ref'] = '#' diff --git a/tests/models/test_openai.py b/tests/models/test_openai.py index b19c03b6c3..0e3f6fe994 100644 --- a/tests/models/test_openai.py +++ b/tests/models/test_openai.py @@ -829,7 +829,10 @@ def tool_with_tuples(x: tuple[int], y: tuple[str] = ('abc',)) -> str: '$defs': { 'MyDefaultRecursiveDc': { 'properties': { - 'field': {'anyOf': [{'$ref': '#/$defs/MyDefaultRecursiveDc'}, {'type': 'null'}]} + 'field': { + 'anyOf': [{'$ref': '#/$defs/MyDefaultRecursiveDc'}, {'type': 'null'}], + 'default': None, + } }, 'type': 'object', }, @@ -947,7 +950,7 @@ def tool_with_tuples(x: tuple[int], y: tuple[str] = ('abc',)) -> str: { '$defs': { 'MyDefaultDc': { - 'properties': {'x': {'type': 'integer'}}, + 'properties': {'x': {'default': 1, 'type': 'integer'}}, 'type': 'object', } }, @@ -987,7 +990,7 @@ def tool_with_tuples(x: tuple[int], y: tuple[str] = ('abc',)) -> str: { '$defs': { 'MyDefaultDc': { - 'properties': {'x': {'type': 'integer'}}, + 'properties': {'x': {'default': 1, 'type': 'integer'}}, 'type': 'object', } }, @@ -1027,7 +1030,7 @@ def tool_with_tuples(x: tuple[int], y: tuple[str] = ('abc',)) -> str: { '$defs': { 'MyDefaultDc': { - 'properties': {'x': {'type': 'integer'}}, + 'properties': {'x': {'default': 1, 'type': 'integer'}}, 'type': 'object', } }, From c31e1a343f1d84ac25aa750f699a52d61e98d2f9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Jul 2025 19:28:14 +0000 Subject: [PATCH 2/2] Branch is OK to not be covered --- pydantic_ai_slim/pydantic_ai/profiles/openai.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydantic_ai_slim/pydantic_ai/profiles/openai.py b/pydantic_ai_slim/pydantic_ai/profiles/openai.py index 1663b3b469..4367216add 100644 --- a/pydantic_ai_slim/pydantic_ai/profiles/openai.py +++ b/pydantic_ai_slim/pydantic_ai/profiles/openai.py @@ -102,7 +102,7 @@ def transform(self, schema: JsonSchema) -> JsonSchema: # noqa C901 # better, so we keep it around when not strict if self.strict is True: schema.pop('default', None) - elif self.strict is None: + elif self.strict is None: # pragma: no branch self.is_strict_compatible = False if schema_ref := schema.get('$ref'):