项目文件夹

文件
wehub-resource-sync 7d6672c63e
Deploy Documentation / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:19:20 +08:00

277 行
11 KiB
Python

import os
import time
import uuid
import logging
from typing import List, Optional
from .config import Config
try:
from .image_dashscope import DashScopeClient
from .image_seedream import SeedreamClient
from .image_gpt import ImageGPT
from .image_processor import ImageProcessor
except ImportError:
from .image_dashscope import DashScopeClient
from .image_seedream import SeedreamClient
from .image_gpt import ImageGPT
from .image_processor import ImageProcessor
class ImageClient:
def __init__(self,
dashscope_api_key: Optional[str] = None,
dashscope_base_url: Optional[str] = None,
dashscope_local_proxy: Optional[str] = None,
gpt_api_key: Optional[str] = None,
gpt_base_url: Optional[str] = None,
local_proxy: Optional[str] = None,
ark_api_key: Optional[str] = None,
ark_base_url: Optional[str] = None,
ark_local_proxy: Optional[str] = None):
"""
Unified Image Generation Client
Routes requests to DashScope, Seedream, or GPT based on model name.
"""
self._dashscope_api_key = dashscope_api_key or Config.DASHSCOPE_API_KEY
self._dashscope_base_url = dashscope_base_url or Config.DASHSCOPE_BASE_URL
self._dashscope_local_proxy = dashscope_local_proxy
self._gpt_api_key = gpt_api_key or Config.OPENAI_API_KEY
self._gpt_base_url = gpt_base_url or Config.OPENAI_BASE_URL
self._gpt_local_proxy = local_proxy or Config.LOCAL_PROXY
self._ark_api_key = ark_api_key or Config.ARK_API_KEY
self._ark_base_url = ark_base_url or Config.ARK_BASE_URL
self._ark_local_proxy = ark_local_proxy
self._dashscope_client = None
self._seedream_client = None
self._gpt_client = None
# Initialize Image Processor for downloads
self.image_processor = ImageProcessor()
# Default save directory
self.base_save_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "code", "result", "image_client")
@property
def dashscope_client(self):
"""Create DashScope client only when a DashScope model is selected."""
if self._dashscope_client is None:
self._dashscope_client = DashScopeClient(
api_key=self._dashscope_api_key,
base_url=self._dashscope_base_url,
local_proxy=self._dashscope_local_proxy,
)
return self._dashscope_client
@property
def seedream_client(self):
"""Create Seedream client only when a Seedream/ARK model is selected."""
if not self._ark_api_key:
raise RuntimeError("ARK_API_KEY not set. Configure ARK only when using Seedream image models.")
if self._seedream_client is None:
self._seedream_client = SeedreamClient(
api_key=self._ark_api_key,
base_url=self._ark_base_url,
local_proxy=self._ark_local_proxy,
)
return self._seedream_client
@property
def gpt_client(self):
"""Create OpenAI image client only when a GPT/OpenAI image model is selected."""
if not self._gpt_api_key:
raise RuntimeError("OPENAI_API_KEY not set. Configure OpenAI only when using GPT image models.")
if self._gpt_client is None:
self._gpt_client = ImageGPT(
api_key=self._gpt_api_key,
base_url=self._gpt_base_url,
local_proxy=self._gpt_local_proxy,
)
return self._gpt_client
def generate_image(self,
prompt: str,
image_paths: Optional[List[str]] = None,
model: str = "wan2.7-image",
save_dir: Optional[str] = None,
session_id: Optional[str] = None,
video_ratio: Optional[str] = "16:9",
resolution: Optional[str] = "2K") -> List[str]:
"""
Generate images based on prompt and optional reference images.
Args:
prompt: Text prompt for generation.
image_paths: List of local file paths or URLs for reference images.
model: Model name to determine which provider to use.
save_dir: Custom directory to save downloaded images.
session_id: Session ID for organizing saved files.
video_ratio: Aspect ratio of the video, e.g., "16:9", "9:16", "4:3", "3:4", "1:1".
resolution: Resolution string, e.g., "720P", "1080P", "2K", "4K".
Returns:
List of absolute file paths of the generated images.
"""
# Determine size from video_ratio and resolution
size_map = {
"16:9": {
"720P": "1280*720",
"1080P": "1920*1080",
"2K": "2560*1440",
"4K": "3840*2160"
},
"9:16": {
"720P": "720*1280",
"1080P": "1080*1920",
"2K": "1440*2560",
"4K": "2160*3840"
},
"4:3": {
"720P": "960*720",
"1080P": "1440*1080",
"2K": "2560*1920",
"4K": "3840*2880"
},
"3:4": {
"720P": "720*960",
"1080P": "1080*1440",
"2K": "1920*2560",
"4K": "2880*3840"
},
"1:1": {
"720P": "720*720",
"1080P": "1080*1080",
"2K": "2560*2560",
"4K": "3840*3840"
}
}
# Default fallback if ratio or resolution is not found
size = size_map.get(video_ratio, size_map["16:9"]).get(resolution, "1920*1080")
if not model:
model = "wan2.7-image" # Default model
if Config.PRINT_MODEL_INPUT:
print("---- IMAGE GENERATION REQUEST ----")
print(f"Prompt: {prompt}")
if image_paths:
print(f"Refs: {len(image_paths)}")
for p in image_paths:
if str(p).startswith("data:"):
print(f" - [Base64图片]")
else:
print(f" - {p}")
print(f"Model: {model}")
print(f"Video Ratio: {video_ratio}")
print(f"Resolution: {resolution}")
print(f"Final Size: {size}")
if session_id:
print(f"Session ID: {session_id}")
print("-" * 30)
# Determine backend provider
is_seedream = "seedream" in model.lower()
is_sora = "sora" in model.lower() or "gpt" in model.lower()
# Prepare save directory
if not save_dir:
if session_id:
save_dir = os.path.join(self.base_save_dir, session_id)
else:
save_dir = self.base_save_dir
os.makedirs(save_dir, exist_ok=True)
generated_local_paths = []
if is_seedream:
# --- Seedream Logic ---
try:
logging.info(f"ImageClient requesting Seedream: {model}")
paths = self.seedream_client.generate_image(
prompt=prompt,
model=model,
session_id=session_id or "default",
size=size or "2048*2048",
image_paths=image_paths
)
if paths:
generated_local_paths.extend(paths)
except Exception as e:
logging.error(f"Seedream generation failed: {e}")
elif is_sora:
# --- GPT/Sora Logic ---
try:
logging.info(f"ImageClient requesting GPT/Sora: {model}")
if image_paths:
logging.warning("Sora/GPT model only supports Text-to-Image. Ignoring reference images.")
# OpenAI uses 'x' separator, e.g. 1024x1024
# Attempt to map size if needed or just replace '*'
gpt_size = size.replace('*', 'x') if size else "1024x1024"
path = self.gpt_client.generate_image(
prompt=prompt,
size=gpt_size,
model=model,
save_dir=save_dir
)
if path and os.path.exists(path):
generated_local_paths.append(path)
else:
logging.error(f"GPT/Sora returned invalid path or download failed: {path}")
except Exception as e:
logging.error(f"GPT/Sora generation failed: {e}")
else:
# --- DashScope Logic ---
try:
logging.info(f"ImageClient requesting DashScope: {model}")
if image_paths and len(image_paths) > 0:
# Pre-process image paths for DashScope
# Convert local paths to file:// URIs if they aren't already URLs
# DashScope SDK (via MultiModalConversation) handles file://
formatted_urls = []
for p in image_paths:
if p.startswith("http") or p.startswith("file://"):
formatted_urls.append(p)
else:
abs_path = os.path.abspath(p)
formatted_urls.append(f"file://{abs_path}")
paths = self.dashscope_client.edit_image(
prompt=prompt,
image_urls=formatted_urls,
model=model,
size=size,
session_id=session_id,
save_dir=save_dir
)
else:
# Text to Image
# Assuming default size 1024*1024 or similar
paths = self.dashscope_client.generate_image(
prompt=prompt,
model=model,
size=size,
session_id=session_id,
save_dir=save_dir
)
if paths:
generated_local_paths.extend(paths)
except Exception as e:
logging.error(f"DashScope generation failed: {e}")
raise RuntimeError(f"DashScope generation failed: {e}") from e
return generated_local_paths