Quickstart
This guide assumes that recp is installed (see Installation). It shows how to write and run a minimal recipe, and then some typical pipelines.
Minimal recipe
recp works by parsing a recipe file describing the steps you want to run. A recipe file is a .yaml file with a defined structure. A minimal example is shown below:
recipe:
first_step:
run:
- echo "Hello, recp!"
second_step:
run:
- python -c "from datetime import datetime; print('The current year is', datetime.now().year)"
In this example:
recipeis the root key specifying your recipe.first_stepandsecond_stepare the steps of your recipe.yamlfile. A recipe file can contain multiple steps.runis the key specifying what commands are run for each step. A single step can have multiple commands.
To run this recipe, save it as minimal.yaml and run it as follows:
If you run it, you should see the following output:
Using recipe '/path/to/your/recipe/minimal.yaml' ...
2 step(s) found
Pre-processing recipe data ...
Recipe pre-processing successfully completed
Parsing step environments ...
Step environments processing successfully completed
Pre-processing recipe commands ...
Recipe commands successfully completed
Running commands ...
[1/2] Running step 'first_step' ...
Command: echo "Hello, recp!"
Hello, recp!
[2/2] Running step 'second_step' ...
Command: python -c "from datetime import datetime; print('The current year is', datetime.now().year)"
The current year is 2026
Summary: 2 step(s) completed, 0 failed, 0 not run
This output shows how your recipe file was parsed and then run step by step. So far it looks like a .sh would suffice to do this with less verbosity. However, recp offer many more options that make it stand out and be useful for automating more complex flows comprised of multiple steps.
Additional features
Let's take our example one step further by adding some additional instructions to our .yaml recipe file:
minimum_required_version: 0.4.0
recipe:
first_step:
env:
YOUR_NAME: !input
name: MY_NAME
default: Juhani
run:
- echo "Hello, my name is $YOUR_NAME"
second_step:
run:
- cmd: echo "The current year is {YEAR}"
apply:
- fn: date
args:
token: "{YEAR}"
format: "%Y"
Here we can observe a few new features being introduced:
-
first_stepnow has anenvkey. This lets you define environment variables for that step. We defineYOUR_NAME, which reads from theMY_NAMEenvironment variable. IfMY_NAMEis not set,YOUR_NAMEdefaults toJuhani. The!inputdirective allows capturing this variable from the terminal when the recipe is run usingrecp.
In therunsection, we use this variable as$YOUR_NAME. This shows how to capture and use environment variables. -
second_stepnow has an expanded command in therunkey. The command is modified using theapplykey. Modifiers are functions that take the command incmdand process it to produce one or more new commands. There are many built-in modifiers, and you can also create your own. This lets you generate multiple commands automatically without writing each one manually -
There is a top level
minimum_required_versionthat you can use to prevent running your recipes with outdatedrecpversions.
To run this recipe, update your previous minimal.yaml file, and run it as follows:
You should see the following output:
Using recipe '/path/to/your/recipe/minimal.yaml' ...
2 step(s) found
Pre-processing recipe data ...
Recipe pre-processing successfully completed
Parsing step environments ...
Step environments processing successfully completed
Pre-processing recipe commands ...
Recipe commands successfully completed
Running commands ...
[1/2] Running step 'first_step' ...
Command: echo "Hello, my name is Olavi"
Hello, my name is Olavi
[2/2] Running step 'second_step' ...
Command: echo "The current year is 2026"
The current year is 2026
Summary: 2 step(s) completed, 0 failed, 0 not run
Here you can see that both steps run correctly and show the expected values. The environment variable MY_NAME and the date modifier are both replaced as intended. If MY_NAME is not set, the default value Juhani is used.
For the full structure of a recipe and the list of constructors and modifiers, see the Reference.
Example pipelines
The examples below show how the features of recp combine in typical pipelines. They can be used as a starting point for your own recipes.
Processing a dataset file by file
This recipe resamples all .wav files of a corpus to 16 kHz, keeping the folder structure, and then extracts features from the result. Files that were already resampled are skipped, so the recipe can be run again after it stops or after new files are added to the corpus.
minimum_required_version: 0.4.0
env:
CORPUS_DIR: !input {name: CORPUS_DIR, required: true}
OUTPUT_DIR: !input {name: OUTPUT_DIR, default: output}
recipe:
resample:
description: Resample all files to 16 kHz
parallel: auto
run:
- cmd: mkdir -p $OUTPUT_DIR/16k/__REL_DIR__ && sox __FILE__ -r 16000 $OUTPUT_DIR/16k/__REL_DIR__/__STEM__.wav
skip_if_exists: $OUTPUT_DIR/16k/__REL_DIR__/__STEM__.wav
apply:
- fn: dir_files
args:
token: __FILE__
dir: $CORPUS_DIR
ext: wav
stem_token: __STEM__
rel_dir_token: __REL_DIR__
features:
description: Extract features
run:
- python extract_features.py $OUTPUT_DIR/16k $OUTPUT_DIR/features
Since the resample step has a parallel key, recp shows a live table with the status of each file while it runs, and recp follow <id> streams the output of one command from another terminal. See Parallel commands for details.
Running on a cluster
The same recipe can be split across the jobs of a SLURM array job with the shard modifier. Each job processes a different subset of the files:
- cmd: sox __FILE__ -r 16000 $OUTPUT_DIR/16k/__STEM__.wav
apply:
- fn: dir_files
args:
token: __FILE__
dir: $CORPUS_DIR
ext: wav
stem_token: __STEM__
- fn: shard
args:
index: $SLURM_ARRAY_TASK_ID
num: 20
Hyperparameter sweeps
This recipe trains every combination of models and seeds, and stores the results of each run in a folder named after the date and time when the recipe was started.
minimum_required_version: 0.4.0
env:
RUN_DIR: !now "runs/%Y%m%d-%H%M"
COMMIT: !shell git rev-parse --short HEAD
recipe:
train:
run:
- cmd: python train.py --model __MODEL__ --seed __SEED__ --output $RUN_DIR/__MODEL__-__SEED__ --commit $COMMIT
apply:
- fn: cartesian_product
args:
__MODEL__: [small, base, large]
__SEED__: [0, 1, 2]
evaluate:
run:
- python evaluate.py $RUN_DIR