Files
cbc2027/latex/pipeline_resumen_latex.md
renato97 915f827305 feat: Implementación de Resúmenes Matemáticos con LaTeX y Pandoc
##  Novedades
- **Soporte LaTeX**: Generación de PDFs y DOCX con fórmulas matemáticas renderizadas correctamente usando Pandoc.
- **Sanitización Automática**: Corrección de caracteres Unicode (griegos/cirílicos) y sintaxis LaTeX para evitar errores de compilación.
- **GLM/Claude Prioritario**: Cambio de proveedor de IA predeterminado a Claude/GLM para mayor estabilidad y capacidad de razonamiento.
- **Mejoras en Formato**: El formateo final del resumen ahora usa el modelo principal (GLM) en lugar de Gemini para consistencia.

## 🛠️ Cambios Técnicos
- `document/generators.py`: Reemplazo de generación manual por `pandoc`. Añadida función `_sanitize_latex`.
- `services/ai/claude_provider.py`: Soporte mejorado para variables de entorno de Z.ai.
- `services/ai/provider_factory.py`: Prioridad ajustada `Claude > Gemini`.
- `latex/`: Añadida documentación de referencia para el pipeline LaTeX.
2026-01-26 23:40:16 +00:00

6.5 KiB

Pipeline de Generación de Resúmenes Matemáticos (LaTeX -> PDF)

Este documento contiene un script genérico en Python diseñado para integrarse en pipelines de automatización (GitHub Actions, Jenkins, GitLab CI). El script toma un archivo de texto plano, genera un resumen académico con fórmulas matemáticas usando LLMs (MiniMax, GLM, Gemini) y lo compila a PDF preservando la notación LaTeX.

1. Requisitos del Sistema

El entorno donde se ejecute este script debe tener instalado:

  • Python 3.8+
  • Pandoc (para conversión de documentos)
  • PDFLaTeX (generalmente parte de TexLive, para renderizar fórmulas)

Instalación en Debian/Ubuntu (Docker o CI)

apt-get update && apt-get install -y pandoc texlive-latex-base texlive-fonts-recommended python3-pip
pip install requests

2. Script Genérico (math_summary.py)

Guarda el siguiente código como math_summary.py. Este script es agnóstico al proveedor y se configura mediante argumentos o variables de entorno.

#!/usr/bin/env python3
import os
import sys
import argparse
import subprocess
import requests
import json

# Configuración de Modelos
PROVIDERS = {
    "minimax": {
        "url": "https://api.minimax.io/anthropic/v1/messages",
        "model": "MiniMax-M2",
        "header_key": "x-api-key",
        "version_header": {"anthropic-version": "2023-06-01"},
        "env_var": "MINIMAX_API_KEY"
    },
    "glm": {
        "url": "https://api.z.ai/api/anthropic/v1/messages",
        "model": "glm-4.7",
        "header_key": "x-api-key",
        "version_header": {"anthropic-version": "2023-06-01"},
        "env_var": "GLM_API_KEY"
    }
}

PROMPT_SYSTEM = """
Eres un asistente académico experto en matemáticas y economía.
Tu tarea es resumir el texto proporcionado manteniendo el rigor científico.

REGLAS DE FORMATO (CRÍTICO):
1. La salida debe ser Markdown válido.
2. TODAS las fórmulas matemáticas deben estar en formato LaTeX.
3. Usa bloques $$ ... $$ para ecuaciones centradas importantes.
4. Usa $ ... $ para ecuaciones en línea.
5. NO uses bloques de código (```latex) para las fórmulas, úsalas directamente en el texto para que Pandoc las renderice.
6. Incluye una sección de 'Conceptos Matemáticos' con las fórmulas desglosadas.
"""

def get_api_key(provider):
    env_var = PROVIDERS[provider]["env_var"]
    key = os.getenv(env_var)
    if not key:
        print(f"Error: La variable de entorno {env_var} no está definida.")
        sys.exit(1)
    return key

def call_llm(provider, text, api_key):
    print(f"--- Contactando API: {provider.upper()} ---")
    
    config = PROVIDERS[provider]
    headers = {
        "Content-Type": "application/json",
        config["header_key"]: api_key,
    }
    if "version_header" in config:
        headers.update(config["version_header"])

    payload = {
        "model": config["model"],
        "max_tokens": 4096,
        "messages": [
            {"role": "user", "content": f"{PROMPT_SYSTEM}\n\nTEXTO A RESUMIR:\n{text}"}
        ]
    }
    
    try:
        resp = requests.post(config["url"], json=payload, headers=headers, timeout=120)
        resp.raise_for_status()
        data = resp.json()
        
        # Manejo específico para MiniMax que puede devolver bloques de "thinking"
        content = ""
        for part in data.get("content", []):
            if part.get("type") == "text":
                content += part.get("text", "")
        
        # Fallback si no hay tipo explícito (GLM estándar)
        if not content and data.get("content"):
            if isinstance(data["content"], list):
                    content = data["content"][0].get("text", "")
            
        return content
        
    except Exception as e:
        print(f"Error llamando a {provider}: {e}")
        return None

def convert_to_pdf(markdown_content, output_file):
    base_name = os.path.splitext(output_file)[0]
    md_file = f"{base_name}.md"
    
    with open(md_file, "w", encoding="utf-8") as f:
        f.write(markdown_content)
        
    print(f"--- Generando PDF: {output_file} ---")
    cmd = [
        "pandoc", md_file,
        "-o", output_file,
        "--pdf-engine=pdflatex",
        "-V", "geometry:margin=2.5cm",
        "-V", "fontsize=12pt",
        "--highlight-style=tango"
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode == 0:
        print("Éxito: PDF generado correctamente.")
        return True
    else:
        print("Error en Pandoc:")
        print(result.stderr)
        return False

def main():
    parser = argparse.ArgumentParser(description="Generador de Resúmenes Matemáticos PDF")
    parser.add_argument("input_file", help="Ruta al archivo de texto (.txt) fuente")
    parser.add_argument("--provider", choices=["minimax", "glm"], default="glm", help="Proveedor de IA a usar")
    parser.add_argument("--output", default="resumen_output.pdf", help="Nombre del archivo PDF de salida")
    
    args = parser.parse_args()
    
    if not os.path.exists(args.input_file):
        print(f"Error: No se encuentra el archivo {args.input_file}")
        sys.exit(1)
        
    with open(args.input_file, "r", encoding="utf-8") as f:
        text_content = f.read()
        
    api_key = get_api_key(args.provider)
    summary_md = call_llm(args.provider, text_content, api_key)
    
    if summary_md:
        convert_to_pdf(summary_md, args.output)
    else:
        print("Fallo en la generación del resumen.")
        sys.exit(1)

if __name__ == "__main__":
    main()

3. Ejemplo de Uso en Pipeline

Ejecución Local

export GLM_API_KEY="tu_api_key_aqui"
python3 math_summary.py entrada.txt --provider glm --output reporte_final.pdf

GitHub Actions (Ejemplo .yaml)

Este paso automatizaría la creación del PDF cada vez que se sube un .txt a la carpeta docs/.

name: Generar PDF Matemático
on:
  push:
    paths:
      - 'docs/*.txt'

jobs:
  build-pdf:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Instalar dependencias
        run: |
          sudo apt-get update
          sudo apt-get install -y pandoc texlive-latex-base texlive-fonts-recommended
          pip install requests

      - name: Generar Resumen
        env:
          GLM_API_KEY: ${{ secrets.GLM_API_KEY }}
        run: |
          python3 math_summary.py docs/archivo.txt --provider glm --output docs/resumen.pdf
          
      - name: Subir Artefacto
        uses: actions/upload-artifact@v3
        with:
          name: PDF-Resumen
          path: docs/resumen.pdf