nano_banana_task.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import logging
  2. import time
  3. from app.config import settings
  4. from app.service.task.base_task import BaseTask
  5. from app.service.task.task_param import TaskParam
  6. from app.service.task.task_result import TaskResult
  7. from app.util.http_util import logged_client
  8. from app.util.retry_util import retry_on_failure
  9. logger = logging.getLogger(__name__)
  10. class NanoBananaTask(BaseTask):
  11. def get_type(self) -> str:
  12. return "nano_banana"
  13. def _submit(self, endpoint: str, headers: dict, inputs: dict) -> dict:
  14. with logged_client(timeout=120) as client:
  15. resp = client.post(endpoint, headers=headers, json=inputs)
  16. logger.info("[nano_banana] Submit response: status=%d, body_size=%d",
  17. resp.status_code, len(resp.content))
  18. if not resp.is_success:
  19. raise RuntimeError(f"HTTP {resp.status_code}: {resp.text}")
  20. data = resp.json()
  21. if not data.get("candidates"):
  22. raise RuntimeError("No candidates in response")
  23. return data
  24. def execute(self, param: TaskParam) -> TaskResult:
  25. endpoint = f"{settings.gemini_base_url}/models/{settings.gemini_nano_banana_model}:generateContent"
  26. headers = {
  27. "x-goog-api-key": settings.gemini_api_key,
  28. "Content-Type": "application/json",
  29. }
  30. logger.info("[nano_banana] Calling Gemini API: POST %s", endpoint)
  31. t0 = time.time()
  32. try:
  33. data = retry_on_failure(
  34. lambda: self._submit(endpoint, headers, param.inputs),
  35. task_name="nano_banana",
  36. )
  37. except Exception as e:
  38. elapsed = time.time() - t0
  39. logger.error("[nano_banana] Request failed in %.2fs: %s", elapsed, e)
  40. return TaskResult.failure(f"submit execute fail: {e}")
  41. encoded_files: list[str] = []
  42. candidates = data.get("candidates", [])
  43. parts = candidates[0].get("content", {}).get("parts", [])
  44. for part in parts:
  45. text = part.get("text")
  46. if text:
  47. logger.info("[nano_banana] Text part: %s", text[:200])
  48. inline_data = part.get("inlineData")
  49. if inline_data and inline_data.get("data"):
  50. encoded_files.append(inline_data["data"])
  51. logger.info("[nano_banana] Completed: %d images generated", len(encoded_files))
  52. return TaskResult.success({"images": encoded_files})