P3.5-P3.7: 2-layer inventory, capability routing, STT/TTS adapters, Dev Contract
NCS:
- _collect_worker_caps() fetches capability flags from node-worker /caps
- _derive_capabilities() merges served model types + worker provider flags
- installed_artifacts replaces inventory_only (disk scan with DISK_SCAN_PATHS env)
- New endpoints: /capabilities/caps, /capabilities/installed
Node Worker:
- STT_PROVIDER, TTS_PROVIDER, OCR_PROVIDER, IMAGE_PROVIDER env flags
- /caps endpoint returns capabilities + providers for NCS aggregation
- STT adapter (providers/stt_mlx_whisper.py) — remote + local mode
- TTS adapter (providers/tts_mlx_kokoro.py) — remote + local mode
- OCR handler via vision_prompted (ollama_vision with OCR prompt)
- NATS subjects: node.{id}.stt/tts/ocr/image.request
Router:
- POST /v1/capability/{stt,tts,ocr,image} — capability-based offload routing
- GET /v1/capabilities — global view with capabilities_by_node
- require_fresh_caps(ttl) preflight guard
- find_nodes_with_capability(cap) + load-based node selection
Ops:
- ops/fabric_snapshot.py — full runtime snapshot collector
- ops/fabric_preflight.sh — quick check + snapshot save + diff
- docs/fabric_contract.md — Dev Contract v0.1 (preflight-first)
- tests/test_fabric_contract.py — CI enforcement (6 tests)
Made-with: Cursor
This commit is contained in:
159
docs/fabric_contract.md
Normal file
159
docs/fabric_contract.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Dev Contract: Preflight-first, Node-specific, Zero Assumptions (v0.1)
|
||||
|
||||
## 0. Заборона припущень
|
||||
|
||||
Будь-яка дія/пропозиція щодо моделей, провайдерів, портів, compose або routing **заборонена без preflight snapshot** з цільової ноди.
|
||||
|
||||
Без snapshot — ні коміт, ні деплой, ні рекомендація.
|
||||
|
||||
## 1. Обов'язковий Preflight Snapshot
|
||||
|
||||
### 1.1 Збір
|
||||
|
||||
Перед кожною зміною запустити `ops/fabric_snapshot.py` на цільовій ноді:
|
||||
|
||||
```bash
|
||||
# NODA2 (локально)
|
||||
python3 ops/fabric_snapshot.py --node-id NODA2
|
||||
|
||||
# NODA1 (через SSH tunnel)
|
||||
ssh -L 18099:127.0.0.1:8099 -L 18109:127.0.0.1:8109 \
|
||||
-L 19102:127.0.0.1:9102 -fN root@144.76.224.179
|
||||
python3 ops/fabric_snapshot.py --node-id noda1 \
|
||||
--ncs http://127.0.0.1:18099 --worker http://127.0.0.1:18109 \
|
||||
--router http://127.0.0.1:19102 --ollama http://127.0.0.1:21434 \
|
||||
--ssh root@144.76.224.179
|
||||
```
|
||||
|
||||
Snapshot зберігається в `ops/preflight_snapshots/<node_id>_<timestamp>.json`.
|
||||
|
||||
### 1.2 Endpoints (всі обов'язкові)
|
||||
|
||||
| Endpoint | Що перевіряє |
|
||||
|---|---|
|
||||
| `NCS /capabilities` | served_models, capabilities, node_load, runtimes |
|
||||
| `NCS /capabilities/caps` | capability flags (stt/tts/ocr/image) |
|
||||
| `NCS /capabilities/installed` | installed_artifacts (disk scan) |
|
||||
| `node-worker /caps` | provider flags (STT_PROVIDER, TTS_PROVIDER...) |
|
||||
| `node-worker /healthz` | NATS connection, concurrency |
|
||||
| `Router /v1/capabilities` | global view (всі ноди, capabilities_by_node) |
|
||||
| `Router /v1/models` | глобальний пул моделей |
|
||||
| `Ollama /api/tags` | реальні Ollama моделі на ноді |
|
||||
| `docker ps` | список контейнерів |
|
||||
|
||||
### 1.3 Quality gate (must-pass)
|
||||
|
||||
Snapshot валідний тільки якщо:
|
||||
|
||||
- Router healthy
|
||||
- NCS healthy
|
||||
- Node-worker healthy
|
||||
- `capabilities_by_node` містить цільову ноду
|
||||
- `served_models` не порожній (або є пояснення "compute-less node")
|
||||
|
||||
## 2. Served ≠ Installed
|
||||
|
||||
### 2.1 Два шари правди
|
||||
|
||||
| Шар | Джерело | Використання |
|
||||
|---|---|---|
|
||||
| **served_models** | NCS /capabilities → runtimes (Ollama/llama-server/...) | Routing, model selection, offload |
|
||||
| **installed_artifacts** | NCS /capabilities/installed → disk scan | Інвентаризація, recommendations, cleanup |
|
||||
|
||||
Модель на диску — це **candidate**, не "доступна".
|
||||
|
||||
### 2.2 Заборона hardcode
|
||||
|
||||
- Заборонено комітити `models:` списки в swapper/router configs
|
||||
- Дозволено: policy-only `prefer` (тип/клас моделі), але не імена, крім критичних cloud SKU
|
||||
|
||||
## 3. Capability-first routing
|
||||
|
||||
### 3.1 Routing rules
|
||||
|
||||
Router обирає **ноду**, не "модель":
|
||||
|
||||
1. `find_nodes_with_capability(cap)` — тільки ноди де cap=true
|
||||
2. `require_fresh_caps(ttl=30)` — preflight guard
|
||||
3. Circuit breaker — виключити ноди з відкритим breaker
|
||||
4. Load scoring — `inflight * 10 + (100 if mem_pressure=high)`
|
||||
5. Offload через NATS `node.{id}.{cap}.request`
|
||||
|
||||
### 3.2 Fail fast
|
||||
|
||||
Якщо capability відсутня на всіх нодах — **fail fast** з явним повідомленням:
|
||||
|
||||
```json
|
||||
{"error": "No node with capability 'stt' available", "capabilities_by_node": {...}}
|
||||
```
|
||||
|
||||
Заборонено: тихий fallback на cloud без WARNING log.
|
||||
|
||||
### 3.3 Нодозалежність
|
||||
|
||||
STT/TTS/OCR/Image **можуть бути різними** на різних нодах:
|
||||
|
||||
- NODA2: `STT_PROVIDER=mlx_whisper`, `TTS_PROVIDER=mlx_kokoro`
|
||||
- NODA1: `STT_PROVIDER=none`, `OCR_PROVIDER=vision_prompted`
|
||||
|
||||
Вмикання capability = тільки через env flags в node-worker → `/caps`.
|
||||
|
||||
## 4. Безпечний контроль змін
|
||||
|
||||
### 4.1 План змін (обов'язково)
|
||||
|
||||
Перед зміною відповісти на:
|
||||
|
||||
1. **Що** міняємо
|
||||
2. **Чому**
|
||||
3. **Що може зламатися**
|
||||
4. **Як перевіряємо** (postflight)
|
||||
5. **Rollback** (точна команда)
|
||||
|
||||
### 4.2 Rollback
|
||||
|
||||
Кожна зміна має мати:
|
||||
|
||||
- git commit hash / tag
|
||||
- одну команду rollback (`docker compose up -d --force-recreate <service>`, `git checkout`)
|
||||
|
||||
## 5. Postflight
|
||||
|
||||
Після кожної зміни — повторний snapshot і порівняння:
|
||||
|
||||
- served_models count (не зменшився без причини)
|
||||
- capabilities map (нові = очікувані)
|
||||
- container count
|
||||
- error rate (prom_metrics)
|
||||
|
||||
## 6. Жодних прихованих fallback
|
||||
|
||||
- Невідомий профіль або відсутній API key → WARNING + deterministic fallback на `agent.fallback_llm`
|
||||
- Заборонено: "мовчки пішли в DeepSeek" без логу
|
||||
|
||||
## 7. Канонічні артефакти
|
||||
|
||||
| Артефакт | Призначення |
|
||||
|---|---|
|
||||
| `ops/fabric_snapshot.py` | Збір повного snapshot |
|
||||
| `ops/fabric_preflight.sh` | Quick check + snapshot save |
|
||||
| `ops/preflight_snapshots/` | Зберігання snapshots |
|
||||
| `docs/fabric_contract.md` | Цей контракт |
|
||||
|
||||
## Реальний стан (snapshot 2026-02-27)
|
||||
|
||||
### NODA1 (production)
|
||||
|
||||
- **49 контейнерів** (gateway, router, memory, qdrant, neo4j, redis, postgres, minio, rag, swapper, farmos, brand-*, oneok-*, plant-vision, crawl4ai, grafana, prometheus...)
|
||||
- **5 Ollama моделей**: qwen3-vl:8b (vision), qwen3:8b, qwen3.5:27b-q4_K_M, smollm2:135m, deepseek-v3.1:671b-cloud
|
||||
- **14 Telegram агентів** active
|
||||
- **NCS P3.5 не задеплоєний** — capabilities flags відсутні, installed_artifacts = 0
|
||||
- `swapper=disabled`, worker NATS connected
|
||||
|
||||
### NODA2 (development)
|
||||
|
||||
- **14 контейнерів** (router, node-worker, node-capabilities, nats, gateway, memory, qdrant, postgres, neo4j, redis, open-webui, sofiia-console, swapper)
|
||||
- **13 served моделей** (Ollama: 12 + llama_server: 1)
|
||||
- **29 installed artifacts** на диску (150.3GB LLM + 0.3GB TTS kokoro-v1_0)
|
||||
- **capabilities**: llm=Y, vision=Y, ocr=Y, stt=N, tts=N, image=N
|
||||
- `OCR_PROVIDER=vision_prompted`
|
||||
Reference in New Issue
Block a user