Skip to Content

@loopstack/prompt-example-workflow

A module for the Loopstack AI  automation framework.

This module provides an example workflow demonstrating how to integrate an LLM using a simple prompt pattern.

Overview

The Prompt Example Workflow shows the most basic way to call an LLM in Loopstack — using a simple text prompt. It generates a haiku about a user-provided subject.

By using this workflow as a reference, you’ll learn how to:

  • Define workflow input arguments with a Zod schema and default values
  • Use the prompt parameter for simple LLM calls
  • Render Handlebars template files with dynamic variables
  • Store instance state on the workflow class
  • Save LLM responses as documents using this.documentStore.save

This example is the ideal starting point for developers new to LLM integration in Loopstack.

Installation

See SETUP.md for installation and setup instructions.

How It Works

Key Concepts

1. Workflow Input Schema

Define input parameters with default values using a Zod schema in the @Workflow decorator. The workflow class extends BaseWorkflow<TArgs> with a matching type:

@Workflow({ uiConfig: __dirname + '/prompt.ui.yaml', schema: z.object({ subject: z.string().default('coffee'), }), }) export class PromptWorkflow extends BaseWorkflow<{ subject: string }> {

The start @Transition method receives the validated arguments:

@Transition({ to: 'prompt_executed' }) async prompt(args: { subject: string }) {

2. Simple Prompt Pattern

Use the prompt parameter for straightforward LLM calls without conversation history. The prompt content is rendered from a Handlebars template file with variables:

@Transition({ to: 'prompt_executed' }) async prompt(args: { subject: string }) { const result = await this.llmGenerateText.call({ prompt: this.render(__dirname + '/templates/prompt.md', { subject: args.subject }), }); this.llmResult = result.data; }

The this.render() method loads a Handlebars template and interpolates the provided variables.

3. Storing Results as Instance State

Tool results are stored as instance properties on the workflow class, making them available in subsequent transitions:

llmResult?: LlmGenerateTextResult;

4. Saving Documents in a Final Transition

The terminal @Transition decorator marks the last transition. Here the stored LLM result is saved as a LlmMessageDocument:

@Transition({ from: 'prompt_executed', to: 'end' }) async respond() { await this.documentStore.save(LlmMessageDocument, this.llmResult!.message, { meta: { response: this.llmResult!.response, provider: 'claude' }, }); }

Workflow Class

The complete workflow class:

import { z } from 'zod'; import { BaseWorkflow, Final, Initial, InjectTool, Workflow } from '@loopstack/common'; import type { LlmGenerateTextResult } from '@loopstack/llm-provider-module'; import { LlmGenerateTextTool, LlmMessageDocument } from '@loopstack/llm-provider-module'; @Workflow({ uiConfig: __dirname + '/prompt.ui.yaml', schema: z.object({ subject: z.string().default('coffee'), }), }) export class PromptWorkflow extends BaseWorkflow<{ subject: string }> { @InjectTool({ provider: 'claude', model: 'claude-sonnet-4-6' }) llmGenerateText: LlmGenerateTextTool; llmResult?: LlmGenerateTextResult; @Transition({ to: 'prompt_executed' }) async prompt(args: { subject: string }) { const result = await this.llmGenerateText.call({ prompt: this.render(__dirname + '/templates/prompt.md', { subject: args.subject }), }); this.llmResult = result.data; } @Transition({ from: 'prompt_executed', to: 'end' }) async respond() { await this.documentStore.save(LlmMessageDocument, this.llmResult!.message, { meta: { response: this.llmResult!.response, provider: 'claude' }, }); } }

Dependencies

This workflow uses the following Loopstack modules:

  • @loopstack/common - Core framework functionality, BaseWorkflow, decorators
  • @loopstack/llm-provider-module - Provides LlmGenerateTextTool tool, LlmGenerateTextResult type, and LlmMessageDocument

About

Author: Jakob Klippel 

License: MIT

Additional Resources

Last updated on