feat: support configurable image generation models for poster creation
- Expand poster image model settings with provider, model name, API key, and base URL fields (was only model name + API key) - Add presets for OpenAI (gpt-image-1), Doubao (火山引擎), and Zhipu (CogView) - Refactor image_generator.py to read config from system_settings DB instead of hardcoding gpt-image-1 and OPENAI_API_KEY env var - All OpenAI-compatible image APIs work via configurable base URL - Add doubao and cogview to brand name mapping Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
13f7623cb5
commit
02e299682e
@ -507,8 +507,10 @@ class PptAdminService:
|
||||
"gemini": "Google",
|
||||
"qwen": "通义千问",
|
||||
"glm": "智谱",
|
||||
"cogview": "智谱",
|
||||
"MiniMax": "MiniMax",
|
||||
"moonshot": "月之暗面",
|
||||
"doubao": "豆包",
|
||||
}
|
||||
|
||||
def _infer_brand(self, model_name: str) -> str:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""海报图片生成器 — 调用 GPT image API + Pillow 降级方案。"""
|
||||
"""海报图片生成器 — 支持多供应商生图模型(OpenAI / 豆包 / 智谱等)。"""
|
||||
import os
|
||||
import base64
|
||||
import logging
|
||||
@ -14,17 +14,43 @@ SIZE_MAP = {
|
||||
}
|
||||
|
||||
|
||||
def _load_image_config() -> dict:
|
||||
"""从数据库读取海报图片模型配置。"""
|
||||
try:
|
||||
from insurance.db.compat import db
|
||||
from insurance.models.system_setting import SystemSetting
|
||||
keys = ["poster_image_provider", "poster_image_model",
|
||||
"poster_image_api_key", "poster_image_base_url"]
|
||||
settings = {s.key: s.value for s in SystemSetting.query.filter(SystemSetting.key.in_(keys)).all()}
|
||||
return settings
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
class PosterImageGenerator:
|
||||
"""海报图片生成器,调用 GPT image 模型。"""
|
||||
"""海报图片生成器,支持 OpenAI 兼容接口的多供应商生图模型。"""
|
||||
|
||||
def __init__(self):
|
||||
self.client = None
|
||||
self._client = None
|
||||
self._config = None
|
||||
|
||||
def _get_config(self) -> dict:
|
||||
if self._config is None:
|
||||
self._config = _load_image_config()
|
||||
return self._config
|
||||
|
||||
def _get_client(self):
|
||||
if self.client is None:
|
||||
if self._client is None:
|
||||
import openai
|
||||
self.client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
||||
return self.client
|
||||
cfg = self._get_config()
|
||||
api_key = cfg.get("poster_image_api_key") or os.getenv("OPENAI_API_KEY", "")
|
||||
base_url = cfg.get("poster_image_base_url") or os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
|
||||
self._client = openai.OpenAI(api_key=api_key, base_url=base_url)
|
||||
return self._client
|
||||
|
||||
def _get_model(self) -> str:
|
||||
cfg = self._get_config()
|
||||
return cfg.get("poster_image_model") or "gpt-image-1"
|
||||
|
||||
def build_prompt(self, template: dict = None, product: dict = None,
|
||||
company: dict = None, copy: dict = None, size: str = "1024x1792") -> str:
|
||||
@ -81,16 +107,17 @@ class PosterImageGenerator:
|
||||
return "\n".join(parts)
|
||||
|
||||
def generate(self, prompt: str, size: str = "1024x1792", reference_image: str = None) -> bytes:
|
||||
"""调用 GPT image 模型生成海报图片。
|
||||
"""调用配置的生图模型生成海报图片。
|
||||
|
||||
返回:
|
||||
PNG 图片的 bytes
|
||||
"""
|
||||
api_size = SIZE_MAP.get(size, "1024x1792")
|
||||
client = self._get_client()
|
||||
model = self._get_model()
|
||||
|
||||
kwargs = {
|
||||
"model": "gpt-image-1",
|
||||
"model": model,
|
||||
"prompt": prompt,
|
||||
"n": 1,
|
||||
"size": api_size,
|
||||
@ -106,7 +133,7 @@ class PosterImageGenerator:
|
||||
try:
|
||||
with open(reference_image, "rb") as img_file:
|
||||
response = client.images.edit(
|
||||
model="gpt-image-1",
|
||||
model=model,
|
||||
image=img_file,
|
||||
prompt=prompt,
|
||||
n=1,
|
||||
|
||||
@ -86,12 +86,25 @@
|
||||
<!-- 海报图片模型 -->
|
||||
<el-divider content-position="left">海报图片模型</el-divider>
|
||||
|
||||
<el-form-item label="供应商">
|
||||
<el-select v-model="form.poster_image_provider" placeholder="选择供应商" style="width: 100%" @change="onImageProviderChange">
|
||||
<el-option label="OpenAI (gpt-image-1)" value="openai" />
|
||||
<el-option label="豆包 (火山引擎)" value="doubao" />
|
||||
<el-option label="智谱 (CogView)" value="zhipu" />
|
||||
<el-option label="自定义" value="custom" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="模型名称">
|
||||
<el-input v-model="form.poster_image_model" placeholder="默认 gpt-image-1" />
|
||||
<el-input v-model="form.poster_image_model" placeholder="如 gpt-image-1" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="API Key">
|
||||
<el-input v-model="form.poster_image_api_key" type="password" show-password placeholder="留空则使用环境变量配置" />
|
||||
<el-input v-model="form.poster_image_api_key" type="password" show-password placeholder="该供应商的 API Key" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Base URL">
|
||||
<el-input v-model="form.poster_image_base_url" placeholder="如 https://api.openai.com/v1" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- Dify 集成 -->
|
||||
@ -129,8 +142,10 @@ const form = reactive({
|
||||
poster_llm_model: '',
|
||||
poster_llm_api_key: '',
|
||||
poster_llm_base_url: '',
|
||||
poster_image_provider: 'openai',
|
||||
poster_image_model: 'gpt-image-1',
|
||||
poster_image_api_key: '',
|
||||
poster_image_base_url: 'https://api.openai.com/v1',
|
||||
dify_workspace_api_key: '',
|
||||
})
|
||||
|
||||
@ -141,6 +156,20 @@ const DEFAULT_MODELS: Record<string, string> = {
|
||||
openai: 'gpt-4o',
|
||||
}
|
||||
|
||||
const IMAGE_PROVIDER_PRESETS: Record<string, { model: string; baseUrl: string }> = {
|
||||
openai: { model: 'gpt-image-1', baseUrl: 'https://api.openai.com/v1' },
|
||||
doubao: { model: 'doubao-seedream-3-0-t2i-250415', baseUrl: 'https://ark.cn-beijing.volces.com/api/v3' },
|
||||
zhipu: { model: 'cogview-4-250304', baseUrl: 'https://open.bigmodel.cn/api/paas/v4' },
|
||||
}
|
||||
|
||||
function onImageProviderChange(val: string) {
|
||||
const preset = IMAGE_PROVIDER_PRESETS[val]
|
||||
if (preset) {
|
||||
form.poster_image_model = preset.model
|
||||
form.poster_image_base_url = preset.baseUrl
|
||||
}
|
||||
}
|
||||
|
||||
function onPptProviderChange(val: string) {
|
||||
if (val !== 'custom' && !form.ppt_llm_model) {
|
||||
form.ppt_llm_model = DEFAULT_MODELS[val] || ''
|
||||
|
||||
Loading…
Reference in New Issue
Block a user