patient / services /vectorService.js
aliroohan179's picture
first
5266fc5
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();