UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

431 lines (430 loc) 14.7 kB
/** * Reranker Factory * * Factory for creating reranker instances with configuration. * Follows the BaseFactory pattern for consistent lifecycle management. */ import { BaseFactory } from "../../core/infrastructure/index.js"; import { logger } from "../../utils/logger.js"; import { RAGErrorCodes, RerankerError } from "../errors/RAGError.js"; /** * Default reranker metadata entries */ const DEFAULT_RERANKER_METADATA = { llm: { description: "LLM-powered semantic reranking with multi-factor scoring", defaultConfig: { topK: 3, weights: { semantic: 0.4, vector: 0.4, position: 0.2 }, }, supportedOptions: ["model", "provider", "topK", "weights"], useCases: [ "High-quality semantic reranking", "Complex query understanding", "Context-aware scoring", ], aliases: ["semantic", "ai", "model-based"], requiresModel: true, requiresExternalAPI: false, }, "cross-encoder": { description: "Cross-encoder model for query-document relevance scoring", defaultConfig: { topK: 3, model: "ms-marco-MiniLM-L-6-v2", }, supportedOptions: ["model", "topK"], useCases: [ "High-precision reranking", "Search result refinement", "Academic/research applications", ], aliases: ["cross", "encoder", "bi-encoder"], requiresModel: true, requiresExternalAPI: false, }, cohere: { description: "Cohere Rerank API for production-grade relevance scoring", defaultConfig: { topK: 3, model: "rerank-v3.5", }, supportedOptions: ["model", "topK", "apiKey"], useCases: [ "Production search systems", "Enterprise applications", "High-volume reranking", ], aliases: ["cohere-rerank", "cohere-api"], requiresModel: false, requiresExternalAPI: true, }, simple: { description: "Position and vector score-based reranking (no LLM required)", defaultConfig: { topK: 3, weights: { vector: 0.8, position: 0.2 }, }, supportedOptions: ["topK", "weights"], useCases: [ "Fast reranking", "Low-latency requirements", "When LLM is unavailable", ], aliases: ["fast", "basic", "position-based"], requiresModel: false, requiresExternalAPI: false, }, batch: { description: "Batch LLM reranking for efficient multi-document scoring", defaultConfig: { topK: 3, weights: { semantic: 0.4, vector: 0.4, position: 0.2 }, }, supportedOptions: ["model", "provider", "topK", "weights"], useCases: [ "Large result sets", "Cost-efficient LLM usage", "Batch processing pipelines", ], aliases: ["batch-llm", "efficient", "bulk"], requiresModel: true, requiresExternalAPI: false, }, }; /** * Reranker Factory * * Creates reranker instances based on type with configuration support. * Uses lazy loading via dynamic imports to avoid circular dependencies. */ export class RerankerFactory extends BaseFactory { static instance = null; metadataMap = new Map(); modelProvider = null; constructor() { super(); } /** * Get singleton instance */ static getInstance() { if (!RerankerFactory.instance) { RerankerFactory.instance = new RerankerFactory(); } return RerankerFactory.instance; } /** * Reset singleton (for testing) */ static resetInstance() { if (RerankerFactory.instance) { RerankerFactory.instance.clear(); RerankerFactory.instance = null; } } /** * Set the AI provider for LLM-based rerankers */ setModelProvider(provider) { this.modelProvider = provider; } /** * Register all default rerankers */ async registerAll() { // Register LLM reranker this.registerReranker("llm", async (config) => { const { rerank } = await import("./reranker.js"); return this.createLLMReranker(rerank, config); }, DEFAULT_RERANKER_METADATA.llm); // Register cross-encoder reranker this.registerReranker("cross-encoder", async (config) => { const { CrossEncoderReranker } = await import("./reranker.js"); return this.createCrossEncoderReranker(CrossEncoderReranker, config); }, DEFAULT_RERANKER_METADATA["cross-encoder"]); // Register Cohere reranker this.registerReranker("cohere", async (config) => { const { CohereRelevanceScorer } = await import("./reranker.js"); return this.createCohereReranker(CohereRelevanceScorer, config); }, DEFAULT_RERANKER_METADATA.cohere); // Register simple reranker this.registerReranker("simple", async (config) => { const { simpleRerank } = await import("./reranker.js"); return this.createSimpleReranker(simpleRerank, config); }, DEFAULT_RERANKER_METADATA.simple); // Register batch reranker this.registerReranker("batch", async (config) => { const { batchRerank } = await import("./reranker.js"); return this.createBatchReranker(batchRerank, config); }, DEFAULT_RERANKER_METADATA.batch); logger.debug(`[RerankerFactory] Registered ${this.items.size} reranker types`); } /** * Create LLM-based reranker wrapper */ createLLMReranker(rerankFn, config) { const factory = this; return { type: "llm", async rerank(results, query, options) { if (!factory.modelProvider) { throw new RerankerError("LLM reranker requires a model provider. Call setModelProvider() first.", { rerankerType: "llm", }); } return rerankFn(results, query, factory.modelProvider, { ...options, topK: config?.topK ?? options?.topK, weights: config?.weights ?? options?.weights, }); }, }; } /** * Create cross-encoder reranker wrapper */ createCrossEncoderReranker(CrossEncoderClass, config) { const encoder = new CrossEncoderClass(typeof config?.model === "string" ? config.model : undefined); return { type: "cross-encoder", async rerank(results, query, options) { const documents = results.map((r) => r.text || r.metadata?.text || ""); const scores = await encoder.rerank(query, documents); const topK = config?.topK ?? options?.topK ?? 3; return scores .map((s) => ({ result: results[s.index], score: s.score, details: { semantic: s.score, vector: results[s.index].score ?? 0, position: 1 - s.index / results.length, }, })) .sort((a, b) => b.score - a.score) .slice(0, topK); }, }; } /** * Create Cohere reranker wrapper */ createCohereReranker(CohereClass, config) { const scorer = new CohereClass(typeof config?.model === "string" ? config.model : undefined); return { type: "cohere", async rerank(results, query, options) { const documents = results.map((r) => r.text || r.metadata?.text || ""); const scores = await scorer.score(query, documents); const topK = config?.topK ?? options?.topK ?? 3; return scores .map((s) => ({ result: results[s.index], score: s.score, details: { semantic: s.score, vector: results[s.index].score ?? 0, position: 1 - s.index / results.length, }, })) .sort((a, b) => b.score - a.score) .slice(0, topK); }, }; } /** * Create simple reranker wrapper */ createSimpleReranker(simpleRerankFn, config) { return { type: "simple", async rerank(results, _query, options) { return simpleRerankFn(results, { topK: config?.topK ?? options?.topK, vectorWeight: config?.weights?.vector, positionWeight: config?.weights?.position, }); }, }; } /** * Create batch reranker wrapper */ createBatchReranker(batchRerankFn, config) { const factory = this; return { type: "batch", async rerank(results, query, options) { if (!factory.modelProvider) { throw new RerankerError("Batch reranker requires a model provider. Call setModelProvider() first.", { rerankerType: "batch", }); } return batchRerankFn(results, query, factory.modelProvider, { ...options, topK: config?.topK ?? options?.topK, weights: config?.weights ?? options?.weights, }); }, }; } /** * Register a reranker with metadata and aliases */ registerReranker(type, factory, metadata) { // Store metadata this.metadataMap.set(type, metadata); // Register with aliases this.register(type, factory, metadata.aliases, { metadata }); logger.debug(`[RerankerFactory] Registered reranker '${type}' with aliases: ${metadata.aliases.join(", ")}`); } /** * Create a reranker by type or alias */ async createReranker(typeOrAlias, config) { await this.ensureInitialized(); const resolvedName = this.resolveName(typeOrAlias); if (!this.has(resolvedName)) { const available = this.getAvailable(); throw new RerankerError(`Unknown reranker type: '${typeOrAlias}'. Available types: ${available.join(", ")}`, { code: RAGErrorCodes.RERANKER_NOT_FOUND, rerankerType: typeOrAlias, details: { requestedType: typeOrAlias, availableTypes: available, }, }); } try { const reranker = await this.create(resolvedName, config); logger.debug(`[RerankerFactory] Created reranker '${resolvedName}' with config:`, config); return reranker; } catch (error) { // Re-throw if already a RerankerError if (error instanceof RerankerError) { throw error; } throw new RerankerError(`Failed to create reranker '${resolvedName}': ${error instanceof Error ? error.message : String(error)}`, { rerankerType: resolvedName, cause: error instanceof Error ? error : undefined, details: { type: resolvedName, config }, }); } } /** * Get metadata for a reranker */ getRerankerMetadata(typeOrAlias) { const resolvedName = this.resolveName(typeOrAlias); return this.metadataMap.get(resolvedName); } /** * Get default configuration for a reranker */ getDefaultConfig(typeOrAlias) { const metadata = this.getRerankerMetadata(typeOrAlias); return metadata?.defaultConfig; } /** * Get available reranker types (not including aliases) */ async getAvailableTypes() { await this.ensureInitialized(); return this.getAvailable(); } /** * Get all aliases mapped to their types */ getTypeAliases() { return this.getAliases(); } /** * Check if a type exists */ hasType(typeOrAlias) { const resolved = this.resolveName(typeOrAlias); return this.has(resolved); } /** * Get rerankers suitable for a use case */ getRerankersForUseCase(useCase) { const matches = []; const useCaseLower = useCase.toLowerCase(); for (const [type, metadata] of this.metadataMap) { const hasMatch = metadata.useCases.some((uc) => uc.toLowerCase().includes(useCaseLower)); if (hasMatch) { matches.push(type); } } return matches; } /** * Get rerankers that don't require external APIs */ getLocalRerankers() { const matches = []; for (const [type, metadata] of this.metadataMap) { if (!metadata.requiresExternalAPI) { matches.push(type); } } return matches; } /** * Get rerankers that don't require AI models */ getModelFreeRerankers() { const matches = []; for (const [type, metadata] of this.metadataMap) { if (!metadata.requiresModel) { matches.push(type); } } return matches; } /** * Get all reranker metadata */ getAllMetadata() { return new Map(this.metadataMap); } /** * Clear factory and metadata */ clear() { super.clear(); this.metadataMap.clear(); this.modelProvider = null; } } /** * Global reranker factory singleton */ export const rerankerFactory = RerankerFactory.getInstance(); /** * Convenience function to create a reranker */ export async function createReranker(typeOrAlias, config) { return rerankerFactory.createReranker(typeOrAlias, config); } /** * Convenience function to get available reranker types */ export async function getAvailableRerankerTypes() { return rerankerFactory.getAvailableTypes(); } /** * Convenience function to get reranker metadata */ export function getRerankerMetadata(typeOrAlias) { return rerankerFactory.getRerankerMetadata(typeOrAlias); } /** * Convenience function to get default config */ export function getRerankerDefaultConfig(typeOrAlias) { return rerankerFactory.getDefaultConfig(typeOrAlias); }