Skip to content

Commit 0d23fe7

Browse files
authored
fix: Fix invoker to work when using dataclass with from_dict but dataclass… (#9434)
* Fix invoker to work when using dataclass with from_dict but dataclass is already given * add reno * Add unit test * Remove line
1 parent f025501 commit 0d23fe7

File tree

3 files changed

+35
-4
lines changed

3 files changed

+35
-4
lines changed

haystack/tools/component_tool.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,19 @@ def component_invoker(**kwargs):
159159
target_type = get_args(param_type)[0] if get_origin(param_type) is list else param_type
160160
if hasattr(target_type, "from_dict"):
161161
if isinstance(param_value, list):
162-
param_value = [target_type.from_dict(item) for item in param_value if isinstance(item, dict)]
162+
resolved_param_value = [
163+
target_type.from_dict(item) if isinstance(item, dict) else item for item in param_value
164+
]
163165
elif isinstance(param_value, dict):
164-
param_value = target_type.from_dict(param_value)
166+
resolved_param_value = target_type.from_dict(param_value)
167+
else:
168+
resolved_param_value = param_value
165169
else:
166170
# Let TypeAdapter handle both single values and lists
167171
type_adapter = TypeAdapter(param_type)
168-
param_value = type_adapter.validate_python(param_value)
172+
resolved_param_value = type_adapter.validate_python(param_value)
169173

170-
converted_kwargs[param_name] = param_value
174+
converted_kwargs[param_name] = resolved_param_value
171175
logger.debug(f"Invoking component {type(component)} with kwargs: {converted_kwargs}")
172176
return component.run(**converted_kwargs)
173177

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
Fix component_invoker used by ComponentTool to work when a dataclass like ChatMessage is directly passed to `component_tool.invoke(...)`.
5+
Previously this would either cause an error or silently skip your input.

test/tools/test_component_tool.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@
3030
# Component and Model Definitions
3131

3232

33+
@component
34+
class SimpleComponentUsingChatMessages:
35+
"""A simple component that generates text."""
36+
37+
@component.output_types(reply=str)
38+
def run(self, messages: List[ChatMessage]) -> Dict[str, str]:
39+
"""
40+
A simple component that generates text.
41+
42+
:param messages: Users messages
43+
:return: A dictionary with the generated text.
44+
"""
45+
return {"reply": f"Hello, {messages[0].text}!"}
46+
47+
3348
@component
3449
class SimpleComponent:
3550
"""A simple component that generates text."""
@@ -306,6 +321,13 @@ def foo(self, text: str):
306321
with pytest.raises(ValueError):
307322
ComponentTool(component=not_a_component, name="invalid_tool", description="This should fail")
308323

324+
def test_component_invoker_with_chat_message_input(self):
325+
tool = ComponentTool(
326+
component=SimpleComponentUsingChatMessages(), name="simple_tool", description="A simple tool"
327+
)
328+
result = tool.invoke(messages=[ChatMessage.from_user(text="world")])
329+
assert result == {"reply": "Hello, world!"}
330+
309331

310332
# Integration tests
311333
class TestToolComponentInPipelineWithOpenAI:

0 commit comments

Comments
 (0)