Skip to content
Color theme

LLM Examples

LLM workflow examples for the Loopstack automation framework.

A collection of workflow examples that demonstrate how to integrate LLMs into Loopstack workflows. Use these as starting points for prompts, structured output, multi-provider setups, and fetching web content.

Use in Your App

Copy this directory into your app, then install what it imports:

Terminal window
npm install @loopstack/claude @loopstack/common @loopstack/llm-provider @loopstack/openai @loopstack/web

Register the module:

import { Module } from '@nestjs/common';
import { LoopstackModule } from '@loopstack/loopstack-module';
import { LlmExamplesModule } from './llm/llm-examples.module';
@Module({
imports: [LoopstackModule.forRoot(), LlmExamplesModule],
})
export class AppModule {}

Required app-module configuration

Every workflow in this module calls a tool from @loopstack/llm-provider (LlmGenerateTextTool, LlmGenerateObjectTool). 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 { LlmExamplesModule } from './llm/llm-examples.module';
@Module({
imports: [LoopstackModule.forRoot(), LlmProviderModule.forRoot({ model: 'claude-sonnet-4-6' }), LlmExamplesModule],
})
export class AppModule {}

LlmExamplesModule already re-imports ClaudeModule, OpenAiModule, and WebModule — the Claude and OpenAI providers register themselves with the LLM registry so the Multi-Provider example can dispatch to either. LlmProviderModule.forRoot(...) sets the default model the tools fall back to when a call doesn’t override { config: { provider, model } } explicitly.

Environment

Set provider API keys for the workflows you want to run:

Terminal window
ANTHROPIC_API_KEY=sk-ant-... # required for Claude examples
OPENAI_API_KEY=sk-... # required for the multi-provider example

Examples

Example Studio title Description
Prompt LLM - Prompt Example (Write a haiku) Single-shot LLM call with a Handlebars-rendered prompt template
Structured Output LLM - Structured Output Example (Hello World Script) Generate Zod-validated structured output with a custom document widget
Multi-Provider LLM - Multi-Provider Example Run the same prompt through Claude and OpenAI side by side
Web Fetch LLM - Web Fetch Example Fetch a URL, convert HTML to Markdown, summarize with an LLM

Prompt

Single-shot LLM call using a Handlebars-rendered prompt template. Generates a haiku about a user-provided subject.

What it demonstrates

  • Defining workflow input arguments with a Zod schema and default values
  • Using the prompt parameter for a simple LLM call
  • Rendering Handlebars templates with this.render(path, vars)
  • Automatic assistant message persistence via LlmGenerateTextTool

Key code

@Transition({ to: 'end' })
async prompt(state, ctx: RunContext<PromptExampleArgs>) {
await this.llmGenerateText.call(
{ prompt: this.render(join(__dirname, 'templates', 'prompt.md'), { subject: ctx.args.subject }) },
{ config: { provider: 'claude', model: 'claude-sonnet-4-6' } },
);
}

LlmGenerateTextTool saves the assistant message to the document store automatically — no manual documentStore.save() needed. Pass config: { save: false } to opt out.

Files

  • prompt-example.workflow.ts — workflow class
  • templates/prompt.md — Handlebars prompt template

Structured Output

Generates a Zod-validated object (a FileDocument with filename, description, code) using LlmGenerateObjectTool. Renders in Studio via a custom widget.

What it demonstrates

  • Defining a @Document class with a Zod schema and a YAML widget
  • Calling LlmGenerateObjectTool with outputSchema for Zod-validated JSON output
  • Persisting the structured result via this.documentStore.save(FileDocument, result.data.data)
  • Multi-transition state flow: start → ready → prompt_executed → end

Key code

const result = await this.llmGenerateObject.call(
{
outputSchema: FileDocumentSchema,
prompt: this.render(join(__dirname, 'templates', 'prompt.md'), { language: state.language }),
},
{ config: { provider: 'claude', model: 'claude-sonnet-4-6' } },
);
const llmResult = await this.documentStore.save(FileDocument, result.data.data as FileDocumentType);
this.assignState({ llmResult });

Files

  • structured-output-example.workflow.ts — workflow class
  • documents/file-document.ts — @Document class + Zod schema
  • documents/file-document.yaml — Studio widget definition
  • templates/prompt.md — Handlebars prompt template

Multi-Provider

Sends the same prompt to Claude and OpenAI and renders responses side by side for comparison.

What it demonstrates

  • Using the same tool class (LlmGenerateTextTool) for multiple providers
  • Selecting provider and model at call time via { config: { provider, model } }
  • Opting out of auto-save with config: { save: false } to control message formatting
  • Custom start-form widget via widget: './multi-provider.ui.yaml'

Key code

const result = await this.llmGenerateText.call(
{ prompt: ctx.args.prompt },
{
config: {
save: false,
provider: 'claude',
model: 'claude-sonnet-4-6',
},
},
);
await this.documentStore.save(LlmMessageDocument, {
role: 'assistant',
text: `**Claude:** ${result.data.message.text}`,
});

The save: false opt-out lets us prefix each response with the provider name so the comparison is clear in the Studio chat view.

Files

  • multi-provider-example.workflow.ts — workflow class
  • multi-provider.ui.yaml — Studio start-form widget

Web Fetch

Fetches a URL, converts HTML to Markdown, and optionally summarizes it against a user-provided prompt using a small model via the configured LLM provider.

What it demonstrates

  • Using WebFetchTool from @loopstack/web
  • HTML → Markdown conversion with size caps and same-origin redirect handling
  • Optional prompt-based summarization built into the tool

Key code

const result = await this.webFetch.call({
url: ctx.args.url,
prompt: ctx.args.prompt,
});
this.assignState({ summary: result.data.result });

When prompt is omitted, WebFetchTool returns the raw Markdown (truncated if very long). When provided, the tool summarizes the content through the configured LLM provider (model claude-haiku-4-5-20251001 by default) and returns the provider, model and token usage as result metadata.

Files

  • web-fetch-example.workflow.ts — workflow class
  • @loopstack/web — fetch + Markdown conversion + summarization
  • @loopstack/claude — claude_native_web_search, Claude’s server-side web search tool (alternative when you want the LLM to search rather than fetch a specific URL)

About

Author: Jakob Klippel

License: MIT