Spaces:
Sleeping
README al día con el estado real del código
Browse filesARCHITECTURE_REVIEW §4.3. Cinco afirmaciones habían quedado FALSAS, no sólo
desactualizadas, y una documentación de seguridad que promete de más es peor que
no tenerla:
- «Con MORPHOS_MYSQL_DSN definido se usa MySQL/MariaDB»: esas variables no las
leía nadie y se eliminaron. Quien las configurara seguiría en SQLite.
- «/api/interpret y /api/papers exigen sesión»: papers NO la exige, y es
deliberado. Ahora se dice eso, y por qué.
- «Sexo: felinos machos tienen mayor tolerancia a creatinina»: ese ajuste se
retiró por falta de respaldo en el corpus —toleraba un 15 % más justo en la
población con más ERC—.
- «Raza: Shiba/Akita (RBC)»: es VCM. Subir la serie roja generaba falsas anemias;
lo documentado es microcitosis fisiológica SIN anemia.
- «SQLite en instance/morphos.db»: ahora prefiere el volumen persistente si lo
hay, y avisa cuando cae a almacenamiento efímero.
PRIVACIDAD, dicha con precisión. «Sin exponer la data sensible a los LLM» era
falso para la ruta por defecto y, a la vez, se quedaba corto: el esquema de la
petición admite SÓLO especie, raza, edad y sexo, así que no hay campo donde
quepan el nombre del animal o el propietario —no es una política, es una
imposibilidad estructural—. Las imágenes son campos de microscopio sin
identificadores y el PDF nunca se sube. El único texto libre es «signos
clínicos», que hoy viaja tal cual; se dice así, en presente, y el filtro por
vocabulario clínico previsto queda en Mejoras futuras, que es donde va lo que
todavía no existe.
PUERTA DE CI, sin adornos: se nombran los dos workflows y se dice que las evals
corren con --simular, así que hoy comprueban el medidor y no el modelo real.
Se añaden a Seguridad las cinco defensas nuevas (alta cerrada por allowlist,
aislamiento por clínica, sesiones revocables, suelo de seguridad recalculado en
servidor, rate limiting consciente del proxy) y se actualizan las estructuras de
data/, backend/ y frontend/.
Sin tocar Objetivo, Retos ni Mejoras futuras salvo la línea añadida.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@@ -22,7 +22,21 @@ pinned: false
|
|
| 22 |
Morphos es una aplicación web de apoyo al diagnóstico veterinario. Detecta patrones clínicos en tiempo real a partir de valores de laboratorio con un motor propio que corre entero en el navegador, y permite interpretarlos con un modelo de IA especializado en medicina (medGemma multimodal de Google DeepMind, auto-alojado) o con Claude. Incluye búsqueda de artículos científicos en PubMed relacionados con los diagnósticos diferenciales del paciente.
|
| 23 |
|
| 24 |
Está orientada a caninos y felinos, con ajuste automático de rangos de referencia por especie, edad, raza y sexo.
|
| 25 |
-
Ataca una necesidad real del sector veterinario que
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
Funcionalidades principales:
|
| 28 |
|
|
@@ -69,19 +83,23 @@ Conceptos aplicados:
|
|
| 69 |
|
| 70 |
```text
|
| 71 |
/frontend
|
| 72 |
-
src/analisis.ts → motor de detección de patrones clínicos (
|
| 73 |
src/main.ts → orquestación general, eventos y renderizado
|
| 74 |
src/ia.ts → cliente tipado de /api/interpret y render de la salida estructurada
|
| 75 |
src/ui.ts → navegación por tabs, gestos, sincronización móvil
|
| 76 |
src/auth.ts → modal de autenticación y validación en tiempo real
|
| 77 |
src/papers.ts → búsqueda y paginación de literatura científica
|
| 78 |
src/pdf-parser.ts→ extracción de valores desde PDF en el navegador
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
/backend
|
| 82 |
app/main.py → app FastAPI, CORS, cabeceras, montaje de estáticos
|
| 83 |
app/config.py → configuración por variables de entorno (sin secretos por defecto)
|
| 84 |
app/schemas.py → esquemas Pydantic: petición y salida clínica validada
|
|
|
|
| 85 |
app/ai/ → rutas de modelo (hf_space, medgemma/Ollama, claude), prompt y citas
|
| 86 |
app/rag/ → ingesta e índice LanceDB + recuperación híbrida con reranking
|
| 87 |
app/routers/ → interpret, auth, papers, lab
|
|
@@ -101,14 +119,19 @@ Conceptos aplicados:
|
|
| 101 |
|
| 102 |
/data
|
| 103 |
valores_referencia.json → rangos de referencia por especie y analito
|
|
|
|
|
|
|
| 104 |
alteraciones.json → descripciones clínicas de los patrones
|
|
|
|
|
|
|
| 105 |
|
| 106 |
/assets
|
| 107 |
/fonts → Inter y JetBrains Mono (carga local)
|
| 108 |
/icons → iconos SVG de la interfaz
|
| 109 |
/lib/pdfjs → librería PDF.js en local
|
| 110 |
|
| 111 |
-
/instance → BD de usuarios
|
|
|
|
| 112 |
index.html → SPA principal (carga el bundle de frontend/)
|
| 113 |
Dockerfile → imagen de despliegue (frontend + backend + índice RAG horneado)
|
| 114 |
```
|
|
@@ -170,8 +193,15 @@ ni al repositorio; en HF Spaces se usan los *Secrets* del Space en su lugar.
|
|
| 170 |
|
| 171 |
### 3. Base de datos
|
| 172 |
|
| 173 |
-
Se crea sola al arrancar
|
| 174 |
-
`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
### 4. Iniciar la aplicacion
|
| 177 |
|
|
@@ -237,14 +267,29 @@ El mismo Ollama sirve ademas de **juez gratuito** para las evals (ver `evals/REA
|
|
| 237 |
|
| 238 |
## Motor de deteccion de patrones
|
| 239 |
|
| 240 |
-
`frontend/src/analisis.ts` compara cada valor ingresado contra los rangos de referencia del JSON,
|
|
|
|
| 241 |
|
| 242 |
* **Especie**: canino / felino
|
| 243 |
-
* **Edad**: cachorro, adulto, senior, geriatrico
|
| 244 |
-
|
| 245 |
-
* **
|
| 246 |
-
|
| 247 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
|
| 249 |
El motor está congelado por una suite de regresión (`frontend/tests`, Vitest) que se ejecuta
|
| 250 |
con `make frontend-test`. Es la red que permite tocar el resto del stack sin cambiar
|
|
@@ -276,7 +321,8 @@ validación veterinaria firmada. Detalle en `evals/README.md`.
|
|
| 276 |
* Sesiones firmadas con cookie `HttpOnly` / `SameSite` / `Secure`, y CSRF de doble token
|
| 277 |
* Contraseñas hasheadas con **scrypt** y comparación en tiempo constante
|
| 278 |
* Consultas parametrizadas (sin interpolacion directa)
|
| 279 |
-
* `/api/interpret`
|
|
|
|
| 280 |
* Rate limiting por IP **y por usuario** (la cuota de GPU es compartida entre veterinarios)
|
| 281 |
* CORS restringido a orígenes conocidos, nunca `*`
|
| 282 |
* Cabeceras de seguridad: CSP estricta, HSTS en producción, `nosniff`, `frame-ancestors none`
|
|
@@ -284,6 +330,18 @@ validación veterinaria firmada. Detalle en `evals/README.md`.
|
|
| 284 |
* BD de usuarios e índice RAG **fuera de la raíz servida** (`instance/`), no descargables
|
| 285 |
* Validación en servidor de las imágenes de citología (número, tipo MIME y tamaño)
|
| 286 |
* Texto del modelo y de APIs externas insertado con escapado, sin `eval` ni `document.write`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
|
| 288 |
---
|
| 289 |
|
|
@@ -293,7 +351,7 @@ validación veterinaria firmada. Detalle en `evals/README.md`.
|
|
| 293 |
* CSS personalizado: variables, fuentes fluidas, grid, flexbox, media queries, temas claro/oscuro
|
| 294 |
* JavaScript/TypeScript: ES Modules, `fetch`, `async/await`, eventos, DOM API, tipos estrictos
|
| 295 |
* Python: FastAPI, Pydantic, `async`/`await`, gestión de dependencias con uv
|
| 296 |
-
* Bases de datos: creacion de tablas, consultas con parametros, indices unicos
|
| 297 |
|
| 298 |
---
|
| 299 |
|
|
@@ -303,6 +361,8 @@ validación veterinaria firmada. Detalle en `evals/README.md`.
|
|
| 303 |
* Desarrollo de extensión de navegador para captar datos del DOM de PIMS y obtener los datos de los analisis de los pacientes con intervención mínima del usuario
|
| 304 |
* Desarrollo de mobile app dedicada
|
| 305 |
* Integración con PIMS más utilizados en veterinaria
|
|
|
|
|
|
|
| 306 |
* Rankeo de papers basado en confiabilidad y relevancia
|
| 307 |
* Creación de Dataset específico para citologías de animales
|
| 308 |
* Hosting del modelo en VPS serverless para finetuning y menor latencia
|
|
|
|
| 22 |
Morphos es una aplicación web de apoyo al diagnóstico veterinario. Detecta patrones clínicos en tiempo real a partir de valores de laboratorio con un motor propio que corre entero en el navegador, y permite interpretarlos con un modelo de IA especializado en medicina (medGemma multimodal de Google DeepMind, auto-alojado) o con Claude. Incluye búsqueda de artículos científicos en PubMed relacionados con los diagnósticos diferenciales del paciente.
|
| 23 |
|
| 24 |
Está orientada a caninos y felinos, con ajuste automático de rangos de referencia por especie, edad, raza y sexo.
|
| 25 |
+
Ataca una necesidad real del sector veterinario, que hoy no dispone de herramientas de este tipo
|
| 26 |
+
gratuitas, de uso sencillo y capaces de dar información complementaria relevante en muy poco tiempo.
|
| 27 |
+
|
| 28 |
+
**Qué sale del navegador y qué no.** El esquema de la petición (`PacienteEntrada`) admite
|
| 29 |
+
únicamente **especie, raza, edad y sexo**: no existe ningún campo para el nombre del animal ni
|
| 30 |
+
para datos del propietario, así que no es una política sino una imposibilidad estructural. A eso
|
| 31 |
+
se suman los valores de laboratorio y, si las hay, imágenes de citología o microscopía: campos de
|
| 32 |
+
microscopio, sin identificadores. El PDF original **nunca se sube**: se parsea entero en el
|
| 33 |
+
navegador.
|
| 34 |
+
|
| 35 |
+
El único campo de texto libre es «signos clínicos». Hoy viaja tal cual —el servidor lo inspecciona
|
| 36 |
+
para la guarda de alcance, pero no lo filtra—, así que es el único sitio donde alguien podría
|
| 37 |
+
teclear algo identificativo. Está previsto acotarlo a vocabulario clínico (ver *Mejoras futuras*).
|
| 38 |
+
|
| 39 |
+
Con la ruta auto-alojada (Ollama en la clínica) no sale nada en absoluto: ver más abajo.
|
| 40 |
|
| 41 |
Funcionalidades principales:
|
| 42 |
|
|
|
|
| 83 |
|
| 84 |
```text
|
| 85 |
/frontend
|
| 86 |
+
src/analisis.ts → motor de detección de patrones clínicos (tiempo real, en el navegador)
|
| 87 |
src/main.ts → orquestación general, eventos y renderizado
|
| 88 |
src/ia.ts → cliente tipado de /api/interpret y render de la salida estructurada
|
| 89 |
src/ui.ts → navegación por tabs, gestos, sincronización móvil
|
| 90 |
src/auth.ts → modal de autenticación y validación en tiempo real
|
| 91 |
src/papers.ts → búsqueda y paginación de literatura científica
|
| 92 |
src/pdf-parser.ts→ extracción de valores desde PDF en el navegador
|
| 93 |
+
src/lab-import.ts→ importación desde analizadores (cola de muestras y emparejado)
|
| 94 |
+
src/panel-vacio.ts→ estado vacío de los paneles de exámenes (escritorio)
|
| 95 |
+
src/form-inject.ts→ base común de los importadores (PDF y analizador)
|
| 96 |
+
tests/ → suite de regresión del motor y del parser de PDF (Vitest)
|
| 97 |
|
| 98 |
/backend
|
| 99 |
app/main.py → app FastAPI, CORS, cabeceras, montaje de estáticos
|
| 100 |
app/config.py → configuración por variables de entorno (sin secretos por defecto)
|
| 101 |
app/schemas.py → esquemas Pydantic: petición y salida clínica validada
|
| 102 |
+
app/motor/ → suelo de seguridad recalculado en servidor (rangos y gravedad)
|
| 103 |
app/ai/ → rutas de modelo (hf_space, medgemma/Ollama, claude), prompt y citas
|
| 104 |
app/rag/ → ingesta e índice LanceDB + recuperación híbrida con reranking
|
| 105 |
app/routers/ → interpret, auth, papers, lab
|
|
|
|
| 119 |
|
| 120 |
/data
|
| 121 |
valores_referencia.json → rangos de referencia por especie y analito
|
| 122 |
+
ajustes_clinicos.json → umbrales de gravedad, cortes clínicos, ajustes por edad/raza e IRIS
|
| 123 |
+
(única fuente de verdad: la leen los DOS motores)
|
| 124 |
alteraciones.json → descripciones clínicas de los patrones
|
| 125 |
+
lab_mapeos/ → códigos de prueba → analito canónico, por fabricante
|
| 126 |
+
rag_alcance.json → qué páginas del corpus entran y con qué especie
|
| 127 |
|
| 128 |
/assets
|
| 129 |
/fonts → Inter y JetBrains Mono (carga local)
|
| 130 |
/icons → iconos SVG de la interfaz
|
| 131 |
/lib/pdfjs → librería PDF.js en local
|
| 132 |
|
| 133 |
+
/instance → índice RAG y BD de usuarios si no hay volumen persistente; FUERA de la
|
| 134 |
+
raíz servida (gitignored)
|
| 135 |
index.html → SPA principal (carga el bundle de frontend/)
|
| 136 |
Dockerfile → imagen de despliegue (frontend + backend + índice RAG horneado)
|
| 137 |
```
|
|
|
|
| 193 |
|
| 194 |
### 3. Base de datos
|
| 195 |
|
| 196 |
+
Se crea sola al arrancar y el esquema se migra solo (`PRAGMA user_version`; ver `_MIGRACIONES`
|
| 197 |
+
en `backend/app/db.py`). Es SQLite, siempre fuera de la raiz servida:
|
| 198 |
+
|
| 199 |
+
* Si hay un **volumen persistente** montado en `/data`, la BD vive ahí y sobrevive a los reinicios.
|
| 200 |
+
* Si no, cae a `instance/morphos.db`, que en HF Spaces es **efímero**: cada reinicio borra
|
| 201 |
+
cuentas, contraseñas e historial de intentos. Se avisa por log al arrancar.
|
| 202 |
+
|
| 203 |
+
Se puede forzar la ruta con `MORPHOS_DB_PATH`. **No hay soporte de MySQL/MariaDB**: existieron
|
| 204 |
+
variables `MORPHOS_MYSQL_*` que ningún código leía y se eliminaron.
|
| 205 |
|
| 206 |
### 4. Iniciar la aplicacion
|
| 207 |
|
|
|
|
| 267 |
|
| 268 |
## Motor de deteccion de patrones
|
| 269 |
|
| 270 |
+
`frontend/src/analisis.ts` compara cada valor ingresado contra los rangos de referencia del JSON,
|
| 271 |
+
ajustados dinamicamente segun:
|
| 272 |
|
| 273 |
* **Especie**: canino / felino
|
| 274 |
+
* **Edad**: cachorro, adulto, senior, geriatrico (p. ej. el fosforo del cachorro, sin el cual todo
|
| 275 |
+
animal en crecimiento sale hiperfosforemico)
|
| 276 |
+
* **Raza**: lebreles —galgo, greyhound, whippet, saluki…— en serie roja, plaquetas, creatinina y
|
| 277 |
+
T4 (sin ese ajuste, ~90 % de los galgos sanos salen hipotiroideos); razas asiaticas (shiba,
|
| 278 |
+
akita, chow, shar pei) en **VCM**, que es microcitosis fisiologica **sin** anemia; Maine Coon y
|
| 279 |
+
Birman en felinos
|
| 280 |
+
* **Sexo**: sin ajustes. El que habia —mas tolerancia a creatinina en el gato macho— se **retiro**
|
| 281 |
+
por no tener respaldo en el corpus: toleraba un 15 % mas justo en la poblacion con mas ERC
|
| 282 |
+
|
| 283 |
+
La gravedad se mide como desviacion relativa al ancho del rango, **salvo** donde esa regla no sabe
|
| 284 |
+
expresar la clinica: hematocrito y plaquetas por lo bajo, y creatinina, SDMA y UP/C por lo alto,
|
| 285 |
+
tienen cortes explicitos (con rango 24-45, un gato necesitaria un hematocrito negativo para llegar
|
| 286 |
+
a «grave»). Con los hallazgos se identifican mas de 50 patrones clinicos (anemias, hepatopatias,
|
| 287 |
+
nefropatia, alteraciones endocrinas, electrolitos, entre otros) y el estadiaje IRIS de ERC.
|
| 288 |
+
|
| 289 |
+
**Las reglas clinicas son datos, no codigo.** Umbrales, cortes, limites de edad, factores por raza
|
| 290 |
+
y cortes IRIS viven en `data/ajustes_clinicos.json`, con su procedencia bibliografica, y los leen
|
| 291 |
+
por igual el motor del navegador y el del servidor. Un veterinario puede revisarlos y ajustarlos
|
| 292 |
+
sin desarrollador ni build.
|
| 293 |
|
| 294 |
El motor está congelado por una suite de regresión (`frontend/tests`, Vitest) que se ejecuta
|
| 295 |
con `make frontend-test`. Es la red que permite tocar el resto del stack sin cambiar
|
|
|
|
| 321 |
* Sesiones firmadas con cookie `HttpOnly` / `SameSite` / `Secure`, y CSRF de doble token
|
| 322 |
* Contraseñas hasheadas con **scrypt** y comparación en tiempo constante
|
| 323 |
* Consultas parametrizadas (sin interpolacion directa)
|
| 324 |
+
* `/api/interpret` exige sesión: no hay acceso anónimo al modelo. `/api/papers` **no** la exige
|
| 325 |
+
a propósito (búsqueda en PubMed, ni sensible ni cara); lo acota el rate limiting
|
| 326 |
* Rate limiting por IP **y por usuario** (la cuota de GPU es compartida entre veterinarios)
|
| 327 |
* CORS restringido a orígenes conocidos, nunca `*`
|
| 328 |
* Cabeceras de seguridad: CSP estricta, HSTS en producción, `nosniff`, `frame-ancestors none`
|
|
|
|
| 330 |
* BD de usuarios e índice RAG **fuera de la raíz servida** (`instance/`), no descargables
|
| 331 |
* Validación en servidor de las imágenes de citología (número, tipo MIME y tamaño)
|
| 332 |
* Texto del modelo y de APIs externas insertado con escapado, sin `eval` ni `document.write`
|
| 333 |
+
* **Alta de cuentas cerrada** por lista blanca de emails (`MORPHOS_REGISTRO_ALLOWLIST`): una
|
| 334 |
+
cuenta alcanza el modelo, así que el alta abierta era una autorización de gasto para cualquiera
|
| 335 |
+
* **Resultados de analizador aislados por clínica**: el tenant lo pone el servidor —de la API key
|
| 336 |
+
del dispositivo al ingerir, de la cookie firmada al leer—, nunca el cuerpo de la petición. Una
|
| 337 |
+
muestra de otra clínica responde 404, no 403
|
| 338 |
+
* **Sesiones revocables**: el logout invalida el token de verdad (no sólo borra la cookie) y
|
| 339 |
+
`/api/auth/logout-todas` cierra la sesión en todos los dispositivos
|
| 340 |
+
* **El suelo de seguridad se recalcula en el servidor**: los hallazgos y su gravedad se derivan de
|
| 341 |
+
los valores crudos (`app/motor/gravedad.py`), así que omitir o falsear el veredicto del cliente
|
| 342 |
+
ya no relaja la derivación obligatoria. Lo que afirma el navegador sólo puede endurecerla
|
| 343 |
+
* Rate limiting consciente del proxy inverso (`MORPHOS_PROXY_SALTOS_CONFIABLES`): detrás de un
|
| 344 |
+
proxy, `request.client.host` es el proxy y los límites «por IP» serían en realidad globales
|
| 345 |
|
| 346 |
---
|
| 347 |
|
|
|
|
| 351 |
* CSS personalizado: variables, fuentes fluidas, grid, flexbox, media queries, temas claro/oscuro
|
| 352 |
* JavaScript/TypeScript: ES Modules, `fetch`, `async/await`, eventos, DOM API, tipos estrictos
|
| 353 |
* Python: FastAPI, Pydantic, `async`/`await`, gestión de dependencias con uv
|
| 354 |
+
* Bases de datos: creacion de tablas, consultas con parametros, indices unicos y migraciones versionadas (SQLite)
|
| 355 |
|
| 356 |
---
|
| 357 |
|
|
|
|
| 361 |
* Desarrollo de extensión de navegador para captar datos del DOM de PIMS y obtener los datos de los analisis de los pacientes con intervención mínima del usuario
|
| 362 |
* Desarrollo de mobile app dedicada
|
| 363 |
* Integración con PIMS más utilizados en veterinaria
|
| 364 |
+
* Filtrado del campo «signos clínicos» contra una lista de términos clínicos permitidos, para que
|
| 365 |
+
ni datos identificativos ni texto arbitrario lleguen al modelo
|
| 366 |
* Rankeo de papers basado en confiabilidad y relevancia
|
| 367 |
* Creación de Dataset específico para citologías de animales
|
| 368 |
* Hosting del modelo en VPS serverless para finetuning y menor latencia
|