auto-save 2026-05-18 01:07 (~8)

This commit is contained in:
2026-05-18 01:07:51 +08:00
parent 4c43d89346
commit 7ca5a95a5e
8 changed files with 43 additions and 41 deletions

View File

@@ -1,12 +1,5 @@
{
"entries": [
{
"files_changed": 4,
"hash": "4b44c28",
"message": "auto-save 2026-05-15 15:59 (~4)",
"ts": "2026-05-15T16:00:08+08:00",
"type": "commit"
},
{
"files_changed": 1,
"message": "Codex 会话活跃 · 最近命令codex · 1 项未提交变更 · 最近提交auto-save 2026-05-15 15:59 (~4)",
@@ -3257,6 +3250,13 @@
"type": "session-heartbeat",
"message": "Codex 会话活跃 · 最近命令codex · 分支 main · 1 项未提交变更 · 最近提交auto-save 2026-05-18 00:57 (~4)",
"files_changed": 1
},
{
"ts": "2026-05-18T01:02:30+08:00",
"type": "commit",
"message": "auto-save 2026-05-18 01:02 (~2)",
"hash": "4c43d89",
"files_changed": 2
}
]
}

View File

@@ -59,8 +59,8 @@
- `REWRITE_MODEL`:通用改写/分镜描述模型,默认 `gemini-2.5-pro`
- `AUDIO_REWRITE_MODEL`:后续音频口播改写模型,默认跟随 `REWRITE_MODEL`;当前第一步不默认调用口播改写,只保留原文案和声音分析
- `AUDIO_PRODUCT_BRIEF`:音频口播改写时注入的 SKG 产品卖点
- `IMAGE_BASE_URL` / `IMAGE_API_KEY` / `IMAGE_MODEL`OpenAI 兼容生图网关;当前生图默认走 GPT 图片模型,`IMAGE_MODEL=gpt-image-2`
- `GPT_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODELS`主体 6 视图专用 GPT 图片模型链,默认 `gpt-image-2,gpt-image-1.5`,不要降回 Gemini 图像模型
- `IMAGE_BASE_URL` / `IMAGE_API_KEY` / `IMAGE_MODEL`OpenAI 兼容生图网关;当前所有生图入口一律强制使用 `gpt-image-2`,不做其他图片模型 fallback
- `GPT_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODELS`保留兼容旧环境变量名,但服务端会强制主体 6 视图和所有其他生图入口都只使用 `gpt-image-2`
- `VOICE_PROVIDER`:配音通道,当前固定使用 `azure_openai`
- `AZURE_OPENAI_BASE_URL` / `AZURE_OPENAI_API_KEY`:微软 Azure OpenAI 协议配音网关;本地未单独配置 Key 时回退复用 `LLM_API_KEY`
- `AZURE_TTS_MODEL` / `AZURE_TTS_VOICE_ID` / `AZURE_TTS_VOICE_POOL` / `AZURE_TTS_PATH`Azure OpenAI TTS 模型、默认音色、音色池和 OpenAI 协议语音路径

View File

@@ -23,7 +23,7 @@ IMAGE_API_KEY=
IMAGE_MODEL=gpt-image-2
GPT_IMAGE_MODEL=gpt-image-2
SUBJECT_ASSET_IMAGE_MODEL=gpt-image-2
SUBJECT_ASSET_IMAGE_MODELS=gpt-image-2,gpt-image-1.5
SUBJECT_ASSET_IMAGE_MODELS=gpt-image-2
VIDEO_MODEL=seedance
VIDEO_MODEL_SEEDANCE=seedance-2-fast
VIDEO_MODEL_KLING=kling-omni

View File

@@ -2851,7 +2851,7 @@ def health() -> dict:
"vision": VISION_MODEL,
"image": IMAGE_MODEL,
"image_base_url": IMAGE_BASE_URL or LLM_BASE_URL or "openai-default",
"image_fallbacks": list(dict.fromkeys([IMAGE_MODEL, GPT_IMAGE_MODEL, "gpt-image-1.5"])),
"image_fallbacks": [GPT_IMAGE_MODEL],
"subject_image": SUBJECT_ASSET_IMAGE_MODEL,
"subject_image_fallbacks": SUBJECT_ASSET_IMAGE_MODELS,
"voice_provider": VOICE_PROVIDER,
@@ -3109,7 +3109,7 @@ class GenerateReq(BaseModel):
prompt: str
extra_prompt: str = "" # ✓ 需要的元素(正向)
negative_prompt: str = "" # ✗ 不需要的元素(负向)
model: str = "" # 留空用 IMAGE_MODEL 默认
model: str = "" # 兼容旧前端字段;服务端强制使用 gpt-image-2
mode: str = "edit" # "edit" 带参考图,"text" 纯文字
from_selected: bool = False # True 时优先用 frame.selected 的生成图作 reference迭代否则原关键帧
@@ -3148,7 +3148,7 @@ def generate_image(job_id: str, idx: int, req: GenerateReq) -> Job:
if not IMAGE_API_KEY:
raise HTTPException(503, "IMAGE_API_KEY 或 LLM_API_KEY 未配置")
model = req.model or IMAGE_MODEL
model = GPT_IMAGE_MODEL
gen_id = uuid.uuid4().hex[:12]
import base64 as b64lib
@@ -3392,7 +3392,7 @@ def _region_to_phrase(r: dict) -> str:
@app.post("/jobs/{job_id}/frames/{idx}/cleanup", response_model=Job)
def cleanup_frame(job_id: str, idx: int, req: CleanupReq | None = None) -> Job:
"""nano-banana image edit 清洗关键帧:去水印 / @用户名 / 字幕 / 平台 logo。
"""gpt-image-2 image edit 清洗关键帧:去水印 / @用户名 / 字幕 / 平台 logo。
输出干净版到 jobs/<id>/cleaned/<idx>.jpg写回 frame.cleaned_url。
可选 region: 限定只清洗框内区域。"""
import time as _time
@@ -3431,12 +3431,7 @@ def cleanup_frame(job_id: str, idx: int, req: CleanupReq | None = None) -> Job:
"hashtags, usernames, or platform logos. Keep the composition and style."
)
# 模型轮换nano-banana-pro 失败时换 flash 系列
models = [
IMAGE_MODEL, # gemini-3-pro-image-preview (nano-banana-pro)
"gemini-3.1-flash-image-preview",
"gemini-2.5-flash-image",
]
models = [GPT_IMAGE_MODEL]
try:
img_bytes, _mode = _image_edit_call(
frame_path, prompt, models=models, fallback_text=False, max_attempts=3,
@@ -3812,7 +3807,7 @@ def generate_scene_asset(job_id: str, idx: int, req: GenerateSceneAssetReq) -> J
+ "Do not create a plain background plate. Do not remove the character. Do not include SKG product unless the user prompt explicitly asks for it. "
+ "The output should be ready as a first/last frame for Seedance video generation, with stable composition, believable perspective, clear subject, no text, no watermark, no gore, no medical surgery imagery."
)
models = [IMAGE_MODEL, "gemini-3.1-flash-image-preview", "gemini-2.5-flash-image"]
models = [GPT_IMAGE_MODEL]
try:
if req.asset_role == "scene":
img_bytes, _mode = _image_edit_call(model_src, prompt, models=models, fallback_text=False, max_attempts=3, max_side=1280)
@@ -3862,7 +3857,7 @@ def generate_scene_asset(job_id: str, idx: int, req: GenerateSceneAssetReq) -> J
@app.post("/jobs/{job_id}/frames/{idx}/elements/{element_id}/cutout", response_model=Job)
def cutout_element(job_id: str, idx: int, element_id: str) -> Job:
"""AI 提取元素 · 每次累积一张新图:
nano-banana 模型生成**完整、清晰**的元素图(即使原图只露出部分也补全)。
gpt-image-2 生成**完整、清晰**的元素图(即使原图只露出部分也补全)。
region 元素:先把 region + 30% padding 区域裁出作为 focus再发给模型聚焦补全。"""
from PIL import Image as _PILImage
import io as _io
@@ -3925,7 +3920,7 @@ def cutout_element(job_id: str, idx: int, element_id: str) -> Job:
"Preserve the element's original color palette, style, lighting character, and proportions. "
"Output must be a clean, high-quality asset image suitable for downstream composition."
)
models = [IMAGE_MODEL, "gemini-2.5-flash-image"]
models = [GPT_IMAGE_MODEL]
img_bytes: bytes
try:
try:
@@ -4013,7 +4008,7 @@ def generate_subject_assets(job_id: str, idx: int, element_id: str, req: Generat
"If user direction requests a gender, age, or style change, apply that one change uniformly to all views; never mix male/female, young/old, or multiple style identities inside the same six-view pack. "
"For transparent humanoids, keep the same transparent skin shell, skeleton proportions, visible spine/rib cage/pelvis/limb bones, and non-horror wellness character style in every view. "
)
models = SUBJECT_ASSET_IMAGE_MODELS
models = [GPT_IMAGE_MODEL]
generated: list[SubjectAsset] = []
try:
for view, view_label in _subject_view_labels(req.subject_kind, req.views):
@@ -5019,7 +5014,7 @@ def generate_product_angle_asset(job_id: str, req: GenerateProductAngleAssetReq)
"If the target view is not fully visible in the source, infer the missing surfaces conservatively from the same product design without inventing a new model. "
+ (f"Additional operator note: {note}. " if note else "")
)
models = [IMAGE_MODEL, "gemini-3.1-flash-image-preview", "gemini-2.5-flash-image"]
models = [GPT_IMAGE_MODEL]
try:
img_bytes, _mode = _image_edit_call(source_path, prompt, models=models, fallback_text=False, max_attempts=3, max_side=1280)
except RuntimeError as e:

View File

@@ -28,7 +28,7 @@ IMAGE_API_KEY=
IMAGE_MODEL=gpt-image-2
GPT_IMAGE_MODEL=gpt-image-2
SUBJECT_ASSET_IMAGE_MODEL=gpt-image-2
SUBJECT_ASSET_IMAGE_MODELS=gpt-image-2,gpt-image-1.5
SUBJECT_ASSET_IMAGE_MODELS=gpt-image-2
# Audio rewrite and Azure OpenAI TTS
AUDIO_REWRITE_MODEL=gemini-2.5-pro

View File

@@ -589,7 +589,7 @@
<tr><td><code>web/next.config.mjs</code></td><td>Next.js 构建配置:静态导出、图片不走优化、禁用开发环境左下角 Next Dev Indicator并移除 Next 16 已不支持的 <code>eslint</code> 顶层配置,避免本地 dev 出现配置 Issue 提示。</td></tr>
<tr><td><code>web/app/globals.css</code></td><td>全局主题变量、登录页视觉样式、ReactFlow 样式引用,以及本地开发态 <code>nextjs-portal</code> 遮挡隐藏规则。</td></tr>
<tr><td><code>web/app/page.tsx</code></td><td>产品工作台主状态jobs、activeJobId、生成任务状态主渲染为全屏素材输入列 + 信息流广告复刻工作表;“开始”编排状态只负责在下载完成后自动触发 <code>triggerTranscribe</code>不再默认触发抽帧、Vision 扫描或分镜初稿保存;底部吸附音频条不再从主界面渲染。</td></tr>
<tr><td><code>web/components/ad-recreation-board.tsx</code></td><td>信息流广告复刻工作表:左侧素材输入只负责链接/上传和任务切换,不再重复放横版原视频预览;右侧展示视频下载状态、默认折叠的文案依据,以及源视频工作区。音频解析结果改成默认折叠的辅助信息,展开后同一行看讲话人/节奏/背景音;主工作区左侧是按 9:16 显示的竖版原视频播放器,播放器内覆盖“当前点抽帧”,按当前播放秒数手动补参考帧;右侧上方是音频波形 / 切点参考,下方是逐句时间轴;下一行铺开“关键帧 / 相似主体”。音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点,顶部同时显示当前播放秒数、总时长和鼠标指针停点秒数。视频播放时通过 <code>requestAnimationFrame</code> 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。关键帧区的主入口是“自动抽帧 12 张”,一键按动作峰值目标重新抽取 12 张源视频参考帧,优先抓手势、表情变化、节奏点和镜头变化,缩略图按竖版完整比例显示不裁切并用更多列紧凑铺开,鼠标停留会通过固定浮层放大展示完整帧。“生成 6 视图”放在相似主体白底视图区,不和抽参考按钮平齐;如果用户没有勾选帧,默认把全部关键帧作为主体参考,勾选后只传已选帧;生成区可在“透明骨架 / 普通真人”之间切换,并可填写统一主体方向,例如年轻女性、更运动、更高级。前端调用 <code>generateSubjectAssets</code> 时按主体类型传 <code>subject_style=transparent_human</code><code>source_actor</code>,均使用 <code>reconstruction_mode=similar</code>;后端会把这些帧视为同一个主体的证据,并锁定同一性别表现、年龄段、体型、材质、风格和视觉身份,避免六视图出现男女性别、老少年龄或样式混杂。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后按“套在脖子上的 U 形肩颈按摩仪”进行同一产品批量识别,左/右按佩戴者身体左右、上/下按佩戴方向,额外标注内外侧、开口方向、局部结构点、背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。脚本区在分镜行上方提供“作者想法”和“整片改写”,每行新口播文案可直接编辑并可单段 AI 改写,分镜时间和原内容列压缩为窄摘要列,把横向空间留给新口播、画面规划和视频候选;生成本条视频时使用当前编辑后的新口播文案。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入和 6 个候选视频槽;候选视频槽在宽屏下一排显示 6 个竖版预览,避免前面空旷、后面拥挤。单条生成会从全局选中关键帧或 12 张关键帧中取最贴近本句时间点的参考帧。单条生成会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图,不会把全部产品图提交给生视频模型,然后把产品坐标系、视角标注、方向、结构点和风险写入 Seedance 提示。<code>ModelTrace</code> 会在音频解析、产品识别/补图、相似主体 6 视图、脚本改写和单条生视频入口旁直接展示模型名;其中相似主体 6 视图显示主体专<code>gpt-image-2</code> / <code>gpt-image-1.5</code> 链路;点击后用固定浮层展示模型链路、输入输出和回退逻辑。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。</td></tr>
<tr><td><code>web/components/ad-recreation-board.tsx</code></td><td>信息流广告复刻工作表:左侧素材输入只负责链接/上传和任务切换,不再重复放横版原视频预览;右侧展示视频下载状态、默认折叠的文案依据,以及源视频工作区。音频解析结果改成默认折叠的辅助信息,展开后同一行看讲话人/节奏/背景音;主工作区左侧是按 9:16 显示的竖版原视频播放器,播放器内覆盖“当前点抽帧”,按当前播放秒数手动补参考帧;右侧上方是音频波形 / 切点参考,下方是逐句时间轴;下一行铺开“关键帧 / 相似主体”。音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点,顶部同时显示当前播放秒数、总时长和鼠标指针停点秒数。视频播放时通过 <code>requestAnimationFrame</code> 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。关键帧区的主入口是“自动抽帧 12 张”,一键按动作峰值目标重新抽取 12 张源视频参考帧,优先抓手势、表情变化、节奏点和镜头变化,缩略图按竖版完整比例显示不裁切并用更多列紧凑铺开,鼠标停留会通过固定浮层放大展示完整帧。“生成 6 视图”放在相似主体白底视图区,不和抽参考按钮平齐;如果用户没有勾选帧,默认把全部关键帧作为主体参考,勾选后只传已选帧;生成区可在“透明骨架 / 普通真人”之间切换,并可填写统一主体方向,例如年轻女性、更运动、更高级。前端调用 <code>generateSubjectAssets</code> 时按主体类型传 <code>subject_style=transparent_human</code><code>source_actor</code>,均使用 <code>reconstruction_mode=similar</code>;后端会把这些帧视为同一个主体的证据,并锁定同一性别表现、年龄段、体型、材质、风格和视觉身份,避免六视图出现男女性别、老少年龄或样式混杂。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后按“套在脖子上的 U 形肩颈按摩仪”进行同一产品批量识别,左/右按佩戴者身体左右、上/下按佩戴方向,额外标注内外侧、开口方向、局部结构点、背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。脚本区在分镜行上方提供“作者想法”和“整片改写”,每行新口播文案可直接编辑并可单段 AI 改写,分镜时间和原内容列压缩为窄摘要列,把横向空间留给新口播、画面规划和视频候选;生成本条视频时使用当前编辑后的新口播文案。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入和 6 个候选视频槽;候选视频槽在宽屏下一排显示 6 个竖版预览,避免前面空旷、后面拥挤。单条生成会从全局选中关键帧或 12 张关键帧中取最贴近本句时间点的参考帧。单条生成会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图,不会把全部产品图提交给生视频模型,然后把产品坐标系、视角标注、方向、结构点和风险写入 Seedance 提示。<code>ModelTrace</code> 会在音频解析、产品识别/补图、相似主体 6 视图、脚本改写和单条生视频入口旁直接展示模型名;所有生图入口都显示并使<code>gpt-image-2</code>,没有其他图片模型 fallback;点击后用固定浮层展示模型链路、输入输出和回退逻辑。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。</td></tr>
<tr><td><code>web/app/login/page.tsx</code></td><td>生产登录页:访问账号/访问密钥表单、保持登录、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加一个组合登录框,桌面端左侧是动态角色,右侧是图标化登录表单;面板左上角展示官网 SKG 字标和中文“营销内容工作台”系统标识。</td></tr>
<tr><td><code>web/app/login/layout.tsx</code></td><td>登录路由专属 layout覆盖全站默认网页标题和描述为空避免 <code>/login</code> 继承工作台 metadata 后在页面源码里继续出现登录界面文字以外的文案。</td></tr>
<tr><td><code>web/components/login/oasis-canvas.tsx</code></td><td>登录页全屏动态视觉层:用 iframe 直接承载下载包 <code>web/public/oasis-source/index.html</code> 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标同时用原生事件和 <code>postMessage</code> 转发给 iframe避免登录面板或输入框遮挡时草地失去鼠标响应。</td></tr>
@@ -888,7 +888,7 @@ ProductRefStateItem {
<tr><td>应用清洗</td><td><code>POST /cleanup/apply</code></td><td><code>applyCleanedFrame</code></td><td>物理覆盖 frames/{idx}.jpg并备份原图。</td></tr>
<tr><td>元素增改删</td><td><code>POST/PATCH/DELETE /elements</code></td><td><code>addElement/updateElement/deleteElement</code></td><td>让用户修正 Vision 错误,避免候选结果锁死。</td></tr>
<tr><td>元素提取</td><td><code>POST /elements/{element_id}/cutout</code></td><td><code>cutoutElement</code></td><td>调用图像模型生成独立白底素材图,每次累积一张 cutout。</td></tr>
<tr><td>主体资产包</td><td><code>POST /elements/{element_id}/subject-assets</code></td><td><code>generateSubjectAssets</code></td><td>根据参考帧重新绘制一个统一主体资产包;前端默认把全部关键帧作为 <code>source_frame_indices</code>,如果用户手动选择了关键帧则只传已选帧,后端拼参考板。当前源视频工作区支持 <code>subject_style=transparent_human</code><code>subject_style=source_actor</code> 两种相似主体:透明骨架人会保持透明/半透明皮肤包裹可见白色骨架,普通真人会保持正常广告演员方向。两种模式都使用 <code>reconstruction_mode=similar</code>,最多读取 12 张参考帧,生成 6 张白底视图;后端使用 <code>SUBJECT_ASSET_IMAGE_MODELS</code>,默认 <code>gpt-image-2</code> / <code>gpt-image-1.5</code>,不再沿用通用 Gemini 图片模型;后端会加身份锁定约束,统一性别表现、年龄段、体型、材质、风格和视觉身份,避免六视图混成不同人物。前端白底视图缩略图和关键帧一样,鼠标停留会用顶层浮层放大预览,点击仍打开原图;后端每个 <code>view</code> 单独调用一次生图并明确禁止六视图拼图、contact sheet、多主体、多面板、标签或对比排版保证一个视角一张照片。</td></tr>
<tr><td>主体资产包</td><td><code>POST /elements/{element_id}/subject-assets</code></td><td><code>generateSubjectAssets</code></td><td>根据参考帧重新绘制一个统一主体资产包;前端默认把全部关键帧作为 <code>source_frame_indices</code>,如果用户手动选择了关键帧则只传已选帧,后端拼参考板。当前源视频工作区支持 <code>subject_style=transparent_human</code><code>subject_style=source_actor</code> 两种相似主体:透明骨架人会保持透明/半透明皮肤包裹可见白色骨架,普通真人会保持正常广告演员方向。两种模式都使用 <code>reconstruction_mode=similar</code>,最多读取 12 张参考帧,生成 6 张白底视图;后端强制使用 <code>gpt-image-2</code>,不再接受前端或环境变量切到其他图片模型,也不做 <code>gpt-image-1.5</code> / Gemini fallback;后端会加身份锁定约束,统一性别表现、年龄段、体型、材质、风格和视觉身份,避免六视图混成不同人物。前端白底视图缩略图和关键帧一样,鼠标停留会用顶层浮层放大预览,点击仍打开原图;后端每个 <code>view</code> 单独调用一次生图并明确禁止六视图拼图、contact sheet、多主体、多面板、标签或对比排版保证一个视角一张照片。</td></tr>
<tr><td>首尾帧资产</td><td><code>POST /frames/{idx}/scene-asset</code></td><td><code>generateSceneAsset</code></td><td>同一接口兼容旧场景图和新首尾帧;新流程传 <code>asset_role=first_frame/last_frame</code>,后端走文字生图,参考帧只用于理解透明骨架人形象、比例、机位和光线,生成结果仍保存在 <code>scene_assets</code> 并自动填入产品融合镜头。</td></tr>
<tr><td>产品图库</td><td><code>GET /product-library/skg</code></td><td><code>listProductLibrary</code></td><td>读取内置 SKG 白底图库 manifest返回产品标题、品类、尺寸、白底评分和预览图 URL。</td></tr>
<tr><td>产品图入库到 job</td><td><code>POST /jobs/{id}/assets</code><code>POST /jobs/{id}/assets/product-library</code></td><td><code>uploadStoryboardAsset</code><code>copyProductLibraryAsset</code></td><td>上传产品图或把内置产品图库条目复制为当前 job 的普通 asset。后端统一生成最长边 1600px、JPEG 92 的 AI 工作副本,透明底铺白,过大/过小图片会在 <code>ImageRef.asset_meta</code> 里返回转换动作和风险;黑底/白底背景本身不强行转换。注意该接口只写图片文件,产品素材池列表另由 <code>PUT /jobs/{id}/product-refs</code> 持久化。</td></tr>
@@ -1004,6 +1004,19 @@ ProductRefStateItem {
<h2>变更记录</h2>
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
<div class="changelog">
<article class="change">
<header>
<h3>2026-05-18 · 生图模型统一锁定为 gpt-image-2</h3>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
<span class="tag amber">Config</span>
</header>
<div class="body">
<p><strong>问题:</strong>部分生图路径仍保留 <code>gpt-image-1.5</code> 或 Gemini 图片模型 fallback旧分镜生图区也还能选择 Gemini 模型;这会导致同一流程里不同图片入口走不同模型。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>GPT_IMAGE_MODEL</code><code>IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODELS</code> 全部锁定为 <code>gpt-image-2</code><code>_image_edit_call</code><code>_image_text_call</code><code>/frames/{idx}/generate</code> 即使收到旧前端 model 字段或旧 models 列表,也只使用 <code>gpt-image-2</code>。旧 Dashboard 生图选择器改成只展示 <code>gpt-image-2</code>,模型链路标注不再显示图片模型 fallback。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/components/ad-recreation-board.tsx</code><code>web/components/dashboard.tsx</code><code>api/.env.example</code><code>deploy/.env.production.example</code><code>RULES.md</code><code>docs/source-analysis.html</code>。后续凡是“生图”,包括清洗、元素提取、主体 6 视图、产品补角度、首尾帧/场景图和旧分镜生图,都只能走 <code>gpt-image-2</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 主体 6 视图改为单图预览和单视角生成</h3>
@@ -1039,7 +1052,7 @@ ProductRefStateItem {
</header>
<div class="body">
<p><strong>问题:</strong>“相似透明骨架主体”实际已经在后端生成并写入 job但前端的相似主体识别只匹配“相似主体 / similar subject”等窄名称无法命中“相似透明骨架主体 / similar transparent skeleton humanoid subject”所以界面仍显示 <code>0/6</code>,重复点击还会新增重复元素。同时 6 视图模型链路仍跟随通用 <code>IMAGE_MODEL</code>,默认显示 Gemini 图片模型,不符合主体资产必须走 GPT 图片模型的要求。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>GPT_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODELS</code><code>generateSubjectAssets</code> 默认使用 <code>gpt-image-2</code> / <code>gpt-image-1.5</code><code>GET /health</code> 返回 <code>subject_image</code><code>subject_image_fallbacks</code><code>web/components/ad-recreation-board.tsx</code> 扩大相似主体元素匹配规则,并在优先帧找不到时回退到全局关键帧里查找已生成主体资产,避免图片被当前选择集隐藏;模型标注改为显示主体专用 GPT 图片链路。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>GPT_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODELS</code>当时把 <code>generateSubjectAssets</code> 切到 GPT 图片链路;后续已进一步收敛为所有生图入口都只使用 <code>gpt-image-2</code><code>GET /health</code> 返回 <code>subject_image</code><code>subject_image_fallbacks</code><code>web/components/ad-recreation-board.tsx</code> 扩大相似主体元素匹配规则,并在优先帧找不到时回退到全局关键帧里查找已生成主体资产,避免图片被当前选择集隐藏;模型标注改为显示主体专用 GPT 图片链路。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续主体 6 视图不应复用通用补图模型链,必须走主体资产专用 GPT 图片模型配置。</p>
</div>
</article>

View File

@@ -337,11 +337,11 @@ function modelList(values: Array<string | undefined>) {
}
function imageModelChain(models?: RuntimeModels) {
return modelList(models?.image_fallbacks?.length ? models.image_fallbacks : [models?.image, "gemini-3.1-flash-image-preview", "gemini-2.5-flash-image"])
return modelList([models?.image || "gpt-image-2"])
}
function subjectImageModelChain(models?: RuntimeModels) {
return modelList(models?.subject_image_fallbacks?.length ? models.subject_image_fallbacks : [models?.subject_image, "gpt-image-2", "gpt-image-1.5"])
return modelList([models?.subject_image || "gpt-image-2"])
}
function resolveVideoModelLabel(models: RuntimeModels | undefined, model: string) {

View File

@@ -716,7 +716,7 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
}) {
const [extra, setExtra] = useState("")
const [negative, setNegative] = useState("水印, @用户名, TikTok logo, 平台文字, 浮水印")
const [model, setModel] = useState("gemini-3-pro-image-preview")
const model = "gpt-image-2"
const [mode, setMode] = useState<"edit" | "text">("edit")
const [generating, setGenerating] = useState(false)
const basePrompt = frame.description?.suggested_prompt ?? "(尚未识别 · 点关键帧打开 lightbox 先识别)"
@@ -857,15 +857,9 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
{/* 模型 + 模式 + 生成 */}
<div className="mt-2 flex gap-1.5 items-center">
<select
value={model}
onChange={(e) => setModel(e.target.value)}
className="flex-1 text-[10.5px] px-2 py-1.5 rounded-md bg-black/40 border border-white/15 text-[var(--text-strong)]"
>
<option value="gemini-3-pro-image-preview">nano-banana-pro</option>
<option value="gemini-3.1-flash-image-preview">gemini-3.1-flash-image</option>
<option value="gemini-2.5-flash-image">gemini-2.5-flash-image</option>
</select>
<div className="flex-1 rounded-md border border-white/15 bg-black/40 px-2 py-1.5 text-[10.5px] font-semibold text-[var(--text-strong)]">
gpt-image-2
</div>
<select
value={mode}
onChange={(e) => setMode(e.target.value as "edit" | "text")}