# platform/mock.py from __future__ import annotations import asyncio import random import uuid from datetime import UTC, datetime from typing import Any import structlog from platform.interface import MessageResponse, User, UserSettings logger = structlog.get_logger(__name__) class MockPlatformClient: """ Заглушка SDK платформы Lambda. Реализует PlatformClient Protocol. При подключении реального SDK заменяется только этот файл — core/ и адаптеры не трогаются. Ключевое отличие от реальной платформы: не управляет lifecycle контейнера. Master делает это сам при получении send_message. """ def __init__(self) -> None: self._users: dict[str, dict] = {} self._messages: dict[str, list] = {} # "{user_id}:{chat_id}" → messages self._settings: dict[str, dict] = {} logger.info("MockPlatformClient initialized") async def get_or_create_user( self, external_id: str, platform: str, display_name: str | None = None, ) -> User: await self._latency() key = f"{platform}:{external_id}" is_new = key not in self._users if is_new: self._users[key] = { "user_id": f"usr-{platform}-{external_id}", "external_id": external_id, "platform": platform, "display_name": display_name, "created_at": "2025-01-01T00:00:00Z", "is_new": True, } data = {**self._users[key], "is_new": is_new} return User(**data) async def send_message( self, user_id: str, chat_id: str, text: str, attachments: list | None = None, ) -> MessageResponse: await self._latency(200, 600) key = f"{user_id}:{chat_id}" if key not in self._messages: self._messages[key] = [] message_id = str(uuid.uuid4()) preview = text[:50] + ("..." if len(text) > 50 else "") response = f"[MOCK] Ответ на: «{preview}»" self._messages[key].append({ "message_id": message_id, "user_text": text, "response": response, "tokens_used": len(text.split()) * 2, "finished": True, "created_at": datetime.now(UTC).isoformat(), }) logger.info("Message sent", user_id=user_id, chat_id=chat_id, message_id=message_id) return MessageResponse( message_id=message_id, response=response, tokens_used=len(text.split()) * 2, finished=True, ) async def get_settings(self, user_id: str) -> UserSettings: await self._latency() stored = self._settings.get(user_id, {}) return UserSettings( skills=stored.get("skills", { "web-search": True, "fetch-url": True, "email": False, "browser": False, "image-gen": False, "files": True, }), connectors=stored.get("connectors", {}), soul=stored.get("soul", {"name": "Лямбда", "style": "friendly"}), safety=stored.get("safety", { "email-send": True, "file-delete": True, "social-post": True, }), plan=stored.get("plan", { "name": "Beta", "tokens_used": 0, "tokens_limit": 1000, }), ) async def update_settings(self, user_id: str, action: Any) -> None: await self._latency() settings = self._settings.setdefault(user_id, {}) if action.action == "toggle_skill": skills = settings.setdefault("skills", {}) skills[action.payload["skill"]] = action.payload.get("enabled", True) elif action.action == "set_soul": soul = settings.setdefault("soul", {}) soul[action.payload["field"]] = action.payload["value"] elif action.action == "set_safety": safety = settings.setdefault("safety", {}) safety[action.payload["trigger"]] = action.payload.get("enabled", True) logger.info("Settings updated", user_id=user_id, action=action.action) def get_stats(self) -> dict: return { "total_users": len(self._users), "total_messages": sum(len(msgs) for msgs in self._messages.values()), } async def _latency(self, min_ms: int = 10, max_ms: int = 80) -> None: await asyncio.sleep(random.randint(min_ms, max_ms) / 1000)