Generator Dispatch - cprima-forks/uipath-ai-skills GitHub Wiki

Generator Dispatch Mechanism

generate_workflow.py uses a registry-and-dispatch architecture to route each JSON spec activity to its generator function.

_GenEntry and _REGISTRY

Every generator is registered as a _GenEntry frozen dataclass:

@dataclasses.dataclass(frozen=True, slots=True)
class _GenEntry:
    fn: object        # the generator function
    idref: str        # IdRef prefix, e.g. "NTypeInto"
    required: tuple   # required arg names for spec validation
    container: bool   # True = fn is a manual container handler

_REGISTRY is a dict mapping gen name strings to _GenEntry objects. All 93 core generators are registered at module load time using the _e() shorthand:

_REGISTRY = {
    "ntypeinto": _e(gen_ntypeinto, "NTypeInto", ("display_name", "selector", "text_variable")),
    "nclick":    _e(gen_nclick,    "NClick",    ("display_name", "selector")),
    "try_catch": _e(_handle_try_catch, "TryCatch", container=True),
    ...
}

_auto_dispatch

For non-container generators, dispatch goes through _auto_dispatch, which maps the JSON args dict to the generator function's parameters using runtime signature introspection:

def _auto_dispatch(fn, args: dict, **extra) -> str:
    sig = _cached_signature(fn)   # LRU-cached inspect.signature()
    kwargs = {}
    for name, param in sig.parameters.items():
        if name in extra:
            kwargs[name] = extra[name]   # framework args: id_ref, scope_id, indent
        elif name in args:
            kwargs[name] = args[name]    # from JSON spec
        elif param.default is not inspect.Parameter.empty:
            pass                          # use function default
    return fn(**kwargs)

Framework args (id_ref, scope_id, indent) are injected by the caller. Optional generator args (is_secure, empty_field_mode, etc.) fall back to function defaults if not in the spec. Missing required args cause a Python TypeError at generation time.

_generate_activity Dispatch Flow

The unified entry point _generate_activity(spec, scope_id, counter, indent) handles the full dispatch sequence:

  1. Extract gen name and args from spec
  2. Auto-detection hooks (desktop context, multiple_assign type enrichment, Object Repository wiring)
  3. Blocked generator check — delay raises ValueError
  4. Special-case: log_message / logmessage — custom arg mapping outside the registry
  5. Registry lookup → if entry.container: call entry.fn(spec, args, scope_id, counter, indent) directly; else: call _auto_dispatch
  6. Plugin generator fallback — same _auto_dispatch path as core
  7. ValueError if gen name is unknown

Special Case: log_message

log_message and its alias logmessage are not in _REGISTRY. They use a custom arg mapping: message_expr maps to the message parameter with explicit bracket wrapping. This predates the unified registry and was never migrated into it.

IdRef Prefix Derivation

The idref field in _GenEntry is the prefix for sap2010:WorkflowViewState.IdRef values. If not explicitly set, _derive_idref_prefix converts the gen name to PascalCase with acronym corrections:

"read_csv"  → "ReadCSV"   (not "ReadCsv")
"read_pdf_text" → "ReadPDFText"

Plugin Generators

Plugins registered via register_generator() in plugin_loader.py are stored separately from _REGISTRY but go through the same _auto_dispatch path. They are checked after core registry lookup, before the unknown-gen error.