"""Nano Banana — Gemini 2.5 Flash Image (generativelanguage API). POST /v1beta/models/{MODEL}:generateContent → candidates[0].content.parts[*].inlineData.data (b64 png) """ from __future__ import annotations import logging, os import requests from nas_client import webhook_update_task from providers._media import save_b64_png logger = logging.getLogger(__name__) GEMINI_BASE = "https://generativelanguage.googleapis.com/v1beta" DEFAULT_MODEL = "gemini-2.5-flash-image" def _extract_b64(data: dict): for cand in data.get("candidates", []): for part in cand.get("content", {}).get("parts", []): inline = part.get("inlineData") or part.get("inline_data") if inline and inline.get("data"): return inline["data"] return None def run_nano_banana_generation(task_id: str, params: dict) -> None: try: if not os.getenv("GEMINI_API_KEY"): webhook_update_task(task_id, "failed", 0, "", error="GEMINI_API_KEY 미설정 (Windows .env)") return webhook_update_task(task_id, "processing", 10, "Nano Banana (Gemini) 호출 중...") model_id = params.get("model") or DEFAULT_MODEL body = {"contents": [{"parts": [{"text": params["prompt"]}]}]} resp = requests.post( f"{GEMINI_BASE}/models/{model_id}:generateContent", headers={"x-goog-api-key": os.getenv("GEMINI_API_KEY"), "Content-Type": "application/json"}, json=body, timeout=120, ) if resp.status_code != 200: webhook_update_task(task_id, "failed", 0, "", error=f"Gemini {resp.status_code}: {resp.text[:200]}") return b64 = _extract_b64(resp.json()) if not b64: webhook_update_task(task_id, "failed", 0, "", error="Gemini 응답에 이미지 없음") return url = save_b64_png(task_id, b64) webhook_update_task(task_id, "succeeded", 100, "완료", image_url=url) except Exception as e: logger.exception("nano_banana task=%s 실패", task_id) webhook_update_task(task_id, "failed", 0, "", error=str(e))