import { describe, expect, it } from 'vitest'; import { createQdrantRegulatoryIndexFromEnvironment, REGULATORY_QDRANT_COLLECTION } from '../../server/regulatory/qdrant-regulatory-index'; import { DenseRegulatoryCandidateRetriever, RegulatoryIngestionService } from '../../server/regulatory/regulatory-rag-service'; import type { RegulatoryControlDocument } from '../../src/domain/regulatory-rag'; import type { TextEmbeddingClient } from '../../server/regulatory/embedding-client'; const runIntegration = process.env.ASSESSOR_QDRANT_INTEGRATION === '1'; const describeQdrant = runIntegration ? describe : describe.skip; describeQdrant('Qdrant regulatory index integration', () => { it('creates, replaces, filters, and retrieves a synthetic control', async () => { const dimensions = Number(process.env.ASSESSOR_RAG_EMBEDDING_DIMENSIONS); const model = process.env.ASSESSOR_RAG_EMBEDDING_MODEL ?? ''; if (!Number.isInteger(dimensions) || dimensions < 64 || !model) throw new Error('Configured embedding dimensions and model are required.'); const vector = Array.from({ length: dimensions }, (_, index) => index === 0 ? 1 : 0); const embeddings: TextEmbeddingClient = { model, dimensions, embed: async texts => texts.map(() => vector) }; const index = createQdrantRegulatoryIndexFromEnvironment(dimensions, model); const ingestion = new RegulatoryIngestionService(embeddings, index); const document: RegulatoryControlDocument = { schemaVersion: 1, corpusId: 'assessor-qdrant-integration', corpusVersion: '1', documentId: 'synthetic-live-control-de', framework: 'ASSESSOR Synthetic Controls', frameworkVersion: '1', controlId: 'SYN-LIVE', language: 'de', title: 'Synthetische Livekontrolle', text: 'Dieser synthetische Text prueft ausschliesslich den lokalen rebuildbaren Qdrant-Index.' }; try { await expect(ingestion.ingest([document])).resolves.toMatchObject({ documentCount: 1, chunkCount: 1, embeddingModel: model }); const retrieval = new DenseRegulatoryCandidateRetriever(embeddings, index); const candidates = await retrieval.retrieveCandidates({ query: 'synthetic local index check', limit: 3, filters: { corpusId: document.corpusId, framework: document.framework, language: 'de' } }); expect(candidates).toHaveLength(1); expect(candidates[0]).toMatchObject({ chunk: { corpusId: document.corpusId, documentId: document.documentId, controlId: document.controlId, language: 'de' } }); } finally { await deleteSyntheticCorpus(document.corpusId); } }); }); async function deleteSyntheticCorpus(corpusId: string): Promise { const baseUrl = process.env.ASSESSOR_QDRANT_URL; const apiKey = process.env.ASSESSOR_QDRANT_API_KEY; if (!baseUrl) return; const response = await fetch(new URL(`/collections/${REGULATORY_QDRANT_COLLECTION}/points/delete?wait=true`, baseUrl), { method: 'POST', headers: { 'Content-Type': 'application/json', ...(apiKey ? { 'api-key': apiKey } : {}) }, body: JSON.stringify({ filter: { must: [{ key: 'corpus_id', match: { value: corpusId } }] } }) }); if (!response.ok) throw new Error(`Synthetic Qdrant integration cleanup failed with HTTP ${response.status}.`); }