feat: Add Chat API endpoints for Matrix integration

City Service:
- GET /api/v1/chat/rooms/{room_id}/messages - get message history
- POST /api/v1/chat/rooms/{room_id}/messages - send message
- Add get_room_by_id to repo_city

Synapse:
- Increase rate limits for room creation
- Add rc_joins, rc_invites configuration

All 27 rooms now have matrix_room_id
This commit is contained in:
Apple
2025-11-30 10:34:27 -08:00
parent aa3b9970fd
commit ec57f7a596
3 changed files with 452 additions and 0 deletions

View File

@@ -0,0 +1,338 @@
# TASK_PHASE_MATRIX_FINALIZE_v2 — Matrix / Chat / Presence
Version: 2.0
Status: Ready
Priority: Critical (Live Chat & Presence)
---
## 1. Поточний стан (вихідні дані)
Вже виконано у попередніх фазах:
- Matrix Gateway:
-`POST /internal/matrix/room/join`
-`POST /internal/matrix/message/send`
-`GET /internal/matrix/rooms/{id}/messages`
- City Service:
- ✅ Matrix-клієнт (join, send, get messages)
-`ensure_room_has_matrix()`
-`POST /city/rooms/sync/matrix` — bulk sync
- Auto-create Matrix Rooms:
- ✅ agent chat rooms (частково)
- ✅ node support rooms (NODE1, NODE2)
- ✅ microDAO lobby rooms (DAARION)
- Frontend:
- ✅ AgentChatWidget на `/agents/:agentId`, `/nodes/:nodeId`, `/microdao/:slug`
- ✅ Відображає "Увійти щоб почати спілкування" для неавторизованих
Обмеження зараз:
- частина кімнат має `matrix_room_id = NULL` через rate limiting Synapse
- не всі агенти додані в кімнати
- чат-виджет працює лише як оболонка (немає повного send/receive циклу)
- presence (online/offline) не реалізовано
---
## 2. Мета TASK_PHASE_MATRIX_FINALIZE_v2
Завершити інтеграцію Matrix так, щоб:
1. **Усі кімнати** (City, MicroDAO, Node, Agent) мали валідний `matrix_room_id`.
2. **Усі потрібні агенти** були учасниками своїх кімнат.
3. **Message flow** працював end-to-end:
- frontend → city-service → gateway → Matrix → gateway → city-service → frontend.
4. **Chat widget** показував реальні повідомлення (історія + нові).
5. **Presence (online/offline)** відображався для агентів.
6. **chat_available = true** там, де Matrix-кімната та gateway працюють.
---
## 3. Scope (модулі та компоненти)
Цей таск включає:
1. Synapse (Matrix Homeserver) — налаштування rate limits.
2. Matrix Gateway — доопрацювання endpoint'ів та presence.
3. City Service — покращення room sync + чат API.
4. Frontend (Next.js) — повна інтеграція чат-виджета з Matrix.
5. Smoke-тести та короткий звіт.
---
## 4. Модуль 1 — Synapse (Matrix HS) Rate Limits
### 4.1. Завдання
- Відкрити конфігурацію Synapse (homeserver.yaml або еквівалент).
- Підняти або вимкнути rate limiting для:
- room creation (createRoom)
- room join
- message send
- Увімкнути/перевірити:
- `/versions`
- `/presence` (якщо потрібно окремо)
### 4.2. Орієнтовні параметри
(Конкретні значення — на розсуд Cursor, але ціль — щоб sync 2030 кімнат проходив без помилок.)
```yaml
# homeserver.yaml
rc_message:
per_second: 100
burst_count: 500
rc_joins:
local:
per_second: 50
burst_count: 100
remote:
per_second: 10
burst_count: 20
rc_login:
address:
per_second: 10
burst_count: 50
account:
per_second: 10
burst_count: 50
rc_admin_redaction:
per_second: 100
burst_count: 500
```
### 4.3. Acceptance
- Повторний виклик `POST /city/rooms/sync/matrix` не падає по rate limit.
- Всі кімнати з `rooms` отримали `matrix_room_id`.
---
## 5. Модуль 2 — Повна синхронізація кімнат
### 5.1. City Rooms
- Для кожної city-кімнати (`rooms.scope = 'city'`):
- якщо `matrix_room_id IS NULL` → створити через gateway (`room/create` або існуючу логіку).
- оновити `rooms.matrix_room_id`.
- додати системних агентів:
- DARIO, DARIA, DAARWIZZ
- інші public-агенти за потреби.
### 5.2. MicroDAO Rooms
- Для кожного microDAO:
- lobby, governance, news, builders, help (згідно Rooms_Layer_Architecture_v1).
- створити Matrix-кімнати, якщо ще не створені.
- orchestrator-агент має бути учасником lobby + governance.
- core-team агенти — учасники builders/news (якщо такі задані).
### 5.3. Node Support Rooms
- Для кожної ноди:
- кімната `node-{slug}-support`.
- додати:
- Node Guardian
- Node Steward
- (опційно) технічні агенти ноди (Pulse, Atomis, GuardianOS тощо).
### 5.4. Agent Direct Rooms
- Для кожного агента:
- створити або пересвідчитись в існуванні агенто-специфічної кімнати.
- агент повинен бути учасником.
- ця кімната використовується для direct-chat з агентом.
### 5.5. Acceptance
- Усі записи `rooms` мають заповнений `matrix_room_id` для:
- scope in ('city', 'microdao', 'node', 'agent').
- Для кожного public-агента існує принаймні одна кімната, де він учасник.
---
## 6. Модуль 3 — Message Flow (send/receive)
### 6.1. Backend (city-service)
Переконатись/додати:
- API для надсилання повідомлень від імені користувача/агента:
- `POST /api/v1/chat/rooms/{room_id}/messages`
- вхід: `{ "body": "text" }`
- діє:
- перевіряє, які Matrix credentials використовувати (service user або impersonation)
- викликає `matrix-gateway /internal/matrix/message/send`.
- API для отримання історії:
- `GET /api/v1/chat/rooms/{room_id}/messages?limit=50`
- повертає список повідомлень (час, автор, текст).
За потреби використати існуючий `get_room_messages()` у city-service.
### 6.2. Gateway
- Гарантувати:
- правильне формування body для Matrix `send` (m.room.message, msgtype: m.text).
- розбір `GET /internal/matrix/rooms/{id}/messages` для history.
### 6.3. Acceptance
- Через API (curl/Postman) можливо:
- надіслати повідомлення у кімнату.
- прочитати історію.
- Це працює хоча б для:
- agent room (DAARWIZZ),
- node support room (NODE1),
- DAARION microDAO lobby.
---
## 7. Модуль 4 — Presence (online/offline)
### 7.1. Gateway
- Реалізувати простий Presence Poller:
- періодично опитувати Matrix API (presence endpoints або sync).
- зберігати стан `online/away/offline` для Matrix user IDs агентів.
- Надати internal endpoint:
- `GET /internal/matrix/presence/{matrix_user_id}``{ "state": "online" | "offline" | "unavailable" }`.
### 7.2. City-Service
- Додати endpoint:
- `GET /api/v1/agents/{agent_id}/presence`
- мапить agent_id → matrix_user_id → викликає gateway → повертає presence.
- (Опційно) кешувати presence у пам'яті на 1560 секунд.
### 7.3. Frontend
- На сторінках:
- `/agents`
- `/agents/:agentId`
- `/nodes/:nodeId` (для Guardian/Steward)
- `/microdao/:slug` (для orchestrator)
Показати:
- зелена точка — online
- жовта — away/degraded
- сіра — offline/unknown
### 7.4. Acceptance
- Presence-індикатори відображаються в UI.
- Відповідають реальному status (принаймні для test users/agents).
---
## 8. Модуль 5 — Chat Widget FULL Integration
### 8.1. AgentChatWidget (Next.js)
Розширити:
- Додавання станів:
- `loading`
- `no_room`
- `chat_available`
- `auth_required`
- Для `chat_available = true`:
- при відкритті:
- `GET /api/v1/chat/rooms/{room_id}/messages?limit=N` — завантажити історію.
- при відправці:
- `POST /api/v1/chat/rooms/{room_id}/messages` — надіслати.
- відображати список повідомлень (без fancy UI, MVP-стиль).
- Для неавторизованих:
- показати CTA "Увійти, щоб продовжити спілкування".
### 8.2. Інтеграція за контекстами
- Для `/agents/:agentId`:
- кімната агента (direct)
- Для `/nodes/:nodeId`:
- node support room
- Для `/microdao/:slug`:
- microDAO lobby room
### 8.3. Acceptance
- На кожній із трьох сторінок можливий діалог:
- відправити повідомлення,
- бачити історію,
- не отримувати помилки 4xx/5xx (за нормальних умов).
---
## 9. Smoke-тести (обов'язкові)
1. **DAARWIZZ Chat**
- Відкрити `/agents/daarwizz`.
- Відкрити чат.
- Написати повідомлення.
- Переконатися, що воно з'являється в історії.
2. **NODE1 Guardian Chat**
- Відкрити `/nodes/node-1-hetzner-gex44`.
- Відкрити чат.
- Написати повідомлення.
- Переконатися, що воно доходить до Matrix.
3. **DAARION MicroDAO Chat**
- Відкрити `/microdao/daarion`.
- Переконатися, що кімната існує, є історія, можна написати повідомлення.
4. **Presence**
- На `/agents` бачити індикатор присутності (хоча б у базовій формі).
- Змінити стан в Matrix (якщо можливо) та перевірити оновлення.
---
## 10. Вихідні артефакти
Після виконання таска Cursor має створити:
- `docs/debug/matrix_finalize_v2_report_<DATE>.md`
Зміст:
- перелік створених Matrix кімнат;
- список agent rooms, node rooms, microDAO rooms;
- приклади presence-станів;
- приклад message flow (request/response);
- скріншоти або опис перевірених UI-сторінок.
---
## 11. PROMPT ДЛЯ CURSOR
Рекомендований текст:
```
Виконай, будь ласка,
docs/tasks/TASK_PHASE_MATRIX_FINALIZE_v2.md
у повному обсязі.
Зосередься на:
Synapse rate limits,
повній синхронізації Matrix rooms,
message send/receive,
presence,
інтеграції AgentChatWidget з реальними повідомленнями.
Після завершення створи файл
docs/debug/matrix_finalize_v2_report_<DATE>.md
з результатами smoke-тестів та фактами реалізації.
```
---
**Target Date**: Immediate
**Priority**: Critical
**Dependencies**: Matrix Gateway running, Synapse accessible

View File

@@ -142,6 +142,24 @@ async def get_room_by_slug(slug: str) -> Optional[dict]:
return dict(row) if row else None
async def get_room_by_id(room_id: str) -> Optional[dict]:
"""Отримати кімнату по ID (UUID)"""
pool = await get_pool()
query = """
SELECT
cr.id, cr.slug, cr.name, cr.description, cr.is_default, cr.created_at, cr.created_by,
cr.matrix_room_id, cr.matrix_room_alias, cr.logo_url, cr.banner_url,
cr.microdao_id, m.name AS microdao_name, m.slug AS microdao_slug, m.logo_url AS microdao_logo_url
FROM city_rooms cr
LEFT JOIN microdaos m ON cr.microdao_id = m.id
WHERE cr.id = $1
"""
row = await pool.fetchrow(query, room_id)
return dict(row) if row else None
async def create_room(
slug: str,
name: str,

View File

@@ -1589,6 +1589,102 @@ async def get_microdao_chat_room(slug: str):
raise HTTPException(status_code=500, detail="Failed to get microdao chat room")
# =============================================================================
# Chat API (TASK_PHASE_MATRIX_FINALIZE_v2)
# =============================================================================
class SendMessageRequest(BaseModel):
body: str
sender: Optional[str] = None
class ChatMessage(BaseModel):
event_id: str
sender: str
body: str
timestamp: int
@api_router.get("/chat/rooms/{room_id}/messages")
async def get_chat_messages(room_id: str, limit: int = 50):
"""
Отримати історію повідомлень з Matrix кімнати.
room_id може бути slug або UUID кімнати.
"""
try:
# Get room from DB
room = await repo_city.get_room_by_id(room_id)
if not room:
# Try by slug
room = await repo_city.get_room_by_slug(room_id)
if not room:
raise HTTPException(status_code=404, detail=f"Room not found: {room_id}")
matrix_room_id = room.get("matrix_room_id")
if not matrix_room_id:
raise HTTPException(status_code=400, detail="Room has no Matrix integration")
# Get messages from Matrix via gateway
messages = await get_room_messages(matrix_room_id, limit=limit)
return {
"room_id": room.get("id"),
"room_slug": room.get("slug"),
"matrix_room_id": matrix_room_id,
"messages": messages,
"count": len(messages)
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to get chat messages for {room_id}: {e}")
raise HTTPException(status_code=500, detail="Failed to get chat messages")
@api_router.post("/chat/rooms/{room_id}/messages")
async def send_chat_message(room_id: str, payload: SendMessageRequest):
"""
Надіслати повідомлення в Matrix кімнату.
room_id може бути slug або UUID кімнати.
"""
try:
# Get room from DB
room = await repo_city.get_room_by_id(room_id)
if not room:
# Try by slug
room = await repo_city.get_room_by_slug(room_id)
if not room:
raise HTTPException(status_code=404, detail=f"Room not found: {room_id}")
matrix_room_id = room.get("matrix_room_id")
if not matrix_room_id:
raise HTTPException(status_code=400, detail="Room has no Matrix integration")
# Send message via gateway
event_id = await send_message_to_room(
room_id=matrix_room_id,
body=payload.body,
sender=payload.sender
)
if not event_id:
raise HTTPException(status_code=500, detail="Failed to send message to Matrix")
return {
"ok": True,
"event_id": event_id,
"room_id": room.get("id"),
"matrix_room_id": matrix_room_id
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to send chat message to {room_id}: {e}")
raise HTTPException(status_code=500, detail="Failed to send chat message")
# =============================================================================
# Presence API (TASK_PHASE_AGENT_PRESENCE_INDICATORS_MVP)
# =============================================================================