@jackhua/mini-langchain
Version:
A lightweight TypeScript implementation of LangChain with cost optimization features
246 lines • 8.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SequentialChain = exports.SimpleSequentialChain = exports.BaseChain = void 0;
/**
* Base class for all chains
*/
class BaseChain {
constructor(config) {
this.callbacks = [];
this.verbose = false;
this.memory = config?.memory;
this.callbacks = config?.callbacks || [];
this.verbose = config?.verbose || false;
}
/**
* Invoke the chain with automatic memory handling
*/
async invoke(inputs) {
const allCallbacks = [...this.callbacks];
// Load memory variables
let chainInputs = { ...inputs };
if (this.memory) {
const memoryVariables = this.memory.loadMemoryVariables(inputs);
chainInputs = { ...chainInputs, ...memoryVariables };
}
// Handle callbacks
await this.handleChainStart(chainInputs, allCallbacks);
try {
// Run the chain
const outputs = await this.call(chainInputs, allCallbacks);
// Save to memory
if (this.memory) {
await this.memory.saveContext(inputs, outputs);
}
await this.handleChainEnd(outputs, allCallbacks);
return outputs;
}
catch (error) {
await this.handleChainError(error, allCallbacks);
throw error;
}
}
/**
* Run the chain with a single string input/output
*/
async run(text) {
const inputKey = this.inputKeys[0];
const outputKey = this.outputKeys[0];
if (!inputKey || !outputKey) {
throw new Error('Chain must have at least one input and output key to use run()');
}
const result = await this.invoke({ [inputKey]: text });
return result[outputKey];
}
/**
* Batch run the chain
*/
async batch(inputs) {
const results = [];
for (const input of inputs) {
const result = await this.invoke(input);
results.push(result);
}
return results;
}
/**
* Stream the chain output
*/
async *stream(inputs) {
// Default implementation just yields the final result
// Subclasses can override for true streaming
const result = await this.invoke(inputs);
yield result;
}
/**
* Add a callback handler
*/
addCallback(callback) {
this.callbacks.push(callback);
}
/**
* Handle chain start callbacks
*/
async handleChainStart(inputs, callbacks) {
for (const callback of callbacks) {
if (callback.handleChainStart) {
await callback.handleChainStart(this.constructor.name, inputs);
}
}
}
/**
* Handle chain end callbacks
*/
async handleChainEnd(outputs, callbacks) {
for (const callback of callbacks) {
if (callback.handleChainEnd) {
await callback.handleChainEnd(outputs);
}
}
}
/**
* Handle chain error callbacks
*/
async handleChainError(error, callbacks) {
for (const callback of callbacks) {
if (callback.handleChainError) {
await callback.handleChainError(error);
}
}
}
/**
* Serialize the chain
*/
serialize() {
return {
_type: this.constructor.name,
inputKeys: this.inputKeys,
outputKeys: this.outputKeys
};
}
}
exports.BaseChain = BaseChain;
/**
* Simple sequential chain that runs multiple chains in sequence
*/
class SimpleSequentialChain extends BaseChain {
constructor(config) {
super(config);
this.chains = config.chains;
this.inputKey = config.inputKey || 'input';
this.outputKey = config.outputKey || 'output';
this.validateChains();
}
validateChains() {
if (this.chains.length === 0) {
throw new Error('SimpleSequentialChain must have at least one chain');
}
// Validate that each chain has exactly one input and output
for (const chain of this.chains) {
if (chain.inputKeys.length !== 1 || chain.outputKeys.length !== 1) {
throw new Error('All chains in SimpleSequentialChain must have exactly one input and output key');
}
}
}
get inputKeys() {
return [this.inputKey];
}
get outputKeys() {
return [this.outputKey];
}
async call(inputs, callbacks) {
let currentOutput = inputs[this.inputKey];
for (const chain of this.chains) {
const chainInputKey = chain.inputKeys[0];
const chainOutputKey = chain.outputKeys[0];
const result = await chain.call({ [chainInputKey]: currentOutput }, callbacks);
currentOutput = result[chainOutputKey];
}
return { [this.outputKey]: currentOutput };
}
}
exports.SimpleSequentialChain = SimpleSequentialChain;
/**
* Sequential chain that can handle multiple inputs/outputs
*/
class SequentialChain extends BaseChain {
constructor(config) {
super(config);
this.chains = config.chains;
this._inputKeys = config.inputVariables;
this._outputKeys = config.outputVariables;
this.returnAll = config.returnAll || false;
this.validateChains();
}
validateChains() {
if (this.chains.length === 0) {
throw new Error('SequentialChain must have at least one chain');
}
// Track all available variables
const availableVariables = new Set(this._inputKeys);
const allOutputVariables = new Set();
for (const chain of this.chains) {
// Check that all input keys are available
for (const inputKey of chain.inputKeys) {
if (!availableVariables.has(inputKey)) {
throw new Error(`Chain ${chain.constructor.name} requires input "${inputKey}" which is not available`);
}
}
// Add output keys to available variables
for (const outputKey of chain.outputKeys) {
availableVariables.add(outputKey);
allOutputVariables.add(outputKey);
}
}
// Check that all output variables are produced
if (!this.returnAll) {
for (const outputVar of this._outputKeys) {
if (!allOutputVariables.has(outputVar)) {
throw new Error(`Output variable "${outputVar}" is not produced by any chain`);
}
}
}
}
get inputKeys() {
return this._inputKeys;
}
get outputKeys() {
return this._outputKeys;
}
async call(inputs, callbacks) {
const allVariables = { ...inputs };
for (const chain of this.chains) {
// Prepare inputs for this chain
const chainInputs = {};
for (const inputKey of chain.inputKeys) {
if (inputKey in allVariables) {
chainInputs[inputKey] = allVariables[inputKey];
}
else {
throw new Error(`Missing required input "${inputKey}" for chain ${chain.constructor.name}`);
}
}
// Run the chain
const outputs = await chain.call(chainInputs, callbacks);
// Store outputs
for (const [key, value] of Object.entries(outputs)) {
allVariables[key] = value;
}
}
// Return requested outputs
const result = {};
if (this.returnAll) {
return allVariables;
}
else {
for (const outputKey of this._outputKeys) {
if (outputKey in allVariables) {
result[outputKey] = allVariables[outputKey];
}
}
return result;
}
}
}
exports.SequentialChain = SequentialChain;
//# sourceMappingURL=base.js.map