Skip to content

Design the API

The API defines the contracts between all components in your task: what files each component reads, what files it writes, and what slots those files must contain. Defining the API before writing any component code keeps all components consistent and enables automated testing and documentation generation.

The structure of an OpenProblems task. Legend: grey rectangles are AnnData (.h5ad) files; purple parallelograms are Viash components.

DatasetloaderDatasetMethodOutputMetricScore
DatasetloaderDatasetMethodOutputMetricScore
The structure of an OpenProblems task. Legend: grey rectangles are AnnData (.h5ad) files; purple parallelograms are Viash components.

For a concrete reference, see the dimensionality reduction task as an example of a fully defined API.

A formal API:

  • Ensures all methods, control methods, and metrics share a consistent interface.
  • Allows Viash to auto-generate CLI argument parsers and unit tests.
  • Makes it straightforward to add new methods later without touching existing ones.
  • Documents exactly what data each component expects and produces.

Sketch the data flow of your task before writing any YAML. Identify which files carry information that methods should not see (the solution) and which files are safe to expose.

Common node types:

  • Common dataset - the source AnnData from the OpenProblems dataset collection.
  • Dataset processor - transforms the common dataset into task-specific files.
  • Dataset - task-specific input handed to every method.
  • Solution - held-out ground truth; only control methods and metrics may read it.
  • Method - candidate technique being benchmarked.
  • Control method - reference implementation (positive or negative control).
  • Output - predictions produced by a method or control method.
  • Metric - evaluates an output against the solution.
  • Score - scalar evaluation result.

Example: label projection task

Example of a label projection task. The processor splits the common dataset into train, test, and solution files. Methods receive train and test; the metric compares the method output against the solution.

CommondatasetDatasetprocessorTrainTestSolutionMethodControlmethodOutputMetricScore
CommondatasetDatasetprocessorTrainTestSolutionMethodControlmethodOutputMetricScore
Example of a label projection task. The processor splits the common dataset into train, test, and solution files. Methods receive train and test; the metric compares the method output against the solution.

Example: multimodal task

Example of a multimodal task. The processor outputs two modality files and a solution. Methods receive both modalities; the metric compares the method output against the solution.

CommondatasetDatasetprocessorRNA countsADT countsSolutionMethodControlmethodOutputMetricScore
CommondatasetDatasetprocessorRNA countsADT countsSolutionMethodControlmethodOutputMetricScore
Example of a multimodal task. The processor outputs two modality files and a solution. Methods receive both modalities; the metric compares the method output against the solution.

For each AnnData file in your diagram, create a corresponding file_*.yaml in src/api/. Start with a minimal skeleton:

src/api/file_solution.yaml
type: file
example: "resources_test/<task_id>/cxg_mouse_pancreas_atlas/solution.h5ad"
label: Solution
summary: "FILL IN: what this file represents"
info:
format:
  • type: file marks this as a file format definition.
  • example points to a small test resource that is used in unit tests.
  • label is the human-readable display name.
  • summary is a one-sentence description used in generated documentation.
  • info.format holds the slot definitions (filled in Step 4).

Create one file per AnnData node in your diagram: file_dataset.yaml, file_train.yaml, file_test.yaml, file_solution.yaml, file_output.yaml, file_score.yaml, and any others your task requires.

For each component type in your diagram, create a corresponding comp_*.yaml in src/api/. A minimal method component definition looks like:

src/api/comp_method.yaml
namespace: methods
info:
type: method
type_info:
label: "Method"
summary: "FILL IN: one-sentence description of what a method does in this task"
description: "FILL IN: longer description"
arguments:
- name: "--input"
__merge__: file_dataset.yaml
required: true
- name: "--output"
__merge__: file_prediction.yaml
required: true
direction: output
  • namespace must match the directory under src/ (e.g. methods for src/methods/).
  • info.type identifies the component role (method, control_method, or metric).
  • Each argument uses __merge__ to import the slot definitions from the matching file_*.yaml.
  • direction: output marks arguments that are written by the component.

Create one file per component type: comp_dataset_processor.yaml, comp_method.yaml, comp_control_method.yaml, comp_metric.yaml.

Once you know what data each file carries, add slot definitions to the info.format section of each file_*.yaml. A fuller example:

src/api/file_dataset.yaml
type: file
example: "resources_test/<task_id>/cxg_mouse_pancreas_atlas/dataset.h5ad"
label: Dataset
summary: "Task-specific dataset passed to every method."
info:
format:
type: h5ad
layers:
- name: counts
type: integer
description: "Raw UMI counts."
required: true
obs:
- name: cell_type
type: string
description: "Cell type annotation."
required: true
- name: batch
type: string
description: "Batch identifier."
required: false
var:
- name: gene_id
type: string
description: "Ensembl gene identifier."
required: true
obsm:
- name: X_pca
type: double
description: "PCA embedding (50 components)."
required: false
uns:
- name: dataset_id
type: string
description: "Identifier of the source dataset."
required: true

Each slot entry uses:

  • name - the key in the AnnData object.
  • type - data type (integer, double, string, boolean).
  • description - what this slot contains, used in generated docs.
  • required - whether a component must produce or can rely on this slot being present.