UNPKG

quantum-cli-core

Version:

Quantum CLI Core - Multi-LLM Collaboration System

365 lines 15.1 kB
/** * @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