""" Сервис генерации гипнотических названий для картин галереи. Включает: - Локальный генератор псевдослучайных гипно-заголовков (в стиле Гипножабы, психоделики и мемов). - Закладку (интерфейс и спецификацию) для будущей интеграции с OpenRouter API (мультимодальные VLM). """ import random from typing import Optional # Словари для локального генератора гипно-тайтлов HYPNO_ADJECTIVES = [ "Мерцающий", "Астральный", "Гипнотический", "Психоделический", "Пульсирующий", "Неоновый", "Трансцендентный", "Мозговой", "Ментальный", "Бессознательный", "Космический", "Галлюциногенный", "Фрактальный", "Гипножабий", "Завораживающий", "Магнетический", "Спиральный", "Зыбкий", "Иллюзорный", "Онейрический", "Эфирный", "Трансовый", "Квантовый", "Синтетический", "Хроматический", ] HYPNO_NOUNS = [ "Транс", "Пульсар", "Взгляд", "Слизень", "Вихрь", "Портал", "Разум", "Фрактал", "Сигнал", "Спектр", "Резонанс", "Морок", "Оазис", "Омут", "Лабиринт", "Импульс", "Монолит", "Оракул", "Вектор", "Сгусток", "Каскад", "Горизонт", "Мухомор", "Паттерн", "Контур", "Фантом", "Всплеск", "Взмах", "Взгляд", "Морок", ] HYPNO_GENITIVES = [ "сознания", "Гипножабы", "бессознательного", "пустоты", "матрицы", "психо-паутины", "слизня", "ноосферы", "нео-модерна", "транса", "глубины", "эфира", "плесени", "измерения", "пуантилизма", "субстанции", "неопределенности", "континуума", "кислоты", ] HYPNO_POSTFIXES = [ "#42", "v3.14", "MK-Ultra", "Фаза 7", "Пси-излучение", "Альфа", "Омега", "Частота 432Гц", "Протокол 09", "Ревизия 8", "Экстракт", "Слой 9", "Палата №6", "Ангар 18", "Ангар 18", ] def generate_hypno_title(seed: Optional[int] = None) -> str: """ Генерирует сочное гипнотическое название для картины на основе словарей. Примеры: - «Мерцающий Транс сознания #42» - «Гипножабий Пульсар психо-паутины» - «Астральный Взгляд v3.14» """ rng = random.Random(seed) adj = rng.choice(HYPNO_ADJECTIVES) noun = rng.choice(HYPNO_NOUNS) pattern_type = rng.randint(1, 3) if pattern_type == 1: genitive = rng.choice(HYPNO_GENITIVES) postfix = rng.choice(HYPNO_POSTFIXES) return f"{adj} {noun} {genitive} {postfix}" elif pattern_type == 2: genitive = rng.choice(HYPNO_GENITIVES) return f"{adj} {noun} {genitive}" else: postfix = rng.choice(HYPNO_POSTFIXES) return f"{adj} {noun} {postfix}" # ===================================================================== # ЗАКЛАДКА: OpenRouter VLM API Integration (Post-MVP / AI Title) # ===================================================================== def generate_title_openrouter( image_bytes: bytes, *, api_key: Optional[str] = None, model: str = "google/gemini-2.5-flash", system_prompt: str = ( "Ты — глашатай всемогущей Гипножабы. Придумай короткое (2-4 слова), " "яркое, иронично-абсурдное и гипнотическое название на русском языке " "для полутоновой SVG-картины, изображенной на миниатюре. " "Отвечай ТОЛЬКО названием, без кавычек и вводных слов." ), timeout: int = 5, ) -> Optional[str]: """ [ЗАКЛАДКА ПОД OPENROUTER API] Спецификация интеграции: 1. Исходное растровое изображение сжимается до thumbnail 256x256 WebP/JPEG. 2. Кодируется в Base64 (data:image/jpeg;base64,...). 3. Отправляется POST-запрос на https://openrouter.ai/api/v1/chat/completions: Payload: { "model": model, "messages": [ {"role": "system", "content": system_prompt}, { "role": "user", "content": [ {"type": "text", "text": "Дай гипнотическое название для этой картины:"}, {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64_data}"}} ] } ], "max_tokens": 50, "temperature": 0.9 } 4. При ошибке таймаута, отсутствии OPENROUTER_API_KEY в .env или сбое сети функция возвращает None, вызывая локальный fallback (generate_hypno_title). """ # TODO: Интегрировать requests / httpx при добавлении OPENROUTER_API_KEY в settings.py if not api_key: return None # Заглушка: при отсутствии реального сетевого коннектора возвращаем None для fallback return None