/**
* Workflow configuration and builder utilities
*/
import { ChainConfig, ChainStep } from './types.js';
import { readFile, writeFile } from 'fs/promises';
import { existsSync } from 'fs';
/**
* Fluent API for building chain configurations
*/
export class ChainBuilder {
private config: Partial<ChainConfig> = {
steps: []
};
/**
* Set the chain name
*/
name(name: string): this {
this.config.name = name;
return this;
}
/**
* Set initial input for the chain
*/
input(input: string): this {
this.config.initialInput = input;
return this;
}
/**
* Add a step to the chain
*/
addStep(step: ChainStep): this {
this.config.steps!.push(step);
return this;
}
/**
* Add an OpenAI step
*/
addOpenAIStep(
name: string,
model: string = 'gpt-4',
options: Partial<ChainStep> = {}
): this {
return this.addStep({
name,
provider: 'openai',
model,
...options
});
}
/**
* Add an Anthropic step
*/
addAnthropicStep(
name: string,
model: string = 'claude-3-5-sonnet-20241022',
options: Partial<ChainStep> = {}
): this {
return this.addStep({
name,
provider: 'anthropic',
model,
...options
});
}
/**
* Add a Gemini step
*/
addGeminiStep(
name: string,
model: string = 'gemini-2.5-flash',
options: Partial<ChainStep> = {}
): this {
return this.addStep({
name,
provider: 'gemini',
model,
...options
});
}
/**
* Build the final configuration
*/
build(): ChainConfig {
if (!this.config.name) {
throw new Error('Chain name is required');
}
if (!this.config.initialInput) {
throw new Error('Initial input is required');
}
if (!this.config.steps || this.config.steps.length === 0) {
throw new Error('At least one step is required');
}
return this.config as ChainConfig;
}
}
/**
* Load chain configuration from JSON file
*/
export async function loadChainFromFile(filePath: string): Promise<ChainConfig> {
if (!existsSync(filePath)) {
throw new Error(`Configuration file not found: ${filePath}`);
}
const content = await readFile(filePath, 'utf-8');
const config = JSON.parse(content);
// Validate configuration
if (!config.name || !config.initialInput || !config.steps) {
throw new Error('Invalid configuration: missing required fields (name, initialInput, steps)');
}
return config as ChainConfig;
}
/**
* Save chain configuration to JSON file
*/
export async function saveChainToFile(config: ChainConfig, filePath: string): Promise<void> {
// Remove transform functions as they cannot be serialized
const serializableConfig = {
...config,
steps: config.steps.map(step => {
const { transform, ...rest } = step;
return rest;
})
};
await writeFile(filePath, JSON.stringify(serializableConfig, null, 2), 'utf-8');
}
/**
* Predefined workflow templates
*/
export const WorkflowTemplates = {
/**
* Simple pass-through: one AI to another
*/
simplePipeline(
input: string,
step1Provider: 'openai' | 'anthropic',
step2Provider: 'openai' | 'anthropic'
): ChainConfig {
return new ChainBuilder()
.name('Simple Pipeline')
.input(input)
.addStep({
name: 'First Analysis',
provider: step1Provider,
model: step1Provider === 'openai' ? 'gpt-4' : 'claude-3-5-sonnet-20241022'
})
.addStep({
name: 'Second Analysis',
provider: step2Provider,
model: step2Provider === 'openai' ? 'gpt-4' : 'claude-3-5-sonnet-20241022'
})
.build();
},
/**
* Creative refinement: idea generation -> critique -> refinement
*/
creativeRefinement(idea: string): ChainConfig {
return new ChainBuilder()
.name('Creative Refinement')
.input(idea)
.addStep({
name: 'Generate Ideas',
provider: 'openai',
model: 'gpt-4',
systemPrompt: 'You are a creative idea generator. Expand on the given idea with innovative suggestions.',
temperature: 0.9
})
.addStep({
name: 'Critical Analysis',
provider: 'anthropic',
model: 'claude-3-5-sonnet-20241022',
systemPrompt: 'You are a critical analyst. Evaluate the ideas presented and provide constructive criticism.',
temperature: 0.3
})
.addStep({
name: 'Final Refinement',
provider: 'openai',
model: 'gpt-4',
systemPrompt: 'You are a refinement specialist. Take the ideas and criticism to produce a polished final version.',
temperature: 0.5
})
.build();
},
/**
* Translation chain with quality check
*/
translationWithQA(text: string, targetLanguage: string): ChainConfig {
return new ChainBuilder()
.name('Translation with QA')
.input(`Translate the following to ${targetLanguage}:\n\n${text}`)
.addStep({
name: 'Initial Translation',
provider: 'openai',
model: 'gpt-4',
systemPrompt: `You are a professional translator. Translate accurately to ${targetLanguage}.`
})
.addStep({
name: 'Quality Review',
provider: 'anthropic',
model: 'claude-3-5-sonnet-20241022',
systemPrompt: 'Review this translation for accuracy and naturalness. Provide suggestions for improvement.',
transform: (input) => `Review this translation:\n\n${input}\n\nProvide feedback and an improved version if needed.`
})
.addStep({
name: 'Final Translation',
provider: 'openai',
model: 'gpt-4',
systemPrompt: 'Based on the review, provide the final polished translation.',
transform: (input) => `Based on this review and suggestions, provide the final translation:\n\n${input}`
})
.build();
},
/**
* Code review chain
*/
codeReview(code: string, language: string): ChainConfig {
return new ChainBuilder()
.name('Code Review Chain')
.input(`Review this ${language} code:\n\n${code}`)
.addStep({
name: 'Initial Review',
provider: 'openai',
model: 'gpt-4',
systemPrompt: 'You are a senior software engineer. Review the code for bugs, best practices, and improvements.'
})
.addStep({
name: 'Security Analysis',
provider: 'anthropic',
model: 'claude-3-5-sonnet-20241022',
systemPrompt: 'You are a security expert. Analyze the code and previous review for security vulnerabilities.',
transform: (input, prev) => `Previous review:\n${prev[0].content}\n\nOriginal code:\n${code}\n\nProvide security analysis.`
})
.addStep({
name: 'Consolidated Report',
provider: 'openai',
model: 'gpt-4',
systemPrompt: 'Create a consolidated report with prioritized recommendations.',
transform: (input, prev) => `Initial review:\n${prev[0].content}\n\nSecurity analysis:\n${input}\n\nCreate a final consolidated report.`
})
.build();
}
};