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:
wsb1224 2026-07-24 16:11:37 +08:00
parent 13f7623cb5
commit 02e299682e
3 changed files with 69 additions and 11 deletions

View File

@ -507,8 +507,10 @@ class PptAdminService:
"gemini": "Google",
"qwen": "通义千问",
"glm": "智谱",
"cogview": "智谱",
"MiniMax": "MiniMax",
"moonshot": "月之暗面",
"doubao": "豆包",
}
def _infer_brand(self, model_name: str) -> str:

View File

@ -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,

View File

@ -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] || ''