From 6f0b54cc57650242d0ffb3f9bd8a64789163fa26 Mon Sep 17 00:00:00 2001 From: kang Date: Mon, 18 May 2026 00:29:11 +0800 Subject: [PATCH] auto-save 2026-05-18 00:29 (~8) --- .memory/worklog.json | 26 +++++++++++++------------- .project.json | 14 +++++++++++++- RULES.md | 9 +++++++-- api/.env.example | 18 ++++++++++++++++-- api/main.py | 15 +++++++++++++-- deploy/.env.production.example | 18 ++++++++++++++++-- docs/source-analysis.html | 27 ++++++++++++++++++++------- web/lib/api.ts | 7 +++++++ 8 files changed, 105 insertions(+), 29 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index b1ded1b..5a5abbd 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,18 +1,5 @@ { "entries": [ - { - "files_changed": 1, - "hash": "45e7401", - "message": "auto-save 2026-05-15 15:32 (~1)", - "ts": "2026-05-15T15:32:22+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-15 15:32 (~1)", - "ts": "2026-05-15T07:34:47Z", - "type": "session-heartbeat" - }, { "files_changed": 1, "hash": "26f5d2e", @@ -3258,6 +3245,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:fix: show generated subject views", "files_changed": 1 + }, + { + "ts": "2026-05-18T00:23:43+08:00", + "type": "commit", + "message": "auto-save 2026-05-18 00:23 (~2)", + "hash": "a7b1315", + "files_changed": 2 + }, + { + "ts": "2026-05-17T16:28:31Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 8 项未提交变更 · 最近提交:auto-save 2026-05-18 00:23 (~2)", + "files_changed": 8 } ] } diff --git a/.project.json b/.project.json index 4c7ab81..a7867ba 100644 --- a/.project.json +++ b/.project.json @@ -14,6 +14,18 @@ "storage": "api/.env", "type": "api_key" }, + { + "description": "OpenAI-compatible GPT 图片模型 Key;未单独配置 IMAGE_API_KEY 时复用 LLM_API_KEY,本地开发只放 api/.env,不入库", + "name": "IMAGE_API_KEY", + "storage": "api/.env / deploy/.env.production", + "type": "api_key" + }, + { + "description": "Azure OpenAI 协议语音/配音 Key;未单独配置 AZURE_OPENAI_API_KEY 时复用 LLM_API_KEY,本地开发只放 api/.env,不入库", + "name": "AZURE_OPENAI_API_KEY", + "storage": "api/.env / deploy/.env.production", + "type": "api_key" + }, { "description": "SKG 豆包 / Seedance 视频生成 API Key,生产只放服务器 deploy/.env.production 的 VIDEO_API_KEY,本地开发放 api/.env,不入库", "name": "VIDEO_API_KEY", @@ -52,7 +64,7 @@ "username": "skg" }, "stack": [ - "Next.js + Python(yt-dlp/ffmpeg) + OpenAI-compatible LLM + MiniMax T2A + nano-banana-pro/GPT Image + Seedance/Kling/Veo3" + "Next.js + Python(yt-dlp/ffmpeg) + OpenAI-compatible LLM + GPT Image + Azure OpenAI TTS + Seedance" ], "status": "active", "urls": [ diff --git a/RULES.md b/RULES.md index 97c8666..6c6b79f 100644 --- a/RULES.md +++ b/RULES.md @@ -50,7 +50,7 @@ - 能联网和鉴权时必须 `git push origin main`;如果不能推送,最终回复必须写清楚当前分支、领先/落后数量、最新未推送 commit 和失败原因 ## 环境变量 -- `LLM_BASE_URL` / `LLM_API_KEY`:OpenAI 兼容网关,用于 ASR、翻译、文案改写、图像等模型调用 +- `LLM_BASE_URL` / `LLM_API_KEY`:OpenAI 兼容网关,用于 ASR、翻译、文案改写、音频分析等文本/音频理解模型调用 - `ASR_MODEL`:OpenAI Audio Transcriptions 音频转写模型,默认 `whisper-1` - `ASR_FALLBACK_MODEL`:远端 ASR 和本机 ASR 都不可用时才尝试的多模态兜底,默认 `gemini-2.5-flash`;如果模型不能真实听到音频或返回疑似逐秒假字幕,后端必须拒绝写入时间轴 - `ASR_TIMEOUT_SECONDS`:远端 ASR / 音频分析单次请求超时,默认 45 秒,避免第一步长时间停在转录中 @@ -59,8 +59,13 @@ - `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 图像模型 +- `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 协议语音路径 - `MINIMAX_API_KEY`:MiniMax T2A 配音 Key,只能放本地 `api/.env`,不能入库;当前第一步暂不默认调用 -- `MINIMAX_TTS_BASE_URL` / `MINIMAX_TTS_MODEL` / `MINIMAX_TTS_VOICE_ID`:MiniMax 配音端点、模型和兜底音色配置,供后续新配音阶段使用 +- `MINIMAX_TTS_BASE_URL` / `MINIMAX_TTS_MODEL` / `MINIMAX_TTS_VOICE_ID`:MiniMax 旧配音端点、模型和兜底音色配置,仅作为保留兼容;当前不作为默认语音通道 - `MINIMAX_TTS_VOICE_POOL`:MiniMax 英文随机音色池;当前默认男声 `English_magnetic_voiced_man`、女声 `English_Upbeat_Woman`、成熟声 `English_MaturePartner`,供后续新配音阶段使用 - `POE_API_KEY` / `VIDEO_API_KEY`:视频生成通道 Key,只能放本地环境变量 - `WEB_AUTH_USERNAME` / `WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET`:生产网页登录和会话签名配置;密码和 session secret 只放服务器环境变量,不入库 diff --git a/api/.env.example b/api/.env.example index c16f8de..f8d0133 100644 --- a/api/.env.example +++ b/api/.env.example @@ -18,15 +18,29 @@ LOCAL_ASR_MODEL=mlx-community/whisper-tiny LOCAL_ASR_TIMEOUT_SECONDS=180 TRANSLATE_MODEL=gemini-2.5-flash REWRITE_MODEL=gemini-2.5-pro -IMAGE_MODEL=gemini-3-pro-image-preview +IMAGE_BASE_URL=https://ai.skg.com/ezlink/v1 +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 VIDEO_MODEL=seedance VIDEO_MODEL_SEEDANCE=seedance-2-fast VIDEO_MODEL_KLING=kling-omni VIDEO_MODEL_VEO3=veo-3.1-fast -# 音频文案改写 + MiniMax 配音 +# 音频文案改写 + Azure OpenAI 配音 AUDIO_REWRITE_MODEL=gemini-2.5-pro AUDIO_PRODUCT_BRIEF="SKG 智能按摩产品,主打日常肩颈、腰背、眼部、膝盖或足部放松;广告表达要高级、干净、可信,不做医疗疗效承诺。" +VOICE_PROVIDER=azure_openai +AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure +AZURE_OPENAI_API_KEY= +AZURE_TTS_MODEL=gpt-4o-mini-tts +AZURE_TTS_VOICE_ID=alloy +AZURE_TTS_VOICE_POOL=alloy,verse,shimmer +AZURE_TTS_PATH=/audio/speech + +# MiniMax 旧配音通道,保留兼容;默认不走 MINIMAX_API_KEY= MINIMAX_TTS_BASE_URL=https://api.minimax.io MINIMAX_TTS_MODEL=speech-2.8-turbo diff --git a/api/main.py b/api/main.py index e285d68..8bf3a12 100644 --- a/api/main.py +++ b/api/main.py @@ -624,6 +624,17 @@ def video_uses_ark() -> bool: return "ark.cn-beijing.volces.com" in base or "ai.skg.com/doubao" in base +def video_provider_name() -> str: + base = video_api_base() + if video_uses_poe(): + return "poe" + if "ai.skg.com/doubao" in base: + return "doubao" + if "ark.cn-beijing.volces.com" in base: + return "ark" + return "custom" + + def video_api_base() -> str: if VIDEO_API_BASE_URL: return VIDEO_API_BASE_URL.rstrip("/") @@ -2853,7 +2864,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": [IMAGE_MODEL, GPT_IMAGE_MODEL, "gpt-image-1.5"], + "image_fallbacks": list(dict.fromkeys([IMAGE_MODEL, GPT_IMAGE_MODEL, "gpt-image-1.5"])), "subject_image": SUBJECT_ASSET_IMAGE_MODEL, "subject_image_fallbacks": SUBJECT_ASSET_IMAGE_MODELS, "voice_provider": VOICE_PROVIDER, @@ -2868,7 +2879,7 @@ def health() -> dict: "minimax_configured": bool(MINIMAX_API_KEY), "video": VIDEO_MODEL, "video_aliases": VIDEO_MODEL_ALIASES, - "video_provider": "poe" if video_uses_poe() else ("ark" if video_uses_ark() else "custom"), + "video_provider": video_provider_name(), "video_base_url": video_api_base(), "video_configured": bool(video_api_key()), "video_create_paths": VIDEO_CREATE_PATHS, diff --git a/deploy/.env.production.example b/deploy/.env.production.example index 1a63f48..fb07f2b 100644 --- a/deploy/.env.production.example +++ b/deploy/.env.production.example @@ -23,11 +23,25 @@ ASR_MODEL=whisper-1 ASR_FALLBACK_MODEL=gemini-2.5-flash TRANSLATE_MODEL=gemini-2.5-flash REWRITE_MODEL=gemini-2.5-pro -IMAGE_MODEL=gemini-3-pro-image-preview +IMAGE_BASE_URL=https://ai.skg.com/ezlink/v1 +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 -# Audio rewrite and MiniMax TTS +# Audio rewrite and Azure OpenAI TTS AUDIO_REWRITE_MODEL=gemini-2.5-pro AUDIO_PRODUCT_BRIEF="SKG smart massage products for daily neck, shoulder, back, eye, knee, and foot relaxation. Keep claims premium, clean, credible, and non-medical." +VOICE_PROVIDER=azure_openai +AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure +AZURE_OPENAI_API_KEY= +AZURE_TTS_MODEL=gpt-4o-mini-tts +AZURE_TTS_VOICE_ID=alloy +AZURE_TTS_VOICE_POOL=alloy,verse,shimmer +AZURE_TTS_PATH=/audio/speech + +# Legacy MiniMax TTS fallback; not the default voice provider. MINIMAX_API_KEY= MINIMAX_TTS_BASE_URL=https://api.minimax.io MINIMAX_TTS_MODEL=speech-2.8-turbo diff --git a/docs/source-analysis.html b/docs/source-analysis.html index 9139e22..a742148 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -743,7 +743,7 @@ api/main.py background_audio_profile, product_brief, rewrite_model, - voice_provider: minimax, + voice_provider: azure_openai | minimax, voice_model, voice_id, voice_url, @@ -870,16 +870,16 @@ ProductRefStateItem { 网页登录POST /auth/loginGET /auth/checkPOST /auth/logoutweb/app/login/page.tsx、Nginx auth_request登录页提交账号密码到 /api/auth/login,后端设置 HttpOnly 会话 Cookie;生产 Nginx 对工作台和 /api//auth/check 做统一校验,未登录页面跳 /login/,API 返回 JSON 401。 - 运行配置 / 模型标注GET /healthgetRuntimeHealthModelTrace返回 models:ASR、本机 ASR、ASR fallback、翻译、改写、Vision、通用图像模型、主体 6 视图 GPT 图像模型、MiniMax TTS、视频别名和视频服务商。前端所有当前主路径里会调用模型的按钮旁显示模型名,点击弹出小窗口查看模型链路和输入输出逻辑;不返回 API Key 或敏感凭证。 + 运行配置 / 模型标注GET /healthgetRuntimeHealthModelTrace返回 models:ASR、本机 ASR、ASR fallback、翻译、改写、Vision、GPT 图像模型、主体 6 视图 GPT 图像模型、Azure OpenAI TTS、视频别名和 Seedance 服务商。前端所有当前主路径里会调用模型的按钮旁显示模型名,点击弹出小窗口查看模型链路和输入输出逻辑;不返回 API Key 或敏感凭证。 历史列表GET /jobslistJobs所有 job 精简列表(id/url/status/thumbnail/mtime…),按 state.json mtime 倒序。前端 URL 无 ?job= 时拉它回填全部历史;带 limit 可截断。 创建任务POST /jobscreateJob提交 TK 链接,后台开始下载;前端“开始”队列会在 downloaded 后自动触发音频解析。 上传视频POST /jobs/uploaduploadJob保存 source.mp4,然后同样进入下载完成状态;当前上传后也加入第一步队列,下载完成后自动解析音频。 删除输入视频DELETE /jobs/{id}deleteJob从任务队列、URL 和磁盘 jobs/<id> 目录移除整个 job,包括源视频、关键帧、元素提取图和生成视频。 解析视频POST /jobs/{id}/analyze?frames=&target=&mode=&quality=analyzeJob后续阶段保留的抽帧能力。默认 frames=12target 支持透明骨架人、综合、清晰主体、转场变化、表情瞬间、动作峰值。当前第一步主流程不自动调用该接口;原版视频旁的“抽参考 12 帧”会显式用 target=motionquality=accuratemode=replace 重新生成全局动作/节奏参考帧池。 - 音频文案轨POST /jobs/{id}/transcribetriggerTranscribe若尚未拆轨,先从 source.mp4 提取 audio.wav 并回填 source_audio_url;随后用 ASR 提取原始文案,翻译成中文,写入 audio_script.source_textsource_zh 和逐句 transcript。远端 ASR_MODEL 失败后先走本机 LOCAL_ASR_BIN/LOCAL_ASR_MODEL(默认 mlx_whisper),再尝试 ASR_FALLBACK_MODEL。后端会拒绝重复文本、逐秒假字幕或覆盖率过低的结果,不再把不可听的多模态输出写进时间轴。再用 ASR_FALLBACK_MODEL 多模态音频分析讲话人、语速节奏、停顿、背景音乐/环境声/音效,写入 speaker_profilerhythm_profilebackground_audio_profile。当前第一步不默认生成 SKG 新口播和 MiniMax 配音。 + 音频文案轨POST /jobs/{id}/transcribetriggerTranscribe若尚未拆轨,先从 source.mp4 提取 audio.wav 并回填 source_audio_url;随后用 ASR 提取原始文案,翻译成中文,写入 audio_script.source_textsource_zh 和逐句 transcript。远端 ASR_MODEL 失败后先走本机 LOCAL_ASR_BIN/LOCAL_ASR_MODEL(默认 mlx_whisper),再尝试 ASR_FALLBACK_MODEL。后端会拒绝重复文本、逐秒假字幕或覆盖率过低的结果,不再把不可听的多模态输出写进时间轴。再用 ASR_FALLBACK_MODEL 多模态音频分析讲话人、语速节奏、停顿、背景音乐/环境声/音效,写入 speaker_profilerhythm_profilebackground_audio_profile。当前第一步不默认生成 SKG 新口播和 Azure OpenAI 配音。 分镜脚本改写POST /jobs/{id}/script/rewriterewriteStoryboardScript根据原参考文案、当前新口播、分镜角色、时间段和作者想法改写中文口播。mode=segment 只改一段;mode=all 一次改完整片,要求整片前后连贯。接口只返回 items[index,text],前端暂存在当前页面状态里,生成本条视频时写入 StoryboardScene.action。 原始音频文件GET /jobs/{id}/audio.wavsourceAudioUrl返回拆轨得到的 wav;当前主界面不再渲染底部吸附音频条,右侧复刻工作表会读取该文件生成参考图式横向响度波形,并和原视频、逐句时间轴联动;波形标题栏显示当前播放秒数、总时长和鼠标指针停点秒数。 - 改写配音文件GET /jobs/{id}/audio-script.mp3apiAssetUrl(job.audio_script.voice_url)后续新配音阶段保留的 MiniMax T2A 产物。当前第一步不默认生成该文件。 + 改写配音文件GET /jobs/{id}/audio-script.mp3apiAssetUrl(job.audio_script.voice_url)后续新配音阶段保留的 TTS 产物;默认走 VOICE_PROVIDER=azure_openai,通过 AZURE_OPENAI_BASE_URL 的 OpenAI 协议 /audio/speech 生成 mp3。当前第一步不默认生成该文件。 手动加帧POST /jobs/{id}/frames?t=addManualFrame按视频时间戳抽一帧,index 递增但 frames 按 timestamp 排序。当前主界面会把原版视频播放器的播放秒数传给 AudioIntakePanel 标题栏右侧的“当前点抽帧”。 删除关键帧DELETE /jobs/{id}/frames/{idx}deleteFrame删除单张关键帧并清掉对应选择态;当前主界面每张缩略图右下角提供删除入口,方便手动抽错后直接修正。 Vision 识别POST /frames/{idx}/describedescribeFrame写入 frame.description,后续可从 objects 加候选元素。 @@ -955,16 +955,16 @@ ProductRefStateItem {
  • 主体候选确认、改名、删除和主体资产包生成能力保留在底层旧面板和接口中,当前第一步主界面不主动展示。
  • 分镜工作台 4 图槽和改造说明自动保存。
  • 音频文案轨:点击开始或提取音频后提取原文案、中文翻译、讲话人、语速节奏、背景音乐/环境声/音效;结果集中在右侧工作表展示。
  • -
  • nano-banana-pro image-to-image 生图。
  • +
  • GPT Image 生图;当前 IMAGE_MODEL 和主体 6 视图链路默认使用 gpt-image-2
  • 阻塞 / 占位

    @@ -1004,6 +1004,19 @@ ProductRefStateItem {

    变更记录

    这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。

    +
    +
    +

    2026-05-18 · 模型链路拆分为 GPT 生图、Azure 语音和 Seedance 视频

    + API + Workflow + Config +
    +
    +

    问题:之前图片、文本、音频分析共用 LLM_BASE_URL,配音默认仍是 MiniMax,视频虽然已接豆包/Seedance,但模型标注没有把“生图 GPT / 语音 Azure / 视频 Seedance”三条高优先级链路清楚拆开。

    +

    改动:api/main.py 新增 IMAGE_BASE_URLIMAGE_API_KEYVOICE_PROVIDERAZURE_OPENAI_BASE_URLAZURE_OPENAI_API_KEYAZURE_TTS_MODEL 等配置;所有 /images/generations 调用改走图片专用 OpenAI-compatible client,默认 gpt-image-2;TTS 新增 Azure OpenAI 协议 /audio/speech 通道,默认 VOICE_PROVIDER=azure_openaiGET /health 回传图片、主体、语音和视频的实际模型与 base URL 供前端模型标注使用。

    +

    影响:api/main.pyweb/lib/api.tsRULES.md.project.jsondocs/source-analysis.html。真实 key 仍只写本地 api/.env / 生产环境变量,不能入库。

    +
    +

    2026-05-18 · 6 视图改用 GPT 图片模型并修复前端展示

    diff --git a/web/lib/api.ts b/web/lib/api.ts index f21a8eb..8de2cb3 100644 --- a/web/lib/api.ts +++ b/web/lib/api.ts @@ -143,9 +143,16 @@ export interface RuntimeModels { audio_rewrite?: string vision?: string image?: string + image_base_url?: string image_fallbacks?: string[] subject_image?: string subject_image_fallbacks?: string[] + voice_provider?: string + voice_base_url?: string + voice_tts?: string + voice_id?: string + voice_pool?: string[] + voice_configured?: boolean minimax_tts?: string minimax_voice?: string minimax_voice_pool?: string[]