How to add a new pipeline#

ViPPET supports two kinds of pipelines:

  • Built-in (predefined) pipelines: shipped with the backend image and loaded at startup from YAML files in vippet/pipelines/. All their variants are marked read_only=true.

  • User-created pipelines: created at runtime through the REST API (POST /api/v1/pipelines) or from the Pipelines page in the UI. They are kept in-memory by the singleton PipelineManager, are editable, and disappear when the backend restarts.

This page explains how to add both. In day-to-day contribution work, “adding a new pipeline” almost always means adding a YAML file under vippet/pipelines/, that is the path covered first and in most detail.

You can use the example pull request adding the Defect Detection pipeline as a real-world reference.

A. Add a built-in pipeline (YAML)#

1. Create the YAML file#

Drop a new file into vippet/pipelines/. The filename (without extension) becomes part of the on-disk identifier; use kebab-case, for example vehicle-detection.yaml. PipelineLoader.list() auto-discovers every *.yaml file in this directory at startup, no registration code is needed.

2. Required structure#

name: Vehicle Detection           # required, non-empty
definition: >                     # optional human-readable description
  Short, user-facing description of what the pipeline does and where it fits.
tags:                             # optional list of strings
  - Traffic
  - Detection
thumbnail: images/vehicle-detection.png   # optional; path relative to vippet/pipelines/
variants:                         # required, at least one
  - name: CPU                     # required, non-empty (trimmed)
    pipeline_description: >-
      filesrc location=/videos/input/sample.mp4 !
      decodebin3 !
      gvadetect
        model=/models/output/.../model.xml
        device=CPU
        ... !
      queue !
      gvafpscounter starting-frame=100 !
      gvawatermark !
      gvametaconvert !
      queue !
      gvametapublish file-format=json-lines file-path=/dev/null !
      fakesink name=default_output_sink sync=false async=false
  - name: GPU
    pipeline_description: >-
      ...
  - name: NPU
    pipeline_description: >-
      ...

Field reference (enforced by PipelineManager.load_predefined_pipelines()):

Field

Required

Notes

name

Yes

Non-empty after trimming. The pipeline ID is derived from it.

definition

No

Free-text description shown in the UI. May be empty.

tags

No

List of strings. Anything that is not a list is ignored and replaced with [].

thumbnail

No

PNG/JPEG path relative to vippet/pipelines/. Loaded and inlined as base64.

variants

Yes

At least one entry. Read-only at runtime.

variants[].name

Yes

Non-empty after trimming. Variant ID is derived from it.

variants[].pipeline_description

Yes

Non-empty after trimming. A standard gst-launch-style description.

3. Pipeline description conventions#

The string in pipeline_description is parsed by Graph.from_pipeline_description() (see vippet/graph.py) and must follow the same conventions used by the other built-in pipelines:

  • Separate elements with !.

  • The pipeline must end with a fakesink that the runner can promote to the main output. The recommended pattern is fakesink name=default_output_sink sync=false async=false. The default_output_sink marker explicitly identifies the user-facing sink and is required when more than one fakesink is present.

  • Reference models by absolute path inside the container (/models/output/...). Add the matching model-proc JSON when needed.

  • Reference videos by absolute path inside the container (/videos/input/<file>). At parse time the backend rewrites these to filenames, so the pipeline remains portable across hosts.

  • Provide one variant per supported device target. The other built-in pipelines use the following naming convention: CPU, GPU, NPU, GPU_NPU. Use whichever subset makes sense for the pipeline, only the variants you actually ship are exposed.

  • Insert queue elements where you would do so in a normal DLStreamer pipeline (after inference, before sinks, around tee branches).

  • For metadata-only pipelines (no rendering) gvametaconvert then gvametapublish then fakesink is the standard tail.

4. Models and videos#

  • Add any new models to the supported models catalog so they can be installed at runtime by the model-download service. The catalog resolution is driven by SUPPORTED_MODELS_FILE (default /models/supported_models.yaml).

  • Sample input videos must be downloadable via the recordings YAML (DEFAULT_RECORDINGS_FILE, default /videos/default_recordings.yaml) or uploaded by the user, never commit binary media to the repo.

  • If a referenced model is not installed, PipelineManager clears the model field on load and surfaces a clear error when the user tries to run the pipeline.

5. Add a thumbnail#

Place a small PNG (or JPEG) under vippet/pipelines/images/ and reference it via thumbnail:. It is loaded as base64 and surfaced through the API so the UI can display it on the Pipelines page.

6. Test it#

Add or extend a unit test in vippet/tests/unit/managers_tests/pipeline_manager_test.py, at minimum make sure the new YAML is loaded successfully by PipelineManager.load_predefined_pipelines() (covered by the test_load_predefined_pipelines test, which iterates every YAML in the directory). Then run:

make test
make lint

Finally, start the stack with make run-dev and verify in the UI that:

  • the pipeline appears on the Pipelines page with the right name, description, tags and thumbnail,

  • both the Simple and Advanced views render correctly,

  • each variant can be executed against the matching device.

B. Add a user-created pipeline at runtime#

User-created pipelines are not committed to the repository, they are created by API consumers. Contributors typically interact with this path only when adding tests, examples or UI flows.

REST API#

POST /api/v1/pipelines
Content-Type: application/json

{
  "name": "vehicle-detection",
  "description": "Simple vehicle detection pipeline",
  "tags": ["detection", "vehicle"],
  "variants": [
    {
      "name": "CPU",
      "pipeline_graph":        { "nodes": [...], "edges": [...] },
      "pipeline_graph_simple": { "nodes": [...], "edges": [...] }
    }
  ]
}

Behavior (see vippet/api/routes/pipelines.py::create_pipeline):

  • The source field is forced to USER_CREATED by the route; any client value is ignored.

  • The pipeline ID, variant IDs, and created_at / modified_at timestamps are generated server-side.

  • All variants are stored with read_only=false.

  • User-created pipelines have thumbnail=null by default.

  • Successful creation returns 201 with the generated id.

After creation, additional variants can be added with POST /api/v1/pipelines/{pipeline_id}/variants, properties patched with PATCH /api/v1/pipelines/{pipeline_id}, and the simple / advanced graph views converted into each other through POST /api/v1/convert/....

Full request/response schemas and examples are available in the auto-generated OpenAPI docs at http://localhost:7860/docs. If you change any of these endpoints, remember to regenerate both the schema and the UI client, see Backend - API schema and clients.