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",
|
"gemini": "Google",
|
||||||
"qwen": "通义千问",
|
"qwen": "通义千问",
|
||||||
"glm": "智谱",
|
"glm": "智谱",
|
||||||
|
"cogview": "智谱",
|
||||||
"MiniMax": "MiniMax",
|
"MiniMax": "MiniMax",
|
||||||
"moonshot": "月之暗面",
|
"moonshot": "月之暗面",
|
||||||
|
"doubao": "豆包",
|
||||||
}
|
}
|
||||||
|
|
||||||
def _infer_brand(self, model_name: str) -> str:
|
def _infer_brand(self, model_name: str) -> str:
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
"""海报图片生成器 — 调用 GPT image API + Pillow 降级方案。"""
|
"""海报图片生成器 — 支持多供应商生图模型(OpenAI / 豆包 / 智谱等)。"""
|
||||||
import os
|
import os
|
||||||
import base64
|
import base64
|
||||||
import logging
|
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:
|
class PosterImageGenerator:
|
||||||
"""海报图片生成器,调用 GPT image 模型。"""
|
"""海报图片生成器,支持 OpenAI 兼容接口的多供应商生图模型。"""
|
||||||
|
|
||||||
def __init__(self):
|
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):
|
def _get_client(self):
|
||||||
if self.client is None:
|
if self._client is None:
|
||||||
import openai
|
import openai
|
||||||
self.client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
cfg = self._get_config()
|
||||||
return self.client
|
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,
|
def build_prompt(self, template: dict = None, product: dict = None,
|
||||||
company: dict = None, copy: dict = None, size: str = "1024x1792") -> str:
|
company: dict = None, copy: dict = None, size: str = "1024x1792") -> str:
|
||||||
@ -81,16 +107,17 @@ class PosterImageGenerator:
|
|||||||
return "\n".join(parts)
|
return "\n".join(parts)
|
||||||
|
|
||||||
def generate(self, prompt: str, size: str = "1024x1792", reference_image: str = None) -> bytes:
|
def generate(self, prompt: str, size: str = "1024x1792", reference_image: str = None) -> bytes:
|
||||||
"""调用 GPT image 模型生成海报图片。
|
"""调用配置的生图模型生成海报图片。
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
PNG 图片的 bytes
|
PNG 图片的 bytes
|
||||||
"""
|
"""
|
||||||
api_size = SIZE_MAP.get(size, "1024x1792")
|
api_size = SIZE_MAP.get(size, "1024x1792")
|
||||||
client = self._get_client()
|
client = self._get_client()
|
||||||
|
model = self._get_model()
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"model": "gpt-image-1",
|
"model": model,
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
"n": 1,
|
"n": 1,
|
||||||
"size": api_size,
|
"size": api_size,
|
||||||
@ -106,7 +133,7 @@ class PosterImageGenerator:
|
|||||||
try:
|
try:
|
||||||
with open(reference_image, "rb") as img_file:
|
with open(reference_image, "rb") as img_file:
|
||||||
response = client.images.edit(
|
response = client.images.edit(
|
||||||
model="gpt-image-1",
|
model=model,
|
||||||
image=img_file,
|
image=img_file,
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
n=1,
|
n=1,
|
||||||
|
|||||||
@ -86,12 +86,25 @@
|
|||||||
<!-- 海报图片模型 -->
|
<!-- 海报图片模型 -->
|
||||||
<el-divider content-position="left">海报图片模型</el-divider>
|
<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-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>
|
||||||
|
|
||||||
<el-form-item label="API Key">
|
<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>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- Dify 集成 -->
|
<!-- Dify 集成 -->
|
||||||
@ -129,8 +142,10 @@ const form = reactive({
|
|||||||
poster_llm_model: '',
|
poster_llm_model: '',
|
||||||
poster_llm_api_key: '',
|
poster_llm_api_key: '',
|
||||||
poster_llm_base_url: '',
|
poster_llm_base_url: '',
|
||||||
|
poster_image_provider: 'openai',
|
||||||
poster_image_model: 'gpt-image-1',
|
poster_image_model: 'gpt-image-1',
|
||||||
poster_image_api_key: '',
|
poster_image_api_key: '',
|
||||||
|
poster_image_base_url: 'https://api.openai.com/v1',
|
||||||
dify_workspace_api_key: '',
|
dify_workspace_api_key: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -141,6 +156,20 @@ const DEFAULT_MODELS: Record<string, string> = {
|
|||||||
openai: 'gpt-4o',
|
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) {
|
function onPptProviderChange(val: string) {
|
||||||
if (val !== 'custom' && !form.ppt_llm_model) {
|
if (val !== 'custom' && !form.ppt_llm_model) {
|
||||||
form.ppt_llm_model = DEFAULT_MODELS[val] || ''
|
form.ppt_llm_model = DEFAULT_MODELS[val] || ''
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user