The DSPy.ts module system provides a declarative way to define language model interactions:
import { defineModule } from 'dspy.ts';
const module = defineModule<TInput, TOutput>({
name: string;
signature: {
inputs: FieldDefinition[];
outputs: FieldDefinition[];
};
promptTemplate: (input: TInput) => string;
strategy?: 'Predict' | 'ChainOfThought' | 'ReAct';
});DSPy.ts supports multiple language model backends:
// ONNX Runtime Web
import { ONNXModel } from 'dspy.ts';
const onnxModel = new ONNXModel({
modelPath: string;
executionProvider?: 'wasm' | 'webgl' | 'webgpu';
});
// JS-PyTorch
import { TorchModel } from 'dspy.ts';
const torchModel = new TorchModel({
modelPath?: string;
deviceType?: 'cpu' | 'webgl';
});Chain multiple modules together:
import { Pipeline } from 'dspy.ts';
const pipeline = new Pipeline(
modules: Module<any, any>[],
config?: {
stopOnError?: boolean;
debug?: boolean;
maxRetries?: number;
retryDelay?: number;
}
);interface Module<TInput, TOutput> {
name: string;
signature: Signature;
run(input: TInput): Promise<TOutput>;
}
interface Signature {
inputs: FieldDefinition[];
outputs: FieldDefinition[];
}
interface FieldDefinition {
name: string;
type: 'string' | 'number' | 'boolean' | 'object';
description?: string;
required?: boolean;
}interface LMDriver {
generate(prompt: string, options?: GenerationOptions): Promise<string>;
init?(): Promise<void>;
cleanup?(): Promise<void>;
}
interface GenerationOptions {
maxTokens?: number;
temperature?: number;
topP?: number;
stopSequences?: string[];
}interface PipelineConfig {
stopOnError?: boolean;
debug?: boolean;
maxRetries?: number;
retryDelay?: number;
}
interface PipelineResult {
success: boolean;
finalOutput: any;
steps: StepResult[];
totalDuration: number;
error?: Error;
}
interface StepResult {
moduleName: string;
input: any;
output: any;
duration: number;
error?: Error;
}Basic module for single-step LM calls:
const predictModule = defineModule<TInput, TOutput>({
name: 'Predictor',
signature: {
inputs: [{ name: 'input', type: 'string' }],
outputs: [{ name: 'output', type: 'string' }]
},
promptTemplate: (input) => input,
strategy: 'Predict'
});For complex reasoning tasks:
const cotModule = defineModule<TInput, TOutput>({
name: 'Reasoner',
signature: {
inputs: [{ name: 'problem', type: 'string' }],
outputs: [
{ name: 'steps', type: 'string[]' },
{ name: 'solution', type: 'string' }
]
},
strategy: 'ChainOfThought'
});For tool-using agents:
const reactModule = defineModule<TInput, TOutput>({
name: 'Agent',
signature: {
inputs: [{ name: 'task', type: 'string' }],
outputs: [{ name: 'result', type: 'string' }]
},
strategy: 'ReAct',
tools: [/* tool definitions */]
});DSPy.ts provides several error types:
class LMError extends Error {
constructor(message: string, public readonly cause?: Error);
}
class ModuleError extends Error {
constructor(
message: string,
public readonly moduleName: string,
public readonly cause?: Error
);
}
class PipelineError extends Error {
constructor(
message: string,
public readonly step: number,
public readonly cause?: Error
);
}Example error handling:
try {
const result = await module.run(input);
} catch (error) {
if (error instanceof LMError) {
// Handle LM-specific error
} else if (error instanceof ModuleError) {
// Handle module-specific error
}
}Global configuration functions:
// Set the global LM driver
function configureLM(lm: LMDriver): void;
// Get the current LM driver
function getLM(): LMDriver;-
Type Safety
- Use TypeScript for better type checking
- Define clear input/output signatures
- Validate data at runtime
-
Error Handling
- Catch specific error types
- Use pipeline error handling for graceful failures
- Implement proper cleanup
-
Performance
- Configure appropriate retry settings
- Use caching when possible
- Clean up resources properly
-
Testing
- Use DummyLM for testing
- Write comprehensive unit tests
- Test error handling paths
See the examples directory for complete working examples of various use cases.