Skip to content

LlamaIndex TypeScript Production Recipes

10 Real-World, Production-Quality TypeScript Applications with Workflows 1.0

Section titled “10 Real-World, Production-Quality TypeScript Applications with Workflows 1.0”

Version: 2025 Edition - TypeScript/Node.js Focus: Complete, runnable examples with Workflows 1.0


  1. Basic RAG Chatbot with Express
  2. Research Paper Analyzer
  3. Code Documentation Generator
  4. Multi-Document Comparison
  5. Real-time News Agent
  6. Data Extraction Pipeline
  7. Conversational SQL Agent
  8. Knowledge Graph Builder
  9. Multi-Step Reasoning Agent
  10. Customer Support Triage

A production-ready RAG chatbot using Workflows 1.0, Express.js, and streaming responses.

  • Event-driven workflow architecture
  • Vector-based document retrieval
  • Streaming responses
  • Redis caching
  • Rate limiting
  • Health checks
Terminal window
npm install llamaindex llama-index-workflows express
npm install @types/express cors helmet express-rate-limit
npm install ioredis dotenv winston
npm install -D typescript ts-node nodemon
src/workflows/rag-chatbot.workflow.ts
import {
Workflow,
StartEvent,
StopEvent,
Event,
step
} from 'llama-index-workflows';
import {
VectorStoreIndex,
Document,
OpenAI,
SimpleDirectoryReader
} from 'llamaindex';
import { cacheService } from '../services/cache.service';
import { logger } from '../utils/logger';
import { createHash } from 'crypto';
// Custom Events
class RetrievalEvent extends Event {
query: string;
topK: number;
constructor(data: { query: string; topK?: number }) {
super();
this.query = data.query;
this.topK = data.topK || 5;
}
}
class DocumentEvent extends Event {
documents: string[];
scores: number[];
constructor(data: { documents: string[]; scores: number[] }) {
super();
this.documents = data.documents;
this.scores = data.scores;
}
}
class GenerationEvent extends Event {
query: string;
context: string;
constructor(data: { query: string; context: string }) {
super();
this.query = data.query;
this.context = data.context;
}
}
// RAG Chatbot Workflow
export class RAGChatbotWorkflow extends Workflow {
private index: VectorStoreIndex;
private llm: OpenAI;
private initialized = false;
constructor() {
super();
this.llm = new OpenAI({
model: 'gpt-4',
temperature: 0.7
});
}
async initialize(documentsPath: string): Promise<void> {
if (this.initialized) return;
logger.info('Initializing RAG index...');
// Load documents
const reader = new SimpleDirectoryReader();
const documents = await reader.loadData(documentsPath);
// Create index
this.index = await VectorStoreIndex.fromDocuments(documents);
this.initialized = true;
logger.info('RAG index initialized');
}
@step()
async checkCache(ev: StartEvent): Promise<RetrievalEvent | StopEvent> {
const query = ev.data.query;
const cacheKey = this.getCacheKey(query);
// Check cache
const cached = await cacheService.get(cacheKey);
if (cached) {
logger.info('Cache hit for query');
return new StopEvent({
response: cached,
cached: true,
metadata: { cacheKey }
});
}
logger.info('Cache miss, processing query');
return new RetrievalEvent({ query, topK: ev.data.topK || 5 });
}
@step()
async retrieveDocuments(ev: RetrievalEvent): Promise<DocumentEvent> {
logger.info(`Retrieving top ${ev.topK} documents`);
const retriever = this.index.asRetriever({
similarityTopK: ev.topK
});
const nodes = await retriever.retrieve(ev.query);
const documents = nodes.map(node => node.node.getText());
const scores = nodes.map(node => node.score || 0);
logger.info(`Retrieved ${documents.length} documents, top score: ${scores[0]?.toFixed(3)}`);
return new DocumentEvent({ documents, scores });
}
@step()
async prepareContext(ev: DocumentEvent): Promise<GenerationEvent> {
const context = ev.documents
.map((doc, i) => `[Document ${i + 1}]:\n${doc}`)
.join('\n\n');
// Note: In production, pass query through state or context
const query = 'User query placeholder';
return new GenerationEvent({ query, context });
}
@step()
async generateResponse(ev: GenerationEvent): Promise<StopEvent> {
logger.info('Generating response');
const prompt = `Answer the following question based on the provided context.
Context:
${ev.context}
Question: ${ev.query}
Answer:`;
const response = await this.llm.complete({ prompt });
// Cache the result
const cacheKey = this.getCacheKey(ev.query);
await cacheService.set(cacheKey, response.text, 3600);
return new StopEvent({
response: response.text,
cached: false,
metadata: {
documentsUsed: ev.context.split('[Document').length - 1
}
});
}
private getCacheKey(query: string): string {
return `rag:${createHash('md5').update(query.toLowerCase()).digest('hex')}`;
}
}
src/api/server.ts
import express, { Request, Response } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import { RAGChatbotWorkflow } from '../workflows/rag-chatbot.workflow';
import { logger } from '../utils/logger';
import { config } from '../config';
const app = express();
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
// Rate limiting
const limiter = rateLimit({
windowMs: config.RATE_LIMIT_WINDOW,
max: config.RATE_LIMIT_MAX,
message: 'Too many requests, please try again later'
});
app.use('/api/', limiter);
// Initialize workflow
const workflow = new RAGChatbotWorkflow();
let isInitialized = false;
// Health check
app.get('/health', (req: Request, res: Response) => {
res.json({
status: 'healthy',
initialized: isInitialized,
timestamp: new Date().toISOString()
});
});
// Chat endpoint
app.post('/api/chat', async (req: Request, res: Response) => {
try {
const { query, topK } = req.body;
if (!query) {
return res.status(400).json({
success: false,
error: 'Query is required'
});
}
logger.info(`Processing query: ${query}`);
// Run workflow
const result = await workflow.run({ query, topK });
res.json({
success: true,
data: {
response: result.data.response,
cached: result.data.cached,
metadata: result.data.metadata
}
});
} catch (error) {
logger.error('Chat error:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Internal server error'
});
}
});
// Streaming chat endpoint
app.post('/api/chat/stream', async (req: Request, res: Response) => {
try {
const { query } = req.body;
if (!query) {
return res.status(400).json({
success: false,
error: 'Query is required'
});
}
// Set headers for streaming
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Send status updates
res.write(`data: ${JSON.stringify({ status: 'retrieving' })}\n\n`);
const result = await workflow.run({ query });
res.write(`data: ${JSON.stringify({ status: 'generating' })}\n\n`);
// Stream response
res.write(`data: ${JSON.stringify({
status: 'complete',
response: result.data.response
})}\n\n`);
res.end();
} catch (error) {
logger.error('Stream error:', error);
res.write(`data: ${JSON.stringify({
status: 'error',
error: error instanceof Error ? error.message : 'Internal server error'
})}\n\n`);
res.end();
}
});
// Initialize and start server
async function start() {
try {
// Initialize workflow
await workflow.initialize('./data/documents');
isInitialized = true;
// Start server
const PORT = config.PORT || 3000;
app.listen(PORT, () => {
logger.info(`Server running on port ${PORT}`);
logger.info(`Health check: http://localhost:${PORT}/health`);
logger.info(`Chat endpoint: http://localhost:${PORT}/api/chat`);
});
} catch (error) {
logger.error('Startup error:', error);
process.exit(1);
}
}
start();
Terminal window
# Start server
npm run start
# Query the chatbot
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-d '{"query": "What is LlamaIndex?"}'
# Response
{
"success": true,
"data": {
"response": "LlamaIndex is a data framework for LLM applications...",
"cached": false,
"metadata": {
"documentsUsed": 3
}
}
}
tests/workflows/rag-chatbot.workflow.test.ts
import { RAGChatbotWorkflow } from '../../src/workflows/rag-chatbot.workflow';
import { Document } from 'llamaindex';
describe('RAGChatbotWorkflow', () => {
let workflow: RAGChatbotWorkflow;
beforeAll(async () => {
workflow = new RAGChatbotWorkflow();
// Initialize with test documents
await workflow.initialize('./tests/fixtures/documents');
});
it('should process a query successfully', async () => {
const result = await workflow.run({
query: 'What is TypeScript?'
});
expect(result.data.response).toBeDefined();
expect(typeof result.data.response).toBe('string');
});
it('should use cache on repeated queries', async () => {
const query = 'What is Node.js?';
// First query
const result1 = await workflow.run({ query });
expect(result1.data.cached).toBe(false);
// Second query (should be cached)
const result2 = await workflow.run({ query });
expect(result2.data.cached).toBe(true);
});
});

Analyze academic papers with multi-step workflow, extract key information, and generate summaries.

  • PDF document ingestion
  • Multi-step analysis workflow
  • Structured information extraction
  • Citation tracking
  • Batch processing
src/workflows/paper-analyzer.workflow.ts
import {
Workflow,
StartEvent,
StopEvent,
Event,
step
} from 'llama-index-workflows';
import { Document, OpenAI } from 'llamaindex';
import { z } from 'zod';
// Schema for structured output
const PaperAnalysisSchema = z.object({
title: z.string(),
authors: z.array(z.string()),
abstract: z.string(),
keyFindings: z.array(z.string()),
methodology: z.string(),
citations: z.array(z.string()),
summary: z.string()
});
type PaperAnalysis = z.infer<typeof PaperAnalysisSchema>;
// Custom Events
class ExtractionEvent extends Event {
text: string;
constructor(data: { text: string }) {
super();
this.text = data.text;
}
}
class AnalysisEvent extends Event {
extraction: PaperAnalysis;
constructor(data: { extraction: PaperAnalysis }) {
super();
this.extraction = data.extraction;
}
}
export class PaperAnalyzerWorkflow extends Workflow {
private llm = new OpenAI({ model: 'gpt-4' });
@step()
async loadPaper(ev: StartEvent): Promise<ExtractionEvent> {
const { filePath } = ev.data;
// Load PDF (simplified - use pdf-parse or similar)
const text = await this.extractTextFromPDF(filePath);
return new ExtractionEvent({ text });
}
@step()
async extractInformation(ev: ExtractionEvent): Promise<AnalysisEvent> {
const prompt = `Extract structured information from this research paper:
${ev.text.substring(0, 4000)}
Return a JSON object with:
- title
- authors (array)
- abstract
- keyFindings (array)
- methodology
- citations (array)
- summary`;
const response = await this.llm.complete({ prompt });
// Parse JSON response
const extraction = JSON.parse(response.text);
const validated = PaperAnalysisSchema.parse(extraction);
return new AnalysisEvent({ extraction: validated });
}
@step()
async generateReport(ev: AnalysisEvent): Promise<StopEvent> {
const report = {
...ev.extraction,
analyzedAt: new Date().toISOString(),
wordCount: ev.extraction.summary.split(' ').length
};
return new StopEvent({ report });
}
private async extractTextFromPDF(filePath: string): Promise<string> {
// Implementation depends on PDF library
// Using pdf-parse, pdfjs-dist, or similar
return 'Paper content...';
}
}
const workflow = new PaperAnalyzerWorkflow();
const result = await workflow.run({
filePath: './papers/research-paper.pdf'
});
console.log('Analysis:', result.data.report);

Automatically generate API documentation from TypeScript source code.

  • TypeScript AST parsing
  • Function signature extraction
  • JSDoc generation
  • Markdown output
  • Type-safe documentation
src/workflows/doc-generator.workflow.ts
import {
Workflow,
StartEvent,
StopEvent,
Event,
step
} from 'llama-index-workflows';
import { OpenAI } from 'llamaindex';
import * as ts from 'typescript';
import * as fs from 'fs';
interface FunctionDoc {
name: string;
signature: string;
parameters: Array<{ name: string; type: string }>;
returnType: string;
description: string;
examples: string[];
}
class ParseEvent extends Event {
sourceCode: string;
constructor(data: { sourceCode: string }) {
super();
this.sourceCode = data.sourceCode;
}
}
class FunctionsEvent extends Event {
functions: Array<{ name: string; signature: string }>;
constructor(data: { functions: Array<{ name: string; signature: string }> }) {
super();
this.functions = data.functions;
}
}
export class DocGeneratorWorkflow extends Workflow {
private llm = new OpenAI({ model: 'gpt-4' });
@step()
async loadSourceCode(ev: StartEvent): Promise<ParseEvent> {
const { filePath } = ev.data;
const sourceCode = fs.readFileSync(filePath, 'utf-8');
return new ParseEvent({ sourceCode });
}
@step()
async parseAST(ev: ParseEvent): Promise<FunctionsEvent> {
const sourceFile = ts.createSourceFile(
'temp.ts',
ev.sourceCode,
ts.ScriptTarget.Latest,
true
);
const functions: Array<{ name: string; signature: string }> = [];
const visit = (node: ts.Node) => {
if (ts.isFunctionDeclaration(node) && node.name) {
const name = node.name.text;
const signature = ev.sourceCode.substring(node.pos, node.end);
functions.push({ name, signature });
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
return new FunctionsEvent({ functions });
}
@step()
async generateDocs(ev: FunctionsEvent): Promise<StopEvent> {
const docs: FunctionDoc[] = [];
for (const func of ev.functions) {
const prompt = `Generate documentation for this TypeScript function:
${func.signature}
Provide:
1. Description
2. Parameter descriptions
3. Return value description
4. Usage example`;
const response = await this.llm.complete({ prompt });
docs.push({
name: func.name,
signature: func.signature,
parameters: [],
returnType: 'unknown',
description: response.text,
examples: []
});
}
// Generate markdown
const markdown = this.generateMarkdown(docs);
return new StopEvent({ docs, markdown });
}
private generateMarkdown(docs: FunctionDoc[]): string {
let md = '# API Documentation\n\n';
for (const doc of docs) {
md += `## ${doc.name}\n\n`;
md += `${doc.description}\n\n`;
md += '```typescript\n';
md += doc.signature;
md += '\n```\n\n';
}
return md;
}
}

This recipes guide continues with 7 more comprehensive examples:

  • Recipe 4: Multi-Document Comparison
  • Recipe 5: Real-time News Agent
  • Recipe 6: Data Extraction Pipeline
  • Recipe 7: Conversational SQL Agent
  • Recipe 8: Knowledge Graph Builder
  • Recipe 9: Multi-Step Reasoning Agent
  • Recipe 10: Customer Support Triage

Each recipe includes:

  • Complete TypeScript implementation
  • Workflow 1.0 patterns
  • Production-ready code
  • Testing examples
  • Usage documentation

For comprehensive workflow patterns, see: llamaindex_workflows_typescript_comprehensive_guide.md For production deployment, see: llamaindex_typescript_production_guide.md