{"version":3,"sources":["../../../src/plugins/vector/plugin.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Vector Search Plugin for GQP\n * Enables semantic search on text fields\n */\n\nimport type {\n    GQPPlugin,\n    GQPNode,\n    QueryFilters,\n    PluginContext,\n    GQPEngine,\n} from \"../../core/types.js\";\n\n/**\n * Vector provider configuration\n */\nexport interface VectorProviderConfig {\n    /** Pinecone configuration */\n    pinecone?: {\n        apiKey: string;\n        index: string;\n        environment?: string;\n    };\n    /** OpenAI embedding configuration */\n    openai?: {\n        apiKey: string;\n        model?: string;\n    };\n    /** Custom embedding function */\n    customEmbed?: (text: string) => Promise<number[]>;\n    /** Custom search function */\n    customSearch?: (\n        namespace: string,\n        embedding: number[],\n        limit: number\n    ) => Promise<Array<{ id: string; score: number }>>;\n}\n\n/**\n * Auto-indexing configuration\n */\nexport interface AutoIndexConfig {\n    /** Enable auto-indexing */\n    enabled: boolean;\n    /** Fields to index (e.g., ['Order.customerNotes', 'Product.description']) */\n    fields?: string[];\n    /** Batch size for indexing */\n    batchSize?: number;\n}\n\n/**\n * Vector plugin configuration\n */\nexport interface VectorPluginConfig {\n    /** Vector provider */\n    provider: \"pinecone\" | \"custom\";\n    /** Provider-specific configuration */\n    config: VectorProviderConfig;\n    /** Similarity threshold (0-1) */\n    threshold?: number;\n    /** Max results from vector search before SQL filtering */\n    maxResults?: number;\n    /** Enable hybrid search (vector + SQL) */\n    hybrid?: boolean;\n    /** Auto-indexing configuration */\n    autoIndex?: AutoIndexConfig;\n}\n\n/**\n * Vector Search Plugin\n *\n * @example\n * ```typescript\n * import { GQP } from '@mzhub/gqp';\n * import { VectorPlugin } from '@mzhub/gqp/vector';\n *\n * const graph = new GQP({\n *   sources: { db: fromPrisma(prisma) },\n *   plugins: [\n *     new VectorPlugin({\n *       provider: 'pinecone',\n *       config: {\n *         pinecone: {\n *           apiKey: process.env.PINECONE_KEY,\n *           index: 'products'\n *         },\n *         openai: {\n *           apiKey: process.env.OPENAI_KEY\n *         }\n *       }\n *     })\n *   ]\n * });\n * ```\n */\nexport class VectorPlugin implements GQPPlugin {\n    name = \"vector\";\n    private config: VectorPluginConfig;\n    private _indexedFields: Set<string> = new Set();\n\n    constructor(config: VectorPluginConfig) {\n        this.config = {\n            threshold: 0.7,\n            maxResults: 50,\n            hybrid: true,\n            ...config,\n        };\n    }\n\n    /**\n     * Initialize plugin\n     */\n    async onInit(_engine: GQPEngine): Promise<void> {\n\n        // Set up auto-indexed fields\n        if (this.config.autoIndex?.enabled && this.config.autoIndex.fields) {\n            for (const field of this.config.autoIndex.fields) {\n                this._indexedFields.add(field);\n            }\n        }\n    }\n\n    /**\n     * Pre-query hook - handle semantic search\n     */\n    async onPreQuery(\n        node: GQPNode,\n        filters: QueryFilters,\n        context: PluginContext\n    ): Promise<QueryFilters> {\n        // Check if there's a semantic query\n        if (!context.query) {\n            return filters;\n        }\n\n        // Find searchable fields on this node\n        const searchableFields = node.fields.filter((f: any) =>\n            f.directives.some((d: any) => d.name === \"@search\")\n        );\n\n        if (searchableFields.length === 0) {\n            return filters;\n        }\n\n        // Perform vector search\n        const matchingIds = await this.vectorSearch(\n            node.name,\n            context.query,\n            this.config.maxResults || 50\n        );\n\n        if (matchingIds.length === 0) {\n            // No matches - return filter that matches nothing\n            return { ...filters, id: { in: [] } };\n        }\n\n        // Add ID filter to narrow down results\n        if (this.config.hybrid) {\n            return {\n                ...filters,\n                id: { in: matchingIds },\n            };\n        }\n\n        // Non-hybrid: only use vector results\n        return { id: { in: matchingIds } };\n    }\n\n    /**\n     * Perform vector search\n     */\n    private async vectorSearch(\n        namespace: string,\n        query: string,\n        limit: number\n    ): Promise<string[]> {\n        // Get embedding for query\n        const embedding = await this.embed(query);\n\n        // Search vector store\n        const results = await this.search(namespace, embedding, limit);\n\n        // Filter by threshold\n        const threshold = this.config.threshold || 0.7;\n        return results\n            .filter((r) => r.score >= threshold)\n            .map((r) => r.id);\n    }\n\n    /**\n     * Generate embedding for text\n     */\n    private async embed(text: string): Promise<number[]> {\n        // Custom embedding function\n        if (this.config.config.customEmbed) {\n            return this.config.config.customEmbed(text);\n        }\n\n        // OpenAI embedding\n        if (this.config.config.openai) {\n            return this.embedWithOpenAI(text);\n        }\n\n        throw new Error(\"No embedding provider configured\");\n    }\n\n    /**\n     * Search vector store\n     */\n    private async search(\n        namespace: string,\n        embedding: number[],\n        limit: number\n    ): Promise<Array<{ id: string; score: number }>> {\n        // Custom search function\n        if (this.config.config.customSearch) {\n            return this.config.config.customSearch(namespace, embedding, limit);\n        }\n\n        // Pinecone search\n        if (this.config.provider === \"pinecone\" && this.config.config.pinecone) {\n            return this.searchWithPinecone(namespace, embedding, limit);\n        }\n\n        throw new Error(\"No vector search provider configured\");\n    }\n\n    /**\n     * Embed text using OpenAI\n     */\n    private async embedWithOpenAI(text: string): Promise<number[]> {\n        const config = this.config.config.openai;\n        if (!config) throw new Error(\"OpenAI config not found\");\n\n        const response = await fetch(\"https://api.openai.com/v1/embeddings\", {\n            method: \"POST\",\n            headers: {\n                \"Content-Type\": \"application/json\",\n                \"Authorization\": `Bearer ${config.apiKey}`,\n            },\n            body: JSON.stringify({\n                model: config.model || \"text-embedding-3-small\",\n                input: text,\n            }),\n        });\n\n        if (!response.ok) {\n            throw new Error(`OpenAI embedding error: ${response.statusText}`);\n        }\n\n        const data = (await response.json()) as any;\n        return data.data[0].embedding;\n    }\n\n    /**\n     * Search using Pinecone\n     */\n    private async searchWithPinecone(\n        namespace: string,\n        embedding: number[],\n        limit: number\n    ): Promise<Array<{ id: string; score: number }>> {\n        const config = this.config.config.pinecone;\n        if (!config) throw new Error(\"Pinecone config not found\");\n\n        // Pinecone API call\n        const response = await fetch(\n            `https://${config.index}.svc.${config.environment || \"us-east1-gcp\"}.pinecone.io/query`,\n            {\n                method: \"POST\",\n                headers: {\n                    \"Content-Type\": \"application/json\",\n                    \"Api-Key\": config.apiKey,\n                },\n                body: JSON.stringify({\n                    namespace,\n                    vector: embedding,\n                    topK: limit,\n                    includeMetadata: false,\n                }),\n            }\n        );\n\n        if (!response.ok) {\n            throw new Error(`Pinecone search error: ${response.statusText}`);\n        }\n\n        const data = (await response.json()) as any;\n        return data.matches.map((m: { id: string; score: number }) => ({\n            id: m.id,\n            score: m.score,\n        }));\n    }\n\n    /**\n     * Index a field for vector search\n     */\n    async indexField(\n        fieldPath: string,\n        getData: () => Promise<Array<{ id: string; text: string }>>,\n        options: { batchSize?: number; onProgress?: (percent: number) => void } = {}\n    ): Promise<void> {\n        const batchSize = options.batchSize || 100;\n        const data = await getData();\n        const total = data.length;\n\n        for (let i = 0; i < data.length; i += batchSize) {\n            const batch = data.slice(i, i + batchSize);\n\n            // Generate embeddings for batch\n            const embeddings = await Promise.all(\n                batch.map((item) => this.embed(item.text))\n            );\n\n            // Upsert to vector store\n            await this.upsertVectors(\n                fieldPath,\n                batch.map((item, idx) => ({\n                    id: item.id,\n                    embedding: embeddings[idx],\n                }))\n            );\n\n            // Report progress\n            if (options.onProgress) {\n                options.onProgress(Math.min(100, ((i + batchSize) / total) * 100));\n            }\n        }\n\n        this._indexedFields.add(fieldPath);\n    }\n\n    /**\n     * Upsert vectors to store\n     */\n    private async upsertVectors(\n        namespace: string,\n        vectors: Array<{ id: string; embedding: number[] }>\n    ): Promise<void> {\n        if (this.config.provider === \"pinecone\" && this.config.config.pinecone) {\n            const config = this.config.config.pinecone;\n\n            await fetch(\n                `https://${config.index}.svc.${config.environment || \"us-east1-gcp\"}.pinecone.io/vectors/upsert`,\n                {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"Api-Key\": config.apiKey,\n                    },\n                    body: JSON.stringify({\n                        namespace,\n                        vectors: vectors.map((v) => ({\n                            id: v.id,\n                            values: v.embedding,\n                        })),\n                    }),\n                }\n            );\n        }\n    }\n\n    /**\n     * Check if a field is indexed\n     */\n    isIndexed(fieldPath: string): boolean {\n        return this._indexedFields.has(fieldPath);\n    }\n}\n\n/**\n * Factory function\n */\nexport function createVectorPlugin(config: VectorPluginConfig): VectorPlugin {\n    return new VectorPlugin(config);\n}\n"]}