Spaces:
Running
Running
File size: 10,457 Bytes
7b53631 69d90a0 7b53631 69d90a0 7b53631 69d90a0 7b53631 69d90a0 7b53631 f7d27ea 7b53631 f7d27ea 7b53631 f7d27ea 7b53631 f7d27ea 7b53631 69d90a0 7b53631 69d90a0 7b53631 f7d27ea 56fccc4 7b53631 69d90a0 7b53631 69d90a0 7b53631 69d90a0 7b53631 69d90a0 7b53631 69d90a0 7b53631 69d90a0 56fccc4 7b53631 56fccc4 7b53631 56fccc4 69d90a0 56fccc4 f7d27ea 56fccc4 7b53631 56fccc4 f7d27ea 56fccc4 7b53631 56fccc4 7b53631 56fccc4 7b53631 69d90a0 56fccc4 7b53631 69d90a0 7b53631 69d90a0 f7d27ea |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
// js/labAnalysisModule.js
// Importaciones (sin cambios)
import { getIaConfig, llmProviders } from './iaConfigModule.js';
// Función buildLabAnalysisPrompt (sin cambios)
function buildLabAnalysisPrompt(rawLabText) {
const prompt = `Quiero que me ayudes a ordenar resultados de análisis médicos. Te proporcionaré datos de entrada desordenados y debes extraer los datos relevantes siguiendo el formato XLabs. El formato XLabs se define como seis categorías en este orden exacto, cada una en una línea continua:
Hematología
Coagulación
Bioquímica
Gasometría
Orina
Otras pruebas
La salida debe estar en español, formateada así:
Categoría: Parametro Resultado unidades | Parametro Resultado unidades | ...
Ejemplo de Entrada:
Glucosa * 171 mg/dL 74 - 109
Urea * 56 mg/dL 17 - 48
Creatinina 0.82 mg/dL 0.7
Ejemplo de Salida Esperada:
Bioquímica: Glucosa **171** mg/dL | Urea **56** mg/dL | Creatinina 0.82 mg/dL
Ajusta el formato con estas modificaciones específicas:
Hematología:
Incluye únicamente: Hematíes, Hemoglobina, Hematocrito, VCM, HCM.
Para elementos celulares (Leucocitos, Neutrófilos, Linfocitos, Monocitos, etc.): agrupa mostrando primero el porcentaje y, entre paréntesis, el valor absoluto. Ejemplo: Neutrofilos 49.1 % (4.03 10^9/L) | Linfocitos 39.8 % (3.27 10^9/L).
No incluyas Eosinófilos ni Basófilos si están dentro de los rangos normales.
Incluye Plaquetas, pero NO incluyas VPM.
Coagulación:
Resume los datos usando abreviaturas: "INR" para Ratio normalizado internacional, "PT" para Tiempo de protrombina (Quick), "PTT" para T. tromboplastina parcial activada.
Otras pruebas:
Si no hay elementos en una categoría (ej., "Otras pruebas: Ninguna"), OMITE completamente esa categoría en la salida. No escribas "Ninguna".
**MUY IMPORTANTE: Alerta de valores alterados:**
Si CUALQUIER parámetro está fuera del rango normal (alterado), **DEBES envolver el valor numérico resultado entre DOBLES ASTERISCOS**. Ejemplo: Glucosa **171** mg/dL. Asegúrate de aplicar esto consistentemente a TODOS y CADA UNO de los valores que identifiques como alterados según los rangos de referencia si están disponibles en la entrada, o si están marcados explícitamente como alterados (ej. con '*'). Si no puedes determinar si está alterado, no lo marques.
Reglas generales:
1. Usa normas gramaticales y mayúsculas correctas (ej., "Hematíes" no "HEMATIES").
2. Si un apartado (Categoría) no tiene ninguna prueba que mostrar después de aplicar las reglas, omite el apartado completo.
3. NO uses formato bold (negrita) ni cambios de tamaño de texto en tu respuesta, EXCEPTO los dobles asteriscos (**valor**) para marcar los valores alterados.
4. NO incluyas introducciones, conclusiones, ni frases extra (ej., evita decir "Aquí está el formato XLabs...", "Resultados formateados:", etc.).
5. Proporciona ÚNICAMENTE la respuesta directa con los resultados formateados como se especifica.
6. Sigue estrictamente TODAS estas instrucciones. No inventes ningún resultado.
Datos de laboratorio para analizar:
--- INICIO DATOS ---
${rawLabText}
--- FIN DATOS ---`;
return prompt;
}
// Función analyzeLabResults (sin cambios)
export async function analyzeLabResults(text) {
if (!text || !text.trim()) {
console.warn("[labAnalysisModule] analyzeLabResults llamada con texto vacío.");
return Promise.resolve("");
}
console.log("[labAnalysisModule] Iniciando análisis de exámenes...");
const config = getIaConfig();
if (!config || !config.llm || !config.llm.provider || !config.llm.model) {
throw new Error("Configuración del LLM incompleta. Por favor, revisa la Configuración IA.");
}
const provider = config.llm.provider;
const model = config.llm.model;
const apiKey = config.llm.apiKeys?.[provider];
if (!apiKey) {
throw new Error(`No se encontró API Key para el proveedor LLM '${provider}'. Revisa la Configuración IA.`);
}
const providerDetails = llmProviders.find(p => p.value === provider);
if (!providerDetails || !providerDetails.url) {
throw new Error(`Detalles (URL) no encontrados para el proveedor LLM '${provider}'.`);
}
let apiUrl;
if (provider === 'openai' || provider === 'deepseek') {
apiUrl = `${providerDetails.url}/v1/chat/completions`;
} else {
throw new Error(`Proveedor LLM '${provider}' no soportado actualmente por labAnalysisModule.`);
}
const systemMessage = "Eres un asistente experto en formatear resultados de análisis de laboratorio médicos siguiendo el formato XLabs y destacando valores alterados.";
const userPrompt = buildLabAnalysisPrompt(text);
let messages;
if (provider === 'openai') {
messages = [{ role: 'user', content: `${systemMessage}\n\n${userPrompt}` }];
} else {
messages = [{ role: 'system', content: systemMessage }, { role: 'user', content: userPrompt }];
}
const payload = { model: model, messages: messages, temperature: 0.2 };
console.log(`[labAnalysisModule] Enviando petición a ${apiUrl} con modelo ${model}`);
try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorBody = await response.text();
throw new Error(`Error ${response.status} de la API LLM: ${errorBody || response.statusText}`);
}
const data = await response.json();
const content = data.choices?.[0]?.message?.content;
if (!content) { throw new Error("La respuesta de la API no contiene el contenido esperado."); }
console.log("[labAnalysisModule] Análisis de exámenes completado exitosamente.");
return content.trim();
} catch (error) {
console.error("[labAnalysisModule] Fallo en la llamada API o procesamiento:", error);
throw error;
}
}
// --- INICIO: Función displayLabResults (MODIFICADA con filtro a NIVEL PARÁMETRO) ---
/**
* Muestra los resultados de laboratorio formateados en la UI.
* @param {string} resultsText - El texto formateado recibido de la IA.
* @param {HTMLElement} containerElement - El elemento del DOM donde mostrar los resultados.
* @param {boolean} filterAltered - `true` si se debe mostrar solo resultados alterados.
*/
export function displayLabResults(resultsText, containerElement, filterAltered) {
if (!containerElement) {
console.error("[labAnalysisModule] Contenedor de resultados no encontrado.");
return;
}
containerElement.innerHTML = '';
if (!resultsText || !resultsText.trim()) {
containerElement.textContent = 'No se generaron resultados.';
return;
}
const lines = resultsText.split('\n').filter(line => line.trim() !== '');
let linesToDisplay = lines; // Por defecto, mostrar todas
// Aplicar filtro A NIVEL DE PARÁMETRO si está activo
if (filterAltered) {
console.log("--- INICIO FILTRO ALTERADOS (Nivel Parámetro) ---");
const filteredOutputLines = []; // Aquí guardaremos las líneas reconstruidas
const alterationRegex = /\*\*.*?\*\*/; // Regex para detectar **...**
for (const line of lines) { // Iterar sobre las líneas originales de la IA
// Intentar separar la línea en "Cabecera: Contenido"
const headerMatch = line.match(/^([^:]+):\s*(.*)$/);
if (headerMatch) {
// Si coincide el formato "Cabecera: Contenido"
const headerName = headerMatch[1].trim(); // Ej: "Hematología"
const contentPart = headerMatch[2].trim(); // Ej: "Hematíes... | Hemoglobina..."
// Dividir el contenido en parámetros individuales usando "|"
const parameters = contentPart.split('|')
.map(p => p.trim()) // Quitar espacios extra de cada parámetro
.filter(p => p); // Quitar posibles parámetros vacíos
const alteredParametersInLine = []; // Guardar solo los parámetros alterados de ESTA línea
// Revisar cada parámetro de la línea actual
for (const param of parameters) {
if (alterationRegex.test(param)) { // Si el parámetro contiene **...**
alteredParametersInLine.push(param); // Añadirlo a la lista de alterados de esta línea
console.log(` -> Parámetro Alterado Encontrado: "${param}" bajo "${headerName}"`);
}
}
// Si encontramos algún parámetro alterado en esta línea...
if (alteredParametersInLine.length > 0) {
// ...reconstruir la línea solo con la cabecera y esos parámetros alterados.
const filteredLine = `${headerName}: ${alteredParametersInLine.join(' | ')}`;
filteredOutputLines.push(filteredLine); // Añadir la línea reconstruida al resultado final
console.log(` -> Línea Filtrada Construida: "${filteredLine}"`);
} else {
// Si no hubo alterados en esta línea, simplemente la ignoramos (no se añade a filteredOutputLines)
console.log(` -> Cabecera "${headerName}" sin parámetros alterados, omitiendo línea completa.`);
}
} else {
// Si una línea no tiene el formato "Cabecera: Contenido", la ignoramos al filtrar
console.log(` -> Línea ignorada en filtro (no coincide formato Cabecera: Contenido): "${line}"`);
}
} // Fin del bucle sobre las líneas originales
// Asignar el resultado del filtrado a linesToDisplay
if (filteredOutputLines.length === 0 && lines.length > 0) {
linesToDisplay = ["No se encontraron resultados marcados como alterados."];
console.log("--- FIN FILTRO ALTERADOS (Resultado: Vacío) ---");
} else {
linesToDisplay = filteredOutputLines; // Usar las líneas reconstruidas y filtradas
console.log("--- FIN FILTRO ALTERADOS (Resultado: Mostrando filtrados por parámetro) ---");
}
console.log("Líneas finales a mostrar (filtrado por parámetro):", linesToDisplay);
} // Fin if(filterAltered)
// Formateo y renderizado (sin cambios, opera sobre linesToDisplay)
const fragment = document.createDocumentFragment();
linesToDisplay.forEach(line => {
const p = document.createElement('p');
p.style.margin = '0 0 0.3em 0';
// Reemplazar **valor** por <strong> con estilo
p.innerHTML = line.replace(
/\*\*(.*?)\*\*/g,
'<strong class="text-red-600 font-bold">$1</strong>'
);
fragment.appendChild(p);
});
containerElement.appendChild(fragment);
}
// --- FIN: Función displayLabResults --- |