Files
metabuilder/workflow/plugins/python/factory.py
johndoe6345789 7ce8b4ae8a refactor(workflow): convert all plugins to class/struct + factory pattern
- Python: class extending NodeExecutor + factory.py (80+ plugins)
- TypeScript: class implements NodeExecutor + factory.ts (7 groups, 116 classes)
- Go: struct with methods + factory.go (36 plugins)
- Rust: struct impl NodeExecutor trait + factory.rs (54 plugins)
- Mojo: struct + factory.mojo (11 plugins)

All package.json files now include:
- files array listing source files
- metadata.class/struct field
- metadata.entrypoint field

This enables a unified plugin loading system across all languages
with no import side effects (Spring-style DI pattern).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-22 14:53:04 +00:00

174 lines
4.5 KiB
Python

"""
Plugin factory for creating workflow plugins from simple functions.
This module provides a factory pattern for generating plugins. Plugins should
NOT call these functions at module load time - instead they should just export
NODE_TYPE, CATEGORY, DESCRIPTION, and impl. The registry handles instantiation.
"""
from typing import Any, Callable, Dict, List
from .base import NodeExecutor
def create_plugin(
node_type: str,
category: str,
description: str,
execute_fn: Callable[[Dict[str, Any], Any], Dict[str, Any]],
) -> NodeExecutor:
"""
Create a plugin executor from a simple function.
Args:
node_type: The node type identifier (e.g., "math.add")
category: Plugin category (e.g., "math")
description: Human-readable description
execute_fn: Function that takes (inputs, runtime) and returns result dict
Returns:
NodeExecutor instance with run() method exposed
"""
class DynamicExecutor(NodeExecutor):
pass
DynamicExecutor.node_type = node_type
DynamicExecutor.category = category
DynamicExecutor.description = description
DynamicExecutor.execute = lambda self, inputs, runtime=None: execute_fn(
inputs, runtime
)
return DynamicExecutor()
def wrap_math_impl(
fn: Callable[..., float],
input_keys: List[str] = None,
array_key: str = None,
) -> Callable[[Dict[str, Any], Any], Dict[str, Any]]:
"""
Wrap a math function to handle common input patterns.
Args:
fn: Math function to apply
input_keys: List of input parameter names (for binary ops like a, b)
array_key: Key for array input (for reduce ops like sum)
Returns:
Wrapped impl function
"""
def impl(inputs, runtime=None):
try:
if array_key:
values = inputs.get(array_key, inputs.get("values", []))
result = fn([float(v) for v in values])
elif input_keys:
args = [float(inputs.get(k, 0)) for k in input_keys]
result = fn(*args)
else:
value = float(inputs.get("value", 0))
result = fn(value)
return {"result": result}
except Exception as e:
return {"error": str(e)}
return impl
def wrap_string_impl(
fn: Callable[[str, Dict[str, Any]], str],
) -> Callable[[Dict[str, Any], Any], Dict[str, Any]]:
"""
Wrap a string function to handle common input patterns.
Args:
fn: Function that takes (value, inputs) and returns transformed string
Returns:
Wrapped impl function
"""
def impl(inputs, runtime=None):
try:
value = str(inputs.get("value", inputs.get("text", "")))
result = fn(value, inputs)
return {"result": result}
except Exception as e:
return {"error": str(e)}
return impl
def wrap_logic_impl(
fn: Callable[[Dict[str, Any]], bool],
) -> Callable[[Dict[str, Any], Any], Dict[str, Any]]:
"""
Wrap a logic function to handle common input patterns.
Args:
fn: Function that takes inputs and returns boolean
Returns:
Wrapped impl function
"""
def impl(inputs, runtime=None):
try:
result = fn(inputs)
return {"result": result}
except Exception as e:
return {"error": str(e)}
return impl
def wrap_list_impl(
fn: Callable[[List[Any], Dict[str, Any]], Any],
) -> Callable[[Dict[str, Any], Any], Dict[str, Any]]:
"""
Wrap a list function to handle common input patterns.
Args:
fn: Function that takes (array, inputs) and returns result
Returns:
Wrapped impl function
"""
def impl(inputs, runtime=None):
try:
array = inputs.get("array", inputs.get("list", []))
result = fn(array, inputs)
return {"result": result}
except Exception as e:
return {"error": str(e)}
return impl
def wrap_dict_impl(
fn: Callable[[Dict[str, Any], Dict[str, Any]], Any],
) -> Callable[[Dict[str, Any], Any], Dict[str, Any]]:
"""
Wrap a dict function to handle common input patterns.
Args:
fn: Function that takes (obj, inputs) and returns result
Returns:
Wrapped impl function
"""
def impl(inputs, runtime=None):
try:
obj = inputs.get("object", inputs.get("dict", {}))
result = fn(obj, inputs)
return {"result": result}
except Exception as e:
return {"error": str(e)}
return impl