UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

454 lines 16.7 kB
"use strict"; /** * Provider Registry and Selection Logic for MCP * * Manages registration, discovery, and selection of LLM providers */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ProviderRegistry = void 0; const types_1 = require("./types"); const openai_provider_1 = require("./providers/openai-provider"); const anthropic_provider_1 = require("./providers/anthropic-provider"); /** * Provider Registry */ class ProviderRegistry { constructor() { this.providers = new Map(); this.factories = new Map(); this.scoringWeights = { capability: 0.3, cost: 0.2, performance: 0.2, reliability: 0.2, preference: 0.1 }; this.registerBuiltInFactories(); } /** * Register built-in provider factories */ registerBuiltInFactories() { this.registerProviderFactory('openai', { createProvider: openai_provider_1.OpenAIProviderFactory.createProvider, validateConfig: openai_provider_1.OpenAIProviderFactory.validateConfig, getProviderType: openai_provider_1.OpenAIProviderFactory.getProviderType }); this.registerProviderFactory('anthropic', { createProvider: anthropic_provider_1.AnthropicProviderFactory.createProvider, validateConfig: anthropic_provider_1.AnthropicProviderFactory.validateConfig, getProviderType: anthropic_provider_1.AnthropicProviderFactory.getProviderType }); } /** * Register a provider factory */ registerProviderFactory(type, factory) { this.factories.set(type, factory); } /** * Create and register provider from config */ async createProvider(config) { // Determine provider type from config or endpoint const providerType = this.detectProviderType(config); const factory = this.factories.get(providerType); if (!factory) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.INVALID_CONFIG, message: `Unknown provider type: ${providerType}`, timestamp: new Date(), retryable: false }); } // Validate configuration if (!factory.validateConfig(config)) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.CONFIG_VALIDATION_ERROR, message: `Invalid configuration for provider type: ${providerType}`, timestamp: new Date(), retryable: false }); } // Create provider instance const provider = factory.createProvider(config); // Register the provider this.providers.set(provider.id, provider); return provider; } /** * Detect provider type from configuration */ detectProviderType(config) { // Check explicit type first if (config.type) { return config.type; } // Detect from endpoint URL if (config.endpoint) { if (config.endpoint.includes('openai.com')) { return 'openai'; } if (config.endpoint.includes('anthropic.com')) { return 'anthropic'; } } // Default to provider ID if it matches known types if (this.factories.has(config.id)) { return config.id; } throw new types_1.MCPError({ code: types_1.MCPErrorCode.INVALID_CONFIG, message: `Cannot detect provider type for: ${config.id}`, timestamp: new Date(), retryable: false }); } /** * Register an existing provider instance */ registerProvider(provider) { this.providers.set(provider.id, provider); } /** * Unregister a provider */ unregisterProvider(providerId) { this.providers.delete(providerId); } /** * Get provider by ID */ getProvider(providerId) { return this.providers.get(providerId); } /** * Get all registered providers */ getAllProviders() { return Array.from(this.providers.values()); } /** * Get available (enabled and healthy) providers */ getAvailableProviders() { return this.getAllProviders().filter(provider => provider.status === types_1.ProviderStatus.AVAILABLE); } /** * Get providers by capability */ getProvidersByCapability(capability) { return this.getAvailableProviders().filter(provider => provider.capabilities[capability]); } /** * Get providers supporting specific model */ getProvidersByModel(modelId) { return this.getAvailableProviders().filter(provider => provider.models.some(model => model.id === modelId)); } /** * Select best provider for request */ selectProvider(criteria, request) { const availableProviders = this.getAvailableProviders(); if (availableProviders.length === 0) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.PROVIDER_UNAVAILABLE, message: 'No providers available', timestamp: new Date(), retryable: true }); } // Filter providers by basic requirements let candidateProviders = this.filterProviders(availableProviders, criteria); if (candidateProviders.length === 0) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.PROVIDER_UNAVAILABLE, message: 'No providers meet the selection criteria', timestamp: new Date(), retryable: false, details: { criteria } }); } // Score and rank providers const scoredProviders = candidateProviders.map(provider => ({ provider, score: this.scoreProvider(provider, criteria, request), reason: this.getSelectionReason(provider, criteria) })); // Sort by score (highest first) scoredProviders.sort((a, b) => b.score - a.score); const selected = scoredProviders[0]; const alternatives = scoredProviders.slice(1, 4); // Top 3 alternatives return { provider: selected.provider, confidence: selected.score, reason: selected.reason, alternatives: alternatives.map(alt => ({ provider: alt.provider, confidence: alt.score, reason: alt.reason })) }; } /** * Filter providers by basic requirements */ filterProviders(providers, criteria) { return providers.filter(provider => { // Check capability requirements if (criteria.requestType === types_1.LLMRequestType.COMPLETION && !provider.capabilities.completion) { return false; } if (criteria.requestType === types_1.LLMRequestType.STREAMING && !provider.capabilities.streaming) { return false; } if (criteria.requestType === types_1.LLMRequestType.FUNCTION_CALL && !provider.capabilities.functionCalling) { return false; } if (criteria.requestType === types_1.LLMRequestType.EMBEDDING && !provider.capabilities.embeddings) { return false; } if (criteria.requestType === types_1.LLMRequestType.MODERATION && !provider.capabilities.moderation) { return false; } // Check additional capability requirements if (criteria.capabilities) { for (const capability of criteria.capabilities) { if (!provider.capabilities[capability]) { return false; } } } // Check model preference if (criteria.modelPreference) { const hasModel = provider.models.some(model => model.id === criteria.modelPreference || model.name.toLowerCase().includes(criteria.modelPreference.toLowerCase())); if (!hasModel) { return false; } } // Check excluded providers if (criteria.excludedProviders?.includes(provider.id)) { return false; } return true; }); } /** * Score a provider based on criteria */ scoreProvider(provider, criteria, request) { let score = 0; const weights = this.scoringWeights; // Capability score (0-1) const capabilityScore = this.calculateCapabilityScore(provider, criteria); score += capabilityScore * weights.capability; // Cost score (0-1, lower cost = higher score) const costScore = this.calculateCostScore(provider, criteria); score += costScore * weights.cost; // Performance score (0-1) const performanceScore = this.calculatePerformanceScore(provider, criteria); score += performanceScore * weights.performance; // Reliability score (0-1) const reliabilityScore = this.calculateReliabilityScore(provider); score += reliabilityScore * weights.reliability; // Preference score (0-1) const preferenceScore = this.calculatePreferenceScore(provider, criteria); score += preferenceScore * weights.preference; return Math.min(1, Math.max(0, score)); } /** * Calculate capability score */ calculateCapabilityScore(provider, criteria) { let score = 0; let checks = 0; // Base capability check const hasRequiredCapability = this.checkRequiredCapability(provider, criteria.requestType); score += hasRequiredCapability ? 1 : 0; checks++; // Additional capabilities if (criteria.capabilities) { for (const capability of criteria.capabilities) { const hasCapability = provider.capabilities[capability]; score += hasCapability ? 1 : 0; checks++; } } // Model availability if (criteria.modelPreference) { const hasPreferredModel = provider.models.some(model => model.id === criteria.modelPreference || model.name.toLowerCase().includes(criteria.modelPreference.toLowerCase())); score += hasPreferredModel ? 1 : 0; checks++; } return checks > 0 ? score / checks : 1; } /** * Check if provider has required capability for request type */ checkRequiredCapability(provider, requestType) { switch (requestType) { case types_1.LLMRequestType.COMPLETION: return provider.capabilities.completion; case types_1.LLMRequestType.STREAMING: return provider.capabilities.streaming; case types_1.LLMRequestType.FUNCTION_CALL: return provider.capabilities.functionCalling; case types_1.LLMRequestType.EMBEDDING: return provider.capabilities.embeddings; case types_1.LLMRequestType.MODERATION: return provider.capabilities.moderation; default: return true; } } /** * Calculate cost score */ calculateCostScore(provider, criteria) { if (!criteria.maxCost) { return 1; // No cost constraint } // Calculate average cost per model const costs = provider.models.map(model => model.inputCost + model.outputCost); const avgCost = costs.length > 0 ? costs.reduce((a, b) => a + b, 0) / costs.length : 0; if (avgCost > criteria.maxCost) { return 0; // Too expensive } // Higher score for lower cost return 1 - (avgCost / criteria.maxCost); } /** * Calculate performance score */ calculatePerformanceScore(provider, criteria) { // This would use historical performance metrics in a real implementation // For now, we'll use some heuristics based on model capabilities let score = 0.7; // Base score // Bonus for streaming capability if (provider.capabilities.streaming) { score += 0.1; } // Bonus for multimodal capability if (provider.capabilities.multimodal) { score += 0.1; } // Bonus for JSON mode if (provider.capabilities.jsonMode) { score += 0.1; } return Math.min(1, score); } /** * Calculate reliability score */ calculateReliabilityScore(provider) { // This would use historical reliability metrics in a real implementation // For now, we'll use provider status as a proxy switch (provider.status) { case types_1.ProviderStatus.AVAILABLE: return 1.0; case types_1.ProviderStatus.RATE_LIMITED: return 0.7; case types_1.ProviderStatus.MAINTENANCE: return 0.3; case types_1.ProviderStatus.ERROR: return 0.1; case types_1.ProviderStatus.UNAVAILABLE: return 0.0; default: return 0.5; } } /** * Calculate preference score */ calculatePreferenceScore(provider, criteria) { if (criteria.preferredProviders?.includes(provider.id)) { return 1.0; } return 0.5; // Neutral } /** * Get selection reason for provider */ getSelectionReason(provider, criteria) { const reasons = []; // Check why this provider was selected if (criteria.preferredProviders?.includes(provider.id)) { reasons.push('preferred provider'); } if (criteria.modelPreference) { const hasModel = provider.models.some(model => model.id === criteria.modelPreference || model.name.toLowerCase().includes(criteria.modelPreference.toLowerCase())); if (hasModel) { reasons.push(`supports ${criteria.modelPreference}`); } } // Add capability reasons const capabilities = []; if (provider.capabilities.functionCalling && criteria.requestType === types_1.LLMRequestType.FUNCTION_CALL) { capabilities.push('function calling'); } if (provider.capabilities.streaming && criteria.requestType === types_1.LLMRequestType.STREAMING) { capabilities.push('streaming'); } if (provider.capabilities.multimodal) { capabilities.push('multimodal'); } if (capabilities.length > 0) { reasons.push(`supports ${capabilities.join(', ')}`); } // Default reason if (reasons.length === 0) { reasons.push('best overall match'); } return reasons.join(', '); } /** * Update scoring weights */ updateScoringWeights(weights) { this.scoringWeights = { ...this.scoringWeights, ...weights }; // Normalize weights to sum to 1 const total = Object.values(this.scoringWeights).reduce((a, b) => a + b, 0); if (total > 0) { Object.keys(this.scoringWeights).forEach(key => { this.scoringWeights[key] /= total; }); } } /** * Get provider statistics */ getProviderStats() { const providers = this.getAllProviders(); const stats = { total: providers.length, available: providers.filter(p => p.status === types_1.ProviderStatus.AVAILABLE).length, byType: {}, byStatus: {} }; // Count by type (inferred from provider name/id) providers.forEach(provider => { const type = this.detectProviderType(provider.config); stats.byType[type] = (stats.byType[type] || 0) + 1; }); // Count by status providers.forEach(provider => { stats.byStatus[provider.status] = (stats.byStatus[provider.status] || 0) + 1; }); return stats; } /** * Clear all providers */ clear() { this.providers.clear(); } } exports.ProviderRegistry = ProviderRegistry; exports.default = ProviderRegistry; //# sourceMappingURL=provider-registry.js.map