From 02e299682ecbc7edac5050fb77a1ef2f3182cd82 Mon Sep 17 00:00:00 2001 From: wsb1224 Date: Fri, 24 Jul 2026 16:11:37 +0800 Subject: [PATCH] feat: support configurable image generation models for poster creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- api/insurance/admin/ppt_admin_service.py | 2 + api/insurance/poster/image_generator.py | 45 +++++++++++++++---- frontend/src/pages/admin/PptSettingsAdmin.vue | 33 +++++++++++++- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/api/insurance/admin/ppt_admin_service.py b/api/insurance/admin/ppt_admin_service.py index c738f8b..28f3b48 100644 --- a/api/insurance/admin/ppt_admin_service.py +++ b/api/insurance/admin/ppt_admin_service.py @@ -507,8 +507,10 @@ class PptAdminService: "gemini": "Google", "qwen": "通义千问", "glm": "智谱", + "cogview": "智谱", "MiniMax": "MiniMax", "moonshot": "月之暗面", + "doubao": "豆包", } def _infer_brand(self, model_name: str) -> str: diff --git a/api/insurance/poster/image_generator.py b/api/insurance/poster/image_generator.py index f855a16..02ea120 100644 --- a/api/insurance/poster/image_generator.py +++ b/api/insurance/poster/image_generator.py @@ -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, diff --git a/frontend/src/pages/admin/PptSettingsAdmin.vue b/frontend/src/pages/admin/PptSettingsAdmin.vue index 7c27299..a5bf278 100644 --- a/frontend/src/pages/admin/PptSettingsAdmin.vue +++ b/frontend/src/pages/admin/PptSettingsAdmin.vue @@ -86,12 +86,25 @@ 海报图片模型 + + + + + + + + + - + - + + + + + @@ -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 = { openai: 'gpt-4o', } +const IMAGE_PROVIDER_PRESETS: Record = { + 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] || ''