Skip to content

Commit db4f237

Browse files
Avoid mutating self.routes in ConditionalRouter to_dict method (#8936)
* Avoid mutating self.routes in ConditionalRouter to_dict method * Add release note * Update releasenotes/notes/fix-conditional-router-to-dict-5af887da50effe11.yaml Co-authored-by: David S. Batista <[email protected]> * Make test_router_to_dict_does_not_mutate_routes more robut (add another roundtrip) --------- Co-authored-by: David S. Batista <[email protected]>
1 parent d1e503e commit db4f237

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

haystack/components/routers/conditional_router.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,14 @@ def to_dict(self) -> Dict[str, Any]:
241241
:returns:
242242
Dictionary with serialized data.
243243
"""
244+
serialized_routes = []
244245
for route in self.routes:
245246
# output_type needs to be serialized to a string
246-
route["output_type"] = serialize_type(route["output_type"])
247+
serialized_routes.append({**route, "output_type": serialize_type(route["output_type"])})
247248
se_filters = {name: serialize_callable(filter_func) for name, filter_func in self.custom_filters.items()}
248249
return default_to_dict(
249250
self,
250-
routes=self.routes,
251+
routes=serialized_routes,
251252
custom_filters=se_filters,
252253
unsafe=self._unsafe,
253254
validate_output_type=self._validate_output_type,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
Fix a bug where the `output_type` of a `ConditionalRouter` was not being serialized correctly.
5+
This would cause the router to work incorrectly after being serialized and deserialized.

test/components/routers/test_conditional_router.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,3 +542,35 @@ def test_warns_on_unused_optional_variables(self):
542542
# Verify router still works normally
543543
result = router.run(question="What?", mode="chat")
544544
assert result == {"chat": "What?"}
545+
546+
def test_router_to_dict_does_not_mutate_routes(self):
547+
routes = [
548+
{"condition": "{{streams|length < 2}}", "output": "{{query}}", "output_type": str, "output_name": "query"}
549+
]
550+
551+
router = ConditionalRouter(routes)
552+
553+
# Store the original output_type before serializing
554+
original_output_type = router.routes[0]["output_type"]
555+
assert original_output_type is str
556+
557+
router_dict = router.to_dict()
558+
559+
# Verify that the original routes are not mutated
560+
assert router.routes[0]["output_type"] is str
561+
assert router.routes[0]["output_type"] is original_output_type
562+
563+
# Verify that the serialized output_type is a string
564+
assert isinstance(router_dict["init_parameters"]["routes"][0]["output_type"], str), (
565+
"Serialized output_type should be a string"
566+
)
567+
568+
# Verify that the router still works correctly after to_dict()
569+
result = router.run(streams=[1], query="test")
570+
assert result == {"query": "test"}, "Router should still work correctly after to_dict()"
571+
572+
# Double check on another ConditionalRouter instance
573+
new_router = ConditionalRouter.from_dict(router_dict)
574+
assert new_router.routes == router.routes
575+
assert new_router.routes[0]["output_type"] is str
576+
assert new_router.routes[0]["output_type"] is original_output_type

0 commit comments

Comments
 (0)