"""Gradio app for cronica-jax: stats -> Spanish crónica.""" from __future__ import annotations import os import sys from pathlib import Path import gradio as gr from huggingface_hub import hf_hub_download # Make the `cronica` package importable on the Space. SRC = Path(__file__).resolve().parent / "src" if SRC.exists(): sys.path.insert(0, str(SRC)) from tokenizers import Tokenizer # noqa: E402 REPO = "DanielRegaladoCardoso/cronica-jax-5m" CKPT_NAME = "ckpt_002000.pkl" STYLES = [ "rioplatense_apasionado", "rioplatense_tecnico", "rioplatense_literario", "mexicano_irreverente", "mexicano_clasico", "centroamericano_espn", "espanol_radiofonico", "comentario_tecnico", ] EXAMPLE_STATS = """ liga: La Liga fecha: 2024-04-21 local: Real Madrid visitante: Barcelona resultado: 3-2 goles: - 18' Jude Bellingham (Real Madrid) - 30' Robert Lewandowski (Barcelona) - 56' Vinicius Junior (Real Madrid) - 71' Robert Lewandowski (Barcelona) - 89' Jude Bellingham (Real Madrid) estadio: Santiago Bernabéu asistencia: 78.412 árbitro: César Soto Grado """ # Load model & tokenizer at startup (once). print("Downloading checkpoint + tokenizer from HF Hub...") tok_path = hf_hub_download(REPO, "tokenizer.json") ckpt_path = hf_hub_download(REPO, CKPT_NAME) print("Loading model...") # Defer heavy imports until after we know files exist from cronica.train import load_ckpt # noqa: E402 from cronica.sample import generate_cronica # noqa: E402 tok = Tokenizer.from_file(tok_path) params, cfg, step = load_ckpt(Path(ckpt_path)) print(f"Loaded ckpt step={step}, vocab={cfg.vocab_size}, layers={cfg.n_layers}") def generate(stats_block: str, style: str, temperature: float, top_p: float, max_tokens: int): if not stats_block.strip(): return "Por favor pega un bloque ." if style not in STYLES: return f"Estilo desconocido: {style}" try: text = generate_cronica( params, cfg, tok, stats_block, style, max_new_tokens=int(max_tokens), temperature=float(temperature), top_k=50, top_p=float(top_p), seed=0, ) return text or "(modelo no produjo texto)" except Exception as e: return f"Error: {type(e).__name__}: {e}" with gr.Blocks(title="cronica-jax — Stats → Crónica") as demo: gr.Markdown(""" # cronica-jax **Mini-LLM (5.26M parámetros) escrito desde cero en JAX puro.** Pegale los stats de un partido en el formato `...` y elegí un estilo de comentarista. El modelo genera la crónica en español. ⚠️ Es un modelo deliberadamente pequeño (≈5M params) — la fluidez no compite con un LLM grande. Su valor es la *craft*: implementación end-to-end del Transformer desde primeros principios en JAX. Esperá texto coherente a nivel de oración pero con hallucinations a nivel de párrafo. [GitHub](https://github.com/DanielRegaladoUMiami/cronica-jax) · [Dataset](https://huggingface.co/datasets/DanielRegaladoCardoso/cronicas-d2t) · [Modelo](https://huggingface.co/DanielRegaladoCardoso/cronica-jax-5m) """) with gr.Row(): with gr.Column(): stats = gr.Textbox( value=EXAMPLE_STATS, lines=15, max_lines=25, label=" block", ) style = gr.Dropdown(STYLES, value=STYLES[0], label="Estilo de comentarista") with gr.Accordion("Parámetros de sampling", open=False): temperature = gr.Slider(0.3, 1.3, value=0.85, step=0.05, label="Temperature") top_p = gr.Slider(0.5, 1.0, value=0.9, step=0.05, label="Top-p (nucleus)") max_tokens = gr.Slider(50, 300, value=150, step=10, label="Max new tokens (≥200 = lento en CPU)") btn = gr.Button("Generar crónica", variant="primary") with gr.Column(): out = gr.Textbox(label="Crónica generada", lines=15, max_lines=25, show_copy_button=True) btn.click(generate, inputs=[stats, style, temperature, top_p, max_tokens], outputs=out) gr.Markdown(f"Modelo cargado: `{REPO}` (step {step}, " f"{cfg.n_layers} layers, d_model={cfg.d_model})") if __name__ == "__main__": demo.launch()