dabc945eda
Extrae al registry funciones del proyecto interno footprint_aurgi: - core (6): slugify_ascii, normalize_for_join, cp_provincia_es, infer_provincia_from_cp, safe_read_csv_fallback, csv_to_parquet_duckdb - geo puras (7): haversine_km, point_in_ring, point_in_polygon, point_in_polygons_bbox, polygon_bbox, extent_with_padding, distance_bucket - geo I/O (4): load_geojson_polygons, load_boundary_gdf, add_basemap_osm, add_basemap_with_timeout - valhalla client (4): valhalla_route, valhalla_isochrone, valhalla_isochrones_async, valhalla_matrix_1_to_n - datascience stats (7): trimmed_mean, geometric_mean, detect_distribution_type, best_central_tendency, summary_stats, kde_density_levels, alpha_shape_concave_hull - datascience fuzzy (3): fuzzy_merge_adaptive (rapidfuzz), words_to_dataset, remove_words_from_column - datascience viz (2): plot_kde_2d, plot_heatmap_log - infra (4): compress_pdf_ghostscript, render_table_page_pdfpages, add_header_logo, osm2pgsql_ingest - pipelines (4): setup_geo_stack_docker, compute_centers_reachability, generate_isochrones_by_zone, count_points_per_zone - types geo (4): LonLat, BBox, IsochroneRequest, Centro Incluye: - apps/footprint_geo_stack/ (PostGIS + Martin + Valhalla via docker-compose) - 131/132 tests pasan (1 skip esperado: osm2pgsql en PATH) - Issue tracker dev/issues/0052-footprint-aurgi-extraction.md - Atribucion uniforme: source_repo internal:footprint_aurgi, source_license internal-aurgi - Build con 9 agentes en paralelo (8 wave 1 + 1 wave 2 pipelines) Tambien commitea trabajo previo no commiteado: aggregate_extraction_results, chunk_with_overlap, clean_pdf_text, merge_entity_aliases, extract_graph_gliner2, extract_relations_mrebel, extract_triples_spacy_es, gliner2/mrebel/marianmt/rebel/spacy_es load_model, parse_rebel_output, translate_es_to_en, issue 0050/0051. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
167 lines
5.8 KiB
Markdown
167 lines
5.8 KiB
Markdown
# 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 <notebook> <code>` 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 <nb> <code> # ❌ HTTP 405
|
|
$JW append-code <nb> <code> # ✅ OK (sin ejecucion)
|
|
$JW append-markdown <nb> <md> # ✅ OK
|
|
$JX cell <nb> <idx> # 🔁 No probado, pero usa el mismo cliente
|
|
$JX kernel <code> # 🔁 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.
|