Task Definitions
What Are Ribbon Tasks?
Ribbon uses a companion repository called Ribbon-Tasks, which contains the ribbon_tasks
Python module. This module defines custom Task
objects that Ribbon can run—both locally and when queueing jobs remotely.
Ribbon dynamically imports this module at runtime, allowing you to define and reuse custom tasks flexibly. To point Ribbon to your copy of the Ribbon-Tasks repo, set the $RIBBON_TASKS_DIR
environment variable. For details, see Customizing Your Tasks.
Structure of a Ribbon Task
A Ribbon Task is defined by three components in the ribbon_tasks
module:
tasks.py
— Python wrapper for each Tasktasks.json
— Defines the shell command and container to use for each Taskcontainers.json
— Maps container names to their local filenames and remote sources
Let’s walk through each component.
1. tasks.py
: Writing the Python Wrapper
Each Task is defined as a class that inherits from Task
. The class must:
- Define
__init__()
to accept user inputs - Set the
task_name
to match its entry intasks.json
- Call
super().__init__()
to initialize the baseTask
class - Store user inputs as instance variables
- Implement a
run()
method that calls_run_task()
with named arguments
Here's an example for the Chai-1
Task:
Example: Chai-1 Task Class
class Chai1(Task):
def __init__(self, fasta_file, output_dir='.', smiles_string=None, num_ligands=1, device='gpu'):
super().__init__()
self.task_name = "Chai-1"
self.fasta_file = fasta_file
self.output_dir = output_dir
self.smiles_string = smiles_string
self.num_ligands = num_ligands
self.device = device
def run(self):
self.output_dir = make_directory(self.output_dir)
self._run_task(
self.task_name,
fasta_file=self.fasta_file,
smiles_string=self.smiles_string,
output_dir=str(self.output_dir),
num_ligands=self.num_ligands,
device=self.device
)
Notes on _run_task()
- This method executes the actual job by filling in the command template from
tasks.json
using the provided arguments. - Only variables defined in
tasks.json
can be passed into_run_task()
. - You can optionally pass an
extra_args
string for additional software-specific flags. Be cautious: this can inject arbitrary code if misused. - Any lightweight pre/post-processing (like file creation or renaming) can also be handled in
run()
. For heavier logic, use a separate script and invoke it from the command string.
2. tasks.json
: Defining the Command and Environment
This JSON file defines each Task’s metadata, including:
- The name and description
- The container to use
- Environment variables (if any)
- The shell command to run
Example: Chai-1 Task Definition
"Chai-1": {
"name": "Chai-1",
"description": "Run Chai-1 structure prediction on a FASTA file and SMILES.",
"container": "Chai-1",
"command": "python $RIBBON_TASKS_MODULE_DIR/task_scripts/chai-1/predict_structure.py {fasta_file} \"{smiles_string}\" {output_dir} --num_ligands {num_ligands} {extra_args}",
"requirements": "None",
"environment_variables": {
"TRANSFORMERS_OFFLINE": "1"
}
}
Command String
The command
field is a string template. Curly-brace {}
placeholders correspond to argument names passed into _run_task()
.
For example:
_run_task(fasta_file=self.fasta_file, output_dir=self.output_dir)
...would fill in {fasta_file}
and {output_dir}
in the command string.
Using Custom Scripts
Instead of calling software directly, your command can invoke a custom Python script stored under task_scripts/<container_name>/
. For example:
python $RIBBON_TASKS_MODULE_DIR/task_scripts/chai-1/predict_structure.py ...
$RIBBON_TASKS_MODULE_DIR
is automatically set within the container to the location of theribbon_tasks
module (which is within theRibbon-Tasks
repo).
This approach keeps your Task class clean while allowing complex logic to live in a dedicated script.
3. containers.json
: Mapping Container Names
This file maps container names to a list containing:
- The local
.sif
filename (used when the container is downloaded) - The remote location (usually via ORAS / DockerHub)
Example: Chai-1 Container
"Chai-1": [
"chai-1_0.5.2.sif",
"oras://docker.io/nicholasfreitas/chai-1:0.5.2"
]
We recommend versioning containers explicitly to avoid compatibility issues—don’t use latest
. Both the remote tag and local filename should reflect the version number.