Parallel Execution

Run independent workflow steps concurrently with DAG-based execution.

Overview

RelayPlane workflows are internally represented as Directed Acyclic Graphs (DAGs). Steps that don't depend on each other automatically execute in parallel, maximizing throughput without any additional configuration.

Automatic Parallelism

When steps share the same dependencies (or no dependencies), they run concurrently:

1import { relay } from "@relayplane/sdk";
2
3const result = await relay
4 .workflow("document-analysis")
5
6 // Step 1: Initial extraction (runs first)
7 .step("extract")
8 .with("openai:gpt-4o")
9 .prompt("Extract key information from: {{input.document}}")
10
11 // Steps 2a, 2b, 2c run IN PARALLEL (all depend only on "extract")
12 .step("sentiment")
13 .with("anthropic:claude-sonnet-4-20250514")
14 .depends("extract")
15 .prompt("Analyze sentiment of: {{steps.extract.output}}")
16
17 .step("entities")
18 .with("openai:gpt-4o")
19 .depends("extract")
20 .prompt("Extract entities from: {{steps.extract.output}}")
21
22 .step("summary")
23 .with("openai:gpt-4o-mini")
24 .depends("extract")
25 .prompt("Summarize: {{steps.extract.output}}")
26
27 // Step 3: Final combination (waits for all parallel steps)
28 .step("combine")
29 .with("openai:gpt-4o")
30 .depends("sentiment", "entities", "summary")
31 .prompt("Combine analysis results into final report")
32
33 .run({ document: "..." });
In this example, sentiment,entities, andsummary all start as soon as extract completes.

Common Patterns

Fork Pattern

One step fans out to multiple parallel steps:

1 ┌─── B ───┐
2A ──┼─── C ───┼── E
3 └─── D ───┘

Diamond Pattern

Parallel branches that reconverge:

1 ┌─── B ───┐
2A ──┤ ├── D
3 └─── C ───┘

Complex DAG

Multiple independent parallel sections:

1A ──┬─── B ───┬── D ──┬─── F
2 │ │ │
3 └─── C ───┘ └─── G

Specifying Dependencies

Use .depends() to declare step dependencies:

1// Single dependency
2.step("validate")
3.depends("extract")
4
5// Multiple dependencies (step waits for ALL to complete)
6.step("combine")
7.depends("sentiment", "entities", "summary")
8
9// No dependencies (runs immediately or in parallel with other independent steps)
10.step("log-start")
11// (no .depends() call)

Execution Order Guarantees

The DAG executor guarantees:

  • Dependency ordering - A step never starts before all its dependencies complete
  • Maximum parallelism - All independent steps start as soon as possible
  • Cycle detection - Circular dependencies are caught at build time
  • Deterministic results - Same input always produces same execution plan
Circular dependencies (A → B → C → A) will throw an error when you call .run().

Performance Benefits

Parallel execution significantly reduces total workflow time:

1Sequential execution:
2 extract (2s) → sentiment (1s) → entities (1s) → summary (1s) → combine (1s)
3 Total: 6 seconds
4
5Parallel execution:
6 extract (2s) → [sentiment, entities, summary in parallel] (1s) → combine (1s)
7 Total: 4 seconds (33% faster)

Complete Example

1import { relay } from "@relayplane/sdk";
2
3const result = await relay
4 .workflow("multi-analysis")
5
6 // Two independent starting points (run in parallel)
7 .step("extract-text")
8 .with("openai:gpt-4o")
9 .prompt("Extract text from document")
10
11 .step("extract-images")
12 .with("openai:gpt-4o-vision")
13 .prompt("Extract and describe images")
14
15 // Parallel analysis of text
16 .step("text-sentiment")
17 .with("anthropic:claude-sonnet-4-20250514")
18 .depends("extract-text")
19
20 .step("text-topics")
21 .with("openai:gpt-4o")
22 .depends("extract-text")
23
24 // Image analysis (parallel with text analysis)
25 .step("image-classification")
26 .with("openai:gpt-4o-vision")
27 .depends("extract-images")
28
29 // Final combination
30 .step("final-report")
31 .with("openai:gpt-4o")
32 .depends("text-sentiment", "text-topics", "image-classification")
33 .prompt("Generate comprehensive report")
34
35 .run({ document: file });
36
37// Check execution timing
38console.log(`Total duration: ${result.durationMs}ms`);
39for (const step of result.steps) {
40 console.log(` ${step.stepName}: ${step.durationMs}ms`);
41}