Core Node Classes
AmritaSense's node system is the foundation of the entire workflow engine. All workflow elements -- whether business functions, control flow instructions, or composition containers -- are ultimately instances or compositions of nodes.
BaseNode
BaseNode is the abstract base class for all workflow nodes. It defines the common interface and minimum metadata set for nodes in the AmritaSense runtime. Developers typically do not subclass BaseNode directly, instead using the @Node() decorator or subclassing Node, though it may be needed when implementing low-level jump nodes for custom instructions.
class BaseNode:
func: Callable[..., Any]
tag: str
wrap_to_async: bool
address_able: bool
fun_frame: FrameType
fun_sign: inspect.SignatureAttributes
func: The underlying callable. This is what the interpreter ultimately calls when executing the node.tag: The node's string identifier. Also serves as the breakpoint name for flow suspension -- external callers can suspend before this node executes viawait_to_suspend(tag). Defaults toNodeSuspend::{function_name}if not specified at creation time.wrap_to_async: IfTrueandfuncis synchronous, the interpreter automatically usesasyncio.to_threadto execute it in a thread pool, preventing event-loop blockage.address_able: IfTrue, the node can be referenced byALIAS. Only addressable nodes can become targets for jump instructions likeGOTOandCALL.fun_sign: The function signature extracted byinspect.signature(func), used by the dependency injection system for parameter resolution at runtime.fun_frame: The stack frame object at node creation time, primarily used for debugging and log location.
Methods
_post_compile(compose: NodeComposeRendered) -> None: Post-compilation hook, called after the workflow graph is fully compiled. Subclasses resolve aliases and validate addresses here (e.g.,JumpNode,CallNode,PUSH_CONTEXT,INTERRUPT_INTO), moving runtime overhead to compile time._pre_check(pointer: WorkflowInterpreter) -> None: Pre-execution hook, called by the interpreter before each node execution. Used for runtime checks that depend on interpreter state (e.g.,BatchRunforks child interpreters here).as_compose() -> NodeCompose: Convenience method that wraps the node in aNodeCompose, enablingnode.as_compose().render()as a one-liner._init(func, tag, wrap_to_async, address_able, frame): Internal construction method that uniformly sets the node's metadata.__call__(*args, **kwargs): Abstract method that subclasses must implement. Defines the node's execution behavior.
Important: BaseNode and its subclasses use __slots__ for attribute definitions, avoiding the default __dict__ overhead and making each node instance as compact as possible. The memory advantage is significant for workflows that may contain hundreds or thousands of nodes.
Node
Node is the generic concrete implementation of BaseNode and the node type developers interact with most frequently. Nodes created with the @Node() decorator are Node instances. It wraps an ordinary Python function or coroutine into a basic workflow execution unit.
class Node(BaseNode, Generic[NODE_T]):
...Creation
@Node(tag="custom_tag", wrap_to_async=True, address_able=True)
def my_function(arg1: str) -> str:
return arg1.upper()Construction parameters
tag: str | None: The node's breakpoint label. Auto-generated by default.wrap_to_async: bool: IfTrueand the function is synchronous, executes in a thread pool. Defaults toTrue.address_able: bool: Whether the node can be bound byALIAS. Defaults toTrue.
Features
- The generic parameter
NODE_Tpreserves the original function's return type for type checker tracking. - Can be chained with any
BaseNode,NodeCompose, orSelfCompileInstructionin>>composition. - If the node's function signature declares a parameter of type
WorkflowInterpreter(injected viaPOINTER_DEPENDS), the node receives full access to the current interpreter instance at execution time. - The node's
__call__attribute directly returns the original function object, sonode(...)is equivalent tofunc(...).
NodeCompose
NodeCompose is a container for node composition. It maintains an ordered list of nodes and supports >> chain appending via __rshift__. NodeCompose is also the standard return type of SelfCompileInstruction.extract() -- self-compile instructions ultimately expand their semantics into a NodeCompose.
class NodeCompose:
_graph: list[BaseNode | NodeCompose | SelfCompileInstruction]Features
- Chain appending:
compose >> new_nodeis equivalent tocompose._graph.append(new_node). - Nesting capability: Elements in
_graphcan themselves beNodeComposeinstances -- this is the data foundation of Bubble scoping. - Compilation entry point: Calling the
render()method triggers the compilation pipeline and produces aNodeComposeRendered.
Methods
__rshift__(other): Implements the>>operator. Appendsotherto the internal list and returnsself.render() -> NodeComposeRendered: Compiles the current composition structure. AllSelfCompileInstructioninstances are expanded during this phase, recursiveNodeComposeinstances are resolved into nestedNodeComposeRendered, and the final executable graph with address mappings is produced.
Usage example
workflow = NodeCompose(node_a, node_b)
# Or more concisely:
workflow = node_a >> node_b >> node_cNodeComposeRendered
NodeComposeRendered is the final compilation product -- a fully resolved, optimized, address-mapped executable workflow graph. This is the type accepted by WorkflowInterpreter.
class NodeComposeRendered:
_graph: list[BaseNode | NodeComposeRendered]
alias2vector_map: dict[str, list[int]]Compilation process
render() triggers _build() to recursively traverse the original NodeCompose structure:
- Expands all
SelfCompileInstructioninstances (calling theirextract()method). - Recursively renders nested
NodeComposeinstances intoNodeComposeRendered. - Assigns a
PointerVectoraddress to each node. - Collects all
AliasNodeinstances, storing alias-to-address mappings intoalias2vector_map.
Key attributes
_graph: list[BaseNode | NodeComposeRendered]: The compiled node sequence. Elements may be concrete nodes or sub-containers.alias2vector_map: dict[str, list[int]]: The alias-to-pointer-vector mapping table. Jump instructions like GOTO and CALL look up addresses from this table at compile time via_post_compile.calc: AddressCalculator: The compiled graph's address calculator, providingresolve_alias(),find_addr(),find_addr_safe(), andadvance()methods. Available only on the top-levelNodeComposeRendered.__bool__(): Checks whether_graphhas been built, used to determine whether compilation has completed.
Obtaining an instance
rendered = workflow.render()
# workflow can be a NodeCompose or any SelfCompileInstructionImportant: NodeComposeRendered is immutable -- once compilation completes, its structure and address mappings should not be modified. All runtime state is managed by WorkflowInterpreter; NodeComposeRendered exists only as a read-only "code segment".
