diff --git a/docs/api-contract.md b/docs/api-contract.md index a578f08..7e7f962 100644 --- a/docs/api-contract.md +++ b/docs/api-contract.md @@ -1,11 +1,18 @@ # API Contract — Lambda Platform -> **Статус:** ЧЕРНОВИК — проектируем сами, не ждём SDK -> **Автор:** @architect -> **Последнее обновление:** заполнить дату +> **Статус:** ЧЕРНОВИК — проектируем сами, уточняем с Азаматом когда SDK будет готов +> **Последнее обновление:** 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 ### GET /users/{external_id}?platform={platform} + Получает или создаёт пользователя. **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 -### POST /sessions/{session_id}/messages -Отправляет сообщение и получает ответ агента. +Бот не управляет сессиями явно. Отправка сообщения — единственная операция. +Master решает: нужен ли новый контейнер, или разбудить существующий. + +### POST /users/{user_id}/chats/{chat_id}/messages + +Отправляет сообщение пользователя агенту. Master поднимает/размораживает контейнер, +монтирует нужный чат (`C1/`, `C2/`...), запускает агента. **Request:** ```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:** ```json -[ - { - "message_id": "msg_qwe012", - "user_text": "Привет", - "response": "Привет!", - "tokens_used": 42, - "created_at": "2025-01-15T10:31:00Z" - } -] +{ + "skills": {"web-search": true, "browser": false}, + "connectors": {"gmail": {"connected": true, "email": "user@gmail.com"}}, + "soul": {"name": "Лямбда", "style": "friendly"}, + "safety": {"email-send": true, "file-delete": true}, + "plan": {"name": "Beta", "tokens_used": 800, "tokens_limit": 1000} +} +``` + +### POST /users/{user_id}/settings + +Применяет действие над настройками. + +**Request:** +```json +{ + "action": "toggle_skill", + "payload": {"skill": "browser", "enabled": true} +} +``` + +**Response 200:** +```json +{"ok": true} ``` --- ## Error format -Все ошибки возвращаются в едином формате: - ```json { "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)? -- [ ] Как обрабатываются вложения (изображения, файлы)? \ No newline at end of file +- [ ] Точный формат эндпоинта отправки сообщения — URL, поля +- [ ] Как передавать вложения (файлы, изображения)? Через S3 pre-signed URL или напрямую? +- [ ] Стриминговый ответ (SSE / WebSocket) или только sync? +- [ ] Как обрабатывается `chat_id` на стороне платформы — это имя директории (C1/C2) или наш произвольный идентификатор? +- [ ] Есть ли endpoint для получения истории чата или она хранится только в `history.db` внутри контейнера? +- [ ] Формат `SettingsAction` — совпадает с нашим или другой? diff --git a/docs/superpowers/specs/2026-03-28-core-design.md b/docs/superpowers/specs/2026-03-28-core-design.md index e279489..bdeb7f1 100644 --- a/docs/superpowers/specs/2026-03-28-core-design.md +++ b/docs/superpowers/specs/2026-03-28-core-design.md @@ -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 handler.py — EventDispatcher (только маршрутизация, без бизнес-логики) store.py — StateStore Protocol + InMemoryStore + SQLiteStore - session.py — SessionManager + chat.py — ChatManager (бывший SessionManager — управляет чатами, не контейнерами) auth.py — AuthManager settings.py — SettingsManager handlers/ @@ -155,20 +167,20 @@ class User(BaseModel): created_at: datetime is_new: bool = False -class Session(BaseModel): - session_id: str - agent_id: str - created_at: datetime - expires_at: datetime - class PlatformClient(Protocol): 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: ... - async def close_session(self, session_id: str) -> bool: ... - async def get_message_history(self, session_id: str, limit: int, offset: int) -> list[dict]: ... + + # Master сам решает: поднять контейнер, разбудить, смонтировать нужный чат. + # Бот передаёт только user_id + chat_id — платформа делает остальное. + 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 @@ -186,16 +198,22 @@ class StateStore(Protocol): Реализации: `InMemoryStore` (тесты), `SQLiteStore` (прод). -### SessionManager (`core/session.py`) +### ChatManager (`core/chat.py`) + +Управляет метаданными чатов (C1/C2/C3). НЕ управляет lifecycle контейнера — это дело Master'а. ```python -class SessionManager: +class ChatManager: def __init__(self, platform: PlatformClient, store: StateStore): ... - async def get_or_create(self, user_id: str, chat_id: str) -> ChatContext: ... - async def close(self, chat_id: str) -> None: ... + async def get_or_create(self, user_id: str, chat_id: str, name: str | None) -> ChatContext: ... + 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]: ... ``` +Метаданные (display_name, platform, surface_ref, is_archived) хранятся в `StateStore`. +Файлы чата и history.db живут в workspace контейнера на стороне платформы — бот их не хранит. + ### AuthManager (`core/auth.py`) ```python