Files
microdao-daarion/docs/tasks/TASK_PHASE9_LIVING_MAP_FULL.md
Apple 3de3c8cb36 feat: Add presence heartbeat for Matrix online status
- matrix-gateway: POST /internal/matrix/presence/online endpoint
- usePresenceHeartbeat hook with activity tracking
- Auto away after 5 min inactivity
- Offline on page close/visibility change
- Integrated in MatrixChatRoom component
2025-11-27 00:19:40 -08:00

449 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TASK PHASE 9 — LIVING MAP (FULL STACK SERVICE)
Version: 1.0
Status: READY FOR IMPLEMENTATION
Scope: Backend + WebSocket + NATS + Minimal Frontend Hook
## 1. Context
DAARION уже має:
- core сервіси:
- `messaging-service` (Matrix-aware Messenger)
- `agent-runtime`, `agent-filter`, `dagi-router`
- `llm-proxy`, `memory-orchestrator`, `toolcore`
- `auth-service`, `pdp-service`, `usage-engine`
- `agents-service` (Agent Hub + lifecycle)
- `microdao-service` (microDAO Console)
- `dao-service` (DAO Dashboard)
- `city-service`, `space-service`
- інфраструктуру:
- PostgreSQL
- NATS JetStream
- docker-compose для різних фаз
- WebSocket-потоки для окремих модулів
Потрібен **єдиний "Living Map" шар**, який агрегує стан всієї мережі:
- City (microDAO, метрики)
- Space (DAO-планети, ноди)
- Nodes (ресурси, алерти)
- Agents (статус, використання)
- DAO (голосування, proposals)
- Messaging (активність каналів)
і видає це:
- через `GET /living-map/snapshot` (full-state)
- через `GET /living-map/history` (event log)
- через `GET /living-map/entities` (каталог сутностей)
- через `WS /living-map/stream` (живий потік подій).
Цей таск — **backend + API + NATS + WS + базовий frontend hook**.
---
## 2. Goals
1. Створити сервіс `living-map-service` (FastAPI), порт `7017`.
2. Зібрати стан мережі з існуючих сервісів **в один snapshot**.
3. Підписатися на ключові NATS-сабджекти й зберігати історію в `living_map_history`.
4. Віддавати:
- `snapshot` (HTTP)
- `history` (HTTP)
- `real-time stream` (WS).
5. Додати мінімальну фронтенд-обгортку `useLivingMapFull` (React hook) для інтеграції з 2D/3D UI.
---
## 3. Architecture Overview
### 3.1. New service: `living-map-service`
- Stack: Python 3 + FastAPI + uvicorn
- Port: `7017`
- Responsibilities:
- Агрегувати дані з:
- `city-service` (city snapshot)
- `space-service` (planets/nodes/events)
- `agents-service` (agents, metrics, events)
- `microdao-service` (microDAOs)
- `dao-service` (dao, proposals, votes)
- `usage-engine` (LLM/tool usage summary)
- Нормалізувати в **єдину структуру сцени**:
- `scene.layers.city`
- `scene.layers.space`
- `scene.layers.nodes`
- `scene.layers.agents`
- `scene.meta` (timestamps, version)
- Підписуватись на NATS-subject'и й створювати event log.
- Видавати snapshot/history + WebSocket stream.
### 3.2. Data sources (HTTP)
Очікувані існуючі/доступні ендпоінти (можна створити прості адаптери, якщо їх ще нема):
- `city-service`:
- `GET http://city-service:7001/api/city/snapshot`
- `space-service`:
- `GET http://space-service:7002/api/space/scene` або окремо planets/nodes/events
- `agents-service`:
- `GET http://agents-service:7014/agents` (list)
- `GET http://agents-service:7014/agents/metrics` (summary)
- `microdao-service`:
- `GET http://microdao-service:7015/microdaos` (list)
- `dao-service`:
- `GET http://dao-service:7016/dao` (list)
- `GET http://dao-service:7016/dao/proposals/summary`
- `usage-engine`:
- `GET http://usage-engine:7013/internal/usage/summary?period_hours=24`
Якщо чогось немає — зробити простий adapter/placeholder з mock даними всередині `living-map-service`.
### 3.3. NATS Subjects
Потрібно підписатися на:
- `city.event.*`
- `dao.event.*`
- `microdao.event.*`
- `node.metrics.*`
- `agent.event.*`
- `usage.llm.*`
- `usage.agent.*`
- `messaging.message.created`
(Якщо частина сабджектів ще не існує — підготувати consumer з graceful handling та вимкненими/placeholder subscriptions.)
---
## 4. API Specification
### 4.1. `GET /living-map/health`
Простий health-check.
```json
{
"status": "ok",
"service": "living-map-service",
"version": "1.0.0",
"time": "2025-11-24T12:34:56Z"
}
```
### 4.2. `GET /living-map/snapshot`
Агрегований стан усієї мережі.
Response (спрощений приклад):
```json
{
"generated_at": "2025-11-24T12:34:56Z",
"layers": {
"city": {
"microdaos_total": 12,
"active_users": 57,
"active_agents": 34,
"health": "green",
"items": [
{
"id": "microdao:7",
"slug": "daarion-city",
"name": "DAARION City",
"status": "active",
"agents": 9,
"nodes": 3
}
]
},
"space": {
"planets": [
{
"id": "dao:daarion-core",
"name": "DAARION CORE",
"type": "dao",
"orbits": ["node:gpu-1", "node:gpu-2"],
"status": "active"
}
],
"nodes": [
{
"id": "node:gpu-1",
"name": "NODE1",
"cpu": 0.42,
"gpu": 0.77,
"memory": 0.63,
"alerts": []
}
]
},
"nodes": {
"items": [
{
"id": "node:gpu-1",
"microdao_id": "microdao:7",
"status": "online",
"metrics": {
"cpu": 0.42,
"gpu": 0.77,
"ram": 0.63,
"net_in": 12345,
"net_out": 9876
}
}
]
},
"agents": {
"items": [
{
"id": "agent:sofia",
"name": "Sofia",
"kind": "system",
"microdao_id": "microdao:7",
"status": "online",
"usage": {
"llm_calls_24h": 123,
"tokens_24h": 45678
}
}
]
}
},
"meta": {
"source_services": [
"city-service",
"space-service",
"agents-service",
"microdao-service",
"dao-service",
"usage-engine"
]
}
}
```
### 4.3. `GET /living-map/entities`
Плоский список сутностей з мінімальними даними для побудови legend/списків.
Query params:
* `type` (optional): `city|space|node|agent|dao|microdao|channel`
* `limit` (optional, default 100)
Response:
```json
{
"items": [
{
"id": "microdao:7",
"type": "microdao",
"label": "DAARION City",
"status": "active",
"layer": "city"
},
{
"id": "dao:daarion-core",
"type": "dao",
"label": "DAARION CORE DAO",
"status": "active",
"layer": "space"
}
]
}
```
### 4.4. `GET /living-map/entities/{id}`
Детальний опис сутності (проксі до відповідного сервісу з нормалізацією).
Response (узагальнений):
```json
{
"id": "agent:sofia",
"type": "agent",
"layer": "agents",
"data": {
"...": "raw or normalized fields"
}
}
```
### 4.5. `GET /living-map/history`
Історія подій Living Map.
Query params:
* `since` (optional, ISO datetime)
* `limit` (optional, default 200)
Response:
```json
{
"items": [
{
"id": "evt-uuid",
"timestamp": "2025-11-24T12:30:00Z",
"event_type": "node.metrics.update",
"payload": {
"node_id": "node:gpu-1",
"cpu": 0.8,
"gpu": 0.95
}
}
]
}
```
### 4.6. `WS /living-map/stream`
WebSocket-потік:
* Типи повідомлень:
* `snapshot` — повний стан (на підключення та за потреби)
* `event` — одинична подія
* Формат:
```json
{
"kind": "event",
"event_type": "agent.event.status",
"timestamp": "2025-11-24T12:34:56Z",
"payload": { ... }
}
```
---
## 5. Database Schema
Створити міграцію, наприклад: `migrations/010_create_living_map_tables.sql`.
```sql
CREATE TABLE IF NOT EXISTS living_map_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
timestamp timestamptz NOT NULL DEFAULT now(),
event_type TEXT NOT NULL,
payload JSONB NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_living_map_history_timestamp
ON living_map_history (timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_living_map_history_event_type
ON living_map_history (event_type);
```
---
## 6. Implementation Plan
### 6.1. Files to create
* `services/living-map-service/main.py`
* `services/living-map-service/models.py`
* `services/living-map-service/repository_history.py`
* `services/living-map-service/adapters/city_client.py`
* `services/living-map-service/adapters/space_client.py`
* `services/living-map-service/adapters/agents_client.py`
* `services/living-map-service/adapters/microdao_client.py`
* `services/living-map-service/adapters/dao_client.py`
* `services/living-map-service/adapters/usage_client.py`
* `services/living-map-service/nats_subscriber.py`
* `services/living-map-service/ws_stream.py`
* `services/living-map-service/Dockerfile`
* `services/living-map-service/requirements.txt`
* `docker-compose.phase9.yml` (або оновити загальний)
* `scripts/start-phase9.sh`
* `scripts/stop-phase9.sh`
* `migrations/010_create_living_map_tables.sql`
* Frontend:
* `src/features/livingMap/hooks/useLivingMapFull.ts`
### 6.2. Minimal frontend hook
`useLivingMapFull.ts`:
* `GET /living-map/snapshot` → зберігає state
* Підключає WS `/living-map/stream`
* При `kind=event` оновлює локальний state (immutable update)
* Повертає:
* `snapshot`
* `events`
* `isLoading`
* `error`
* `connectionStatus`
---
## 7. Security / Auth
* Всі HTTP-ендпоінти:
* Перевірка JWT/Session через `auth-service` (ActorContext).
* Опціонально, `GET /living-map/snapshot` може мати:
* `public_mode` (спрощений, анонімний)
* або вимагати auth (рекомендовано).
* WS:
* `Authorization: Bearer <token>` у заголовках.
* NATS:
* лише internal subjects, використовувати існуючий NATS connection з параметрами як в інших сервісах.
---
## 8. TODO Checklist
Backend:
* [ ] Створити міграцію `010_create_living_map_tables.sql`
* [ ] Створити `living-map-service` структуру
* [ ] Реалізувати адаптери до інших сервісів (з timeout/retry)
* [ ] Реалізувати `GET /living-map/health`
* [ ] Реалізувати `GET /living-map/snapshot`
* [ ] Реалізувати `GET /living-map/entities`
* [ ] Реалізувати `GET /living-map/entities/{id}`
* [ ] Реалізувати `GET /living-map/history`
* [ ] Реалізувати `WS /living-map/stream`
* [ ] Реалізувати `nats_subscriber.py` для key subjects
* [ ] Інтегрувати Auth/PDP (як у інших сервісах)
* [ ] Додати Dockerfile + requirements.txt
* [ ] Оновити docker-compose/script'и
* [ ] Додати базові unit tests (snapshot builder)
Frontend:
* [ ] Створити `useLivingMapFull.ts`
* [ ] Протестувати запит snapshot + WS stream (можна через тимчасовий debug-компонент)
---
## 9. Acceptance Criteria
1. `docker-compose -f docker-compose.phase9.yml up -d` піднімає `living-map-service` без помилок.
2. `GET /living-map/health` повертає `status=ok`.
3. `GET /living-map/snapshot` повертає валідний JSON з `layers.city`, `layers.space`, `layers.nodes`, `layers.agents`.
4. `GET /living-map/history` повертає список подій, які приходять з NATS.
5. `WS /living-map/stream` надсилає:
* при підключенні: `kind="snapshot"`
* далі: `kind="event"` при нових подіях.
6. `useLivingMapFull` успішно підключається до API+WS і оновлює локальний state без TypeScript помилок.
7. Уся нова логіка проходить лінтер та тести.
---
END OF TASK