ProcessingStage
The ProcessingStage class is the base class for all data processing stages in NeMo Curator. Each stage defines a single step in a data curation pipeline.
Import
Class Definition
Abstract Methods
inputs()
Define stage input requirements.
Return one tuple when every supported task type has the same requirements. To
declare different requirements for different task types, return a mapping keyed
by task type. For example, a stage can require the data attribute from a
FileGroupTask and a text column from a DocumentBatch:
outputs()
Define stage output requirements.
process()
Process a single task.
The framework infers fanout when process() has a return annotation containing
list[OutputT]. This includes unions such as OutputT | list[OutputT] | None.
Optional Lifecycle Methods
setup_on_node()
Node-level initialization (e.g., download models).
setup()
Worker-level initialization (e.g., load models).
teardown()
Cleanup after processing.
process_batch()
Vectorized batch processing for better performance.
Backend Configuration Hooks
num_workers()
Return a backend-neutral worker count. None delegates worker sizing to the executor.
num_workers is reserved as a method. A subclass that defines it as a class attribute or dataclass field raises TypeError. Override the method for a class-level default, or use stage.with_(num_workers=...) for one pipeline instance.
The exact meaning depends on the executor: Ray Data creates a fixed actor or task pool, Xenna treats it as a cluster-wide count, and Ray Actor Pool caps it to available resource capacity when necessary. See Stage Worker Sizing for the complete backend matrix.
num_workers_per_node()
Return a backend-neutral worker count per alive Ray node. None delegates worker sizing to the executor.
The executor converts the value to a cluster-wide request. Ray Data and Ray Actor Pool use ceil(num_workers_per_node × alive node count) with best-effort SPREAD placement; Ray Actor Pool also caps the result to available resource capacity. Xenna forwards the per-node value directly. Fractional values are supported; values must be positive.
Like num_workers, num_workers_per_node is reserved as a method. Override it for a class-level default, or use stage.with_(num_workers_per_node=...) for one pipeline instance. Do not combine it with num_workers() or Ray Data’s min_workers, max_workers, or initial_workers actor-pool keys.
ray_stage_spec()
Return Ray-specific stage options. Ray Data consumes the worker-pool keys below, while selected flags are also used by Ray Actor Pool. Use the RayStageSpecKeys enum rather than spelling keys manually:
xenna_stage_spec()
Return Xenna-specific stage options:
Use the common num_workers() and num_workers_per_node() hooks for portable worker sizing. The legacy num_workers_per_node Xenna key remains supported, but do not combine it with either common sizing hook. Do not put num_workers in this dictionary.
task_id is framework-owned. The executor adapter assigns it after either process() or process_batch() returns, so custom stages must not set or derive IDs. For deterministic lineage, preserve positional correspondence in batched code: return one task or None for every input. A batch that maps multiple inputs to a different number of outputs receives random r-prefixed IDs because parentage is ambiguous.
For source-level checkpointing and the complete mapping rules, refer to Resumable Processing.
Creating Custom Stages
Per-Stage Runtime Environments
Stages can declare isolated Python dependencies using Ray’s native runtime_env. Set runtime_env as a class variable to specify packages that should be installed in an isolated virtualenv for that stage’s workers:
You can also override runtime_env at instantiation time using with_():
All three execution backends (XennaExecutor, RayDataExecutor, RayActorPoolExecutor) support per-stage runtime environments. See the Per-Stage Runtime Environments reference for details.
Configuration with with_()
with_() deep-copies a stage and configures the copy without mutating the original. It supports portable properties and backend-specific overrides:
Both specs are shown together here only to document the arguments. In practice you set the one matching the executor you run the pipeline with — ray_stage_spec for Ray Data, xenna_stage_spec for Xenna:
Setting both is harmless — each executor reads only its own spec — but it is only useful if the same stage runs under both backends.
ray_stage_spec and xenna_stage_spec are shallow-merged: user-provided top-level keys win, while nested dictionaries are replaced rather than recursively merged. An explicit None inside a stage-spec dictionary is retained. In contrast, passing the whole ray_stage_spec=None or xenna_stage_spec=None means no override.
num_workers and num_workers_per_node use unset sentinels internally, so omission and explicit None differ:
- Omitting either argument preserves that method’s current result.
- Passing
Noneexplicitly resets that method to executor-controlled behavior.
See Stage Worker Sizing for merge examples, precedence rules, and invalid combinations. See Per-Stage Runtime Environments for dependency isolation.