Skip to Content

Sub-Workflows

Execute workflows within other workflows using @InjectWorkflow() and .run(). The parent workflow pauses at a wait: true transition until the sub-workflow completes.

Injecting a Sub-Workflow

import { InjectWorkflow, CallbackSchema, QueueResult } from '@loopstack/common'; @InjectWorkflow() private subWorkflow: SubWorkflow;

Running a Sub-Workflow

@Initial({ to: 'sub_started' }) async start() { const result: QueueResult = await this.subWorkflow.run( { prompt: 'Hello' }, // Args passed to the sub-workflow { alias: 'subWorkflow', // Identifier for this instance callback: { transition: 'onSubComplete' }, // Method to call when done }, ); // Track with a link document await this.repository.save(LinkDocument, { label: 'Running sub-workflow...', workflowId: result.workflowId, }, { id: `link_${result.workflowId}` }); }

Receiving the Callback

The sub-workflow’s @Final return value is passed as payload.data:

const SubWorkflowCallbackSchema = CallbackSchema.extend({ data: z.object({ message: z.string() }), }); @Transition({ from: 'sub_started', to: 'sub_done', wait: true, schema: SubWorkflowCallbackSchema, }) async onSubComplete(payload: { workflowId: string; data: { message: string } }) { // Update the link document await this.repository.save(LinkDocument, { label: 'Sub-Workflow', status: 'success', workflowId: payload.workflowId, }, { id: `link_${payload.workflowId}` }); // Use the result await this.createChatMessage.call({ role: 'assistant', content: `Sub-workflow said: ${payload.data.message}`, }); }

Sub-Workflow Output

The sub-workflow defines its output as the return value of its @Final method:

@Workflow({ uiConfig: __dirname + '/sub.ui.yaml' }) export class SubWorkflow extends BaseWorkflow { @Initial({ to: 'end' }) async run(): Promise<{ message: string }> { return { message: 'Hi mom!' }; } }

Complete Example

@Workflow({ uiConfig: __dirname + '/parent.ui.yaml' }) export class ParentWorkflow extends BaseWorkflow { @InjectTool() private createChatMessage: CreateChatMessage; @InjectWorkflow() private subWorkflow: SubWorkflow; @Initial({ to: 'sub_started' }) async runWorkflow() { const result: QueueResult = await this.subWorkflow.run( {}, { alias: 'subWorkflow', callback: { transition: 'subWorkflowCallback' } }, ); await this.repository.save( LinkDocument, { label: 'Executing Sub-Workflow...', workflowId: result.workflowId, }, { id: `link_${result.workflowId}` }, ); } @Final({ from: 'sub_started', wait: true, schema: CallbackSchema.extend({ data: z.object({ message: z.string() }) }), }) async subWorkflowCallback(payload: { workflowId: string; data: { message: string } }) { await this.repository.save( LinkDocument, { label: 'Sub-Workflow', status: 'success', workflowId: payload.workflowId, }, { id: `link_${payload.workflowId}` }, ); await this.createChatMessage.call({ role: 'assistant', content: `Message from sub-workflow: ${payload.data.message}`, }); } }

Registering Sub-Workflows

Both workflows must be registered in the module:

@Module({ imports: [LoopCoreModule], providers: [ParentWorkflow, SubWorkflow], exports: [ParentWorkflow, SubWorkflow], }) export class MyModule {}

To hide the sub-workflow from the Studio sidebar:

@Workspace({ config: { title: 'My Workspace' } }) export class DefaultWorkspace implements WorkspaceInterface { @InjectWorkflow() parentWorkflow: ParentWorkflow; @InjectWorkflow({ options: { visible: false } }) subWorkflow: SubWorkflow; }

Registry References

Last updated on