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
- run-sub-workflow-example — Parent workflow calling a sub-workflow with callbacks, LinkDocument tracking, and output passing
Last updated on