call_banana.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 🍌 Nano Banana (千层套路·多模态生成) 外部调用脚本
  5. 【工具定位】:它使用的是 Google 最新的多模态 Imagen 3 引擎(gemini-3.1-flash-image-preview)。
  6. 【独门绝技 - 跨模态多图推理】:
  7. 与传统的 SD / Flux (图生图) 的本质区别在于,你可以给它丢【任意张完全不同的图片】,
  8. 然后在 prompt 里随心所欲地让它“融梗”交汇。
  9. 比如传入图片 [猫.jpg] 和 [未来城.jpg],让它“把这只猫生成在这座未来城里”。
  10. 【参数模式支持】:
  11. 1. 纯文生图:只传 prompt
  12. 2. 单图生图/重绘:传 1 张图 + prompt
  13. 3. 多路意象融合:传 N 张图 + prompt
  14. 【本地文件自动上传机制】:
  15. 大模型 API 往往更喜欢吃稳定的 CDN 外链。
  16. 此脚本在启动前会自动检测你传入的图片:如果是本地硬盘文件(比如 examples/cat.png),
  17. 脚本会自动静默调用内部的 OSS 工具,把它秒传为 https://res.cybertogether.net/.. 干净外链,然后投喂给大脑!
  18. """
  19. import asyncio
  20. import os
  21. import argparse
  22. import sys
  23. import httpx
  24. import json
  25. # 动态引入我们系统现成的 CDN 上传脚本
  26. sys.path.append(os.path.dirname(os.path.abspath(__file__)))
  27. sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'production_restore'))
  28. try:
  29. from upload import upload_image
  30. except ImportError:
  31. print("错误: 找不到 upload.py。请确保在 tests 目录下运行此脚本。")
  32. sys.exit(1)
  33. ROUTER_URL = "http://43.106.118.91:8001/run_tool"
  34. async def process_images(images_list: list[str]) -> list[str]:
  35. """处理图片数组,外链直接过,本地文件传 OSS"""
  36. final_urls = []
  37. for item in images_list:
  38. if item.startswith("http://") or item.startswith("https://"):
  39. print(f"✅ 检测到公网外链,无需转存: {item}")
  40. final_urls.append(item)
  41. elif os.path.exists(item):
  42. print(f"📦 正在极速倒卖本地图片到 CDN: {item}")
  43. try:
  44. uploaded_url = await upload_image(item, 'aigc-admin', 'crawler/image')
  45. if uploaded_url:
  46. print(f"🚀 上传成功: {uploaded_url}")
  47. final_urls.append(uploaded_url)
  48. else:
  49. print(f"❌ 上传失败: {item}")
  50. except Exception as e:
  51. print(f"❌ 上传报错: {e}")
  52. else:
  53. print(f"⚠️ 跳过找不到的本地文件或无法识别的格式: {item}")
  54. return final_urls
  55. async def run_nano_banana(prompt: str, images: list[str] = None, model: str = None):
  56. print(f"\n=======================")
  57. print(f"🍌 Nano Banana 启动中...")
  58. print(f"=======================")
  59. # 1. 整理图片
  60. final_image_urls = []
  61. if images and len(images) > 0:
  62. print(f"🔍 检查到传了 {len(images)} 张神秘原图,准备过安检...")
  63. final_image_urls = await process_images(images)
  64. # 2. 组装发给大模型中枢 Router 的参数
  65. params = {
  66. "prompt": prompt,
  67. "image_urls": final_image_urls if final_image_urls else None
  68. }
  69. if model:
  70. params["model"] = model
  71. payload = {
  72. "tool_id": "nano_banana",
  73. "params": params
  74. }
  75. # 3. 轰入 API
  76. print("\n⚡ 正在呼叫总后台路由节点打怪...")
  77. try:
  78. async with httpx.AsyncClient(timeout=300.0) as client:
  79. resp = await client.post(ROUTER_URL, json=payload)
  80. resp.raise_for_status()
  81. result = resp.json()
  82. if result.get("status") == "success":
  83. gen_data = result.get("result", {})
  84. print("\n🎉 === 生成成功! ===")
  85. # 打印文本回复
  86. if gen_data.get("text"):
  87. print(f"\n💬 模型回话:\n{gen_data.get('text')}")
  88. # 打印图片输出
  89. images_out = gen_data.get("images", [])
  90. if images_out:
  91. print("\n🖼️ 吐出的神图 (Base64 数据流已转为你本地文件):")
  92. for idx, img_b64 in enumerate(images_out):
  93. # 处理前缀 data:image/jpeg;base64,
  94. if img_b64.startswith("data:"):
  95. mime_split = img_b64.split(";base64,")
  96. if len(mime_split) == 2:
  97. ext = mime_split[0].split("/")[-1]
  98. raw_data = mime_split[1]
  99. import base64
  100. save_path = f"banana_output_{idx}.{ext}"
  101. with open(save_path, "wb") as f:
  102. f.write(base64.b64decode(raw_data))
  103. print(f" 💾 已保存到本地 -> {save_path}")
  104. else:
  105. print(f"❌ 大模型傲娇了: {result.get('error')}")
  106. except Exception as e:
  107. print(f"💥 网络大爆炸报错: {e}")
  108. if __name__ == "__main__":
  109. parser = argparse.ArgumentParser(description="调用 Nano Banana 进行任意风格的多段融合魔法")
  110. parser.add_argument("-p", "--prompt", type=str, required=True, help="你想对 AI 喊瞎什么 (比如:用图1的赛博风画一只图2里的猫)")
  111. parser.add_argument("-i", "--images", type=str, nargs="+", help="无限追加的垫图清单(可以是现成的 http 链接,也可以是你电脑里的硬盘文件如 example.png)")
  112. parser.add_argument("-m", "--model", type=str, default=None, help="覆盖模型 (默认后台会走 gemini-3.1-flash-image-preview)")
  113. args = parser.parse_args()
  114. asyncio.run(run_nano_banana(prompt=args.prompt, images=args.images, model=args.model))