import spaces import gradio as gr import torch import html import difflib import spacy from models_inference import execute_inference, preload_all_models, MODEL_CONFIGS nlp = spacy.load("es_core_news_md") print("=" * 60) print("CUDA disponible:", torch.cuda.is_available()) print("Dispositivo:", "cuda" if torch.cuda.is_available() else "cpu") print("=" * 60) # Cache global para la última inferencia realizada LAST_PREDICTION_CACHE = { "text": None, "model_name": None, "prediction": None } nlp = spacy.load("es_core_news_md") TITLE = "Corrector de Errores Gramaticales del idioma Español" DESCRIPTION = """ **Demo interactiva para la correcion de errores gramaticales en el idioma español.** **Funcionamiento:** 1. Introduce un texto en el cuadro **Texto de entrada**. 2. Selecciona el modelo para la correción. 3. Presiona **Procesar** para observar el **Resultado** de la versión corregida por el modelo seleccionado. """ EXAMPLES = [ "Pepito jugar en el parque", "muchos perros juegan en la, parque tambien muchos niño salen de la escuels", "Las gata pasear por el jardin bello", "Mañana Tomy viajarear a Londres" ] MODELS =list(MODEL_CONFIGS.keys()) COLOR_ERROR="#ff3333" COLOR_CORRECT="#D0E6A5" CSS = f""" .correction-box-error, .correction-word {{ display: inline-flex; align-items: center; vertical-align: middle; border-radius: 6px; padding: 2px 6px; margin: 1px 2px; }} .correction-box-error {{ background-color: {COLOR_ERROR}; }} .correction-word {{ background-color: {COLOR_CORRECT}; }} /* Palabra original tachada */ .src-word {{ padding: 1px 4px; text-decoration: line-through; margin-right: 4px; opacity: 0.8; color:#ffffff; font-weight:bold; }} """ CSS += """ /* Contenedor principal al 70% del ancho */ .gradio-container { width: 70% !important; margin: auto; } /* Título centrado y grande */ .gradio-title { text-align: center !important; font-size: 2.3em !important; font-weight: bold; margin-bottom: 20px; } /* Subtítulos Markdown centrados */ .gradio-markdown { text-align: center; } /* Ajuste de filas y spacing */ .gr-row { margin-bottom: 15px; justify-content: center; /* centra los elementos de la fila */ } /* TextBoxes uniformes */ .gr-textbox { width: 48% !important; /* dos cajas lado a lado */ } /* Botones centrados y uniformes */ .gr-button { min-width: 150px; margin: 0 5px; } /* HTML output centrado */ .gr-html { width: 100%; text-align: left; margin-top: 10px; } /* Leyenda de colores */ .leyenda { margin-top: 10px; font-size: 0.9em; text-align: center; } """ LEYEND=f"""
Error
Corrección
""" def render_diff_html(orig_text, pred_text, mode="Corrección"): """ Compara orig_text y pred_text token por token y genera el HTML en una sola pasada, garantizando alineación perfecta sin duplicar palabras. """ orig_doc = nlp(orig_text) pred_doc = nlp(pred_text) orig_words = [t.text for t in orig_doc] pred_words = [t.text for t in pred_doc] # autojunk=False evita que difflib desalinee palabras o puntuaciones repetidas matcher = difflib.SequenceMatcher(None, orig_words, pred_words, autojunk=False) html_out = [] for tag, i1, i2, j1, j2 in matcher.get_opcodes(): # Reconstruir fragmentos preservando espacios originales src_str = "".join([t.text_with_ws for t in orig_doc[i1:i2]]) tgt_str = "".join([t.text_with_ws for t in pred_doc[j1:j2]]) src_clean = src_str.strip() tgt_clean = tgt_str.strip() # Obtener el espaciado correcto ws = "" if tag in ('equal', 'replace', 'insert') and j2 > 0: ws = pred_doc[j2 - 1].whitespace_ elif tag == 'delete' and i2 > 0: ws = orig_doc[i2 - 1].whitespace_ if tag == 'equal': html_out.append(html.escape(tgt_str)) elif tag == 'replace': if mode == "Original": html_out.append(f"{html.escape(src_clean)}{ws}") elif mode == "Corrección": html_out.append(f"{html.escape(tgt_clean)}{ws}") else: # "Ambos" html_out.append(f"{html.escape(src_clean)} {html.escape(tgt_clean)}{ws}") elif tag == 'delete': if mode in ("Original", "Ambos"): html_out.append(f"{html.escape(src_clean)}{ws}") elif tag == 'insert': if mode in ("Corrección", "Ambos"): html_out.append(f"{html.escape(tgt_clean)}{ws}") return "".join(html_out) @spaces.GPU def show_correction(text, model_name, type_draw): global LAST_PREDICTION_CACHE if not text or not text.strip(): return "" # Si el texto y el modelo son idénticos a los almacenados, reutiliza la predicción if (LAST_PREDICTION_CACHE["text"] == text and LAST_PREDICTION_CACHE["model_name"] == model_name and LAST_PREDICTION_CACHE["prediction"] is not None): predict = LAST_PREDICTION_CACHE["prediction"] else: # Si cambiaron, ejecuta la inferencia y actualiza la caché predict = execute_inference(text, model_name) LAST_PREDICTION_CACHE["text"] = text LAST_PREDICTION_CACHE["model_name"] = model_name LAST_PREDICTION_CACHE["prediction"] = predict return render_diff_html(text, predict, mode=type_draw) def clear(): global LAST_PREDICTION_CACHE LAST_PREDICTION_CACHE = {"text": None, "model_name": None, "prediction": None} return "", "" with gr.Blocks() as demo: gr.Markdown(f"

{TITLE}

") gr.Markdown(DESCRIPTION) with gr.Row(): text = gr.Textbox(label="Texto de entrada", placeholder="Texto con errores...", lines=3) with gr.Row(): model_select = gr.Dropdown(MODELS, label="Seleccionar modelo", value=MODELS[0]) with gr.Row(): btn_correct = gr.Button("Procesar", variant="primary") btn_clear = gr.Button("Limpiar", variant="secondary") type_draw = gr.Radio( choices=["Original", "Corrección", "Ambos"], label="Tipo de visualización", value="Corrección" ) gr.Markdown("### Resultado") output_text = gr.HTML(label="Correción") gr.HTML(LEYEND) # Evento al presionar el botón "Procesar" btn_correct.click( fn=show_correction, inputs=[text, model_select, type_draw], outputs=[output_text] ) # Evento reactivo al cambiar de radio button (cambio instantáneo sin re-ejecutar el modelo) type_draw.change( fn=show_correction, inputs=[text, model_select, type_draw], outputs=[output_text] ) btn_clear.click(fn=clear, outputs=[text, output_text]) gr.Examples( examples=[[ex] for ex in EXAMPLES], inputs=[text, model_select, type_draw], outputs=[output_text], fn=show_correction, cache_examples=False, ) if __name__ == "__main__": print("[INFO] Inicializando la interfaz y cargando modelos...") preload_all_models() demo.launch(css=CSS)