Skip to content

feat: Enhance Component registry to update with init and support shortened import paths #9142

@Amnah199

Description

@Amnah199

Is your feature request related to a problem? Please describe.
Currently, the component registry in Haystack only stores the complete module path of components. When users create init files to re-export components with shorter paths (a common Python pattern), the registry isn't updated. This forces users to always use the complete path during serialization and deserialization.
For example, if a user has:

# haystack/components/retrievers/memory.py
@component
class MemoryRetriever:

And __init__ file:

# haystack/components/__init__.py
from .retrievers.memory import MemoryRetriever

They still need to use haystack.components.retrievers.memory.MemoryRetriever in serialized pipelines instead of the shorter haystack.components.MemoryRetriever as it wont work.

Describe the solution you'd like
Enhance the component registry to track both original paths and re-exported paths (aliases). Below is one possible solution.

Dynamic Registry Update During Import: Update the registry when components are re-exported through init files. We could modify the _component decorator to track both the original and re-exported paths. A quick implementation suggest by cursor for reference:

class _Component:
    def __init__(self):
        # Map of original path -> list of alias paths
        self.registry = {}
        self.alias_registry = {}  # alias -> original path

    def _component(self, cls: Any):
        logger.debug("Registering {component} as a component", component=cls)

        if not hasattr(cls, "run"):
            raise ComponentError(f"{cls.__name__} must have a 'run()' method. See the docs for more information.")

        # Create the new class with metaclass as before
        new_cls = new_class(cls.__name__, cls.__bases__, {"metaclass": ComponentMeta}, copy_class_namespace)

        # Get both the original path and current import path
        original_path = f"{cls.__module__}.{cls.__name__}"
        current_path = f"{new_cls.__module__}.{new_cls.__name__}"

        # Register both paths
        if original_path not in self.registry:
            self.registry[original_path] = new_cls
            
        # Add alias if this is a re-export
        if current_path != original_path:
            self.alias_registry[current_path] = original_path
            logger.debug(
                "Added alias {alias} for component {original}",
                alias=current_path,
                original=original_path
            )

        return new_cls

Alternate Solutions
We could maintain a separate index by class name to help with resolution.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions