Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 017ad69

Browse files
authored
Annotating the openai.Omit type so that ModelSettings can be serialized by pydantic (openai#938)
Because `openai.Omit` is not a type which can be serialized by `pydantic`, it produces difficulty in consistently serializing/deserializing types across the agents SDK, many of which are `pydantic` types. This adds an annotation to enable `pydantic` to serialize `Omit`, in particular in the case of `ModelSettings` which contains `Omit` in its `extra_headers`
1 parent 381e90d commit 017ad69

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

src/agents/model_settings.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,50 @@
11
from __future__ import annotations
22

33
import dataclasses
4+
from collections.abc import Mapping
45
from dataclasses import dataclass, fields, replace
5-
from typing import Any, Literal
6+
from typing import Annotated, Any, Literal, Union
67

7-
from openai._types import Body, Headers, Query
8+
from openai import Omit as _Omit
9+
from openai._types import Body, Query
810
from openai.types.responses import ResponseIncludable
911
from openai.types.shared import Reasoning
10-
from pydantic import BaseModel
11-
12+
from pydantic import BaseModel, GetCoreSchemaHandler
13+
from pydantic_core import core_schema
14+
from typing_extensions import TypeAlias
15+
16+
17+
class _OmitTypeAnnotation:
18+
@classmethod
19+
def __get_pydantic_core_schema__(
20+
cls,
21+
_source_type: Any,
22+
_handler: GetCoreSchemaHandler,
23+
) -> core_schema.CoreSchema:
24+
def validate_from_none(value: None) -> _Omit:
25+
return _Omit()
26+
27+
from_none_schema = core_schema.chain_schema(
28+
[
29+
core_schema.none_schema(),
30+
core_schema.no_info_plain_validator_function(validate_from_none),
31+
]
32+
)
33+
return core_schema.json_or_python_schema(
34+
json_schema=from_none_schema,
35+
python_schema=core_schema.union_schema(
36+
[
37+
# check if it's an instance first before doing any further work
38+
core_schema.is_instance_schema(_Omit),
39+
from_none_schema,
40+
]
41+
),
42+
serialization=core_schema.plain_serializer_function_ser_schema(
43+
lambda instance: None
44+
),
45+
)
46+
Omit = Annotated[_Omit, _OmitTypeAnnotation]
47+
Headers: TypeAlias = Mapping[str, Union[str, Omit]]
1248

1349
@dataclass
1450
class ModelSettings:

tests/model_settings/test_serialization.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from dataclasses import fields
33

44
from openai.types.shared import Reasoning
5+
from pydantic import TypeAdapter
6+
from pydantic_core import to_json
57

68
from agents.model_settings import ModelSettings
79

@@ -132,3 +134,32 @@ def test_extra_args_resolve_both_none() -> None:
132134
assert resolved.extra_args is None
133135
assert resolved.temperature == 0.5
134136
assert resolved.top_p == 0.9
137+
138+
def test_pydantic_serialization() -> None:
139+
140+
"""Tests whether ModelSettings can be serialized with Pydantic."""
141+
142+
# First, lets create a ModelSettings instance
143+
model_settings = ModelSettings(
144+
temperature=0.5,
145+
top_p=0.9,
146+
frequency_penalty=0.0,
147+
presence_penalty=0.0,
148+
tool_choice="auto",
149+
parallel_tool_calls=True,
150+
truncation="auto",
151+
max_tokens=100,
152+
reasoning=Reasoning(),
153+
metadata={"foo": "bar"},
154+
store=False,
155+
include_usage=False,
156+
extra_query={"foo": "bar"},
157+
extra_body={"foo": "bar"},
158+
extra_headers={"foo": "bar"},
159+
extra_args={"custom_param": "value", "another_param": 42},
160+
)
161+
162+
json = to_json(model_settings)
163+
deserialized = TypeAdapter(ModelSettings).validate_json(json)
164+
165+
assert model_settings == deserialized

0 commit comments

Comments
 (0)