diff --git a/haystack/core/component/component.py b/haystack/core/component/component.py index 4a0828733..7d5eb00d1 100644 --- a/haystack/core/component/component.py +++ b/haystack/core/component/component.py @@ -139,7 +139,10 @@ class ComponentMeta(type): instance.__canals_input__ = {} run_signature = inspect.signature(getattr(cls, "run")) for param in list(run_signature.parameters)[1:]: # First is 'self' and it doesn't matter. - if run_signature.parameters[param].kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: # ignore `**kwargs` + if run_signature.parameters[param].kind not in ( + inspect.Parameter.VAR_POSITIONAL, + inspect.Parameter.VAR_KEYWORD, + ): # ignore variable args socket_kwargs = {"name": param, "type": run_signature.parameters[param].annotation} if run_signature.parameters[param].default != inspect.Parameter.empty: socket_kwargs["default_value"] = run_signature.parameters[param].default diff --git a/releasenotes/notes/component-kw-only-run-args-eedee8907232d2d4.yaml b/releasenotes/notes/component-kw-only-run-args-eedee8907232d2d4.yaml new file mode 100644 index 000000000..68ef50fbf --- /dev/null +++ b/releasenotes/notes/component-kw-only-run-args-eedee8907232d2d4.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Fix ComponentMeta ignoring keyword-only parameters in the `run` method. ComponentMeta.__call__ handles the creation of InputSockets for the component's inputs when the latter has not explicitly called _Component.set_input_types(). This logic was not correctly handling keyword-only parameters. diff --git a/test/core/component/test_component.py b/test/core/component/test_component.py index cba6fc42a..f2835e62c 100644 --- a/test/core/component/test_component.py +++ b/test/core/component/test_component.py @@ -176,3 +176,17 @@ def test_input_has_default_value(): comp = MockComponent() assert comp.__canals_input__["value"].default_value == 42 assert not comp.__canals_input__["value"].is_mandatory + + +def test_keyword_only_args(): + @component + class MockComponent: + def __init__(self): + component.set_output_types(self, value=int) + + def run(self, *, arg: int): + return {"value": arg} + + comp = MockComponent() + component_inputs = {name: {"type": socket.type} for name, socket in comp.__canals_input__.items()} + assert component_inputs == {"arg": {"type": int}}