docs: update core design and api-contract with platform container architecture
This commit is contained in:
parent
8e955045b2
commit
53685747f6
2 changed files with 92 additions and 79 deletions
|
|
@ -1,11 +1,18 @@
|
||||||
# API Contract — Lambda Platform
|
# API Contract — Lambda Platform
|
||||||
|
|
||||||
> **Статус:** ЧЕРНОВИК — проектируем сами, не ждём SDK
|
> **Статус:** ЧЕРНОВИК — проектируем сами, уточняем с Азаматом когда SDK будет готов
|
||||||
> **Автор:** @architect
|
> **Последнее обновление:** 2026-03-29
|
||||||
> **Последнее обновление:** заполнить дату
|
|
||||||
|
|
||||||
Это описание того, что нам нужно от платформы.
|
---
|
||||||
`MockPlatformClient` реализует этот контракт. При подключении реального SDK — только он меняется.
|
|
||||||
|
## Архитектурный контекст
|
||||||
|
|
||||||
|
Каждому пользователю выделяется **один LXC-контейнер** с workspace 10 ГБ.
|
||||||
|
Workspace содержит директории чатов: `C1/`, `C2/`, `C3/` — файлы + `history.db` в каждом.
|
||||||
|
|
||||||
|
**Master** управляет lifecycle контейнера (запуск, заморозка, пробуждение).
|
||||||
|
Бот **не управляет lifecycle** — он передаёт `user_id` + `chat_id` + сообщение.
|
||||||
|
Master сам решает: нужно ли поднять контейнер, смонтировать нужный чат, запустить агента.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -28,6 +35,7 @@ Authorization: Bearer {SERVICE_TOKEN}
|
||||||
## Users
|
## Users
|
||||||
|
|
||||||
### GET /users/{external_id}?platform={platform}
|
### GET /users/{external_id}?platform={platform}
|
||||||
|
|
||||||
Получает или создаёт пользователя.
|
Получает или создаёт пользователя.
|
||||||
|
|
||||||
**Query params:**
|
**Query params:**
|
||||||
|
|
@ -47,50 +55,15 @@ Authorization: Bearer {SERVICE_TOKEN}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Sessions
|
|
||||||
|
|
||||||
### POST /sessions
|
|
||||||
Создаёт новую сессию с AI-агентом.
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"user_id": "usr_abc123",
|
|
||||||
"platform": "telegram",
|
|
||||||
"context": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response 201:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"session_id": "ses_xyz789",
|
|
||||||
"agent_id": "agt_def456",
|
|
||||||
"created_at": "2025-01-15T10:30:00Z",
|
|
||||||
"expires_at": "2025-01-16T10:30:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### GET /sessions/{session_id}
|
|
||||||
Получает информацию о сессии.
|
|
||||||
|
|
||||||
**Response 200:** — см. структуру выше + поле `status: "active" | "closed"`
|
|
||||||
**Response 404:** `{"error": "SESSION_NOT_FOUND", "message": "..."}`
|
|
||||||
|
|
||||||
### DELETE /sessions/{session_id}
|
|
||||||
Завершает сессию.
|
|
||||||
|
|
||||||
**Response 200:**
|
|
||||||
```json
|
|
||||||
{"closed": true}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Messages
|
## Messages
|
||||||
|
|
||||||
### POST /sessions/{session_id}/messages
|
Бот не управляет сессиями явно. Отправка сообщения — единственная операция.
|
||||||
Отправляет сообщение и получает ответ агента.
|
Master решает: нужен ли новый контейнер, или разбудить существующий.
|
||||||
|
|
||||||
|
### POST /users/{user_id}/chats/{chat_id}/messages
|
||||||
|
|
||||||
|
Отправляет сообщение пользователя агенту. Master поднимает/размораживает контейнер,
|
||||||
|
монтирует нужный чат (`C1/`, `C2/`...), запускает агента.
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
```json
|
```json
|
||||||
|
|
@ -110,28 +83,46 @@ Authorization: Bearer {SERVICE_TOKEN}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /sessions/{session_id}/messages?limit=20&offset=0
|
---
|
||||||
История сообщений сессии.
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
### GET /users/{user_id}/settings
|
||||||
|
|
||||||
|
Настройки пользователя: скиллы, коннекторы, SOUL, безопасность, план.
|
||||||
|
|
||||||
**Response 200:**
|
**Response 200:**
|
||||||
```json
|
```json
|
||||||
[
|
{
|
||||||
{
|
"skills": {"web-search": true, "browser": false},
|
||||||
"message_id": "msg_qwe012",
|
"connectors": {"gmail": {"connected": true, "email": "user@gmail.com"}},
|
||||||
"user_text": "Привет",
|
"soul": {"name": "Лямбда", "style": "friendly"},
|
||||||
"response": "Привет!",
|
"safety": {"email-send": true, "file-delete": true},
|
||||||
"tokens_used": 42,
|
"plan": {"name": "Beta", "tokens_used": 800, "tokens_limit": 1000}
|
||||||
"created_at": "2025-01-15T10:31:00Z"
|
}
|
||||||
}
|
```
|
||||||
]
|
|
||||||
|
### POST /users/{user_id}/settings
|
||||||
|
|
||||||
|
Применяет действие над настройками.
|
||||||
|
|
||||||
|
**Request:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "toggle_skill",
|
||||||
|
"payload": {"skill": "browser", "enabled": true}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response 200:**
|
||||||
|
```json
|
||||||
|
{"ok": true}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Error format
|
## Error format
|
||||||
|
|
||||||
Все ошибки возвращаются в едином формате:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"error": "ERROR_CODE",
|
"error": "ERROR_CODE",
|
||||||
|
|
@ -140,11 +131,15 @@ Authorization: Bearer {SERVICE_TOKEN}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Коды ошибок: `SESSION_NOT_FOUND`, `USER_NOT_FOUND`, `RATE_LIMITED`, `PLATFORM_ERROR`
|
Коды ошибок: `USER_NOT_FOUND`, `RATE_LIMITED`, `PLATFORM_ERROR`, `CONTAINER_UNAVAILABLE`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## TODO (открытые вопросы к команде платформы)
|
## Открытые вопросы к Азамату (SDK)
|
||||||
|
|
||||||
- [ ] Нужна ли стриминговая передача ответа (SSE / WebSocket)?
|
- [ ] Точный формат эндпоинта отправки сообщения — URL, поля
|
||||||
- [ ] Как обрабатываются вложения (изображения, файлы)?
|
- [ ] Как передавать вложения (файлы, изображения)? Через S3 pre-signed URL или напрямую?
|
||||||
|
- [ ] Стриминговый ответ (SSE / WebSocket) или только sync?
|
||||||
|
- [ ] Как обрабатывается `chat_id` на стороне платформы — это имя директории (C1/C2) или наш произвольный идентификатор?
|
||||||
|
- [ ] Есть ли endpoint для получения истории чата или она хранится только в `history.db` внутри контейнера?
|
||||||
|
- [ ] Формат `SettingsAction` — совпадает с нашим или другой?
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,18 @@
|
||||||
Цель — написать логику один раз, легко добавлять новые поверхности и новый функционал
|
Цель — написать логику один раз, легко добавлять новые поверхности и новый функционал
|
||||||
без правки центральных файлов.
|
без правки центральных файлов.
|
||||||
|
|
||||||
|
### Архитектура платформы (важно для дизайна)
|
||||||
|
|
||||||
|
Lambda Lab 3.0 выделяет каждому пользователю **один LXC-контейнер** с workspace 10 ГБ.
|
||||||
|
Workspace содержит директории чатов: `C1/`, `C2/`, `C3/` — каждый чат хранит свои файлы и `history.db`.
|
||||||
|
|
||||||
|
**Master** — управляющий процесс платформы — сам решает когда поднять, заморозить или разбудить контейнер.
|
||||||
|
Бот не управляет lifecycle контейнера — он только передаёт сообщение (`user_id`, `chat_id`, `text`).
|
||||||
|
|
||||||
|
Следствие: **"сессия" и "чат" — разные понятия.**
|
||||||
|
- Чат (C1/C2/C3) — наша забота, храним метаданные в `StateStore`
|
||||||
|
- Контейнер (сессия платформы) — забота Master'а, бот об этом не знает
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Структура файлов
|
## Структура файлов
|
||||||
|
|
@ -25,7 +37,7 @@ core/
|
||||||
SettingsAction, PaymentRequired
|
SettingsAction, PaymentRequired
|
||||||
handler.py — EventDispatcher (только маршрутизация, без бизнес-логики)
|
handler.py — EventDispatcher (только маршрутизация, без бизнес-логики)
|
||||||
store.py — StateStore Protocol + InMemoryStore + SQLiteStore
|
store.py — StateStore Protocol + InMemoryStore + SQLiteStore
|
||||||
session.py — SessionManager
|
chat.py — ChatManager (бывший SessionManager — управляет чатами, не контейнерами)
|
||||||
auth.py — AuthManager
|
auth.py — AuthManager
|
||||||
settings.py — SettingsManager
|
settings.py — SettingsManager
|
||||||
handlers/
|
handlers/
|
||||||
|
|
@ -155,20 +167,20 @@ class User(BaseModel):
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
is_new: bool = False
|
is_new: bool = False
|
||||||
|
|
||||||
class Session(BaseModel):
|
|
||||||
session_id: str
|
|
||||||
agent_id: str
|
|
||||||
created_at: datetime
|
|
||||||
expires_at: datetime
|
|
||||||
|
|
||||||
class PlatformClient(Protocol):
|
class PlatformClient(Protocol):
|
||||||
async def get_or_create_user(self, external_id: str, platform: str, display_name: str | None) -> User: ...
|
async def get_or_create_user(self, external_id: str, platform: str, display_name: str | None) -> User: ...
|
||||||
async def create_session(self, user_id: str, platform: str, context: dict | None) -> Session: ...
|
|
||||||
async def send_message(self, session_id: str, text: str, attachments: list) -> MessageResponse: ...
|
# Master сам решает: поднять контейнер, разбудить, смонтировать нужный чат.
|
||||||
async def close_session(self, session_id: str) -> bool: ...
|
# Бот передаёт только user_id + chat_id — платформа делает остальное.
|
||||||
async def get_message_history(self, session_id: str, limit: int, offset: int) -> list[dict]: ...
|
async def send_message(self, user_id: str, chat_id: str, text: str, attachments: list) -> MessageResponse: ...
|
||||||
|
|
||||||
|
async def get_settings(self, user_id: str) -> UserSettings: ...
|
||||||
|
async def update_settings(self, user_id: str, action: SettingsAction) -> None: ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Нет явных create/close session** — lifecycle контейнера управляется Master'ом, не ботом.
|
||||||
|
Когда будет готов реальный SDK Азамата — контракт уточняется, меняется только `platform/mock.py`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Managers
|
## Managers
|
||||||
|
|
@ -186,16 +198,22 @@ class StateStore(Protocol):
|
||||||
|
|
||||||
Реализации: `InMemoryStore` (тесты), `SQLiteStore` (прод).
|
Реализации: `InMemoryStore` (тесты), `SQLiteStore` (прод).
|
||||||
|
|
||||||
### SessionManager (`core/session.py`)
|
### ChatManager (`core/chat.py`)
|
||||||
|
|
||||||
|
Управляет метаданными чатов (C1/C2/C3). НЕ управляет lifecycle контейнера — это дело Master'а.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class SessionManager:
|
class ChatManager:
|
||||||
def __init__(self, platform: PlatformClient, store: StateStore): ...
|
def __init__(self, platform: PlatformClient, store: StateStore): ...
|
||||||
async def get_or_create(self, user_id: str, chat_id: str) -> ChatContext: ...
|
async def get_or_create(self, user_id: str, chat_id: str, name: str | None) -> ChatContext: ...
|
||||||
async def close(self, chat_id: str) -> None: ...
|
async def rename(self, chat_id: str, name: str) -> ChatContext: ...
|
||||||
|
async def archive(self, chat_id: str) -> None: ...
|
||||||
async def list_active(self, user_id: str) -> list[ChatContext]: ...
|
async def list_active(self, user_id: str) -> list[ChatContext]: ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Метаданные (display_name, platform, surface_ref, is_archived) хранятся в `StateStore`.
|
||||||
|
Файлы чата и history.db живут в workspace контейнера на стороне платформы — бот их не хранит.
|
||||||
|
|
||||||
### AuthManager (`core/auth.py`)
|
### AuthManager (`core/auth.py`)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue