- 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
12 KiB
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-routerllm-proxy,memory-orchestrator,toolcoreauth-service,pdp-service,usage-engineagents-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
- Створити сервіс
living-map-service(FastAPI), порт7017. - Зібрати стан мережі з існуючих сервісів в один snapshot.
- Підписатися на ключові NATS-сабджекти й зберігати історію в
living_map_history. - Віддавати:
snapshot(HTTP)history(HTTP)real-time stream(WS).
- Додати мінімальну фронтенд-обгортку
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.cityscene.layers.spacescene.layers.nodesscene.layers.agentsscene.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.
{
"status": "ok",
"service": "living-map-service",
"version": "1.0.0",
"time": "2025-11-24T12:34:56Z"
}
4.2. GET /living-map/snapshot
Агрегований стан усієї мережі.
Response (спрощений приклад):
{
"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|channellimit(optional, default 100)
Response:
{
"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 (узагальнений):
{
"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:
{
"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— одинична подія
-
Формат:
{
"kind": "event",
"event_type": "agent.event.status",
"timestamp": "2025-11-24T12:34:56Z",
"payload": { ... }
}
5. Database Schema
Створити міграцію, наприклад: migrations/010_create_living_map_tables.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) -
Повертає:
snapshoteventsisLoadingerrorconnectionStatus
7. Security / Auth
-
Всі HTTP-ендпоінти:
- Перевірка JWT/Session через
auth-service(ActorContext).
- Перевірка JWT/Session через
-
Опціонально,
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
-
docker-compose -f docker-compose.phase9.yml up -dпіднімаєliving-map-serviceбез помилок. -
GET /living-map/healthповертаєstatus=ok. -
GET /living-map/snapshotповертає валідний JSON зlayers.city,layers.space,layers.nodes,layers.agents. -
GET /living-map/historyповертає список подій, які приходять з NATS. -
WS /living-map/streamнадсилає:- при підключенні:
kind="snapshot" - далі:
kind="event"при нових подіях.
- при підключенні:
-
useLivingMapFullуспішно підключається до API+WS і оновлює локальний state без TypeScript помилок. -
Уся нова логіка проходить лінтер та тести.
END OF TASK