This commit is contained in:
xds
2026-02-02 16:15:17 +03:00
commit e6aad48e72
21 changed files with 631 additions and 0 deletions

0
adapters/__init__.py Normal file
View File

View File

@@ -0,0 +1,99 @@
import os
import io
import logging
from datetime import datetime
from typing import List, Union, Dict, Any
from PIL import Image
# Импортируем из нового SDK
from google import genai
from google.genai import types
# Для настройки логгера
logger = logging.getLogger(__name__)
class GoogleAdapter:
def __init__(self, api_key: str):
if not api_key:
raise ValueError("API Key for Gemini is missing")
self.client = genai.Client(api_key=api_key)
# Укажите актуальную модель.
# Если gemini-3-pro-image-preview недоступна, используйте gemini-2.0-flash-exp
self.model_name = "gemini-3-pro-preview"
def generate(
self,
prompt: str,
image_bytes: bytes = None,
generate_image: bool = False
) -> Dict[str, Any]:
"""
Универсальный метод:
- Если generate_image=True: просим модель вернуть картинку (Image Generation).
- Если image_bytes переданы + generate_image=False: это Vision (описание фото).
- Если image_bytes + generate_image=True: это Image-to-Image (редактирование).
"""
if generate_image:
self.model_name = "gemini-3-pro-image-preview"
else :
self.model_name = "gemini-3-pro-preview"
contents = [prompt]
# Если есть входное изображение (для Vision или для редактирования)
if image_bytes:
try:
image = Image.open(io.BytesIO(image_bytes))
contents.append(image)
except Exception as e:
logger.error(f"Error processing input image: {e}")
return {"error": "Не удалось обработать входящее изображение."}
# Настраиваем конфигурацию
# Для генерации картинок добавляем 'IMAGE' в response_modalities
modalities = ['TEXT']
if generate_image:
modalities.append('IMAGE')
try:
# Вызов API (синхронный метод в обертке, но aiogram вызывает его в треде,
# либо используйте client.aio для асинхронности если поддерживается версией SDK)
# В google-genai v0.3+ есть асинхронный клиент, но для простоты здесь стандартный вызов.
# Чтобы не блокировать event loop, в main.py мы обернем это в to_thread при необходимости,
# но пока используем стандартный вызов.
response = self.client.models.generate_content(
model=self.model_name,
contents=contents,
config=types.GenerateContentConfig(
response_modalities=modalities,
temperature=0.7 if not generate_image else 1.0,
)
)
result = {"text": "", "images": []}
# Парсим ответ (Text или Inline Data)
if response.parts:
for part in response.parts:
if part.text:
result["text"] += part.text
# Проверяем наличие сгенерированного изображения
if part.inline_data:
# ИСПРАВЛЕНИЕ: Берем "сырые" байты напрямую из ответа
# Это работает быстрее и не вызывает ошибку с PIL
# part.inline_data.data — это уже bytes
byte_arr = io.BytesIO(part.inline_data.data)
now = datetime.now()
# Имя файла для телеграма (формально)
byte_arr.name = f'{now.timestamp()}.png'
result["images"].append(byte_arr)
return result
except Exception as e:
logger.error(f"Gemini API Error: {e}")
return {"error": f"Ошибка API: {str(e)}"}