Conditional Branching

Skip steps dynamically based on runtime conditions using JavaScript expressions.

Overview

Conditional branching allows you to skip steps based on the output of previous steps or the initial workflow input. This enables dynamic workflow routing without complex orchestration code.

Basic Usage

Add a condition to any step to control whether it executes:

1import { relay } from "@relayplane/sdk";
2
3const result = await relay
4 .workflow("ticket-router")
5 .step("classify")
6 .with("openai:gpt-4o")
7 .prompt("Classify this ticket as 'urgent' or 'normal': {{input.text}}")
8
9 // Only runs if classification is urgent
10 .step("escalate", {
11 condition: "steps.classify.output.category === 'urgent'"
12 })
13 .with("anthropic:claude-sonnet-4-20250514")
14 .depends("classify")
15 .prompt("Generate escalation message for: {{input.text}}")
16
17 // Only runs if classification is normal
18 .step("auto-reply", {
19 condition: "steps.classify.output.category === 'normal'"
20 })
21 .with("openai:gpt-4o-mini")
22 .depends("classify")
23 .prompt("Generate auto-reply for: {{input.text}}")
24
25 .run({ text: "My server is on fire!" });

Condition Syntax

Conditions are JavaScript expressions with access to:

  • input - The workflow's initial input
  • steps - Object containing outputs from completed steps
1// Access workflow input
2condition: "input.priority === 'high'"
3
4// Access step output
5condition: "steps.classify.output.confidence > 0.8"
6
7// Complex conditions
8condition: "steps.extract.output && steps.extract.output.total > 1000"
9
10// String matching
11condition: "steps.classify.output.category === 'urgent'"
12
13// Array checks
14condition: "steps.extract.output.items.length > 0"
15
16// Boolean logic
17condition: "steps.check.output.isValid && input.autoApprove"
Conditions must return a truthy or falsy value. If a condition throws an error, the step is skipped and the error is logged.

Common Patterns

If/Else Pattern

1// Mutually exclusive branches
2.step("handle-yes", {
3 condition: "steps.decide.output.answer === 'yes'"
4})
5.depends("decide")
6
7.step("handle-no", {
8 condition: "steps.decide.output.answer === 'no'"
9})
10.depends("decide")

Threshold Pattern

1// Only process if confidence is high enough
2.step("process", {
3 condition: "steps.analyze.output.confidence >= 0.9"
4})
5.depends("analyze")
6
7// Fallback for low confidence
8.step("manual-review", {
9 condition: "steps.analyze.output.confidence < 0.9"
10})
11.depends("analyze")

Feature Flag Pattern

1// Optional step based on input flag
2.step("enhanced-analysis", {
3 condition: "input.enableEnhancedAnalysis === true"
4})
5.depends("basic-analysis")

Handling Skipped Steps

When a step is skipped, it appears in the result with a skipped flag:

1const result = await workflow.run(input);
2
3for (const step of result.steps) {
4 if (step.skipped) {
5 console.log(`Step ${step.stepName} was skipped: ${step.skipReason}`);
6 } else {
7 console.log(`Step ${step.stepName} completed with output:`, step.output);
8 }
9}

Complete Example

1import { relay } from "@relayplane/sdk";
2
3const result = await relay
4 .workflow("document-processor")
5
6 // Step 1: Classify the document
7 .step("classify")
8 .with("openai:gpt-4o")
9 .prompt("Classify this document type: invoice, contract, or other")
10
11 // Step 2a: Process invoices
12 .step("extract-invoice", {
13 condition: "steps.classify.output.type === 'invoice'"
14 })
15 .with("openai:gpt-4o")
16 .depends("classify")
17 .prompt("Extract invoice fields: vendor, amount, date, line items")
18
19 // Step 2b: Process contracts
20 .step("extract-contract", {
21 condition: "steps.classify.output.type === 'contract'"
22 })
23 .with("anthropic:claude-sonnet-4-20250514")
24 .depends("classify")
25 .prompt("Extract contract fields: parties, terms, dates")
26
27 // Step 2c: Handle unknown documents
28 .step("flag-unknown", {
29 condition: "steps.classify.output.type === 'other'"
30 })
31 .with("openai:gpt-4o-mini")
32 .depends("classify")
33 .prompt("Flag for manual review with summary")
34
35 .run({ document: documentText });
36
37// Only one of the three processing steps will have executed
38console.log("Executed steps:",
39 result.steps.filter(s => !s.skipped).map(s => s.stepName)
40);