feat: implement swapper metrics and node cabinet ui

This commit is contained in:
Apple
2025-11-30 15:40:41 -08:00
parent cb9efaf656
commit 281c79f916
5 changed files with 223 additions and 100 deletions

View File

@@ -73,8 +73,7 @@
alter table node_cache
add column swapper_healthy boolean,
add column swapper_models_loaded integer,
add column swapper_models_total integer,
add column swapper_state jsonb; -- Для повного списку моделей
add column swapper_models_total integer;
```
Міграція: `migrations/039_node_cache_swapper_metrics.sql`.
@@ -91,9 +90,8 @@ import requests
def collect_swapper_metrics(swapper_base_url: str) -> dict:
result = {
"swapper_healthy": False,
"swapper_models_loaded": 0,
"swapper_models_total": 0,
"swapper_state": {}
"swapper_models_loaded": None,
"swapper_models_total": None,
}
try:
# healthz
@@ -111,8 +109,8 @@ def collect_swapper_metrics(swapper_base_url: str) -> dict:
loaded = sum(1 for m in models if m.get("loaded") is True)
result["swapper_models_total"] = total
result["swapper_models_loaded"] = loaded
result["swapper_state"] = data # Зберігаємо весь стан
except Exception:
# залишаємо None → UI покаже "невідомо"
pass
return result
@@ -133,7 +131,6 @@ payload = {
"swapper_healthy": swapper_metrics["swapper_healthy"],
"swapper_models_loaded": swapper_metrics["swapper_models_loaded"],
"swapper_models_total": swapper_metrics["swapper_models_total"],
"swapper_state": swapper_metrics["swapper_state"]
}
requests.post(f"{CITY_URL}/internal/node/{node_id}/metrics/update", json=payload, timeout=5)
```
@@ -145,6 +142,16 @@ requests.post(f"{CITY_URL}/internal/node/{node_id}/metrics/update", json=payload
* приймати нові поля;
* оновлювати відповідні колонки в `node_cache`.
Приклад (ескіз):
```python
swapper_healthy = body.get("swapper_healthy")
swapper_models_loaded = body.get("swapper_models_loaded")
swapper_models_total = body.get("swapper_models_total")
# update node_cache set ... where node_id = ...
```
---
## 4. Backend: Swapper detail endpoint
@@ -170,7 +177,11 @@ requests.post(f"{CITY_URL}/internal/node/{node_id}/metrics/update", json=payload
```
Джерело:
* `node_cache` (нові колонки + jsonb `swapper_state`).
* по можливості прямо через Swapper API;
* частину агрегованих значень (loaded/total) можна брати з `node_cache`.
Цей endpoint буде використовуватись Node Cabinet для розгорнутої таблиці моделей.
---
@@ -194,18 +205,42 @@ export function useNodeSwapper(nodeId: string) {
* заголовок: `Swapper Service`;
* статус:
* `🟢 Healthy` / `🟡 Degraded` / `🔴 Down` (на основі `healthy` + `models_loaded`);
* коротке резюме:
* `Моделі: 3/5 завантажено`;
* кнопка/кнопка-розкривалка `Переглянути моделі`:
* список моделей (name, type, loaded).
Макет (ескіз):
```txt
┌─────────────────────────────────────────────┐
│ 🧠 Swapper Service [↻] │
├─────────────────────────────────────────────┤
│ Статус: 🟢 Healthy │
│ Моделі: 3 / 5 завантажено │
├─────────────────────────────────────────────┤
│ d-model-3b | llm | 🟢 loaded │
│ d-code-7b | code | 🟢 loaded │
│ vision-8b | vlm | 🔴 not loaded│
└─────────────────────────────────────────────┘
```
### 5.3. Інтеграція в Node Cabinet
На сторінці `/nodes/[nodeId]`:
* додати `NodeSwapperCard` поруч із `NodeMetricsCard` / `DAGIRouterCard` у секцію Service Agents.
Наприклад:
* **Node Core**: Guardian/Steward
* **Service Agents**: DAGI Router, Swapper, Multimodal (Swapper Card тут)
* **Control & Intelligence**: Tools & Planner, Security, Archivist.
---
## 6. Tests
@@ -214,14 +249,17 @@ export function useNodeSwapper(nodeId: string) {
* Тест міграції `node_cache` (наявність нових колонок).
* Тест `POST /internal/node/{id}/metrics/update`:
* при передачі полів Swapper правильно оновлює `node_cache`.
* Тест `GET /internal/node/{id}/swapper`:
* при коректній відповіді Swapper API повертає очікувану структуру;
* якщо Swapper лежить — `healthy=false`, моделі порожні.
### 6.2. Frontend
* Snapshot-тест `NodeSwapperCard` для випадків:
* healthy + моделі є;
* unhealthy + немає моделей;
* loading/error state.
@@ -232,17 +270,21 @@ export function useNodeSwapper(nodeId: string) {
1. node-guardian-loop регулярно збирає Swapper-метрики та відправляє їх у city-service.
2. `node_cache` містить свіжі поля:
* `swapper_healthy`
* `swapper_models_loaded`
* `swapper_models_total`
* `swapper_state` (JSONB)
3. `GET /internal/node/{id}/metrics/current` відображає Swapper-метрики для обох нод.
4. `GET /internal/node/{id}/swapper` повертає детальну інформацію про Swapper (мінімум: healthy + models).
5. У Кабінеті Ноди (`/nodes/[nodeId]`) є блок Swapper Service:
* показує статус,
* показує кількість завантажених моделей,
* дозволяє переглянути список моделей.
6. `scripts/check-deploy-post.py` оновлено для перевірки Swapper.
6. `scripts/check-deploy-post.py` можна оновити, щоб:
* додати 12 перевірки Swapper-статусу (healthy/models_loaded),
* і вони проходили при нормальному стані.
---
@@ -251,11 +293,12 @@ export function useNodeSwapper(nodeId: string) {
* `migrations/039_node_cache_swapper_metrics.sql`
* Оновлений `node-guardian-loop.py` (з Swapper-метриками)
* Оновлені endpoints:
* `POST /internal/node/{id}/metrics/update`
* `GET /internal/node/{id}/swapper`
* Frontend:
* `useNodeSwapper(nodeId)`
* `NodeSwapperCard`
* Інтеграція в Node Cabinet
* Тести (backend + frontend)