anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
454 lines • 16.7 kB
JavaScript
"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