Spaces:
Paused
Paused
File size: 7,391 Bytes
4d96f75 | 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 | import gradio as gr
import re
import tempfile
import os
# Importaciones de Docling (IA de Layout espacial y Visión)
from docling.document_converter import DocumentConverter
from docling.datamodel.document import DocItemLabel
def heal_and_flatten_medical_text(text):
"""
Motor NLP de Sanación Semántica y Aplanamiento.
Diseñado para cualquier libro médico/biológico universitario.
Convierte el texto crudo en vectores puros para ingesta en RAG.
"""
if not text:
return ""
# 1. PURGA DE REFERENCIAS FANTASMAS
# Elimina referencias cruzadas a figuras y tablas que la IA ya borró.
text = re.sub(r'\(\s*[vV]?\.?\s*(?:fig\.|figs\.|tabla|tablas|cuadro|cuadros)\s*\d+[-.]\d+[^\)]*\)', '', text, flags=re.IGNORECASE)
text = re.sub(r'\b(?:véase|ver)\s+(?:figura|tabla|cuadro)\s+\d+[-.]\d+\b', '', text, flags=re.IGNORECASE)
# 2. APLANAMIENTO Y COSTURA DE COLUMNAS (Flattening & De-hyphenation)
# Docling a veces deja guiones al unir columnas; aquí los forzamos a unirse.
text = re.sub(r'(\w+)-\s*\n\s*(\w+)', r'\1\2', text)
# Destruimos los saltos de línea internos: convertimos párrafos-bloque en cadenas lineales continuas.
text = re.sub(r'(?<!\n)\n(?!\n)', ' ', text)
# Purgar espacios múltiples
text = re.sub(r'[ \t]+', ' ', text)
# 3. SANACIÓN CIENTÍFICO-MATEMÁTICA UNIVERSAL
# Potencias (ej. 104 -> 10^4) cuando están junto a palabras de biología/química.
text = re.sub(r'\b10(3|4|5|6|7|8|9|10|11|12|13)\b(?=\s*(?:y|a|células|linfocitos|macrófagos|bacterias|virus|copias|moléculas|mg|ml|µl|µm|mm|pg|ng))', r'10^\1', text, flags=re.IGNORECASE)
text = re.sub(r'\b1\s*[xX]\s*10(\d{1,2})\b', r'1 x 10^\1', text)
# Corrección de Unidades de Medida corrompidas por OCR
text = re.sub(r'\|xm', 'µm', text)
text = re.sub(r'\bpm\b(?=\s)', 'µm', text)
text = re.sub(r'\[iL', 'µL', text)
# 4. GRECISMOS Y NOMENCLATURA INMUNOLÓGICA (Abbas, Harrison, Robbins)
text = re.sub(r'\ba4p7\b', 'α4β7', text)
text = re.sub(r'\baLp2\b', 'αLβ2', text)
text = re.sub(r'\ba\(3\b', 'αβ', text)
text = re.sub(r'\bLT\(3\b', 'LTβ', text)
text = re.sub(r'\bIFN-7\b', 'IFN-γ', text)
text = re.sub(r'\bIL-ip\b', 'IL-1β', text)
text = re.sub(r'\bCD(\d+)\s*\+', r'CD\1+', text) # Ejemplo: CD8 + -> CD8+
return text.strip()
def process_medical_pdf(pdf_file, progress=gr.Progress()):
"""
Controlador Orquestador. Administra la conversión de Docling, aplica los
filtros topológicos y ensambla el archivo Markdown puro para Gemini/NotebookLM.
"""
if pdf_file is None:
raise gr.Error("Debes subir un archivo PDF.")
try:
# FASE A: Ingesta Visual y Comprensión Geométrica (Docling)
progress(0.1, desc="👁️ Fase 1/3: Iniciando IA de Visión (Cargando modelos locales)...")
converter = DocumentConverter()
progress(0.2, desc="📐 Analizando topología y dobles columnas del PDF (Puede tardar)...")
doc_result = converter.convert(pdf_file.name)
document = doc_result.document
md_blocks = []
# Iterador de elementos detectados por la IA
elements = list(document.iterate_items())
total_elements = len(elements)
# FASE B & C: Triaje Topológico y Sanación
progress(0.5, desc="🧹 Fase 2/3: Purgando tablas, imágenes y ruido editorial...")
for idx, (item, level) in enumerate(elements):
if idx % 10 == 0: # Actualización de UI progresiva
progress(0.5 + (0.4 * (idx / total_elements)))
# REGLA DE EXCLUSIÓN RADICAL: Ignorar ruido visual y tabular
if item.label in[
DocItemLabel.PAGE_HEADER,
DocItemLabel.PAGE_FOOTER,
DocItemLabel.TABLE,
DocItemLabel.PICTURE,
DocItemLabel.FORMULA,
DocItemLabel.CAPTION
]:
continue
# REGLA DE PRESERVACIÓN Y SANACIÓN (Preparación RAG)
if item.label in[DocItemLabel.SECTION_HEADER, DocItemLabel.TITLE]:
clean_text = heal_and_flatten_medical_text(item.text)
if clean_text:
# Garantiza que el nivel del encabezado sea entre # y ######
heading_level = min(max(level, 1), 6)
md_blocks.append(f"{'#' * heading_level} {clean_text}")
elif item.label in [DocItemLabel.PARAGRAPH, DocItemLabel.TEXT]:
clean_text = heal_and_flatten_medical_text(item.text)
if clean_text:
md_blocks.append(clean_text)
elif item.label == DocItemLabel.LIST_ITEM:
clean_text = heal_and_flatten_medical_text(item.text)
if clean_text:
md_blocks.append(f"* {clean_text}")
# FASE D: Ensamblaje Estructural para Gemini/NotebookLM
progress(0.95, desc="📦 Fase 3/3: Ensamblando archivo Markdown optimizado...")
# Une todos los bloques con exactamente DOS saltos de línea
# Esto le indica al LLM dónde termina exactamente un chunk semántico.
final_md = "\n\n".join(md_blocks)
# Generar archivo descargable
fd, path = tempfile.mkstemp(suffix=".md", text=True)
with os.fdopen(fd, 'w', encoding='utf-8') as f:
f.write(final_md)
progress(1.0, desc="✅ ¡Proceso completado con éxito!")
return path
except Exception as e:
raise gr.Error(f"Error crítico en el pipeline: {str(e)}")
# -------------------------------------------------------------------------
# INTERFAZ DE USUARIO (GRADIO 6.0+)
# -------------------------------------------------------------------------
with gr.Blocks() as app:
gr.Markdown(
"""
# 🧠 Generador RAG: PDF Médico a `.md` Optimizado
**Pipeline ETL de Grado Ingeniería:** Este sistema NO usa APIs externas. Descarga modelos de IA Espacial locales para deducir dobles columnas, **purga el 100% de tablas, imágenes, captions y marcas de agua**, y aplana los párrafos sanando anomalías ópticas.
Genera un archivo Markdown puro, diseñado **exclusivamente para ingesta en Gemini 3.1 Pro, NotebookLM y bases de datos vectoriales**.
"""
)
with gr.Row():
with gr.Column():
gr.Markdown("### 1. Subir Libro Académico")
input_file = gr.File(label="Sube tu archivo (.PDF)", file_types=[".pdf"])
process_btn = gr.Button("🚀 Extraer y Generar Markdown", variant="primary")
gr.Markdown("*Nota: Este proceso ocurre localmente. Puede tomar varios minutos si el PDF tiene cientos de páginas.*")
with gr.Column():
gr.Markdown("### 2. Descargar Dataset")
output_file = gr.File(label="Archivo Optimizado para IA (.md)", interactive=False)
# Conexión del orquestador
process_btn.click(
fn=process_medical_pdf,
inputs=[input_file],
outputs=[output_file]
)
if __name__ == "__main__":
app.launch(theme=gr.themes.Soft()) |