10
10
11
11
from haystack .core .serialization import generate_qualified_class_name
12
12
from haystack .tools .errors import ToolInvocationError
13
+ from haystack .utils .callable_serialization import deserialize_callable , serialize_callable
13
14
14
15
15
16
@dataclass
@@ -116,24 +117,18 @@ def to_dict(self) -> dict[str, Any]:
116
117
:returns:
117
118
Dictionary with serialized data.
118
119
"""
119
- # Import here to avoid circular dependency with utils.callable_serialization
120
- from haystack .utils import serialize_callable
121
-
122
120
data = asdict (self )
123
121
data ["function" ] = serialize_callable (self .function )
124
122
125
- # Serialize output handlers if they exist
126
- if self .outputs_to_state :
127
- serialized_outputs = {}
128
- for key , config in self .outputs_to_state .items ():
129
- serialized_config = config .copy ()
130
- if "handler" in config :
131
- serialized_config ["handler" ] = serialize_callable (config ["handler" ])
132
- serialized_outputs [key ] = serialized_config
133
- data ["outputs_to_state" ] = serialized_outputs
123
+ if self .outputs_to_state is not None :
124
+ data ["outputs_to_state" ] = _serialize_outputs_to_state (self .outputs_to_state )
134
125
135
126
if self .outputs_to_string is not None and self .outputs_to_string .get ("handler" ) is not None :
127
+ # This is soft-copied as to not modify the attributes in place
128
+ data ["outputs_to_string" ] = self .outputs_to_string .copy ()
136
129
data ["outputs_to_string" ]["handler" ] = serialize_callable (self .outputs_to_string ["handler" ])
130
+ else :
131
+ data ["outputs_to_string" ] = None
137
132
138
133
return {"type" : generate_qualified_class_name (type (self )), "data" : data }
139
134
@@ -147,21 +142,10 @@ def from_dict(cls, data: dict[str, Any]) -> "Tool":
147
142
:returns:
148
143
Deserialized Tool.
149
144
"""
150
- # Import here to avoid circular dependency with utils.callable_serialization
151
- from haystack .utils import deserialize_callable
152
-
153
145
init_parameters = data ["data" ]
154
146
init_parameters ["function" ] = deserialize_callable (init_parameters ["function" ])
155
-
156
- # Deserialize output handlers if they exist
157
147
if "outputs_to_state" in init_parameters and init_parameters ["outputs_to_state" ]:
158
- deserialized_outputs = {}
159
- for key , config in init_parameters ["outputs_to_state" ].items ():
160
- deserialized_config = config .copy ()
161
- if "handler" in config :
162
- deserialized_config ["handler" ] = deserialize_callable (config ["handler" ])
163
- deserialized_outputs [key ] = deserialized_config
164
- init_parameters ["outputs_to_state" ] = deserialized_outputs
148
+ init_parameters ["outputs_to_state" ] = _deserialize_outputs_to_state (init_parameters ["outputs_to_state" ])
165
149
166
150
if (
167
151
init_parameters .get ("outputs_to_string" ) is not None
@@ -187,3 +171,35 @@ def _check_duplicate_tool_names(tools: Optional[list[Tool]]) -> None:
187
171
duplicate_tool_names = {name for name in tool_names if tool_names .count (name ) > 1 }
188
172
if duplicate_tool_names :
189
173
raise ValueError (f"Duplicate tool names found: { duplicate_tool_names } " )
174
+
175
+
176
+ def _serialize_outputs_to_state (outputs_to_state : dict [str , dict [str , Any ]]) -> dict [str , dict [str , Any ]]:
177
+ """
178
+ Serializes the outputs_to_state dictionary, converting any callable handlers to their string representation.
179
+
180
+ :param outputs_to_state: The outputs_to_state dictionary to serialize.
181
+ :returns: The serialized outputs_to_state dictionary.
182
+ """
183
+ serialized_outputs = {}
184
+ for key , config in outputs_to_state .items ():
185
+ serialized_config = config .copy ()
186
+ if "handler" in config :
187
+ serialized_config ["handler" ] = serialize_callable (config ["handler" ])
188
+ serialized_outputs [key ] = serialized_config
189
+ return serialized_outputs
190
+
191
+
192
+ def _deserialize_outputs_to_state (outputs_to_state : dict [str , dict [str , Any ]]) -> dict [str , dict [str , Any ]]:
193
+ """
194
+ Deserializes the outputs_to_state dictionary, converting any string handlers back to callables.
195
+
196
+ :param outputs_to_state: The outputs_to_state dictionary to deserialize.
197
+ :returns: The deserialized outputs_to_state dictionary.
198
+ """
199
+ deserialized_outputs = {}
200
+ for key , config in outputs_to_state .items ():
201
+ deserialized_config = config .copy ()
202
+ if "handler" in config :
203
+ deserialized_config ["handler" ] = deserialize_callable (config ["handler" ])
204
+ deserialized_outputs [key ] = deserialized_config
205
+ return deserialized_outputs
0 commit comments