Advanced Workflows Examples
Advanced workflow pattern examples for the Loopstack automation framework.
Deep-dive examples for framework patterns authors reach for less often but want available when they need them. Pick the example that matches your problem.
Use in Your App
Copy this directory into your app, then install what it imports:
npm install @loopstack/claude @loopstack/common @loopstack/core @loopstack/llm-providerRegister the module:
import { Module } from '@nestjs/common';import { LoopstackModule } from '@loopstack/loopstack-module';import { AdvancedWorkflowsExamplesModule } from './advanced-workflows/advanced-workflows-examples.module';
@Module({ imports: [LoopstackModule.forRoot(), AdvancedWorkflowsExamplesModule],})export class AppModule {}Required app-module configuration
Examples that exercise LLM tools (LlmGenerateTextTool, LlmGenerateObjectTool — used by Sub-Workflow, Fan-Out, Sequence, Batch Processing, Custom Tool, Module Config) call into @loopstack/llm-provider. That module is @Global and must be configured once in your root module to set the default model:
import { Module } from '@nestjs/common';import { LlmProviderModule } from '@loopstack/llm-provider';import { LoopstackModule } from '@loopstack/loopstack-module';import { AdvancedWorkflowsExamplesModule } from './advanced-workflows/advanced-workflows-examples.module';
@Module({ imports: [ LoopstackModule.forRoot(), LlmProviderModule.forRoot({ model: 'claude-sonnet-4-6' }), AdvancedWorkflowsExamplesModule, ],})export class AppModule {}AdvancedWorkflowsExamplesModule already re-imports ClaudeModule to register the Claude provider; LlmProviderModule.forRoot(...) sets the default model the tools dispatch to. Pure pattern demos that don’t call an LLM (Workflow State, Dynamic Routing, UI Documents) work without it.
Set ANTHROPIC_API_KEY in the environment for the LLM examples.
Examples
| Example | Studio title | Description |
|---|---|---|
| Workflow State | Advanced - Workflow State Example |
Persist typed state across transitions via assignState() |
| Workflow Result | Advanced - Workflow Result Example |
Publish a result for callers via assignResult(), kept apart from private state |
| Dynamic Routing | Advanced - Dynamic Routing Example |
Conditional routing with @Guard decorators and transition priorities |
| Sub-Workflow | Advanced - Sub-Workflow Example |
Launch a child workflow via .run() and resume on callback |
| Fan-Out | Advanced - Fan-Out Example |
Parallel sub-workflows with single aggregated callback (FanOutWorkflow) |
| Sequence | Advanced - Sequence Example |
Sequential sub-workflows with single aggregated callback (SequenceWorkflow) |
| Batch Processing | Advanced - Batch Processing Example |
Chunked processing of a list in fixed-size batches |
| Custom Tool | Advanced - Custom Tool Example |
Authoring custom tools — stateless + stateful, @Tool(), Zod schemas, NestJS injection |
| Module Config | Advanced - Module Config (Default/German/...) |
forRoot / forFeature patterns for configurable modules |
| UI Documents | Advanced - UI Documents Example |
Smoke test for built-in document types (Message, Error, Markdown, Plain) |
Workflow State
WorkflowStateWorkflow — the minimal state pattern: assignState() in one transition, read the typed state parameter in the next, and format it with a private helper method. State is private to the workflow, so the run publishes no result.
Files
workflow-state-example.workflow.ts
Workflow Result
WorkflowResultWorkflow — the workflow’s published result. The first transition keeps a name in state and publishes a greeting with assignResult(). The second reads the name from state and adds a second field with another assignResult(), which merges into the first. The run’s result is { greeting, shout } — what WorkflowRunner callers, parent callbacks (input.data) and the API receive — while the name stays in private state.
Files
workflow-result-example.workflow.ts
Dynamic Routing
Demonstrates @Guard-based conditional routing — multiple transitions out of one state, evaluated in priority order, the first one whose guard returns true wins.
Files
dynamic-routing-example.workflow.ts
Sub-Workflow
Five workflow files demonstrating sub-workflow composition:
sub-workflow-parent.workflow.ts— basic parent that launches a child via.run()sub-workflow-sub.workflow.ts— the child workflowsub-workflow-failing-sub.workflow.ts— a child that always fails (used by error-handling)sub-workflow-error-handling.workflow.ts— parent handles failed children viainput.hasError/input.errorMessagesub-workflow-show-modes.workflow.ts— everyshowmode (inline,link,hidden) in one flow
For parallel and sequential composition see Fan-Out and Sequence.
Fan-Out
Launches multiple sub-workflows in parallel via FanOutWorkflow from @loopstack/core. The parent receives a single callback once all children complete.
Files
fan-out-example.workflow.ts
Sequence
Runs multiple sub-workflows one at a time via SequenceWorkflow from @loopstack/core. The parent receives a single aggregated callback after the last child completes.
Files
sequence-example.workflow.ts
Batch Processing
Processes a list of items in fixed-size batches. Items within a batch run concurrently (Promise.all), batches run sequentially via a state-machine loop. Distinct from FanOutWorkflow which runs all sub-workflows simultaneously.
Useful for rate-limited APIs, memory-bounded processing, or quota-constrained operations.
Files
batch-processing-example.workflow.ts
Custom Tool
Demonstrates authoring custom tools by extending BaseTool:
MathSumTool— stateless, computesa + bCounterTool— stateful, persists across checkpointsMathService— supporting NestJS service injected into a tool
Files
custom-tool-example.workflow.tscustom-tool-example.ui.yamltools/math-sum.tool.ts(stateless)tools/counter.tool.ts(stateful)services/math.service.ts
Module Config
Four scenarios for configurable modules:
- Default Greeting — no
forFeatureoverride; usesforRootglobal defaults - German Greeting —
forFeatureoverride per module - French Greeting — independent
forFeature, proves per-module isolation - Nested Greeting — config passed through a wrapper module (
GreeterAgentModule.forFeature)
Each consumer module registers its own @Workflow so all four show up in the sidebar.
Files
greeter/— configurable module (forRoot,forFeature, constants, tool)consumers/— four module + workflow pairs, one per scenario
UI Documents
A small workflow that saves one of each built-in document type so you can verify Studio renders them correctly.
Files
ui-documents-example.workflow.ts
About
Author: Jakob Klippel
License: MIT