Skip to main content
Glama
vector-store.ts3.42 kB
import { LocalIndex } from "vectra"; import path from "path"; import fs from "fs/promises"; export interface VectorDocument { id: string; content: string; metadata?: Record<string, any>; } export interface SearchResult { id: string; score: number; metadata?: Record<string, any>; } export class VectorStore { private index!: LocalIndex; private embedder: any; private dataDir: string; private indexPath: string; private initialized = false; constructor(dataDir: string) { this.dataDir = dataDir; this.indexPath = path.join(dataDir, "vectors.index"); } async initialize(): Promise<void> { // Ensure data directory exists await fs.mkdir(this.dataDir, { recursive: true }); // Initialize the embedding model using dynamic import const { pipeline } = await import("@xenova/transformers"); this.embedder = await pipeline( "feature-extraction", "Xenova/all-MiniLM-L6-v2" ); // Check if index exists try { await fs.access(this.indexPath); // Load existing index this.index = new LocalIndex(this.indexPath); } catch { // Create new index this.index = new LocalIndex(this.indexPath); await this.index.createIndex(); } this.initialized = true; } async add(document: VectorDocument): Promise<void> { if (!this.initialized) { throw new Error("VectorStore not initialized"); } // Generate embedding for the content const embedding = await this.generateEmbedding(document.content); // Add to index await this.index.insertItem({ vector: embedding, metadata: { id: document.id, ...document.metadata, }, }); } async search( query: string, limit: number = 5, threshold: number = 0.7 ): Promise<SearchResult[]> { if (!this.initialized) { throw new Error("VectorStore not initialized"); } // Generate embedding for the query const queryEmbedding = await this.generateEmbedding(query); // Search the index - updated API call const results = await this.index.queryItems(queryEmbedding, query, limit); // Filter by threshold and format results return results .filter((result: any) => result.score >= threshold) .map((result: any) => ({ id: result.item.metadata.id as string, score: result.score, metadata: result.item.metadata, })); } async delete(id: string): Promise<boolean> { if (!this.initialized) { throw new Error("VectorStore not initialized"); } // Find the item by id in metadata const allItems = await this.index.listItems(); const itemToDelete = allItems.find((item: any) => item.metadata?.id === id); if (itemToDelete) { await this.index.deleteItem(itemToDelete.id); return true; } return false; } async update(document: VectorDocument): Promise<void> { // Delete existing and add new await this.delete(document.id); await this.add(document); } private async generateEmbedding(text: string): Promise<number[]> { // Generate embeddings using the model const output = await this.embedder(text, { pooling: "mean", normalize: true, }); // Convert to array return Array.from(output.data); } async close(): Promise<void> { // Vectra handles cleanup automatically this.initialized = false; } }

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/notbnull/mcp-rag-context'

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