Skip to content

Reuse a processing workflow with RecipePlan

When the same preprocessing steps must run on another recording, copying a method chain also copies its input assumptions into application code. A RecipePlan records the public Frame operations and their parameters separately from the data, so the same workflow can be inspected, stored, and applied to a new input.

This tutorial builds a small preprocessing workflow, turns it into a Recipe, and proves that replay produces the same result as calling the Frame methods directly.

Build the workflow once

Create a representative input and process it with ordinary public Frame methods. No special builder API is required.

Recipe input: signal Operations: ['wandas.audio.remove_dc', 'wandas.audio.normalize']

RecipePlan.from_frame() reads semantic lineage already attached by the public calls. One public call becomes one node, and the original sample values are not stored in the plan.

Store and load the portable schema

to_dict() returns a strict JSON-compatible schema. A JSON roundtrip demonstrates that the payload does not retain live Python operation objects. Loading resolves its stable operation IDs through the built-in registry. A plan does not retain a registry; extensions must pass the same immutable registry to from_frame(), from_dict(), and apply().

Schema: wandas.recipe 2 Serialized bytes: 411

Apply it to a new Frame

Runtime inputs are supplied by the names chosen during extraction. Applying a plan builds a lazy Frame workflow; numerical data is materialized only when this example reads frame.data to verify the result.

Replay matches direct calls: yes Runtime metadata: {'recording': 'next'} History entries: 2

The plan supplies operation intent; the runtime Frame supplies samples, metadata, labels, sampling rate, and source-time information. The input Frame remains unchanged.

Multiple inputs remain explicit

Frame arithmetic, mix(), and external NumPy or Dask operands become additional named inputs. Their values and container implementation are not embedded in the Recipe.

mixed = base.mix(other)
mix_plan = RecipePlan.from_frame(mixed, input_names=("base", "other"))
result = mix_plan.apply({"base": next_base, "other": next_other})

mix() combines samples by array index even when source-time offsets describe different source periods. Align recordings explicitly first when source-time matching is required.

Where to go next