Spaces:
Sleeping
Sleeping
| import { ChromaClient } from 'chromadb'; | |
| class VectorService { | |
| constructor() { | |
| this.chromaClient = null; | |
| this.patientCollection = null; | |
| this.researchCollection = null; | |
| this.initialized = false; | |
| } | |
| async initialize() { | |
| if (this.initialized) return; | |
| try { | |
| this.chromaClient = new ChromaClient({ | |
| path: './chroma_db' | |
| }); | |
| this.patientCollection = await this._getOrCreateCollection('patient_records'); | |
| this.researchCollection = await this._getOrCreateCollection('medical_research'); | |
| this.initialized = true; | |
| console.log('[OK] Vector database initialized'); | |
| } catch (error) { | |
| console.log(`[WARNING] Vector database initialization warning: ${error.message}`); | |
| } | |
| } | |
| async _getOrCreateCollection(name) { | |
| try { | |
| return await this.chromaClient.getCollection({ name }); | |
| } catch { | |
| return await this.chromaClient.createCollection({ | |
| name, | |
| metadata: { description: `Collection for ${name}` } | |
| }); | |
| } | |
| } | |
| async addPatientRecord(patientId, patientData) { | |
| await this.initialize(); | |
| const documents = []; | |
| const metadatas = []; | |
| const ids = []; | |
| // Add medical history | |
| if (patientData.medical_history) { | |
| patientData.medical_history.forEach((condition, idx) => { | |
| const docId = `patient_${patientId}_history_${idx}`; | |
| const conditionText = typeof condition === 'string' ? condition : condition.condition || 'Unknown'; | |
| documents.push(`Patient ${patientData.name} has medical history: ${conditionText}`); | |
| metadatas.push({ | |
| patient_id: patientId, | |
| patient_name: patientData.name, | |
| type: 'medical_history', | |
| condition: conditionText, | |
| age: String(patientData.age || ''), | |
| gender: patientData.gender || '' | |
| }); | |
| ids.push(docId); | |
| }); | |
| } | |
| // Add medications | |
| if (patientData.medications) { | |
| patientData.medications.forEach((med, idx) => { | |
| const docId = `patient_${patientId}_medication_${idx}`; | |
| let medText = `Patient ${patientData.name} is taking ${med.name}, dosage: ${med.dosage}, started: ${med.start_date}`; | |
| if (med.end_date) { | |
| medText += `, ended: ${med.end_date}`; | |
| } | |
| documents.push(medText); | |
| metadatas.push({ | |
| patient_id: patientId, | |
| patient_name: patientData.name, | |
| type: 'medication', | |
| medication_name: med.name, | |
| dosage: med.dosage, | |
| start_date: med.start_date | |
| }); | |
| ids.push(docId); | |
| }); | |
| } | |
| // Add summary | |
| const medHistory = patientData.medical_history || ['None']; | |
| const medNames = (patientData.medications || []).map(m => m.name); | |
| const summaryText = ` | |
| Patient Profile: ${patientData.name}, Age: ${patientData.age}, Gender: ${patientData.gender}. | |
| Medical History: ${medHistory.join(', ')}. | |
| Current Medications: ${medNames.join(', ') || 'None'}. | |
| `.trim(); | |
| documents.push(summaryText); | |
| metadatas.push({ | |
| patient_id: patientId, | |
| patient_name: patientData.name, | |
| type: 'summary', | |
| age: String(patientData.age || ''), | |
| gender: patientData.gender || '' | |
| }); | |
| ids.push(`patient_${patientId}_summary`); | |
| if (documents.length > 0) { | |
| try { | |
| await this.patientCollection.upsert({ | |
| documents, | |
| metadatas, | |
| ids | |
| }); | |
| console.log(` [OK] Indexed ${documents.length} records for patient ${patientData.name}`); | |
| } catch (error) { | |
| console.log(` [ERROR] Error indexing patient ${patientData.name}: ${error.message}`); | |
| } | |
| } | |
| return documents.length; | |
| } | |
| async searchPatientRecords(query, patientId = null, nResults = 5) { | |
| await this.initialize(); | |
| try { | |
| const whereFilter = patientId ? { patient_id: patientId } : undefined; | |
| const results = await this.patientCollection.query({ | |
| queryTexts: [query], | |
| nResults, | |
| where: whereFilter | |
| }); | |
| const formattedResults = []; | |
| if (results && results.documents && results.documents[0]) { | |
| for (let idx = 0; idx < results.documents[0].length; idx++) { | |
| formattedResults.push({ | |
| document: results.documents[0][idx], | |
| metadata: results.metadatas[0][idx], | |
| distance: results.distances ? results.distances[0][idx] : null | |
| }); | |
| } | |
| } | |
| return formattedResults; | |
| } catch (error) { | |
| console.log(`Search error: ${error.message}`); | |
| return []; | |
| } | |
| } | |
| async addResearchPaper(paperId, title, content, metadata) { | |
| await this.initialize(); | |
| try { | |
| const chunkSize = 1000; | |
| const chunks = []; | |
| const words = content.split(' '); | |
| for (let i = 0; i < words.length; i += chunkSize) { | |
| const chunk = words.slice(i, i + chunkSize).join(' '); | |
| chunks.push(chunk); | |
| } | |
| const documents = []; | |
| const metadatas = []; | |
| const ids = []; | |
| chunks.forEach((chunk, idx) => { | |
| documents.push(chunk); | |
| const chunkMetadata = { | |
| paper_id: paperId, | |
| title, | |
| chunk_index: String(idx), | |
| total_chunks: String(chunks.length), | |
| ...Object.fromEntries(Object.entries(metadata).map(([k, v]) => [k, String(v)])) | |
| }; | |
| metadatas.push(chunkMetadata); | |
| ids.push(`research_${paperId}_chunk_${idx}`); | |
| }); | |
| if (documents.length > 0) { | |
| await this.researchCollection.upsert({ | |
| documents, | |
| metadatas, | |
| ids | |
| }); | |
| } | |
| return chunks.length; | |
| } catch (error) { | |
| console.log(`Error adding research paper: ${error.message}`); | |
| return 0; | |
| } | |
| } | |
| async searchResearchPapers(query, nResults = 5) { | |
| await this.initialize(); | |
| try { | |
| const results = await this.researchCollection.query({ | |
| queryTexts: [query], | |
| nResults | |
| }); | |
| const formattedResults = []; | |
| if (results && results.documents && results.documents[0]) { | |
| for (let idx = 0; idx < results.documents[0].length; idx++) { | |
| formattedResults.push({ | |
| document: results.documents[0][idx], | |
| metadata: results.metadatas[0][idx], | |
| distance: results.distances ? results.distances[0][idx] : null | |
| }); | |
| } | |
| } | |
| return formattedResults; | |
| } catch (error) { | |
| console.log(`Research search error: ${error.message}`); | |
| return []; | |
| } | |
| } | |
| async deletePatientRecords(patientId) { | |
| await this.initialize(); | |
| try { | |
| const results = await this.patientCollection.get({ | |
| where: { patient_id: patientId } | |
| }); | |
| if (results && results.ids && results.ids.length > 0) { | |
| await this.patientCollection.delete({ ids: results.ids }); | |
| } | |
| return true; | |
| } catch (error) { | |
| console.log(`Error deleting patient records: ${error.message}`); | |
| return false; | |
| } | |
| } | |
| async getCollectionStats() { | |
| await this.initialize(); | |
| try { | |
| const patientCount = await this.patientCollection.count(); | |
| const researchCount = await this.researchCollection.count(); | |
| return { | |
| patient_records_count: patientCount, | |
| research_papers_count: researchCount | |
| }; | |
| } catch { | |
| return { | |
| patient_records_count: 0, | |
| research_papers_count: 0 | |
| }; | |
| } | |
| } | |
| } | |
| export const vectorService = new VectorService(); | |