quantum-cli-core
Version:
Quantum CLI Core - Multi-LLM Collaboration System
365 lines • 15.1 kB
JavaScript
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { ModelSelector } from './model-selector.js';
import { QueryCache } from './caching/query-cache.js';
import { SimilarityService } from './caching/similarity-service.js';
import { UncertaintyLevel, } from './types.js';
export class TripleModelEngine {
modelSelector;
geminiProvider;
openaiProvider;
anthropicProvider;
config;
queryCache;
similarityService;
constructor(config) {
this.config = {
...{
selectionStrategy: 'weighted',
enableFailureRecovery: true,
maxRetries: 3,
consensusThreshold: 0.7,
enableConsensusMaking: false,
},
...config,
};
// Initialize model selector
const selectorConfig = {
strategy: this.config.selectionStrategy,
enableFailureRecovery: this.config.enableFailureRecovery,
maxRetries: this.config.maxRetries,
providerWeights: this.config.providerWeights,
fallbackStrategy: 'round-robin',
performanceThresholds: {
maxLatency: 10000, // 10 seconds
minSuccessRate: 0.8, // 80%
maxCostPerToken: 0.001, // $0.001 per token
},
};
this.modelSelector = new ModelSelector(selectorConfig);
this.queryCache = new QueryCache();
this.similarityService = new SimilarityService();
}
/**
* Register Gemini provider
*/
registerGeminiProvider(provider) {
this.geminiProvider = provider;
provider.id = 'gemini';
this.modelSelector.registerProvider(provider);
}
/**
* Register OpenAI provider
*/
registerOpenAIProvider(provider) {
this.openaiProvider = provider;
provider.id = 'openai';
this.modelSelector.registerProvider(provider);
}
/**
* Register Anthropic provider
*/
registerAnthropicProvider(provider) {
this.anthropicProvider = provider;
provider.id = 'anthropic';
this.modelSelector.registerProvider(provider);
}
/**
* Generate response with automatic provider selection
*/
async generateWithSelection(prompt, context, options) {
const cached = this.findInCache(prompt);
if (cached) {
return this.createVerifiedResponse(cached.response.content, cached.response.content, undefined, true, cached.response.confidence || 0.8, UncertaintyLevel.LOW, [], 0, [cached.providerId], false);
}
const startTime = Date.now();
let selectedProvider = this.modelSelector.selectProvider(context);
let retryCount = 0;
const maxRetries = this.config.maxRetries;
while (retryCount <= maxRetries) {
if (!selectedProvider) {
throw new Error('No available providers found');
}
try {
const providerStartTime = Date.now();
const response = await selectedProvider.generate(prompt, options);
const latency = Date.now() - providerStartTime;
// Update performance metrics
this.modelSelector.updateMetrics(selectedProvider.id, latency, !response.error, response.cost);
if (!response.error) {
return this.createVerifiedResponse(response.content, response.content, undefined, true, response.confidence || 0.8, UncertaintyLevel.LOW, [], Date.now() - startTime, [selectedProvider.id], false);
}
}
catch (error) {
console.warn(`Provider ${selectedProvider.id} failed:`, error.message);
// Update failure metrics
this.modelSelector.updateMetrics(selectedProvider.id, Date.now() - startTime, false);
}
// Try fallback provider
if (this.config.enableFailureRecovery && retryCount < maxRetries) {
selectedProvider = this.modelSelector.getFallbackProvider(selectedProvider.id, context);
retryCount++;
}
else {
break;
}
}
throw new Error('All providers failed after retries');
}
/**
* Generate with verification using two providers
*/
async generateWithVerification(prompt, context, options) {
const providers = this.modelSelector.getAvailableProviders();
if (providers.length < 2) {
return this.generateWithSelection(prompt, context, options);
}
// Select primary and secondary providers
const primaryProvider = this.modelSelector.selectProvider(context);
const secondaryProvider = this.modelSelector.getFallbackProvider(primaryProvider.id, context);
if (!primaryProvider || !secondaryProvider) {
return this.generateWithSelection(prompt, context, options);
}
const startTime = Date.now();
const results = await Promise.allSettled([
this.callProvider(primaryProvider, prompt, options),
this.callProvider(secondaryProvider, prompt, options),
]);
const primaryResult = results[0];
const secondaryResult = results[1];
// Update metrics for both providers
if (primaryResult.status === 'fulfilled') {
this.modelSelector.updateMetrics(primaryProvider.id, primaryResult.value.latency, primaryResult.value.success, primaryResult.value.cost);
}
if (secondaryResult.status === 'fulfilled') {
this.modelSelector.updateMetrics(secondaryProvider.id, secondaryResult.value.latency, secondaryResult.value.success, secondaryResult.value.cost);
}
// Determine primary and secondary responses
const primaryContent = primaryResult.status === 'fulfilled' && primaryResult.value.success
? primaryResult.value.response.content
: '';
const secondaryContent = secondaryResult.status === 'fulfilled' && secondaryResult.value.success
? secondaryResult.value.response.content
: '';
// Calculate agreement
const agreement = this.calculateAgreement(primaryContent, secondaryContent);
const verified = agreement >= (this.config.consensusThreshold || 0.7);
// Synthesize response if both are available
let synthesizedContent = primaryContent;
if (primaryContent &&
secondaryContent &&
this.config.enableConsensusMaking) {
synthesizedContent = this.synthesizeResponses(primaryContent, secondaryContent);
}
const totalDuration = Date.now() - startTime;
const modelIds = [primaryProvider.id, secondaryProvider.id];
return this.createVerifiedResponse(synthesizedContent || primaryContent || secondaryContent, primaryContent, secondaryContent, verified, Math.max(primaryResult.status === 'fulfilled'
? primaryResult.value.response?.confidence || 0.8
: 0.5, secondaryResult.status === 'fulfilled'
? secondaryResult.value.response?.confidence || 0.8
: 0.5), verified ? UncertaintyLevel.LOW : UncertaintyLevel.MEDIUM, verified ? [] : ['Responses differ significantly'], totalDuration, modelIds, true);
}
/**
* Generate responses from all three models for comparison
*/
async generateWithComparison(prompt, context, options) {
const providers = this.modelSelector.getAvailableProviders();
if (providers.length === 0) {
throw new Error('No providers available');
}
const startTime = Date.now();
const results = await Promise.allSettled(providers.map((provider) => this.callProvider(provider, prompt, options)));
// Update metrics for all providers
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
this.modelSelector.updateMetrics(providers[index].id, result.value.latency, result.value.success, result.value.cost);
}
});
// Extract successful responses
const successfulResults = results
.map((result, index) => ({ result, provider: providers[index] }))
.filter(({ result }) => result.status === 'fulfilled' && result.value.success)
.map(({ result, provider }) => ({
providerId: provider.id,
content: result.value
.response.content,
confidence: result.value.response
.confidence || 0.8,
}));
if (successfulResults.length === 0) {
throw new Error('All providers failed');
}
// Calculate consensus
const consensus = this.calculateConsensus(successfulResults);
const totalDuration = Date.now() - startTime;
const modelIds = successfulResults.map((r) => r.providerId);
return this.createVerifiedResponse(consensus.content, successfulResults[0]?.content || '', successfulResults[1]?.content, consensus.agreement >= (this.config.consensusThreshold || 0.7), consensus.confidence, consensus.agreement >= 0.8
? UncertaintyLevel.LOW
: consensus.agreement >= 0.6
? UncertaintyLevel.MEDIUM
: UncertaintyLevel.HIGH, consensus.agreement < 0.6 ? ['Low consensus among models'] : [], totalDuration, modelIds, true);
}
/**
* Call a provider and measure performance
*/
async callProvider(provider, prompt, options) {
const startTime = Date.now();
try {
const response = await provider.generate(prompt, options);
const latency = Date.now() - startTime;
if (!response.error) {
this.queryCache.set(prompt, response, provider.id);
}
return {
providerId: provider.id,
success: !response.error,
response,
error: response.error,
latency,
cost: response.cost || 0,
confidence: response.confidence || 0.8,
};
}
catch (error) {
return {
providerId: provider.id,
success: false,
error: error.message,
latency: Date.now() - startTime,
cost: 0,
confidence: 0,
};
}
}
/**
* Calculate agreement between two responses
*/
calculateAgreement(response1, response2) {
if (!response1 || !response2)
return 0;
// Simple similarity measure (can be enhanced with more sophisticated algorithms)
const words1 = response1.toLowerCase().split(/\s+/);
const words2 = response2.toLowerCase().split(/\s+/);
const set1 = new Set(words1);
const set2 = new Set(words2);
const intersection = new Set([...set1].filter((x) => set2.has(x)));
const union = new Set([...set1, ...set2]);
return union.size > 0 ? intersection.size / union.size : 0;
}
/**
* Calculate consensus among multiple responses
*/
calculateConsensus(results) {
if (results.length === 1) {
return {
content: results[0].content,
confidence: results[0].confidence,
agreement: 1.0,
};
}
// Calculate pairwise agreements
let totalAgreement = 0;
let comparisons = 0;
for (let i = 0; i < results.length; i++) {
for (let j = i + 1; j < results.length; j++) {
totalAgreement += this.calculateAgreement(results[i].content, results[j].content);
comparisons++;
}
}
const agreement = comparisons > 0 ? totalAgreement / comparisons : 0;
// Select response with highest confidence
const bestResult = results.reduce((best, current) => current.confidence > best.confidence ? current : best);
return {
content: bestResult.content,
confidence: results.reduce((sum, r) => sum + r.confidence, 0) / results.length,
agreement,
};
}
/**
* Synthesize two responses into one
*/
synthesizeResponses(primary, secondary) {
// Simple synthesis - in production, this could use more sophisticated merging
if (primary.length > secondary.length) {
return primary;
}
else {
return secondary;
}
}
/**
* Create a verified response object
*/
createVerifiedResponse(content, primaryResponse, secondaryResponse, verified = false, confidence = 0.8, uncertaintyLevel = UncertaintyLevel.MEDIUM, uncertaintyReasons = [], duration = 0, modelsUsed = [], comparison = false) {
return {
content,
primaryResponse,
secondaryResponse,
synthesizedContent: content !== primaryResponse ? content : undefined,
verified,
confidence,
alignment: verified ? 0.9 : 0.6,
uncertaintyLevel,
uncertaintyReasons,
metadata: {
duration,
modelsUsed,
comparison,
},
};
}
/**
* Get performance metrics for all providers
*/
getPerformanceMetrics() {
return this.modelSelector.getPerformanceMetrics();
}
/**
* Update selection configuration
*/
updateConfig(config) {
this.config = { ...this.config, ...config };
// Update model selector config
this.modelSelector.updateConfig({
strategy: config.selectionStrategy,
enableFailureRecovery: config.enableFailureRecovery,
maxRetries: config.maxRetries,
providerWeights: config.providerWeights,
});
}
/**
* Check health of all providers
*/
async checkProvidersHealth() {
const providers = this.modelSelector.getAvailableProviders();
const healthChecks = await Promise.allSettled(providers.map(async (provider) => ({
id: provider.id,
healthy: await provider.validateCredentials(),
})));
const health = {};
healthChecks.forEach((result, index) => {
if (result.status === 'fulfilled') {
health[result.value.id] = result.value.healthy;
}
else {
health[providers[index].id] = false;
}
});
return health;
}
/**
* Get available provider IDs
*/
getAvailableProviderIds() {
return this.modelSelector.getAvailableProviders().map((p) => p.id);
}
findInCache(query) {
// This is a simple implementation. A more advanced version could use
// the similarityService to find similar queries.
return this.queryCache.get(query);
}
}
//# sourceMappingURL=triple-engine.js.map