UNPKG

quantum-cli-core

Version:

Quantum CLI Core - Multi-LLM Collaboration System

446 lines 16.4 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { readFileSync, existsSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; export const DEFAULT_COLLABORATION_CONFIG = { enabled: false, autoVerifyThreshold: 0.7, showConfidenceScores: false, maxCostPerQuery: 0.05, maxProviders: 3, consensusThreshold: 0.8, verificationMode: 'smart', fallbackBehavior: 'primary', maxLatency: 10000, parallelExecution: true, cacheResponses: true, minConfidenceScore: 0.6, requireConsensus: false, diversityBonus: 0.1, providers: [], }; export const DEFAULT_PROVIDERS = [ { id: 'gemini-primary', name: 'Gemini Pro', type: 'gemini', modelName: 'gemini-1.5-pro-002', maxTokens: 8192, temperature: 0.1, capabilities: ['text', 'code', 'reasoning'], strengths: ['general', 'coding', 'analysis'], costPerToken: 0.000003, enabled: true, }, { id: 'openai-secondary', name: 'GPT-4', type: 'openai', apiKeyEnvVar: 'OPENAI_API_KEY', modelName: 'gpt-4', maxTokens: 8192, temperature: 0.1, capabilities: ['text', 'code', 'reasoning'], strengths: ['general', 'creative', 'reasoning'], costPerToken: 0.00006, enabled: false, }, { id: 'anthropic-tertiary', name: 'Claude 3.5 Sonnet', type: 'anthropic', apiKeyEnvVar: 'ANTHROPIC_API_KEY', modelName: 'claude-3-5-sonnet-20241022', maxTokens: 8192, temperature: 0.1, capabilities: ['text', 'code', 'reasoning', 'analysis'], strengths: ['coding', 'analysis', 'safety'], costPerToken: 0.000015, enabled: false, }, ]; /** * Validates and normalizes collaboration configuration. * Applies defaults for missing values and validates constraints. */ export function validateCollaborationConfig(config) { const errors = []; const normalizedConfig = { ...DEFAULT_COLLABORATION_CONFIG, }; // Merge provided config with defaults Object.assign(normalizedConfig, config); // Validate enabled flag if (typeof normalizedConfig.enabled !== 'boolean') { normalizedConfig.enabled = DEFAULT_COLLABORATION_CONFIG.enabled; } // Validate thresholds (0-1 range) if (normalizedConfig.autoVerifyThreshold !== undefined) { if (typeof normalizedConfig.autoVerifyThreshold !== 'number' || normalizedConfig.autoVerifyThreshold < 0 || normalizedConfig.autoVerifyThreshold > 1) { errors.push({ field: 'autoVerifyThreshold', message: 'Must be a number between 0 and 1', value: normalizedConfig.autoVerifyThreshold, }); normalizedConfig.autoVerifyThreshold = DEFAULT_COLLABORATION_CONFIG.autoVerifyThreshold; } } if (normalizedConfig.consensusThreshold !== undefined) { if (typeof normalizedConfig.consensusThreshold !== 'number' || normalizedConfig.consensusThreshold < 0 || normalizedConfig.consensusThreshold > 1) { errors.push({ field: 'consensusThreshold', message: 'Must be a number between 0 and 1', value: normalizedConfig.consensusThreshold, }); normalizedConfig.consensusThreshold = DEFAULT_COLLABORATION_CONFIG.consensusThreshold; } } if (normalizedConfig.minConfidenceScore !== undefined) { if (typeof normalizedConfig.minConfidenceScore !== 'number' || normalizedConfig.minConfidenceScore < 0 || normalizedConfig.minConfidenceScore > 1) { errors.push({ field: 'minConfidenceScore', message: 'Must be a number between 0 and 1', value: normalizedConfig.minConfidenceScore, }); normalizedConfig.minConfidenceScore = DEFAULT_COLLABORATION_CONFIG.minConfidenceScore; } } // Validate cost constraints if (normalizedConfig.maxCostPerQuery !== undefined) { if (typeof normalizedConfig.maxCostPerQuery !== 'number' || normalizedConfig.maxCostPerQuery < 0) { errors.push({ field: 'maxCostPerQuery', message: 'Must be a positive number', value: normalizedConfig.maxCostPerQuery, }); normalizedConfig.maxCostPerQuery = DEFAULT_COLLABORATION_CONFIG.maxCostPerQuery; } } // Validate provider limits if (normalizedConfig.maxProviders !== undefined) { if (!Number.isInteger(normalizedConfig.maxProviders) || normalizedConfig.maxProviders < 1) { errors.push({ field: 'maxProviders', message: 'Must be a positive integer', value: normalizedConfig.maxProviders, }); normalizedConfig.maxProviders = DEFAULT_COLLABORATION_CONFIG.maxProviders; } } // Validate latency settings if (normalizedConfig.maxLatency !== undefined) { if (!Number.isInteger(normalizedConfig.maxLatency) || normalizedConfig.maxLatency < 1000) { errors.push({ field: 'maxLatency', message: 'Must be an integer >= 1000 (milliseconds)', value: normalizedConfig.maxLatency, }); normalizedConfig.maxLatency = DEFAULT_COLLABORATION_CONFIG.maxLatency; } } // Validate verification mode if (normalizedConfig.verificationMode !== undefined) { const validModes = ['automatic', 'manual', 'smart']; if (!validModes.includes(normalizedConfig.verificationMode)) { errors.push({ field: 'verificationMode', message: `Must be one of: ${validModes.join(', ')}`, value: normalizedConfig.verificationMode, }); normalizedConfig.verificationMode = DEFAULT_COLLABORATION_CONFIG.verificationMode; } } // Validate fallback behavior if (normalizedConfig.fallbackBehavior !== undefined) { const validBehaviors = ['primary', 'random', 'best']; if (!validBehaviors.includes(normalizedConfig.fallbackBehavior)) { errors.push({ field: 'fallbackBehavior', message: `Must be one of: ${validBehaviors.join(', ')}`, value: normalizedConfig.fallbackBehavior, }); normalizedConfig.fallbackBehavior = DEFAULT_COLLABORATION_CONFIG.fallbackBehavior; } } // Validate providers array if (normalizedConfig.providers) { const validatedProviders = []; const seenIds = new Set(); normalizedConfig.providers.forEach((provider, index) => { const providerErrors = validateProviderConfig(provider, index); errors.push(...providerErrors); // Check for duplicate IDs if (seenIds.has(provider.id)) { errors.push({ field: `providers[${index}].id`, message: 'Provider ID must be unique', value: provider.id, }); } else { seenIds.add(provider.id); validatedProviders.push(provider); } }); normalizedConfig.providers = validatedProviders; } return { valid: errors.length === 0, errors, config: normalizedConfig, }; } /** * Validates a single provider configuration. */ function validateProviderConfig(provider, index) { const errors = []; // Required fields if (!provider.id || typeof provider.id !== 'string') { errors.push({ field: `providers[${index}].id`, message: 'Provider ID is required and must be a string', value: provider.id, }); } if (!provider.name || typeof provider.name !== 'string') { errors.push({ field: `providers[${index}].name`, message: 'Provider name is required and must be a string', value: provider.name, }); } if (!provider.type || typeof provider.type !== 'string') { errors.push({ field: `providers[${index}].type`, message: 'Provider type is required and must be a string', value: provider.type, }); } // Optional numeric validations if (provider.maxTokens !== undefined) { if (!Number.isInteger(provider.maxTokens) || provider.maxTokens < 1) { errors.push({ field: `providers[${index}].maxTokens`, message: 'Must be a positive integer', value: provider.maxTokens, }); } } if (provider.temperature !== undefined) { if (typeof provider.temperature !== 'number' || provider.temperature < 0 || provider.temperature > 2) { errors.push({ field: `providers[${index}].temperature`, message: 'Must be a number between 0 and 2', value: provider.temperature, }); } } if (provider.costPerToken !== undefined) { if (typeof provider.costPerToken !== 'number' || provider.costPerToken < 0) { errors.push({ field: `providers[${index}].costPerToken`, message: 'Must be a non-negative number', value: provider.costPerToken, }); } } return errors; } /** * Loads collaboration configuration from ~/.quantum/collaboration.json file */ export function loadCollaborationConfigFromFile() { const configPath = join(homedir(), '.quantum', 'collaboration.json'); if (!existsSync(configPath)) { return {}; } try { const configContent = readFileSync(configPath, 'utf-8'); const fileConfig = JSON.parse(configContent); // Validate the loaded configuration const validationResult = validateCollaborationConfig(fileConfig.collaboration || {}); if (!validationResult.valid) { console.warn('Invalid collaboration configuration file:', validationResult.errors); return {}; } return validationResult.config; } catch (error) { console.warn(`Failed to load collaboration config from ${configPath}:`, error); return {}; } } /** * Loads collaboration configuration from environment variables */ export function loadCollaborationConfigFromEnv() { const envConfig = {}; // Main collaboration settings if (process.env.ENABLE_COLLABORATION === 'true') { envConfig.enabled = true; } if (process.env.COLLABORATION_AUTO_VERIFY_THRESHOLD) { const threshold = parseFloat(process.env.COLLABORATION_AUTO_VERIFY_THRESHOLD); if (!isNaN(threshold) && threshold >= 0 && threshold <= 1) { envConfig.autoVerifyThreshold = threshold; } } if (process.env.COLLABORATION_MAX_COST_PER_QUERY) { const cost = parseFloat(process.env.COLLABORATION_MAX_COST_PER_QUERY); if (!isNaN(cost) && cost >= 0) { envConfig.maxCostPerQuery = cost; } } if (process.env.COLLABORATION_VERIFICATION_MODE) { const mode = process.env.COLLABORATION_VERIFICATION_MODE; if (['automatic', 'manual', 'smart'].includes(mode)) { envConfig.verificationMode = mode; } } if (process.env.COLLABORATION_SHOW_CONFIDENCE === 'true') { envConfig.showConfidenceScores = true; } // Provider configuration from environment const providers = []; // OpenAI Provider if (process.env.OPENAI_API_KEY) { providers.push({ id: 'openai-env', name: 'OpenAI (Environment)', type: 'openai', apiKey: process.env.OPENAI_API_KEY, modelName: process.env.OPENAI_MODEL || 'gpt-4', enabled: process.env.OPENAI_ENABLED !== 'false', temperature: 0.1, maxTokens: 8192, capabilities: ['text', 'code', 'reasoning'], strengths: ['general', 'creative', 'reasoning'], costPerToken: 0.00006, }); } // Anthropic Provider if (process.env.ANTHROPIC_API_KEY) { providers.push({ id: 'anthropic-env', name: 'Anthropic (Environment)', type: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY, modelName: process.env.ANTHROPIC_MODEL || 'claude-3-5-sonnet-20241022', enabled: process.env.ANTHROPIC_ENABLED !== 'false', temperature: 0.1, maxTokens: 8192, capabilities: ['text', 'code', 'reasoning', 'analysis'], strengths: ['coding', 'analysis', 'safety'], costPerToken: 0.000015, }); } if (providers.length > 0) { envConfig.providers = providers; } return envConfig; } /** * Creates an example collaboration configuration file */ export function createExampleCollaborationConfig() { return { $schema: 'https://quantum-cli.dev/schemas/collaboration.json', version: '1.0', collaboration: { enabled: false, autoVerifyThreshold: 0.7, showConfidenceScores: true, verificationMode: 'smart', maxCostPerQuery: 0.05, maxProviders: 3, consensusThreshold: 0.8, fallbackBehavior: 'primary', maxLatency: 10000, parallelExecution: true, cacheResponses: true, minConfidenceScore: 0.6, requireConsensus: false, diversityBonus: 0.1, providers: [], }, defaultProviders: DEFAULT_PROVIDERS, }; } /** * Writes a default collaboration configuration file to ~/.quantum/collaboration.json */ export function writeDefaultCollaborationConfig() { try { const configDir = join(homedir(), '.quantum'); const configPath = join(configDir, 'collaboration.json'); // Create directory if it doesn't exist const { mkdirSync, writeFileSync } = require('fs'); if (!existsSync(configDir)) { mkdirSync(configDir, { recursive: true }); } const exampleConfig = createExampleCollaborationConfig(); writeFileSync(configPath, JSON.stringify(exampleConfig, null, 2), 'utf-8'); console.log(`Created default collaboration config at ${configPath}`); return true; } catch (error) { console.error('Failed to create default collaboration config:', error); return false; } } /** * Loads collaboration configuration from all sources with proper precedence. * Precedence: Environment > File > Defaults */ export function loadAllCollaborationConfigs() { const fileConfig = loadCollaborationConfigFromFile(); const envConfig = loadCollaborationConfigFromEnv(); return mergeCollaborationConfigs(DEFAULT_COLLABORATION_CONFIG, fileConfig, envConfig); } /** * Merges multiple configuration sources with proper precedence. * Environment variables have higher precedence than file configuration. */ export function mergeCollaborationConfigs(...configs) { const merged = { ...DEFAULT_COLLABORATION_CONFIG }; for (const config of configs) { if (config.providers && merged.providers) { // Merge providers instead of replacing const existingIds = new Set(merged.providers.map((p) => p.id)); const newProviders = config.providers.filter((p) => !existingIds.has(p.id)); merged.providers = [...merged.providers, ...newProviders]; // Apply the rest of the config without providers const { providers, ...restConfig } = config; Object.assign(merged, restConfig); } else { Object.assign(merged, config); } } return validateCollaborationConfig(merged).config; } //# sourceMappingURL=collaboration-config.js.map