@nullplatform/llm-gateway
Version:
LLM Gateway Core - Main proxy server
317 lines • 14.4 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtensionsLoader = void 0;
const logger_1 = require("../utils/logger");
const path_1 = __importDefault(require("path"));
const child_process_1 = require("child_process");
const fs_1 = require("fs");
class ExtensionsLoader {
plugins = new Map();
adapters = new Map();
providers = new Map();
logger;
config;
constructor(config, logger) {
this.logger = logger || new logger_1.Logger();
this.config = config;
}
getPluginBuilders() {
return this.plugins;
}
getAdapterBuilders() {
return this.adapters;
}
getProviderBuilders() {
return this.providers;
}
async initializeExtensions() {
const availableExtensions = this.config || [];
for (const extension of availableExtensions) {
let module;
if (extension.path) {
module = path_1.default.resolve(extension.path);
}
else if (extension.module) {
module = await this.resolveModule(extension.module);
}
else {
throw new Error(`Plugin must have either path or module specified at ${JSON.stringify(extension)}`);
}
try {
const extensionModule = await Promise.resolve(`${module}`).then(s => __importStar(require(s)));
const discoveredPlugins = await this.discoverAndRegisterExtensionType("plugin", extensionModule, this.implementsIPlugin.bind(this));
for (const [name, plugin] of discoveredPlugins) {
this.plugins.set(name, plugin);
}
const discoveredProviders = await this.discoverAndRegisterExtensionType("llm-provider", extensionModule, this.implementsIProvider.bind(this));
for (const [name, plugin] of discoveredProviders) {
this.providers.set(name, plugin);
}
const discoveredAdapters = await this.discoverAndRegisterExtensionType("adapter", extensionModule, this.implementsILLMAdapter.bind(this));
for (const [name, plugin] of discoveredAdapters) {
this.adapters.set(name, plugin);
}
}
catch (error) {
this.logger.error(`Failed to load plugin module ${module}:`, error);
throw error;
}
}
}
async resolveModule(moduleName) {
// First try to resolve as local module
try {
// Try to resolve locally first
const localPath = require.resolve(moduleName);
this.logger.info(`Resolved module ${moduleName} locally at: ${localPath}`);
return moduleName; // Return the module name for dynamic import
}
catch (localError) {
this.logger.warn(`Failed to resolve module ${moduleName} locally:`, localError.message);
// If local resolution fails, try global
try {
const globalPath = this.getGlobalModulePath(moduleName);
if (globalPath && (0, fs_1.existsSync)(globalPath)) {
this.logger.info(`Resolved module ${moduleName} globally at: ${globalPath}`);
// Try to find the correct entry point for the global module
return this.findModuleEntryPoint(globalPath, moduleName);
}
else {
throw new Error(`Global module path not found: ${globalPath}`);
}
}
catch (globalError) {
this.logger.error(`Failed to resolve module ${moduleName} globally:`, globalError.message);
// As a last resort, try the module name directly (sometimes works with global modules)
this.logger.warn(`Attempting to import ${moduleName} directly as fallback`);
return moduleName;
}
}
}
getGlobalModulePath(moduleName) {
try {
// Get global node_modules path
const globalRoot = (0, child_process_1.execSync)('npm root -g', { encoding: 'utf8' }).trim();
const globalModulePath = path_1.default.join(globalRoot, moduleName);
this.logger.debug(`Checking global module path: ${globalModulePath}`);
return globalModulePath;
}
catch (error) {
this.logger.error('Failed to get global npm root:', error);
// Fallback: try common global paths
const commonGlobalPaths = [
'/usr/local/lib/node_modules',
'/usr/lib/node_modules',
path_1.default.join(process.env.HOME || '', '.npm-global/lib/node_modules'),
path_1.default.join(process.env.APPDATA || '', 'npm/node_modules') // Windows
];
for (const globalPath of commonGlobalPaths) {
const modulePath = path_1.default.join(globalPath, moduleName);
if ((0, fs_1.existsSync)(modulePath)) {
this.logger.info(`Found global module at fallback path: ${modulePath}`);
return modulePath;
}
}
return null;
}
}
findModuleEntryPoint(modulePath, moduleName) {
try {
// First, try to read package.json to get the correct entry point
const packageJsonPath = path_1.default.join(modulePath, 'package.json');
if ((0, fs_1.existsSync)(packageJsonPath)) {
const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf8'));
this.logger.debug(`Package.json for ${moduleName}:`, {
main: packageJson.main,
module: packageJson.module,
exports: packageJson.exports
});
// Check for various entry points in order of preference
const entryPoints = [
packageJson.main,
packageJson.module,
packageJson.exports?.['.']?.import,
packageJson.exports?.['.']?.require,
packageJson.exports?.['.'],
// Common built entry points
'dist/index.js',
'lib/index.js',
'build/index.js',
// TypeScript source files (fallback if not built)
'index.ts',
'basic-apikey-auth/index.ts',
// CommonJS fallbacks
'index.js',
'index.mjs'
];
for (const entryPoint of entryPoints) {
if (entryPoint) {
const fullPath = path_1.default.resolve(modulePath, entryPoint);
this.logger.debug(`Checking entry point: ${fullPath}`);
if ((0, fs_1.existsSync)(fullPath)) {
this.logger.info(`Found entry point for ${moduleName}: ${fullPath}`);
return fullPath;
}
}
}
this.logger.warn(`No valid entry point found in package.json for ${moduleName}`);
}
// Fallback: try common entry point files
const commonEntryPoints = [
'dist/index.js', // Most likely for your package
'index.js',
'index.ts', // TypeScript source if not built
'basic-apikey-auth/index.ts', // Source file location
'index.mjs',
'lib/index.js',
'build/index.js',
'main.js'
];
this.logger.debug(`Trying fallback entry points for ${moduleName}`);
for (const entryPoint of commonEntryPoints) {
const fullPath = path_1.default.join(modulePath, entryPoint);
this.logger.debug(`Checking fallback: ${fullPath}`);
if ((0, fs_1.existsSync)(fullPath)) {
this.logger.info(`Found fallback entry point for ${moduleName}: ${fullPath}`);
return fullPath;
}
}
// Debug: List what's actually in the directory
try {
const files = (0, fs_1.readdirSync)(modulePath);
this.logger.warn(`Contents of ${modulePath}:`, files);
}
catch (e) {
this.logger.error(`Could not read directory ${modulePath}:`, e);
}
// If we can't find a specific entry point, return the module path and let Node.js try to resolve it
this.logger.warn(`Could not find entry point for ${moduleName}, using module path: ${modulePath}`);
return modulePath;
}
catch (error) {
this.logger.error(`Error finding entry point for ${moduleName}:`, error);
return modulePath;
}
}
async discoverAndRegisterExtensionType(extensionTypeName = "UnknownType", extensionModule, typeValidator) {
// Get all exported values from the module
const extensionType = new Map();
const exports = Object.keys(extensionModule).map(key => ({
name: key,
value: extensionModule[key]
}));
for (const exported of exports) {
const { name, value } = exported;
// Check if it's a class constructor
if (this.isConstructor(value)) {
// Check if it's not abstract and implements IPlugin
if (typeValidator(value) && !this.isAbstractClass(value)) {
try {
// Get plugin metadata - assuming it's a static property or method
const metadata = this.getExtensionMetadata(value);
if (metadata && metadata.name) {
extensionType.set(metadata.name, value);
this.logger.info(`Registered extension of type [${extensionTypeName}] : ${metadata.name}`);
}
else {
this.logger.warn(`Plugin class ${name} doesn't have valid metadata`);
}
}
catch (error) {
this.logger.error(`Failed to register plugin ${name}:`, error);
}
}
}
}
return extensionType;
}
isConstructor(obj) {
return typeof obj === 'function' &&
obj.prototype &&
obj.prototype.constructor === obj;
}
implementsILLMAdapter(constructor) {
const prototype = constructor.prototype;
const metadata = constructor.metadata || prototype.metadata;
return (prototype &&
metadata &&
typeof prototype === 'object' &&
typeof prototype.configure === 'function' &&
(typeof prototype.transformInput === 'function' &&
typeof prototype.transformOutput === 'function' &&
typeof prototype.transformOutputChunk === 'function'));
}
implementsIProvider(constructor) {
const prototype = constructor.prototype;
const metadata = constructor.metadata || prototype.metadata;
return (prototype &&
metadata &&
typeof prototype === 'object' &&
typeof prototype.configure === 'function' &&
(typeof prototype.execute === 'function' &&
typeof prototype.executeStreaming === 'function'));
}
implementsIPlugin(constructor) {
const prototype = constructor.prototype;
const metadata = constructor.metadata || prototype.metadata;
return (prototype &&
metadata &&
typeof prototype === 'object' &&
typeof prototype.configure === 'function' &&
(typeof prototype.beforeModel === 'function' ||
typeof prototype.afterModel === 'function' ||
typeof prototype.configure === 'function'));
}
isAbstractClass(constructor) {
return constructor.prototype &&
Object.getOwnPropertyNames(constructor.prototype)
.some(prop => constructor.prototype[prop] === undefined);
}
getExtensionMetadata(constructor) {
if (constructor.metadata) {
return constructor.metadata;
}
if (constructor.prototype.metadata) {
return constructor.prototype.metadata;
}
throw new Error(`Plugin class ${constructor.name} does not have metadata defined`);
}
}
exports.ExtensionsLoader = ExtensionsLoader;
//# sourceMappingURL=extentionsLoader.js.map
;