ML-PIPES

Architecture

Following up on design.md, this document is a map of how ml-pipes is split internally. It focuses on ownership boundaries: what each layer owns, how one invocation moves through the system, and where a change should usually land.

It is not a guide to using ml-pipes features. The detailed user-facing semantics live in the linked docs.

Overview

Architecturally, ml-pipes is a small generic execution harness around explicit operator boundaries.

The architecture stays split on purpose:

The architectural goal is to provide useful pipeline features while keeping ordinary operators as simple as possible. In the normal case, an operator should only need to define its boundary and its task logic; composition, validation, inspection, tracing, benchmarking, and other cross-cutting behavior should be handled by the surrounding system.

That keeps the core engine small:

input value + fresh context
  -> operator boundary
  -> operator boundary
  -> bounded region (optional)
  -> operator boundary
  -> output value

The pipeline engine itself is payload-agnostic. Image payloads, tensor payloads, registries, and data-preparation values are ordinary operator inputs and outputs, not special pipeline modes.

Main Components

Pipeline

The pipeline engine owns:

It does not own:

Operators

Operators are the unit the engine composes and the unit the tooling sees.

Architecturally, that means:

For operator boundaries and creation guidelines, see operators.md.

Context

Context is the side-channel state model. It exists so a pipeline can keep one clear flowing value while still storing values that need to be recovered later.

The important properties are:

Regions

Regions sit between operators and the pipeline engine. They handle execution strategies that are still generic enough to reuse, but too structural to bury inside ordinary operators and too use-case-specific to hard-code into Pipeline.

A region:

Regions are embedded in the same operator list; they do not create a separate pipeline type. Batch/UnBatch and Scatter/Gather are the main built-in examples.

For region semantics and examples, see regions.md.

Composition

Composition is essentially arranging operators into one larger pipeline. ml-pipes supports two ways to do that, and they imply different runtime boundaries:

This distinction matters architecturally because it changes both runtime isolation and what the tooling can see as one step.

For the user-facing composition model, see composition.md.

Execution Flow

One pipeline invocation follows the same path whether or not extra tooling is attached:

  1. A top-level call starts with an input value, a fresh Context, and optionally a trace object.
  2. The engine walks the operator list left to right.
  3. Normal operators receive the current value. If one step returns a fixed-length tuple and the next step expects multiple positional inputs, that tuple is routed by position.
  4. ContextOp steps receive both the current value and the current Context, and return updated versions of them.
  5. When the pipeline reaches a region, it delegates execution of the enclosed operators to that region as one bounded unit.
  6. If tracing is enabled, spans are recorded for the same operator and region boundaries the engine executes.
  7. After the call completes or fails, the finished trace is delivered to the collector.

That is why validation, inspection, tracing, and benchmarking all line up with operator boundaries rather than a separate internal graph.

Tooling

Tooling in ml-pipes is layered on top of the same execution and boundary model. Most tools do not introduce a new executor. They reuse pipeline execution, operator descriptions, or trace data. Tracing is the exception: it is built into pipeline execution itself and then supports tools such as inspection and benchmarking.

Validation

Validation is the static safety layer over the runtime model. It checks whether a pipeline is structurally and type-wise coherent before runtime, without executing the pipeline.

Built on top of:

Main components:

For the concrete validation rules and contract-resolution model, see validation.md.

Tracing

Tracing is the runtime observation layer. It observes runtime behavior without changing the pipeline structure. It is built into pipeline execution rather than layered on top of it, and other tools, especially inspection and benchmarking, build on top of it.

Built into:

Main components:

For trace lifecycle, built-in collectors, and custom collector patterns, see tracing.md.

Inspection

Each operator creates an explicit data boundary. Inspection builds on tracing at those boundaries: while a pipeline runs, tracing records a span for each operator, and Pipeline.inspect() captures that operator's output in the span. The resulting InspectionResult shows how values move through the operator chain.

PipelineInspector turns the captured artifact into browser output through formatters and renderers. Core owns the shared inspection artifact, registry, and renderers, while package-owned types register their specialized formatters from the owning package at import time.

Built on top of:

Main components:

For inspection setup and user-facing APIs, see inspection.md.

Benchmarking

Benchmarking is built on top of tracing. It measures repeated-run behavior and compares variants or configurations without introducing a different runtime.

Built on top of:

Main components:

For benchmarking workflows, sweeps, and CLI usage, see benchmarking.md. For tuning guidance, see performance.md.

Pipeline Description

Pipeline description is a standalone structural tool. It surfaces pipeline structure without executing the pipeline. Pipeline.describe() and repr(pipeline) are the public entry points; both are thin surfaces over the same structural description model.

Built on top of:

Main components:

Factories

Factories adapt configuration into concrete pipelines or input builders. They package construction logic without changing how pipeline execution works.

Built on top of:

Main components:

CLI

The CLI is a thin interface layer that exposes ml-pipes features as commands. It does not introduce a separate execution model. It mainly loads references, resolves factories, parses config, and invokes existing pipeline and benchmarking surfaces.

Built on top of:

Main components:

Where To Go Next

This document has mapped how ml-pipes is split internally. If you want the rationale behind these boundaries, return to design.md.

If your next step is building with the framework, continue to scaffolding.md or the runnable examples in examples/README.md.