```python:inference/core/workflows/core_steps/composition/workflow_caller/v1.py
from functools import lru_cache
from inference.core.workflows.execution_engine.entities.types import DICTIONARY_KIND, Kind, WILDCARD_KIND
from typing import Dict, List, Union


@lru_cache(maxsize=1)
def _build_kind_name_map() -> Dict[str, Kind]:
    """Build a mapping from kind name to Kind object using the loader.

    Result is cached since the set of registered kinds does not change
    within a process.
    """
    from inference.core.workflows.core_steps.loader import load_kinds

    return {kind.name: kind for kind in load_kinds()}


# Compute the kind name map once at module load time
_KIND_NAME_MAP = _build_kind_name_map()


def _convert_output_descriptions_to_kinds(
    outputs_description: Dict[str, Union[List[str], Dict[str, List[str]]]],
) -> Dict[str, List[Kind]]:
    """Convert ``describe_workflow_outputs()`` result (kind name strings) to
    ``Kind`` objects."""
    result: Dict[str, List[Kind]] = {}
    for name, kind_info in outputs_description.items():
        if isinstance(kind_info, dict):
            # Wildcard selector ($steps.step.*) — nested structure.
            # Represent as DICTIONARY_KIND since we can't express nested
            # typed properties in a single OutputDefinition.
            result[name] = [DICTIONARY_KIND]
        else:
            kinds = [
                _KIND_NAME_MAP[k]
                for k in kind_info
                if k in _KIND_NAME_MAP
            ]
            result[name] = kinds if kinds else [WILDCARD_KIND]
    return result
```