import { QdrantClient } from '@qdrant/js-client-rest'; const isDockerContainer = process.env.IS_DOCKER === 'true' || process.env.HOSTNAME?.includes('klz-app'); const qdrantUrl = process.env.QDRANT_URL || (isDockerContainer ? 'http://klz-qdrant:6333' : 'http://localhost:6333'); const qdrantApiKey = process.env.QDRANT_API_KEY || ''; export const qdrant = new QdrantClient({ url: qdrantUrl, apiKey: qdrantApiKey || undefined, }); export const COLLECTION_NAME = 'klz_products'; export const VECTOR_SIZE = 1536; // OpenAI text-embedding-3-small /** * Ensure the collection exists in Qdrant. */ export async function ensureCollection() { try { const collections = await qdrant.getCollections(); const exists = collections.collections.some((c) => c.name === COLLECTION_NAME); if (!exists) { await qdrant.createCollection(COLLECTION_NAME, { vectors: { size: VECTOR_SIZE, distance: 'Cosine', }, }); console.log(`Successfully created Qdrant collection: ${COLLECTION_NAME}`); } } catch (error) { console.error('Error ensuring Qdrant collection:', error); } } /** * Generate an embedding for a given text using OpenRouter (OpenAI embedding proxy) */ export async function generateEmbedding(text: string): Promise { const openRouterKey = process.env.OPENROUTER_API_KEY; if (!openRouterKey) { throw new Error('OPENROUTER_API_KEY is not set'); } const response = await fetch('https://openrouter.ai/api/v1/embeddings', { method: 'POST', headers: { Authorization: `Bearer ${openRouterKey}`, 'Content-Type': 'application/json', 'HTTP-Referer': process.env.NEXT_PUBLIC_BASE_URL || 'https://klz-cables.com', 'X-Title': 'KLZ Cables Search AI', }, body: JSON.stringify({ model: 'openai/text-embedding-3-small', input: text, }), }); if (!response.ok) { const errorBody = await response.text(); throw new Error( `Failed to generate embedding: ${response.status} ${response.statusText} ${errorBody}`, ); } const data = await response.json(); return data.data[0].embedding; } /** * Upsert a product into Qdrant */ export async function upsertProductVector( id: string | number, text: string, payload: Record, ) { try { await ensureCollection(); const vector = await generateEmbedding(text); await qdrant.upsert(COLLECTION_NAME, { wait: true, points: [ { id: id, vector, payload, }, ], }); } catch (error) { console.error('Error writing to Qdrant:', error); } } /** * Delete a product from Qdrant */ export async function deleteProductVector(id: string | number) { try { await ensureCollection(); await qdrant.delete(COLLECTION_NAME, { wait: true, points: [id] as [string | number], }); } catch (error) { console.error('Error deleting from Qdrant:', error); } } /** * Search products in Qdrant */ export async function searchProducts(query: string, limit = 5) { try { await ensureCollection(); const vector = await generateEmbedding(query); const results = await qdrant.search(COLLECTION_NAME, { vector, limit, with_payload: true, }); return results; } catch (error) { console.error('Error searching in Qdrant:', error); return []; } }