Skip to content

Run Modes & Backends

Pipelex separates what the inference leaves do (the run mode) from where the work executes (the backend). The two axes are independent: any run mode works on any backend, because the run mode travels with every inference job and is honored at the leaf — the last stop before a provider would actually be called.

Axis 1: Run Mode

Run mode CLI trigger Python trigger AI providers Usage & cost report
Live (default) Real calls Real usage, report rendered
Dry run --dry-run pipe_run_mode=PipeRunMode.DRY Never called — leaves build mocks Zero tokens, report suppressed

Live runs call the configured AI providers for real: real cost, real latency, and storage IO for generated images and extracted pages.

Dry run exercises the full pipeline — controllers, working memory, data flow, execution-graph tracing — with no AI cost. Each inference leaf branches to a format-compliant mock instead of calling the provider (see Dry Run Mock Generation). A dry run needs no API keys, performs no storage IO (mocked images and pages never touch the storage provider), reports zero-token usage, and suppresses the end-of-run cost report. Add --mock-inputs to also synthesize any missing required inputs (it requires --dry-run).

Axis 2: Backend

Backend Trigger What runs where
Direct (default) Everything in your process
Temporal --orchestrator temporal, or config plugins.boot_orchestrator = "temporal" Controllers as child workflows, inference leaves as activities on workers

Durable backends are a commercial capability

The Temporal backend is part of Pipelex's durable & distributed execution offer — the Temporal backend is delivered through the Pipelex platform. The --orchestrator <name> flag (and its plugins.boot_orchestrator config equivalent) boots the process under the named orchestrator plugin; omit it for in-process execution.

The run mode rides across the wire with every inference job, so the backend never changes what a leaf does — only where it does it. In particular, when you run a pipeline in dry mode on Temporal, the real inference activities are still dispatched and mock inside them on the worker. That's by design: it lets you test the entire distribution machinery — dispatch, scheduling, serialization, task-queue routing, cross-worker graph tracing — with zero AI spend and no credentials.

One job type deliberately opts out of this fan-out: a validation sweep submitted through the runner API runs as a single in-process activity instead of dispatching nested activities — see Validation Sweeps below for how that fits the model.

The Matrix

All cells below describe pipeline runspipelex run ... or PipelexMTHDSProtocol.execute(). Validation sweeps have their own distribution shape, covered in the next section.

Run mode \ Backend Direct (in-process) Temporal
Live Standard local run. Durable distributed run; real inference executes on workers.
Dry run Leaves mock inline. Validates pipeline logic and data flow at zero cost. Activities are genuinely dispatched and mock inside the worker — proves out the distribution machinery with no API keys, no spend, no storage IO.
# Live + direct (the default)
pipelex run bundle my_bundle.mthds

# Dry run + direct
pipelex run bundle my_bundle.mthds --dry-run --mock-inputs

# Live + Temporal
pipelex run bundle my_bundle.mthds --orchestrator temporal

# Dry run + Temporal — dispatches real activities, mocks inside them
pipelex run bundle my_bundle.mthds --orchestrator temporal --dry-run --mock-inputs

From Python, the same knobs live on the runner — the backend comes from config:

from pathlib import Path

from pipelex.pipe_run.pipe_run_mode import PipeRunMode
from pipelex.pipeline.runner import PipelexMTHDSProtocol

runner = PipelexMTHDSProtocol(pipe_run_mode=PipeRunMode.DRY)
response = await runner.execute(
    mthds_contents=[Path("path/to/my_bundle.mthds").read_text(encoding="utf-8")],
)

Validation Sweeps

pipelex validate (see the validate commands) combines static validation with a dry run of every pipe. It relates to the matrix in two specific ways:

  • The CLI sweep always runs in-process, even when Temporal is enabled. The --orchestrator temporal flag on pipelex validate exists precisely to verify that: it boots validation under the orchestrator plugin so you can confirm a validation sweep does not dispatch anything under a Temporal-enabled setup.
  • The runner API's /validate route ships the sweep to a worker — as one activity: with Temporal enabled, it offloads the whole sweep as a single activity (act_dry_validate, wrapped in the WfDryValidate workflow). Inside that activity, the pipe router and the content generator are scoped to their in-process variants, so nothing nested is dispatched: every dry run in the sweep behaves exactly like the dry × direct cell of the matrix, just hosted on a worker instead of your machine. The per-pipe statuses plus the execution graph come back in one round-trip. See Runtime Bridge & Transport for how a job crosses the worker boundary.

This is not a contradiction of "the run mode never changes the backend" — it's the same leaf-mock foundation with a different backend choice for the nested work. A --orchestrator temporal --dry-run pipeline run picks the Temporal backend all the way down, fanning out for real to test the distribution machinery. A Temporal validation sweep picks the Temporal backend for exactly one hop (get the sweep onto a worker) and the in-process backend for everything inside, because its goal is a cheap interactive answer, not distribution testing. Two deliberate opposites built from the same parts.

Forced Dry Mode

When a process boots without inference configured — Pipelex.make(needs_inference=False), e.g. no AI provider set up — every run that process initiates is forced to dry-run mode. This is a property of the run, not of the backend: under a Temporal-enabled boot the forced-dry run still dispatches activities and mocks inside them, like any other dry run. A warning logs whenever a live-requested run is forced dry.

See Also