Creating Workflows
Workflows are the core building blocks of automation in Loopstack. They define a sequence of states and transitions that execute tools to accomplish specific tasks, from simple data processing to complex multi-step business processes.
Creating a Workflow
A workflow is created by extending the WorkflowBase class and decorating it with @BlockConfig. State is defined using the @WithState decorator with a Zod schema, and tools are injected using the @Tool decorator. The workflow logic is defined in a separate YAML configuration file.
Basic Workflow Definition
TypeScript
import { WorkflowBase } from '@loopstack/core';
import { BlockConfig, Tool, WithState } from '@loopstack/common';
import { z } from 'zod';
import { AiGenerateText } from '@loopstack/ai-module';
import { CreateDocument } from '@loopstack/core-ui-module';
@BlockConfig({
configFile: __dirname + '/chat.workflow.yaml',
})
@WithState(z.object({
llmResponse: z.any(),
}))
export class ChatWorkflow extends WorkflowBase {
@Tool() createDocument: CreateDocument;
@Tool() aiGenerateText: AiGenerateText;
}Key Components
@BlockConfig Decorator
- Marks a class as a Loopstack block
- Links the workflow class to its YAML configuration via
configFile
@WithState Decorator
- Defines the workflowβs state schema using Zod
- Makes state properties accessible in YAML templates via
${ propertyName } - Enables the
assignfeature in YAML transitions to store results
@Tool Decorator
- Injects tool dependencies into the workflow
- Tools decorated with
@Tool()become available for use in YAML transitions - Tool names in YAML correspond to the property names in the class
YAML Configuration
titleanddescription: Metadata for the workflowui.actions: Defines custom UI actions that can trigger transitionstransitions: Define the flow of states and the tools executed at each stepcall: Specifies tool calls triggered in a transitionassign: Assigns the result to a workflow state property for later use
Defining State
State is defined using the @WithState decorator with a Zod schema. This approach provides type safety and validation for your workflowβs state.
@WithState(z.object({
llmResponse: z.any(),
messageCount: z.number().default(0),
conversationHistory: z.array(z.any()).default([]),
}))
export class MyWorkflow extends WorkflowBase {
// ...
}State properties can be accessed in YAML using template syntax:
${ llmResponse }- Access the llmResponse state property${ result.data }- Access the result of the current tool call (used inassign)
Injecting Tools
Tools are injected using the @Tool decorator. Each tool must be a valid Loopstack tool class.
export class MyWorkflow extends WorkflowBase {
@Tool() createDocument: CreateDocument;
@Tool() aiGenerateText: AiGenerateText;
@Tool() sendEmail: SendEmail;
}The property name becomes the tool identifier used in YAML:
call:
- tool: createDocument # Matches @Tool() createDocument
args:
# ...UI Actions
The ui.actions section allows you to define custom UI controls that trigger transitions manually.
ui:
actions:
- type: custom
transition: user_message # The transition to trigger
widget: prompt-input # The UI widget type
enabledWhen: # States where this action is available
- ready
- waiting_for_user
options:
label: Send Message # Display label for the actionTransition Configuration
Basic Transition
transitions:
- id: my_transition
from: state_a
to: state_b
call:
- tool: myTool
args:
param1: value1
param2: ${ stateProperty }Manual Triggers
Transitions can be triggered manually by users through UI actions:
- id: user_message
from: waiting_for_user
to: ready
trigger: manual
call:
- tool: createDocument
args:
document: aiMessageDocument
update:
content:
role: user
parts:
- type: text
text: ${ transition.payload }The transition.payload provides access to data passed from the UI action.
Assigning Results
Use assign to store tool results in workflow state:
- id: prompt
from: ready
to: prompt_executed
call:
- id: execute_prompt
tool: aiGenerateText
args:
llm:
provider: openai
model: gpt-4o
messagesSearchTag: message
assign:
llmResponse: ${ result.data }Complete Example: Chat Assistant
Hereβs a complete example of a chat workflow with a system persona:
TypeScript
import { WorkflowBase } from '@loopstack/core';
import { BlockConfig, Tool, WithState } from '@loopstack/common';
import { z } from 'zod';
import { AiGenerateText } from '@loopstack/ai-module';
import { CreateDocument } from '@loopstack/core-ui-module';
@BlockConfig({
configFile: __dirname + '/chat.workflow.yaml',
})
@WithState(z.object({
llmResponse: z.any(),
}))
export class ChatWorkflow extends WorkflowBase {
@Tool() createDocument: CreateDocument;
@Tool() aiGenerateText: AiGenerateText;
}Registering the Workflow
Add your workflow as a module provider and import it in your workspace to make it available for execution.
my-module.ts
@Module({
imports: [LoopCoreModule],
providers: [
ChatWorkflow,
// ... other providers
],
})
export class MyModule {}Using Your Workflow
Once registered in a workspace, your workflow can be manually executed:
- Navigate to your workspace in the Loopstack Studio
- Click Run to create a new execution
- Select the workflow from the available workflows