File size: 8,400 Bytes
5520989 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | ---
title: MSL Internal API Joint
emoji: 🎻
colorFrom: purple
colorTo: pink
sdk: docker
app_port: 7860
pinned: false
license: other
---
# bert-api-v8-joint/
Backend Python Flask que sirve el **joint intent + slot filling model** entregado por RMIT en Parts 5+6 (PDF Part5_Part6, mayo 2026). Arquitectura distinta a v6/v7-part6: en lugar de un único clasificador `AutoModelForSequenceClassification`, monta una clase custom `JointIntentSlotModel(nn.Module)` con dos heads sobre `AutoModel` — uno para intent, otro para BIO slot labels por token.
**Origen:** copiado de `D:\XDev.Projects\MEHEARSAL-NLP-CONTROLLER-RMIT\Part_5_6\bert-api\bert-api-01` (versión D — defensiva/híbrida del análisis del doc 40 §3.2) + 4 modificaciones para HF Spaces Docker (ver §"Adaptaciones" más abajo).
**Despliegue actual:** HuggingFace Space `MuseSceneLab/msl-internal-api-v8-intent` (Docker SDK). Ver doc 40 §5.2.1 + §6 Fase A.1-A.7.
> **Nota sobre la convención de nombres:** la carpeta local se llama `bert-api-v8-joint/` (refleja la arquitectura técnica — joint intent + slot model). El Space en HF se llama `msl-internal-api-v8-intent` (refleja la convención MSL `vN-{focus}` por consistencia con `msl-internal-api-v7-part6`). El env var `BERT_V8_INTENT_API_URL` y el route `/api/classify-bert-v8-intent` siguen el nombre del Space.
**Modelo HF:** `MuseSceneLab/part-5-6-model` — 21 intents musicales (sin generic MUTE/SOLO/LOOP en el dataset, aunque INTENT_METADATA los conserva por compat) + slot filling BIO (`O`, `B-instrument`, `I-instrument`, `B-section`, `B-bpm`, `B-bar`, `B-percent`, `B-style`).
> **Nota sobre el frontmatter YAML del principio:** lo leen los Spaces de HuggingFace para configurar el deploy (título, SDK, puerto). `app_port: 7860` es crítico — sin él HF no sabe qué puerto exponer y el Space queda en "config error". En cualquier otro contexto (lectura local, repo de GitHub) el frontmatter se ignora o se renderiza como bloque inicial.
**Despliegue futuro:** AWS App Runner cuando se cumplan los disparadores de doc 33 §13.4.
---
## Estructura
```
bert-api/
├── bert_api.py Flask app. Carga modelo en arranque, expone /classify y /health
├── requirements.txt flask, flask-cors, transformers, torch, huggingface_hub, python-dotenv
├── Dockerfile Imagen para HF Spaces. Python 3.11-slim, expone 7860
├── .dockerignore Excluye venv, __pycache__, README, etc. de la imagen
└── README.md Este archivo
```
---
## Endpoints
| Método | Path | Body | Devuelve |
|---|---|---|---|
| `POST` | `/classify` | `{ "utterance": "mute the guitar" }` | `{ intent_label, locale, target, params_json, confidence, model, slots }` |
| `GET` | `/health` | — | `{ status, model, hf_model, num_intents, num_slots }` |
### Diferencias del shape de respuesta vs v6/v7-part6
| Campo | v6/v7-part6 (single-task) | **v8-joint (este Space)** |
|---|---|---|
| `confidence` | `0-100` (percentage, `round(c*100, 2)`) | **`0-1` (fraction, `round(c, 4)`)** — el frontend debe formatear como `(c * 100).toFixed(2)` antes de mostrar, y el `mapConfidence()` del proxy `/api/classify-bert-joint/route.ts` debe usar umbrales 0.80 / 0.50 (no 80 / 50) |
| `model` | `"DistilBERT"` | `"DistilBERT-Joint-v6"` |
| `slots` | (no existe) | `{ "guitar": "B-instrument", "4": "B-bar" }` — diccionario `palabra → BIO label`. Vacío `{}` si no hay slots |
| `/health` extras | (nada) | `hf_model`, `num_intents`, `num_slots` |
---
## Variables de entorno
| Var | Default | Para qué |
|---|---|---|
| `HF_TOKEN` | _(obligatorio)_ | Auth para descargar el modelo privado de HF. En HF Spaces se configura como Secret del Space |
| `BERT_MODEL_NAME` | `MuseSceneLab/part-5-6-model` | Cambiar para forzar otro joint model. **Solo modelos joint compatibles** — un modelo single-task aquí rompería el `model.load_state_dict()` porque la clase `JointIntentSlotModel` espera dos heads (`intent_head`, `slot_head`) y un `label_maps.json` con `id2intent` + `id2slot` |
| `PORT` | `7860` | Puerto. Para gunicorn en el Dockerfile se hardcodea a 7860 en el flag `--bind`. Para Flask local (`python bert_api.py`) se lee de aquí |
## Servidor: gunicorn en producción, Flask dev en local
El Dockerfile lanza el backend con **gunicorn** (WSGI server productivo):
```
gunicorn --bind 0.0.0.0:7860 --workers 2 --preload --timeout 120 bert_api:app
```
- `--workers 2`: 2 procesos paralelos (CPU Basic free de HF Spaces tiene 2 vCPU).
- `--preload`: carga el modelo una vez antes de fork, los workers comparten memoria via COW. Sin esto, cada worker cargaría su copia y duplicaríamos memoria.
- `--timeout 120`: si una inferencia se cuelga, mata el worker y arranca otro. Default 30s es demasiado bajo para casos límite con torch.
Para **desarrollo local rápido** (iteración sobre `bert_api.py` sin tener que reconstruir imagen), el bloque `if __name__ == '__main__'` con `app.run()` sigue funcionando — basta con `python bert_api.py`. Esto NO se ejecuta en Spaces porque gunicorn importa `bert_api:app` directamente, sin pasar por `__main__`.
---
## Desarrollo local
```bash
cd bert-api
python -m venv venv
.\venv\Scripts\activate # Windows PowerShell: .\venv\Scripts\Activate.ps1
pip install -r requirements.txt
$env:HF_TOKEN = "hf_..." # tu token con permiso de lectura sobre el modelo
python bert_api.py
```
Levanta en `http://localhost:7860`. Probar con:
```bash
curl -X POST http://localhost:7860/classify -H "Content-Type: application/json" -d '{"utterance":"mute the guitar"}'
```
**Nota:** la primera ejecución descarga el modelo (~500 MB) desde HF a la caché local (`~/.cache/huggingface/`). Ejecuciones posteriores son instantáneas.
---
## Despliegue como Space (proceso R4-R7 de doc 33)
1. Crear Space en `huggingface.co/new-space`:
- Owner: `MuseSceneLab`
- Name: `msl-internal-api-v8-intent`
- SDK: **Docker** (template Blank — usamos nuestro Dockerfile)
- Hardware: CPU Basic (free)
- Visibility: Public _(el contenedor; los pesos del modelo siguen privados)_
- License: `other` (decisión doc 34 §D1 — IP de MSL)
2. En el Space, ir a Settings → Repository secrets → añadir `HF_TOKEN` con el token `mehearsal-read` de Jose.
3. Subir los 4 archivos vía Web UI (drag & drop en `Files → Add file → Upload files`):
`bert_api.py`, `Dockerfile`, `requirements.txt`, `README.md`.
4. HF construye la imagen (~3-5 min). El cold start del Space joint puede ser **más lento que el de v6/v7-part6** porque `snapshot_download()` descarga el modelo entero la primera vez (en lugar del lazy load de `AutoModelForSequenceClassification.from_pretrained()`). Verificar logs en la UI del Space.
5. URL pública: `https://musescenelab-msl-internal-api-v8-intent.hf.space/classify`.
---
## Adaptaciones respecto al código fuente
Fuente: `Part_5_6/bert-api/bert-api-01` del repo `MEHEARSAL-NLP-CONTROLLER-RMIT` (rama `part_5_6`) — versión **D** del análisis del doc 40 §3.2.
Cuatro diffs vs ese fuente:
1. **Import `os`** al principio del archivo.
2. **`HF_MODEL_NAME` configurable** vía env var (default `MuseSceneLab/part-5-6-model`).
3. **Porting de carga LOCAL → carga HF Hub**: el fuente D carga el modelo desde `nlp_v5/best_joint_model/` (directorio local relativo al script). Sustituido por `snapshot_download(repo_id=HF_MODEL_NAME, repo_type="model")` para que HF Spaces descargue del Hub. Patrón tomado de la versión C (`Part_5_6/bert-api/bert_api.py`).
4. **Puerto y debug** en `app.run()`: puerto leído de env var `PORT` (default 7860, requisito de Spaces), `debug=False` para producción.
La lógica de `JointIntentSlotModel`, `INTENT_METADATA` (21 intents), `extract_entities()` con `params_json` computado por intent, el intent upgrade hack heredado de v5, y la extracción slot-first + regex fallback queda **intacta**. Esta es la "versión defensiva" (D) decidida en doc 40 §3.2.1 frente a la "versión limpia según PDF Part 6" (C).
---
## Roadmap
- **Ahora:** HF Spaces Docker (este folder = código fuente, sin cambios entre dev local y prod Space)
- **Futuro:** migración a AWS App Runner. Ver doc 33 §13. El Dockerfile actual sirve igual sin cambios.
|