37c0364e7e
Replace all print()/stderr logging with Python logging module using logger = logging.getLogger(__name__) pattern for consistent log levels and formatting. Extract score conversion to shared score_utils module. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
101 lines
3.4 KiB
Python
101 lines
3.4 KiB
Python
"""
|
|
统一图像生成服务基类
|
|
支持硅基流动平台的多种图像生成模型
|
|
"""
|
|
|
|
import base64
|
|
import logging
|
|
import uuid
|
|
from pathlib import Path
|
|
from typing import Dict, Any
|
|
import httpx
|
|
from src.core.config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ImageGenerationService:
|
|
"""统一的图像生成服务基类"""
|
|
|
|
def __init__(self):
|
|
self.base_url = "https://api.siliconflow.cn/v1/image/generations"
|
|
self.api_key = settings.siliconflow_api_key
|
|
self.timeout = 60
|
|
|
|
async def call_api(self, model: str, payload: dict) -> dict:
|
|
"""调用硅基流动 API"""
|
|
logger.debug(f"调用SiliconFlow API: {self.base_url}")
|
|
logger.debug(f"模型: {model}")
|
|
logger.debug(f"请求参数: {payload}")
|
|
|
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
response = await client.post(
|
|
self.base_url,
|
|
headers={
|
|
"Authorization": f"Bearer {self.api_key}",
|
|
"Content-Type": "application/json"
|
|
},
|
|
json={"model": model, **payload}
|
|
)
|
|
response.raise_for_status()
|
|
|
|
result = response.json()
|
|
logger.debug(f"API响应状态: {response.status_code}")
|
|
logger.debug(f"API响应类型: {type(result)}")
|
|
logger.debug(f"API响应键: {list(result.keys()) if isinstance(result, dict) else '非字典类型'}")
|
|
|
|
return result
|
|
|
|
def save_image(self, image_id: str, base64_data: str) -> str:
|
|
"""保存 base64 图像到本地文件系统"""
|
|
try:
|
|
# 清理base64数据(移除可能的data URL前缀)
|
|
if base64_data.startswith('data:image'):
|
|
base64_data = base64_data.split(',')[1]
|
|
|
|
# 移除可能的空白字符
|
|
base64_data = base64_data.strip()
|
|
|
|
logger.debug(f"开始解码base64数据,长度: {len(base64_data)}")
|
|
|
|
# 解码 base64 数据
|
|
image_bytes = base64.b64decode(base64_data)
|
|
|
|
# 验证是否为有效的图片数据
|
|
if len(image_bytes) == 0:
|
|
raise Exception("解码后的图像数据为空")
|
|
|
|
logger.debug(f"图像数据大小: {len(image_bytes)} bytes")
|
|
|
|
# 确保目录存在
|
|
image_dir = Path(settings.generated_images_dir)
|
|
image_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# 保存图像文件
|
|
image_path = image_dir / f"{image_id}.png"
|
|
with open(image_path, "wb") as f:
|
|
f.write(image_bytes)
|
|
|
|
logger.debug(f"图像已保存到: {image_path}")
|
|
|
|
return str(image_path)
|
|
except Exception as e:
|
|
logger.error(f"保存图像失败: {str(e)}")
|
|
logger.error(f"base64数据长度: {len(base64_data) if base64_data else 0}")
|
|
logger.error(f"base64数据前100字符: {base64_data[:100] if base64_data else 'None'}")
|
|
raise Exception(f"保存图像失败: {str(e)}")
|
|
|
|
def generate_image_id(self) -> str:
|
|
"""生成唯一的图像ID"""
|
|
return str(uuid.uuid4())
|
|
|
|
def get_image_url(self, image_id: str) -> str:
|
|
"""获取图像的访问URL"""
|
|
return f"/generated_images/{image_id}.png"
|
|
|
|
|
|
|
|
|
|
|
|
|