Skip to Content

LLM Providers

Loopstack supports multiple LLM providers through a runtime registry. Provider modules self-register at startup. Workflows and tools resolve providers by name — swap or use multiple providers in parallel without changing workflow code.

Quick Start

Import LlmProviderModule for the adapter tools and a provider module (e.g. ClaudeModule) to register the LLM backend:

import { ClaudeModule } from '@loopstack/claude-module'; import { LlmProviderModule } from '@loopstack/llm-provider-module'; @Module({ imports: [LoopstackModule.forRoot(), LlmProviderModule.forRoot({}), ClaudeModule], }) export class AppModule {}

Module-Level Defaults

Use LlmProviderModule.forRoot() to set a default model for all LLM calls in your app. Use forFeature() to override per-module:

// app.module.ts — global default model @Module({ imports: [ LoopstackModule.forRoot(), LlmProviderModule.forRoot({ model: 'claude-sonnet-4-5' }), ClaudeModule, ], }) export class AppModule {}
// premium-feature.module.ts — this module uses a stronger model @Module({ imports: [LlmProviderModule.forFeature({ model: 'claude-opus-4-6' })], providers: [PremiumWorkflow], }) export class PremiumFeatureModule {}

Per-Call Configuration

Override provider and model at individual call sites via options.config. Per-call config always takes priority over module defaults.

export class MyWorkflow extends BaseWorkflow { constructor( private readonly llmGenerateText: LlmGenerateTextTool, private readonly llmDelegateToolCalls: LlmDelegateToolCallsTool, ) { super(); } }
const result = await this.llmGenerateText.call( { prompt: 'Hello!' }, { config: { provider: 'claude', model: 'claude-opus-4-6', system: 'You are a helpful assistant.', messagesSearchTag: 'message', tools: ['get_weather'], }, }, );

Args vs Config

LLM tools separate args (per-request data) from config (provider/model/behavior settings):

ParameterLocationDescription
promptargsSimple prompt string
messagesargsExplicit message array
outputSchemaargsJSON Schema (generate object only)
providerconfigLLM provider name (e.g. 'claude')
modelconfigModel name (e.g. 'claude-sonnet-4-6')
systemconfigSystem prompt
messagesSearchTagconfigLoad messages from documents by tag
toolsconfigTool names the LLM can call

Using Multiple Providers

Import both modules and configure each call with its provider:

@Module({ imports: [LoopstackModule.forRoot(), ClaudeModule, OpenAiModule], }) export class AppModule {}
// Use Claude for complex tasks const smartResult = await this.llmGenerateText.call( { prompt: 'Analyze this code...' }, { config: { provider: 'claude', model: 'claude-opus-4-6' } }, ); // Use OpenAI for simple tasks const fastResult = await this.llmGenerateText.call( { prompt: 'Summarize in one line...' }, { config: { provider: 'openai', model: 'gpt-4o-mini' } }, );

Adapter Tools

All LLM interactions go through adapter tools from @loopstack/llm-provider-module. This ensures validation, interceptors, and logging apply to every LLM call.

ToolPurpose
LlmGenerateTextToolText generation with optional tool calling
LlmGenerateObjectToolStructured output conforming to a JSON Schema
LlmDelegateToolCallsToolExecute tool calls from an LLM response
LlmUpdateToolResultToolHandle async tool completion callbacks

Message Documents

All providers share a single LlmMessageDocument with normalized content. Native API responses are stored in entity.meta.response for provider-specific round-trips.

DocumentContent FormatWidget
LlmMessageDocumentNormalized (text, thinking, tool_call blocks)llm-message

Environment Variables

VariableProviderDescription
ANTHROPIC_API_KEYClaudeAPI key
OPENAI_API_KEYOpenAIAPI key
CLAUDE_MODELClaudeDefault model fallback
OPENAI_MODELOpenAIDefault model fallback

Available Providers

ProviderModuleID
Anthropic Claude@loopstack/claude-module'claude'
OpenAI@loopstack/openai-module'openai'
Last updated on