Spaces:
Running
Running
| import os | |
| import base64 | |
| from loguru import logger | |
| from typing import Optional, Dict, Any | |
| # Tenta importar o SDK do Google GenAI | |
| try: | |
| from google import genai | |
| from google.genai import types | |
| HAS_GENAI = True | |
| except ImportError: | |
| HAS_GENAI = False | |
| class GoogleImageGenerator: | |
| """ | |
| Gerador de imagens usando Google Imagen 3 (Nano Banana). | |
| """ | |
| def __init__(self, api_key: Optional[str] = None): | |
| self.api_key = api_key or os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY") | |
| self.client = None | |
| if HAS_GENAI and self.api_key: | |
| try: | |
| # Tenta inicializar o cliente sem forçar versão para deixar o SDK decidir | |
| self.client = genai.Client(api_key=self.api_key) | |
| # Verifica versão do SDK | |
| try: | |
| import google.genai as genai_mod | |
| logger.info(f"✅ Google GenAI SDK v{getattr(genai_mod, '__version__', 'unknown')} inicializado") | |
| except: | |
| logger.info("✅ Google Image Generator (Imagen 3) inicializado") | |
| except Exception as e: | |
| logger.error(f"❌ Falha ao inicializar Google GenAI Client: {e}") | |
| def generate(self, prompt: str, aspect_ratio: str = "1:1", model: str = "flux") -> Dict[str, Any]: | |
| """ | |
| Gera uma imagem via Pollinations como primário e Imagen 3 como fallback. | |
| """ | |
| # ✅ PRIORIDADE: Pollinations (Poly/Flux) é o preferido do usuário agora | |
| try: | |
| res = self._pollinations_fallback(prompt, aspect_ratio, model) | |
| if res.get("success"): | |
| return res | |
| except Exception as poly_err: | |
| logger.warning(f"⚠️ Pollinations falhou: {poly_err}. Tentando Google como fallback final...") | |
| # 🔄 FALLBACK: Google Imagen (Apenas se o Pollinations falhar) | |
| if not self.client: | |
| return {"success": False, "error": "Google GenAI Client não disponível ou sem chave API"} | |
| try: | |
| # Mapeamento de aspect ratio para o formato do Imagen | |
| # Imagen 3 suporta: "1:1", "4:3", "3:4", "16:9", "9:16" | |
| valid_ratios = ["1:1", "4:3", "3:4", "16:9", "9:16"] | |
| if aspect_ratio not in valid_ratios: | |
| aspect_ratio = "1:1" | |
| logger.info(f"🎨 Gerando imagem via Imagen 3 (Nano Banana): '{prompt[:50]}...' [{aspect_ratio}]") | |
| # Debug: Listar modelos disponíveis E FILTRAR válidos | |
| available_models = [] | |
| try: | |
| available_models = [m.name.replace("models/", "") for m in self.client.models.list()] | |
| logger.info(f"📋 Modelos disponíveis: {available_models[:10]}...") # Trunca log | |
| except Exception as le: | |
| logger.warning(f"Não foi possível listar modelos: {le}") | |
| # Modelos preferidos (env override first) | |
| preferred_models = os.getenv("GEMINI_IMAGE_MODEL", "").split(",") if os.getenv("GEMINI_IMAGE_MODEL") else [] | |
| models_to_try = preferred_models + [ | |
| "imagen-3.0-generate-001", | |
| "imagen-3.0-fast-001", | |
| "imagen-4.0-generate-001", | |
| "nano-banana-pro-preview" | |
| ] | |
| # FILTRA APENAS MODELOS QUE REALMENTE EXISTEM | |
| models_to_try = [m.strip() for m in models_to_try if m.strip() in available_models] | |
| if not models_to_try: | |
| logger.error("❌ Nenhum modelo Imagen válido disponível!") | |
| return self._pollinations_fallback(prompt, aspect_ratio, model) | |
| logger.info(f"🎨 Tentando modelos válidos: {models_to_try[:3]}...") | |
| last_err = None | |
| for model_id in models_to_try: | |
| # Tenta tanto singular quanto plural (o SDK mudou entre versões beta/GA) | |
| for method_name in ["generate_image", "generate_images"]: | |
| if not hasattr(self.client.models, method_name): | |
| continue | |
| try: | |
| logger.debug(f"Tentando {method_name} com {model_id}...") | |
| method = getattr(self.client.models, method_name) | |
| # Ajusta a classe de config conforme o método | |
| config_class = types.GenerateImageConfig if method_name == "generate_image" else types.GenerateImagesConfig | |
| img_response = method( | |
| model=model_id, | |
| prompt=prompt, | |
| config=config_class( | |
| number_of_images=1, | |
| aspect_ratio=aspect_ratio, | |
| ) | |
| ) | |
| if img_response and img_response.generated_images: | |
| img = img_response.generated_images[0] | |
| logger.success(f"✅ Imagem gerada com sucesso via Google ({model_id})!") | |
| # Verifica todos os possíveis locais do buffer | |
| data = None | |
| if hasattr(img, 'image') and hasattr(img.image, 'data'): data = img.image.data | |
| elif hasattr(img, 'image') and hasattr(img.image, 'image_bytes'): data = img.image.image_bytes | |
| elif hasattr(img, 'image_bytes'): data = img.image_bytes | |
| if data: | |
| return { | |
| "success": True, | |
| "buffer": data, | |
| "mime_type": "image/png", | |
| "model": model_id | |
| } | |
| except Exception as inner_e: | |
| last_err = inner_e | |
| logger.warning(f"⚠️ {method_name} com {model_id} falhou: {inner_e}") | |
| continue | |
| # Se chegou aqui, todos os modelos do Google falharam | |
| logger.warning(f"⚠️ Todos os modelos Imagen falharam. Erro: {last_err}") | |
| # Fallback para Pollinations (Gratuito) | |
| return self._pollinations_fallback(prompt, aspect_ratio, model) | |
| except Exception as e: | |
| logger.error(f"❌ Erro crítico no gerador de imagem: {e}") | |
| return {"success": False, "error": str(e)} | |
| def _pollinations_fallback(self, prompt: str, aspect_ratio: str = "1:1", model: str = "flux") -> Dict[str, Any]: | |
| """ | |
| Gera uma imagem via Pollinations.ai como fallback gratuito. | |
| """ | |
| try: | |
| import requests | |
| import urllib.parse | |
| import random | |
| logger.info(f"🎙️ Usando Pollinations ({model}) para: '{prompt[:50]}...'") | |
| # Dimensões baseadas no aspect ratio | |
| width, height = 1024, 1024 | |
| if aspect_ratio == "16:9": width, height = 1280, 720 | |
| elif aspect_ratio == "9:16": width, height = 720, 1280 | |
| elif aspect_ratio == "4:3": width, height = 1024, 768 | |
| elif aspect_ratio == "3:4": width, height = 768, 1024 | |
| seed = random.randint(1, 999999) | |
| encoded_prompt = urllib.parse.quote(prompt) | |
| url = f"https://image.pollinations.ai/prompt/{encoded_prompt}?width={width}&height={height}&model={model}&seed={seed}&nologo=true" | |
| response = requests.get(url, timeout=45) | |
| if response.status_code == 200 and len(response.content) > 1000: | |
| logger.success(f"✅ Imagem gerada com sucesso via Pollinations ({model})!") | |
| return { | |
| "success": True, | |
| "buffer": response.content, | |
| "mime_type": "image/png", | |
| "model": f"pollinations-{model}" | |
| } | |
| return {"success": False, "error": f"Pollinations falhou com status {response.status_code}"} | |
| except Exception as e: | |
| logger.error(f"❌ Erro no fallback Pollinations: {e}") | |
| return {"success": False, "error": f"Erro no fallback: {str(e)}"} | |
| # Singleton | |
| _instance = None | |
| def get_google_image_gen(): | |
| global _instance | |
| if _instance is None: | |
| _instance = GoogleImageGenerator() | |
| return _instance | |