4
4
from datetime import timedelta
5
5
from typing import Any , Optional , Union
6
6
7
+ from pydantic import BaseModel , Field
7
8
from agents import (
8
9
AgentOutputSchemaBase ,
9
10
CodeInterpreterTool ,
28
29
AsyncOpenAI ,
29
30
)
30
31
from openai .types .responses .tool_param import Mcp
31
- from pydantic_core import to_json
32
32
try :
33
33
from azure .durable_functions import ApplicationError
34
34
except ImportError :
@@ -40,8 +40,7 @@ def __init__(self, message: str, non_retryable: bool = False, next_retry_delay =
40
40
self .next_retry_delay = next_retry_delay
41
41
42
42
43
- @dataclass
44
- class HandoffInput :
43
+ class HandoffInput (BaseModel ):
45
44
"""Data conversion friendly representation of a Handoff. Contains only the fields which are needed by the model
46
45
execution to determine what to handoff to, not the actual handoff invocation, which remains in the workflow context.
47
46
"""
@@ -53,8 +52,7 @@ class HandoffInput:
53
52
strict_json_schema : bool = True
54
53
55
54
56
- @dataclass
57
- class FunctionToolInput :
55
+ class FunctionToolInput (BaseModel ):
58
56
"""Data conversion friendly representation of a FunctionTool. Contains only the fields which are needed by the model
59
57
execution to determine what tool to call, not the actual tool invocation, which remains in the workflow context.
60
58
"""
@@ -65,8 +63,7 @@ class FunctionToolInput:
65
63
strict_json_schema : bool = True
66
64
67
65
68
- @dataclass
69
- class HostedMCPToolInput :
66
+ class HostedMCPToolInput (BaseModel ):
70
67
"""Data conversion friendly representation of a HostedMCPTool. Contains only the fields which are needed by the model
71
68
execution to determine what tool to call, not the actual tool invocation, which remains in the workflow context.
72
69
"""
@@ -84,13 +81,12 @@ class HostedMCPToolInput:
84
81
]
85
82
86
83
87
- @dataclass
88
- class AgentOutputSchemaInput (AgentOutputSchemaBase ):
84
+ class AgentOutputSchemaInput (AgentOutputSchemaBase , BaseModel ):
89
85
"""Data conversion friendly representation of AgentOutputSchema."""
90
86
91
- output_type_name : Optional [str ]
87
+ output_type_name : Optional [str ] = None
92
88
is_wrapped : bool
93
- output_schema : Optional [dict [str , Any ]]
89
+ output_schema : Optional [dict [str , Any ]] = None
94
90
strict_json_schema : bool
95
91
96
92
def is_plain_text (self ) -> bool :
@@ -131,74 +127,28 @@ class ModelTracingInput(enum.IntEnum):
131
127
ENABLED_WITHOUT_DATA = 2
132
128
133
129
134
- @dataclass
135
- class ActivityModelInput :
130
+ class ActivityModelInput (BaseModel ):
136
131
"""Input for the invoke_model_activity activity."""
137
132
138
133
input : Union [str , list [TResponseInputItem ]]
139
134
model_settings : ModelSettings
140
135
tracing : ModelTracingInput
141
136
model_name : Optional [str ] = None
142
137
system_instructions : Optional [str ] = None
143
- tools : list [ToolInput ] = None
138
+ tools : list [ToolInput ] = Field ( default_factory = list )
144
139
output_schema : Optional [AgentOutputSchemaInput ] = None
145
- handoffs : list [HandoffInput ] = None
140
+ handoffs : list [HandoffInput ] = Field ( default_factory = list )
146
141
previous_response_id : Optional [str ] = None
147
142
prompt : Optional [Any ] = None
148
143
149
- def __post_init__ (self ):
150
- """Initialize default values for list fields."""
151
- if self .tools is None :
152
- self .tools = []
153
- if self .handoffs is None :
154
- self .handoffs = []
155
-
156
144
def to_json (self ) -> str :
157
145
"""Convert the ActivityModelInput to a JSON string."""
158
- return to_json ( self ). decode ( 'utf-8' )
146
+ return self . model_dump_json ( )
159
147
160
148
@classmethod
161
149
def from_json (cls , json_str : str ) -> 'ActivityModelInput' :
162
150
"""Create an ActivityModelInput instance from a JSON string."""
163
- import json
164
- data = json .loads (json_str )
165
-
166
- # Convert complex types back from dictionaries
167
- if 'model_settings' in data and isinstance (data ['model_settings' ], dict ):
168
- data ['model_settings' ] = ModelSettings (** data ['model_settings' ])
169
-
170
- if 'tracing' in data and isinstance (data ['tracing' ], int ):
171
- data ['tracing' ] = ModelTracingInput (data ['tracing' ])
172
-
173
- # Convert tool inputs back to proper types
174
- if 'tools' in data and data ['tools' ]:
175
- converted_tools = []
176
- for tool_data in data ['tools' ]:
177
- if isinstance (tool_data , dict ):
178
- # Check the tool type and convert accordingly
179
- if 'name' in tool_data and 'description' in tool_data and 'params_json_schema' in tool_data :
180
- # FunctionToolInput
181
- converted_tools .append (FunctionToolInput (** tool_data ))
182
- elif 'tool_config' in tool_data :
183
- # HostedMCPToolInput
184
- converted_tools .append (HostedMCPToolInput (** tool_data ))
185
- else :
186
- # For other tool types like FileSearchTool, WebSearchTool, etc.
187
- # These might be already properly serialized/deserialized by pydantic_core
188
- converted_tools .append (tool_data )
189
- else :
190
- converted_tools .append (tool_data )
191
- data ['tools' ] = converted_tools
192
-
193
- # Convert handoffs back to proper types
194
- if 'handoffs' in data and data ['handoffs' ]:
195
- data ['handoffs' ] = [HandoffInput (** handoff ) for handoff in data ['handoffs' ]]
196
-
197
- # Convert output_schema back to proper type
198
- if 'output_schema' in data and data ['output_schema' ] and isinstance (data ['output_schema' ], dict ):
199
- data ['output_schema' ] = AgentOutputSchemaInput (** data ['output_schema' ])
200
-
201
- return cls (** data )
151
+ return cls .model_validate_json (json_str )
202
152
203
153
204
154
class ModelInvoker :
@@ -220,7 +170,7 @@ async def empty_on_invoke_handoff(
220
170
221
171
# workaround for https://github.com/pydantic/pydantic/issues/9541
222
172
# ValidatorIterator returned
223
- input_json = to_json (input .input )
173
+ input_json = json . dumps (input .input , default = str )
224
174
input_input = json .loads (input_json )
225
175
226
176
def make_tool (tool : ToolInput ) -> Tool :
0 commit comments