Tasks#

A task in EOS encapsulates an operation and can be thought of as a function. Tasks are the elementary building block in EOS. A task is ephemeral, meaning it is created, executed, and terminated. A task takes some inputs and returns some outputs, and may use one or more devices.

There are two kinds of inputs: parameters and containers.

  1. Parameters: Data such as integers, decimals, strings, booleans, etc that are passed to the task.

  2. Containers: Vessels that may contain one or more samples.

There are three kinds of outputs: parameters, containers, and files.

  1. Parameters: Data such as integers, decimals, strings, booleans, etc that are returned by the task.

  2. Containers: Vessels that may contain one or more samples.

  3. Files: Raw data or reports generated by the task, such as output files from analysis.

EOS Task Inputs and Outputs

Parameters#

Parameters are values that are input to a task or output from a task. Every parameter has a specific data type. EOS supports the following parameter types:

  • integer: An integer number. Equivalent to Python’s int

  • decimal: A decimal number. Equivalent to Python’s float

  • string: A string (series of text characters). Equivalent to Python’s str

  • boolean: A true/false value. Equivalent to Python’s bool

  • choice: A value that must be one of a set of predefined choices. The choices can be any type.

  • list: A list of values of a specific type. Equivalent to Python’s list.

  • dictionary: A dictionary of key-value pairs. Equivalent to Python’s dict.

Tasks can have multiple parameters of different types. EOS will ensure that the parameters passed to a task are of the correct type and have values that meet their constraints.

Containers#

Containers are referenced by a unique identifier called a container ID. A container ID is a string that uniquely identifies a container. Every container in EOS must have an ID, and these can be specified in the laboratory definition. Containers are treated as global objects and can move across labs. However, every container must have a “home” lab from which it originates.

In order to pass a container to a task or return a container from a task, its container ID is used. Every task may accept specific types of containers, such as beakers, or vials. Multiple different containers can be passed. Users can create their own types of containers, such as beaker_500ml or vial_2ml, which specify a unique container type. EOS will ensure that only container types that are compatible with the task are passed to it.

Files#

Files may be generated by a task and EOS will store them in an object storage (MinIO). Output files can be used to record raw data for future reference, and can be downloaded by the user.

Note

Files cannot currently be passed as inputs to tasks via the EOS runtime and its object storage. This is planned to be supported in the future. It is still possible to pass them using an external object storage (e.g., MinIO), but this has to be implemented and managed manually.

Task Implementation#

  • Tasks are implemented in the tasks subdirectory inside an EOS package

  • Each task has its own subfolder (e.g., tasks/magnetic_mixing)

  • There are two key files per task: task.yml and task.py

YAML File (task.yml)#

  • Specifies the task type, description, and input/output parameters and containers

  • Acts as the interface contract (spec) for the task

  • This contract is used to validate tasks, and EOS enforces the contract statically and dynamically during execution

  • Useful as documentation for the task

Below is an example task YAML file for a GC analysis task for GCs made by SRI Instruments:

task.yml

type: SRI GC Analysis
description: Perform gas chromatography (GC) analysis on a sample.

device_types:
  - sri_gas_chromatograph

input_parameters:
  analysis_time:
    type: integer
    unit: seconds
    value: 480
    description: How long to run the GC analysis

output_parameters:
  known_substances:
    type: dictionary
    description: Peaks and peak areas of identified substances
  unknown_substances:
    type: dictionary
    description: Peaks and peak areas of substances that could not be identified

The task specification makes clear that:

  • The task is of type “SRI GC Analysis”

  • The task requires a device of type “sri_gas_chromatograph”. EOS will enforce this requirement.

  • The task takes an input integer parameter analysis_time in seconds. It has a default value of 480, making this an optional parameter.

  • The task outputs two dictionaries: known_substances and unknown_substances.

Parameter Specification#

Parameters are defined in the input_parameters and output_parameters sections of the task YAML file.

Below are examples and descriptions for each parameter type:

Integer#

sample_rate:
  type: integer
  description: The number of samples per second
  value: 44100
  unit: Hz
  min: 8000
  max: 192000

Integers must have a unit (can be n/a) and can also have a minimum and maximum value.

Decimal#

threshold_voltage:
  type: decimal
  description: The voltage threshold for signal detection
  value: 2.5
  unit: volts
  min: 0.0
  max: 5.0

Decimals must have a unit (can be n/a) and can also have a minimum and maximum value.

String#

file_prefix:
  type: string
  description: Prefix for output file names
  value: "experiment_"

Boolean#

auto_calibrate:
  type: boolean
  description: Whether to perform auto-calibration before analysis
  value: true

Booleans are true/false values.

Choice#

column_type:
  type: choice
  description: HPLC column type
  value: "C18"
  choices:
    - "C18"
    - "C8"
    - "HILIC"
    - "Phenyl-Hexyl"
    - "Amino"

Choice parameters take one of the specified choices.

List#

channel_gains:
  type: list
  description: Gain values for each input channel
  value: [1.0, 1.2, 0.8, 1.1]
  element_type: decimal
  length: 4
  min: [0.5, 0.5, 0.5, 0.5]
  max: [2.0, 2.0, 2.0, 2.0]

List parameters are a sequence of values of a specific type. They can have a specific length and minimum and maximum per-element values.

Dictionary#

buffer_composition:
  type: dictionary
  description: Composition of a buffer solution
  value:
    pH: 7.4
    base: "Tris"
    concentration: 50
    unit: "mM"
    additives:
      NaCl: 150
      KCl: 2.7
      CaCl2: 1.0
    temperature: 25

Dictionaries are key-value pairs. The values can be any type.

Python File (task.yml)#

  • Implements the task

  • All task implementations must inherit from BaseTask

  • The task class name must end with “Task” to be discovered by EOS

task.py

from eos.tasks.base_task import BaseTask


class MagneticMixingTask(BaseTask):
    async def _execute(
        self,
        devices: BaseTask.DevicesType,
        parameters: BaseTask.ParametersType,
        containers: BaseTask.ContainersType,
    ) -> BaseTask.OutputType:
        magnetic_mixer = devices.get_all_by_type("magnetic_mixer")[0]
        mixing_time = parameters["mixing_time"]
        mixing_speed = parameters["mixing_speed"]

        containers["beaker"] = magnetic_mixer.mix(containers["beaker"], mixing_time, mixing_speed)

        return None, containers, None

Let’s walk through this example code:

_execute is the only required function in a task implementation. It is called by EOS to execute a task. The function takes three arguments:

  1. devices: A data structure supporting lookup of specific lab devices assigned to a task. In this case, only one device is given, a magnetic mixer. The devices are represented as wrappers to Ray actor references, and the task implementation can call functions from the device implementation.

  2. parameters: A dictionary of the input parameters. Keys are the parameter names and values are the parameter values.

  3. containers: A dictionary of the input containers. Keys are the container IDs and values are the Container objects.