anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
404 lines • 14.2 kB
JavaScript
"use strict";
/**
* Provider-Agnostic Base Interface for MCP
*
* Abstract base classes and interfaces for LLM providers
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseProviderFactory = exports.BaseLLMProvider = void 0;
const events_1 = require("events");
const types_1 = require("../types");
/**
* Abstract base provider class
*/
class BaseLLMProvider extends events_1.EventEmitter {
constructor(config) {
super();
this.requestCount = 0;
this.errorCount = 0;
this.lastRequestTime = null;
this.lastErrorTime = null;
this.id = config.id;
this.name = config.id;
this.version = '1.0.0';
this.description = `${config.id} LLM provider`;
this.config = config;
this.status = types_1.ProviderStatus.UNAVAILABLE;
// Default capabilities - subclasses should override
this.capabilities = {
completion: true,
streaming: false,
functionCalling: false,
embeddings: false,
moderation: false,
multimodal: false,
codeGeneration: false,
jsonMode: false
};
// Default rate limits - subclasses should override
this.rateLimits = config.rateLimits || {
requestsPerMinute: 60,
tokensPerMinute: 100000,
requestsPerDay: 1000,
tokensPerDay: 1000000,
concurrentRequests: 10
};
// Default models - subclasses should populate
this.models = [];
}
/**
* Process request based on type
*/
async processRequest(request, context) {
this.validateRequest(request);
this.updateMetrics();
try {
let response;
switch (request.type) {
case types_1.LLMRequestType.COMPLETION:
response = await this.completion(request, context);
break;
case types_1.LLMRequestType.FUNCTION_CALL:
response = await this.completion(request, context);
break;
case types_1.LLMRequestType.EMBEDDING:
if (!this.capabilities.embeddings) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.FUNCTION_NOT_FOUND,
message: 'Embeddings not supported by this provider',
timestamp: new Date(),
provider: this.id,
retryable: false
});
}
response = await this.embed(request, context);
break;
case types_1.LLMRequestType.MODERATION:
if (!this.capabilities.moderation) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.FUNCTION_NOT_FOUND,
message: 'Moderation not supported by this provider',
timestamp: new Date(),
provider: this.id,
retryable: false
});
}
response = await this.moderate(request, context);
break;
case types_1.LLMRequestType.STREAMING:
throw new types_1.MCPError({
code: types_1.MCPErrorCode.INVALID_REQUEST,
message: 'Use stream() method for streaming requests',
timestamp: new Date(),
provider: this.id,
retryable: false
});
default:
throw new types_1.MCPError({
code: types_1.MCPErrorCode.INVALID_REQUEST,
message: `Unsupported request type: ${request.type}`,
timestamp: new Date(),
provider: this.id,
retryable: false
});
}
this.emit('request_completed', { request, response, context });
return response;
}
catch (error) {
this.handleError(error, request, context);
throw error;
}
}
/**
* Process streaming request
*/
async *processStreamingRequest(request, context) {
this.validateRequest(request);
this.updateMetrics();
if (!this.capabilities.streaming) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.FUNCTION_NOT_FOUND,
message: 'Streaming not supported by this provider',
timestamp: new Date(),
provider: this.id,
retryable: false
});
}
try {
for await (const chunk of this.stream(request, context)) {
this.emit('stream_chunk', { request, chunk, context });
yield chunk;
}
this.emit('stream_completed', { request, context });
}
catch (error) {
this.handleError(error, request, context);
throw error;
}
}
/**
* Validate request before processing
*/
validateRequest(request) {
if (!request.id) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.INVALID_REQUEST,
message: 'Request ID is required',
timestamp: new Date(),
provider: this.id,
retryable: false
});
}
if (!request.prompt && request.type !== types_1.LLMRequestType.EMBEDDING) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.INVALID_REQUEST,
message: 'Prompt is required',
timestamp: new Date(),
provider: this.id,
requestId: request.id,
retryable: false
});
}
if (!request.agentDID) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.INVALID_REQUEST,
message: 'Agent DID is required',
timestamp: new Date(),
provider: this.id,
requestId: request.id,
retryable: false
});
}
// Validate function calling requirements
if (request.type === types_1.LLMRequestType.FUNCTION_CALL) {
if (!this.capabilities.functionCalling) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.FUNCTION_NOT_FOUND,
message: 'Function calling not supported by this provider',
timestamp: new Date(),
provider: this.id,
requestId: request.id,
retryable: false
});
}
if (!request.functions || request.functions.length === 0) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.INVALID_REQUEST,
message: 'Functions are required for function calling',
timestamp: new Date(),
provider: this.id,
requestId: request.id,
retryable: false
});
}
}
}
/**
* Create base response structure
*/
createBaseResponse(request, context) {
return {
id: `resp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
requestId: request.id,
type: request.type,
provider: this.id,
model: request.parameters?.model || this.config.defaultModel,
timestamp: new Date(),
status: types_1.ResponseStatus.SUCCESS
};
}
/**
* Create error response
*/
createErrorResponse(request, context, error) {
const baseResponse = this.createBaseResponse(request, context);
const mcpError = error instanceof types_1.MCPError ? error : new types_1.MCPError({
code: types_1.MCPErrorCode.PROVIDER_ERROR,
message: error.message,
timestamp: new Date(),
provider: this.id,
requestId: request.id,
retryable: false
});
return {
...baseResponse,
status: types_1.ResponseStatus.ERROR,
error: mcpError
};
}
/**
* Calculate usage information
*/
calculateUsage(request, response, model) {
// Simplified token calculation - in production this would be more accurate
const promptTokens = Math.ceil((request.prompt?.length || 0) / 4);
const completionTokens = Math.ceil(response.length / 4);
const totalTokens = promptTokens + completionTokens;
return {
promptTokens,
completionTokens,
totalTokens,
model,
provider: this.id,
cost: this.calculateCost(promptTokens, completionTokens, model)
};
}
/**
* Calculate cost based on token usage
*/
calculateCost(promptTokens, completionTokens, model) {
const modelInfo = this.models.find(m => m.id === model);
if (!modelInfo)
return 0;
const promptCost = (promptTokens / 1000) * modelInfo.inputCost;
const completionCost = (completionTokens / 1000) * modelInfo.outputCost;
return promptCost + completionCost;
}
/**
* Handle errors
*/
handleError(error, request, context) {
this.errorCount++;
this.lastErrorTime = new Date();
this.emit('request_error', {
error,
request,
context,
provider: this.id
});
// Update provider status based on error frequency
const errorRate = this.errorCount / this.requestCount;
if (errorRate > 0.1) { // More than 10% error rate
this.status = types_1.ProviderStatus.ERROR;
}
}
/**
* Update request metrics
*/
updateMetrics() {
this.requestCount++;
this.lastRequestTime = new Date();
// Reset status if it was in error state and enough time has passed
if (this.status === types_1.ProviderStatus.ERROR && this.lastErrorTime) {
const timeSinceError = Date.now() - this.lastErrorTime.getTime();
if (timeSinceError > 5 * 60 * 1000) { // 5 minutes
this.status = types_1.ProviderStatus.AVAILABLE;
}
}
}
/**
* Convert function definitions to provider-specific format
*/
convertFunctions(functions) {
// Base implementation - subclasses should override for provider-specific formats
return functions.map(fn => ({
name: fn.name,
description: fn.description,
parameters: fn.parameters
}));
}
/**
* Extract function calls from provider response
*/
extractFunctionCalls(response) {
// Base implementation - subclasses should override for provider-specific parsing
const calls = [];
if (response.function_call) {
calls.push({
name: response.function_call.name,
arguments: JSON.parse(response.function_call.arguments || '{}'),
id: `call-${Date.now()}`
});
}
return calls;
}
/**
* Get provider statistics
*/
getStats() {
return {
requestCount: this.requestCount,
errorCount: this.errorCount,
errorRate: this.requestCount > 0 ? this.errorCount / this.requestCount : 0,
lastRequestTime: this.lastRequestTime,
lastErrorTime: this.lastErrorTime,
status: this.status
};
}
/**
* Reset statistics
*/
resetStats() {
this.requestCount = 0;
this.errorCount = 0;
this.lastRequestTime = null;
this.lastErrorTime = null;
this.status = types_1.ProviderStatus.AVAILABLE;
}
/**
* Get supported models
*/
getSupportedModels() {
return [...this.models];
}
/**
* Check if model is supported
*/
isModelSupported(modelId) {
return this.models.some(model => model.id === modelId);
}
/**
* Get default model for request type
*/
getDefaultModel(requestType) {
// Return configured default or first available model
if (this.config.defaultModel && this.isModelSupported(this.config.defaultModel)) {
return this.config.defaultModel;
}
return this.models.length > 0 ? this.models[0].id : '';
}
/**
* Validate model availability
*/
validateModel(modelId) {
if (modelId && !this.isModelSupported(modelId)) {
throw new types_1.MCPError({
code: types_1.MCPErrorCode.MODEL_NOT_FOUND,
message: `Model ${modelId} not supported by provider ${this.id}`,
timestamp: new Date(),
provider: this.id,
retryable: false
});
}
}
/**
* Shutdown provider
*/
async shutdown() {
this.status = types_1.ProviderStatus.UNAVAILABLE;
this.removeAllListeners();
}
}
exports.BaseLLMProvider = BaseLLMProvider;
/**
* Abstract provider factory
*/
class BaseProviderFactory {
/**
* Validate base configuration requirements
*/
validateBaseConfig(config) {
if (!config.id || typeof config.id !== 'string')
return false;
if (!config.endpoint || typeof config.endpoint !== 'string')
return false;
if (config.enabled !== undefined && typeof config.enabled !== 'boolean')
return false;
return true;
}
}
exports.BaseProviderFactory = BaseProviderFactory;
// Export interfaces inline above
// export { ProviderRequestContext, ProviderResponseContext };
//# sourceMappingURL=base-provider.js.map