Skip to main content
Glama
runtime-embeddings.ts4.36 kB
/** * Runtime Embeddings - Lazy-Loaded Neural Search * * Provides optional neural embeddings for query matching. * Uses @xenova/transformers which downloads ~50MB model on first use. * * This module is lazy-loaded to avoid impacting startup time. * Falls back gracefully to TF-IDF if model is unavailable. * * @package WP_Navigator_MCP * @since 2.7.0 */ // Pipeline type from @xenova/transformers type Pipeline = { (text: string | string[]): Promise<{ data: Float32Array }[]>; }; // Module state let pipeline: Pipeline | null = null; let isLoading = false; let loadError: Error | null = null; /** * Model to use for embeddings * all-MiniLM-L6-v2 is a good balance of size (~23MB) and quality */ const MODEL_NAME = 'Xenova/all-MiniLM-L6-v2'; /** * Check if the embedding model is available * * @returns True if model can be loaded (package installed) */ export function isModelAvailable(): boolean { try { // Check if @xenova/transformers is installed // This is a devDependency, so may not be present at runtime require.resolve('@xenova/transformers'); return true; } catch { return false; } } /** * Check if the pipeline is ready for use */ export function isPipelineReady(): boolean { return pipeline !== null; } /** * Get the last load error, if any */ export function getLoadError(): Error | null { return loadError; } /** * Load the embedding pipeline (lazy initialization) * * Downloads the model on first use (~50MB). * Returns null if model is not available. */ export async function loadPipeline(): Promise<Pipeline | null> { if (pipeline !== null) { return pipeline; } if (loadError !== null) { return null; } if (isLoading) { // Wait for existing load to complete while (isLoading) { await new Promise((resolve) => setTimeout(resolve, 100)); } return pipeline; } isLoading = true; try { // Dynamic import to avoid bundling if not used // The module name is constructed dynamically to prevent TypeScript from // analyzing it at compile time (it's an optional devDependency) const moduleName = '@xenova/transformers'; // eslint-disable-next-line @typescript-eslint/no-explicit-any const transformers = await (Function('m', 'return import(m)')(moduleName) as Promise<any>); // Create feature extraction pipeline pipeline = (await transformers.pipeline('feature-extraction', MODEL_NAME, { quantized: true, // Use quantized model for faster inference })) as Pipeline; return pipeline; } catch (error) { loadError = error instanceof Error ? error : new Error(String(error)); console.warn(`Failed to load embedding model: ${loadError.message}`); return null; } finally { isLoading = false; } } /** * Generate embedding for a text query * * @param query Text to embed * @returns 384-dimensional embedding vector, or null if unavailable */ export async function embedQuery(query: string): Promise<number[] | null> { const pipe = await loadPipeline(); if (!pipe) { return null; } try { const result = await pipe(query); // Result is an array of embeddings (one per input) // Each embedding has shape [1, sequence_length, 384] // We need to mean-pool across the sequence dimension const embedding = result[0]; if (!embedding || !embedding.data) { return null; } // Convert Float32Array to regular array return Array.from(embedding.data); } catch (error) { console.warn(`Failed to generate embedding: ${error}`); return null; } } /** * Generate embeddings for multiple texts * * @param texts Array of texts to embed * @returns Array of 384-dimensional embedding vectors */ export async function embedTexts(texts: string[]): Promise<(number[] | null)[]> { const pipe = await loadPipeline(); if (!pipe) { return texts.map(() => null); } try { const results = await pipe(texts); return results.map((result) => { if (!result || !result.data) { return null; } return Array.from(result.data); }); } catch (error) { console.warn(`Failed to generate embeddings: ${error}`); return texts.map(() => null); } } /** * Unload the pipeline to free memory */ export function unloadPipeline(): void { pipeline = null; loadError = null; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/littlebearapps/wp-navigator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server