--- id: "0050" title: "`jupyter_exec` falla por cliente colaborativo (RESUELTO 2026-05-05)" status: completado type: bugfix domain: [] scope: multi-app priority: media depends: [] blocks: [] related: [] created: 2026-05-17 updated: 2026-05-17 tags: [] --- # 0050 — `jupyter_exec` falla por cliente colaborativo (RESUELTO 2026-05-05) ## Cierre (2026-05-05) Resuelto via opcion (b) del propio issue: migrar `jupyter_exec` a REST + `KernelClient` clasico, bypassando `NbModelClient`/Y.js. Bug raiz adicional encontrado al reproducir: `_notebook_exists` usaba `HEAD /api/contents`, y Jupyter Server responde **405 Method Not Allowed** (no soporta HEAD ahi). Cambiado a `GET /api/contents?content=0`. Cambios: - `python/functions/notebook/jupyter_exec.py` reescrito (v2.0.0). Sync, sin asyncio. Append/cell ahora usan REST `/api/contents` para leer/escribir celdas + outputs y `KernelClient` para ejecutar. - `python/functions/notebook/tests/test_jupyter_exec.py` con 5 tests unitarios (incluye guard para que HEAD no vuelva) y 4 tests e2e que arrancan un Jupyter Lab en puerto libre con `--collaborative` y verifican los tres modos. - 9/9 tests pasan en local. Trade-off documentado en el `.md`: los cambios se persisten a disco; Jupyter Lab los detecta y muestra en el browser (puede pedir 'Revert to disk' segun version y conflictos). Esto basta para los analyses del proyecto y es lo que ya se hacia con el workaround `nbformat`+`nbconvert`. --- # 0050 — `jupyter_exec` falla por cliente colaborativo (workaround documentado) ## APP Metadata | Campo | Valor | |-------|-------| | **ID** | 0050 | | **Estado** | pendiente | | **Prioridad** | media | | **Tipo** | bug — `python/functions/notebook/jupyter_exec.py` | ## Dependencias Ninguna. Independiente del resto. --- ## Sintoma Al ejecutar `jupyter_exec.py append ` contra un Jupyter Lab arrancado con el launcher estandar de los analyses (`run-jupyter-lab.sh`, flag `--collaborative`), la operacion falla con: ``` {"error": "HTTP Error 405: Method Not Allowed"} ``` `jupyter_write.py append-code` y `append-markdown` SI funcionan (no usan el canal colaborativo). El bug solo afecta a `jupyter_exec`, que necesita ejecutar la celda en el kernel y para eso usa `jupyter_nbmodel_client` con websocket Y.js. Reproducido en `2026-05-04` durante la construccion del analysis `projects/osint_graph/analysis/gliner_glirel_tuning/`. El resto de funciones del modulo `notebook/` quedan intactas: ```bash $JX append # ❌ HTTP 405 $JW append-code # ✅ OK (sin ejecucion) $JW append-markdown # ✅ OK $JX cell # 🔁 No probado, pero usa el mismo cliente $JX kernel # 🔁 No probado ``` --- ## Diagnostico (parcial) `jupyter_nbmodel_client` espera que el server tenga la extension `jupyter_collaboration` activa y montada en `/api/collaboration/...`. El launcher arranca jupyter con el flag CLI `--collaborative`, que en versiones recientes (`jupyter_server >= 2.x`, `jupyter-collaboration >= 4.x`) **ya no es suficiente** — la extension se carga via entry-point y se controla con flags distintos (`--YDocExtension.disable_rtc` o equivalente), o requiere un fichero de config explicito. Salida de `jupyter_discover.py` confirma el sintoma indirectamente: ```json { "url": "http://localhost:8888", "collaborative": false, ... } ``` aunque `--collaborative` esta en el launch command. Es decir: el server arranca, expone la API REST, pero la capa colaborativa NO esta activa. --- ## Workaround usado en `gliner_glirel_tuning` Cambio de tactica: en lugar de construir el notebook con `jupyter_exec append` celda a celda, **se ejecutan los experimentos en un script externo** y se empotran las celdas (codigo + outputs ya generados) con `nbformat` directo a fichero. El notebook resultante es persistente y no necesita el canal colaborativo. ```python # build_notebook.py import nbformat as nbf nb = nbf.v4.new_notebook() for src, stdout in cells: cell = nbf.v4.new_code_cell(src) cell.outputs = [nbf.v4.new_output("stream", name="stdout", text=stdout)] nb.cells.append(cell) nbf.write(nb, "notebooks/01_foo.ipynb") ``` Si se quieren outputs reales (DataFrames como HTML, figuras matplotlib), ejecutar despues con `nbconvert`: ```bash IPYTHONDIR=$(pwd)/.ipython ./.venv/bin/jupyter nbconvert \ --to notebook --execute notebooks/01_foo.ipynb \ --output 01_foo.ipynb --ExecutePreprocessor.timeout=300 ``` Esto bypassa completamente el canal colaborativo y produce un `.ipynb` funcional, abrible en Jupyter Lab para ver / iterar / re-ejecutar. Ver `projects/osint_graph/analysis/gliner_glirel_tuning/build_notebook.py` y `build_notebook_e2e.py` para ejemplos vivos. --- ## Causas raiz a investigar 1. **Verificar la version de `jupyter-collaboration`** en el venv del analysis. Si es >=4.x, el flag `--collaborative` ya no aplica y el launcher (`write_jupyter_launcher_bash_io`) tiene que actualizarse. 2. **El cliente** `jupyter_nbmodel_client` puede tener su propia ventana de versiones soportadas — comprobar pinning en `python/.venv` y en los venvs de analyses. 3. **El endpoint** `/api/collaboration/document` debe responder a un `GET` con HTTP 200 cuando la extension esta activa. Si responde `405`, el cliente intenta una operacion (POST/PUT) sobre un endpoint que solo acepta GET, sintoma de mismatch. --- ## Tareas 1. Reproducir el `HTTP 405` con un notebook nuevo y un kernel nuevo en un analysis recien creado. 2. Capturar la URL exacta y el metodo HTTP que dispara el 405 (anadir logging a `jupyter_exec.py` linea ~192/229 donde llama a `get_jupyter_notebook_websocket_url`). 3. Verificar version de `jupyter-collaboration` en el venv y comparar con la matriz de compatibilidad de `jupyter_nbmodel_client`. 4. Una de dos: - **(a)** Corregir el flag/config en `write_jupyter_launcher_bash_io` para activar correctamente la colaboracion en versiones nuevas. - **(b)** Si la API colaborativa cambio mucho, **migrar `jupyter_exec.py` a usar el `JupyterClient` clasico** (REST + WebSocket directo al kernel sin Y.js) que es estable a traves de versiones. `jupyter_kernel.py` ya hace algo asi y funciona. 5. Anadir un test e2e basico en `tests/` que arranca jupyter, lanza `jupyter_exec append`, verifica que la celda se ejecuto y captura stdout. Sin esto el bug puede regresar. --- ## Out of scope - Reescribir el sistema completo de notebook collaboration. - Migrar a un MCP. La regla `notebook_collaboration.md` es explicita: estas funciones reemplazan al MCP jupyter. --- ## Riesgos - Si la causa es la matriz de versiones, la opcion (a) puede generar fricion futura cada vez que `jupyter-collaboration` haga un breaking change. La opcion (b) es mas robusta a largo plazo aunque pierde la capacidad de ver cambios en tiempo real desde el navegador. ## Notas operativas Mientras este bug exista, el patron recomendado para construir notebooks desde un agente Claude en un analysis es: 1. `build_notebook.py` con `nbformat` para estructura + outputs estaticos. 2. `nbconvert --execute` para outputs reales (HTML, plots). 3. Si necesitas tiempo real con el browser, abre el notebook ya generado en Jupyter Lab y reejecuta a mano. El propio analysis `gliner_glirel_tuning` es referencia.