Code interpreter funcional en Vercel · Python en el navegador con Pyodide
La versión 7.10.0 migra el code interpreter de Python a Pyodide (WebAssembly), ejecutándose 100% en el navegador del usuario. Antes, en Vercel Hobby, el endpoint /api/code-interpreter siempre fallaba con "No hay runtime de Python disponible" porque el runtime serverless de Vercel no incluye Python. Ahora cualquier bloque de código Python puede ejecutarse desde gia.gesem.es / gia.fgarola.es sin más infraestructura, gratis, con numpy/pandas/matplotlib/scipy/sympy/scikit-learn disponibles vía micropip. La primera ejecución descarga ~6MB cacheados por el navegador; las siguientes son instantáneas.
Nuevo
- Code interpreter ejecutándose en producción (Vercel) sin necesidad de servicio externo de Python.
- CodeExecutionBlock.tsx ya no llama a /api/code-interpreter — usa `executePython()` cliente con onProgress callback que muestra "Cargando Pyodide… → Inicializando Python… → Ejecutando código…".
- MessageBubble.tsx (bloque de código en respuesta del asistente) también migrado a Pyodide con dynamic import (lazy-loaded sólo si el usuario pulsa Ejecutar).
- Detección automática de imports: si tu código tiene `import numpy as np` o `from pandas import …`, los paquetes se cargan vía `loadPackagesFromImports` antes de ejecutar.
- Sandbox: `input()` se bloquea con un mensaje claro porque no hay stdin interactivo en navegador. El resto de Python estándar funciona normal.
- Timeout configurable (30s por defecto) protege contra bucles infinitos.
- Endpoint /api/code-interpreter sigue existiendo como fallback para self-hosted con Docker (el Dockerfile incluye python3) — ahora detecta `process.env.VERCEL` y devuelve un mensaje claro recomendando recargar la app para usar Pyodide.
- Bibliotecas soportadas de fábrica: numpy, pandas, matplotlib, scipy, scikit-learn, sympy, statsmodels, networkx, pillow, regex, lxml, beautifulsoup4, pyyaml, jinja2.
- Bibliotecas adicionales vía `micropip.install(...)` desde el propio código (ej. requests, plotly).
- Coste para ti: 0€. Sin servicio externo, sin Pro plan, sin Modal/E2B.
Mejorado
- Nuevo módulo `src/lib/pyodide-client.ts`: singleton loader con caché, lazy-load del CDN jsdelivr, captura de stdout/stderr, detección automática de paquetes desde imports.
- Coste para el usuario: ~6MB descargados la primera vez, cacheados por el navegador para siempre. Siguientes cargas son instantáneas.
Por qué Pyodide y no otra solución
- Vercel Hobby: el runtime serverless no incluye Python. `child_process.spawn("python")` siempre falla con ENOENT. Imposible añadir Python al runtime de Vercel.
- Alternativa A — Vercel Pro: function timeout de 60s (vs 10s en Hobby) pero SIGUE sin tener Python. No resuelve el problema.
- Alternativa B — servicio externo (E2B, Modal, Replicate): coste mensual + complejidad de auth/proxying. Para un proyecto Hobby es overkill.
- Alternativa C — VPS aparte con Docker: $5-10/mes, requiere mantener un microservicio.
- Alternativa D — Pyodide: 0€, 0 servicios nuevos, ejecuta en el cliente. Funciona en Vercel, Netlify, Cloudflare Pages, GitHub Pages, donde sea. Bibliotecas científicas pre-compiladas. Ganador.
Nuevo módulo `src/lib/pyodide-client.ts`
- Singleton: una sola instancia de Pyodide por tab. La primera ejecución carga ~6MB; las siguientes son instantáneas.
- Lazy-load del script: añade `<script src="https://cdn.jsdelivr.net/pyodide/v0.27.5/full/pyodide.js">` al document.head sólo cuando se llama a `getPyodide()` por primera vez. Sin coste de bundle inicial.
- API simple: `executePython(code, { onProgress, timeoutMs })` devuelve `{ success, output, error, execution_time_ms, runtime, pyodide_version }`.
- Captura de stdout/stderr vía `py.setStdout({ batched })` y `py.setStderr({ batched })` — la salida se acumula y se devuelve concatenada.
- Detección automática de paquetes: regex sobre `import X` y `from X import …`. Si X está en la whitelist (numpy, pandas, matplotlib, scipy, sklearn, sympy, statsmodels, networkx, PIL, regex, lxml, bs4, yaml, jinja2, dateutil, pytz, micropip), se carga antes de ejecutar.
- Sandbox prelude: bloquea `input()` con `RuntimeError("input() interactivo no está disponible en el navegador. Usa variables fijas en el código.")`.
- Helpers exportados: `isPyodideReady()`, `isPyodideLoading()`, `prewarmPyodide()` (precarga en idle para tener Pyodide listo cuando el usuario pulse Ejecutar por primera vez).
Componentes UI actualizados
- CodeExecutionBlock.tsx: `handleExecute` ahora usa `executePython()` directo. Eliminado el check de `conversationId` (no es necesario en cliente).
- Estado `pyodideStatus` para mostrar la fase de carga ("Cargando Pyodide…", "Cargando paquetes: numpy, pandas…", "Ejecutando código…").
- El botón "Ejecutar" muestra el status actual durante la ejecución en lugar de un genérico "Ejecutando…".
- MessageBubble.tsx (CodeBlock interno): `import("@/lib/pyodide-client")` dinámico — el chunk de Pyodide sólo se descarga si el usuario pulsa Ejecutar (no impacta first paint del chat).
- Mensajes de error claros: si el CDN no carga, muestra "No se pudo cargar Pyodide desde el CDN. Comprueba tu conexión a internet." (caso usuario sin conexión).
Endpoint /api/code-interpreter (fallback)
- No se elimina — sigue siendo útil para self-hosted con Docker (el Dockerfile del proyecto YA incluye `apk add python3`).
- Detecta `process.env.VERCEL` o `process.env.VERCEL_ENV` y devuelve un mensaje específico: "El runtime serverless de Vercel no incluye Python. En el cliente, GIA ejecuta Python en el navegador vía Pyodide — actualiza la app (Ctrl+R) y reintenta."
- En self-hosted local sin Vercel y sin Python en PATH, el mensaje recuerda al usuario que el cliente puede ejecutar Pyodide igualmente — recarga la app.
Compatibilidad y limitaciones
- Soportado en Chrome, Firefox, Safari, Edge modernos (todos con WebAssembly + dynamic import).
- NO funciona en navegadores muy antiguos sin BigInt o WebAssembly. En la práctica, irrelevante en 2026.
- NO hay acceso al filesystem del usuario (correcto: es un sandbox).
- NO hay subprocess, sockets reales ni threading (limitaciones del entorno de navegador).
- Las requests HTTP funcionan via `pyodide-http` (que stubbea `requests` y `urllib`) pero CORS aplica como en cualquier fetch del navegador.
- matplotlib renderiza imágenes que se pueden mostrar como base64 en el output. Para gráficos interactivos plotly funciona pero requiere `micropip.install("plotly")`.
- numpy + pandas + matplotlib son los paquetes científicos clave y vienen pre-compilados oficialmente — instantáneos tras el primer load.
Rendimiento
- First load (cold): ~5-15s descargando 6MB de WASM + bibliotecas core.
- Subsequent loads (warm): instantáneo (módulo cacheado en memoria).
- Browser cache: el archivo `.wasm` y los `.whl` quedan en HTTP cache, así que entre sesiones también es rápido.
- Ejecuciones consecutivas: instantáneas (mismo intérprete, sólo se ejecuta el código nuevo).
- Bundle inicial de Next.js sin afectar: el `import("@/lib/pyodide-client")` es dinámico → chunk separado que sólo se descarga al pulsar Ejecutar.
Calidad técnica
- Typecheck verde tras la migración.
- Sin nuevas dependencias en `package.json` — Pyodide se carga vía CDN, no vía npm. Mantiene el bundle ligero.
- Sin breaking changes en la API: el endpoint /api/code-interpreter sigue existiendo y respondiendo, sólo que con un mensaje claro cuando no hay runtime.
- Cero coste para producción Vercel; cero coste para self-hosted; cero coste para dev local.
