fix: force azure openai tts voice path
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
| 新口播改写 | `AUDIO_REWRITE_MODEL=gpt-4o` | 默认跟随 `REWRITE_MODEL`;旧 Gemini 覆盖值会自动归一化。 |
|
||||
| 产品视角识别 | `PRODUCT_VIEW_MODEL=gpt-image-2` | 产品图批量识别视角、左右 / 上下 / 内外侧、用途和风险。 |
|
||||
| 所有生图 / 修图 | `gpt-image-2` | 服务端硬锁,无图片模型 fallback;覆盖关键帧生图、水印清理、元素提取、主体资产包、产品补角度、首尾帧。 |
|
||||
| 配音 | `VOICE_PROVIDER=azure_openai` + `AZURE_TTS_MODEL=gpt-4o-mini-tts` | MiniMax 仍保留兼容,但不是默认语音通道。 |
|
||||
| 配音 | `VOICE_PROVIDER=azure_openai` + `AZURE_TTS_MODEL=gpt-4o-mini-tts` | 语音固定 Azure OpenAI TTS;MiniMax 不再作为 fallback。后端会按 `AZURE_TTS_PATHS` 依次尝试路径,便于区分路径错误和整条语音服务不可用。 |
|
||||
| 视频 | `VIDEO_MODEL=seedance` | 当前主流程暂停直接提交;生产通道默认 `ai.skg.com/doubao`,Seedance 真实 ID 由 `VIDEO_MODEL_SEEDANCE` 配置。 |
|
||||
|
||||
## 当前主流程
|
||||
@@ -85,13 +85,15 @@ POST /jobs/{id}/frames/{idx}/storyboard/video
|
||||
2. 所有生图入口服务端只允许 `gpt-image-2`,不要重新加 Gemini 图片模型或其他 fallback。
|
||||
3. 画面理解和文案改写默认归 GPT:`VISION_MODEL`、`REWRITE_MODEL`、`AUDIO_REWRITE_MODEL` 会拦截旧 `gemini-*` 覆盖值。
|
||||
4. Gemini 仍保留在 ASR fallback / 音频分析 / 翻译链路,不要误删。
|
||||
5. 当前主流程不直接批量提交视频;先走“分镜规划 → 首尾帧 → 人工审核”。
|
||||
6. 产品素材池默认是“同一产品”,不做不同产品身份判断;视角识别必须按佩戴者左 / 右、上 / 下、内 / 外侧描述。
|
||||
7. 后端长任务不要用 `--reload`。
|
||||
8. 关键帧 `index` 是稳定 ID,不等于数组下标;前端取帧用 `frames.find(x => x.index === idx)`。
|
||||
5. 语音只走 Azure OpenAI TTS;不要新增或依赖 MiniMax 配音配置。
|
||||
6. 当前主流程不直接批量提交视频;先走“分镜规划 → 首尾帧 → 人工审核”。
|
||||
7. 产品素材池默认是“同一产品”,不做不同产品身份判断;视角识别必须按佩戴者左 / 右、上 / 下、内 / 外侧描述。
|
||||
8. 后端长任务不要用 `--reload`。
|
||||
9. 关键帧 `index` 是稳定 ID,不等于数组下标;前端取帧用 `frames.find(x => x.index === idx)`。
|
||||
|
||||
## 最近变更
|
||||
- 2026-05-18:`VISION_MODEL`、`REWRITE_MODEL`、`AUDIO_REWRITE_MODEL` 切到 GPT 默认模型 `gpt-4o`,并加旧 Gemini 环境变量归一化保护。
|
||||
- 2026-05-18:语音通道固定 Azure OpenAI TTS,移除 MiniMax fallback,并按 `AZURE_TTS_PATHS` 尝试语音路径。
|
||||
- 2026-05-18:当前主路径暂停直接提交视频,改为逐条首尾帧闸门。
|
||||
- 2026-05-18:媒体素材交互统一收口到 `MediaAssetTile`。
|
||||
- 2026-05-18:产品图视角识别和产品缺角度补图收敛到 `gpt-image-2`。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SKG TK 二创 API
|
||||
|
||||
FastAPI 后端,跑 yt-dlp + ffmpeg + ASR/翻译/英文 SKG 产品介绍文案 + MiniMax 英文配音管线。
|
||||
FastAPI 后端,跑 yt-dlp + ffmpeg + ASR/翻译/英文 SKG 产品介绍文案 + Azure OpenAI 英文配音管线。
|
||||
|
||||
## 启动
|
||||
|
||||
@@ -9,7 +9,7 @@ cd api
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
cp .env.example .env # 按需填 LLM_API_KEY / MINIMAX_API_KEY
|
||||
cp .env.example .env # 按需填 LLM_API_KEY / AZURE_OPENAI_API_KEY
|
||||
uvicorn main:app --host 127.0.0.1 --port 4291
|
||||
```
|
||||
|
||||
@@ -20,19 +20,19 @@ uvicorn main:app --host 127.0.0.1 --port 4291
|
||||
- `GET /health` — 健康检查 + 配置状态
|
||||
- `POST /jobs` `{url}` — 创建 job,后台下载源视频,视频就绪后可手动解析或提取音频
|
||||
- `GET /jobs/{id}` — 当前状态 + 产物;若原始音轨已拆出,会返回 `source_audio_url`
|
||||
- `POST /jobs/{id}/transcribe` — 触发音频提取 + ASR + 翻译 + SKG 英文产品介绍文案;文案长度按原音频时长估算,配置 MiniMax 后从英文随机音色池生成配音。前端 Audio 节点提供“提取音频 / 重新提取音频”按钮,可与抽帧并行,不自动触发
|
||||
- `POST /jobs/{id}/transcribe` — 触发音频提取 + ASR + 翻译 + SKG 英文产品介绍文案;文案长度按原音频时长估算,配置 Azure OpenAI TTS 后从 Azure 音色池生成配音。前端 Audio 节点提供“提取音频 / 重新提取音频”按钮,可与抽帧并行,不自动触发
|
||||
- `GET /jobs/{id}/video.mp4` — 原视频
|
||||
- `GET /jobs/{id}/audio.wav` — 拆轨后的原始音频,供前端底部音频条生成波形
|
||||
- `GET /jobs/{id}/audio-script.mp3` — 英文改写文案的 MiniMax 配音
|
||||
- `GET /jobs/{id}/audio-script.mp3` — 英文改写文案的 Azure OpenAI TTS 配音
|
||||
- `GET /jobs/{id}/frames/{i}.jpg` — 第 i 张关键帧(0-9)
|
||||
|
||||
## Mock 模式
|
||||
|
||||
未设 `LLM_API_KEY` 时,转录走本地 mock,便于 UI 联调;未设 `MINIMAX_API_KEY` 时只生成改写文案,不生成配音文件。
|
||||
未设 `LLM_API_KEY` 时,转录走本地 mock,便于 UI 联调;未设 `AZURE_OPENAI_API_KEY` 且无法复用 `LLM_API_KEY` 时只生成改写文案,不生成配音文件。
|
||||
|
||||
## 依赖
|
||||
|
||||
- `ffmpeg` 系统二进制(拆轨 / 抽帧)
|
||||
- `yt-dlp` 系统二进制(也可走 Python 包)
|
||||
- OpenAI 兼容 LLM 网关(ASR / 翻译 / 文案改写);如果 `/audio/transcriptions` 不可用,会用 `ASR_FALLBACK_MODEL` 走 Gemini 多模态音频识别
|
||||
- MiniMax T2A HTTP(英文产品介绍文案配音,使用 `MINIMAX_API_KEY`;默认随机音色池 `English_magnetic_voiced_man,English_Upbeat_Woman,English_MaturePartner`)
|
||||
- Azure OpenAI TTS(英文产品介绍文案配音,使用 `AZURE_OPENAI_API_KEY` 或回退复用 `LLM_API_KEY`;默认音色池 `alloy,verse,shimmer`)
|
||||
|
||||
78
api/main.py
78
api/main.py
@@ -88,23 +88,6 @@ AUDIO_PRODUCT_BRIEF = os.getenv(
|
||||
"SKG 智能按摩产品,主打日常肩颈、腰背、眼部、膝盖或足部放松;广告表达要高级、干净、可信,不做医疗疗效承诺。",
|
||||
).strip()
|
||||
AUDIO_REWRITE_MODEL = gpt_model_env("AUDIO_REWRITE_MODEL", REWRITE_MODEL)
|
||||
MINIMAX_API_KEY = os.getenv("MINIMAX_API_KEY", "").strip()
|
||||
MINIMAX_TTS_BASE_URL = os.getenv("MINIMAX_TTS_BASE_URL", "https://api.minimax.io").strip().rstrip("/")
|
||||
MINIMAX_TTS_MODEL = os.getenv("MINIMAX_TTS_MODEL", "speech-2.8-turbo").strip() or "speech-2.8-turbo"
|
||||
MINIMAX_TTS_VOICE_ID = os.getenv(
|
||||
"MINIMAX_TTS_VOICE_ID",
|
||||
"English_expressive_narrator",
|
||||
).strip() or "English_expressive_narrator"
|
||||
DEFAULT_MINIMAX_TTS_VOICE_POOL = [
|
||||
"English_magnetic_voiced_man",
|
||||
"English_Upbeat_Woman",
|
||||
"English_MaturePartner",
|
||||
]
|
||||
MINIMAX_TTS_VOICE_POOL = [
|
||||
v.strip()
|
||||
for v in os.getenv("MINIMAX_TTS_VOICE_POOL", ",".join(DEFAULT_MINIMAX_TTS_VOICE_POOL)).split(",")
|
||||
if v.strip()
|
||||
]
|
||||
# Voice is intentionally fixed to Azure OpenAI. Older envs may still contain
|
||||
# VOICE_PROVIDER=minimax, but the runtime must not fall back to MiniMax.
|
||||
VOICE_PROVIDER = "azure_openai"
|
||||
@@ -1866,7 +1849,7 @@ def analyze_queue_worker() -> None:
|
||||
ANALYZE_WORKER_RUNNING = False
|
||||
|
||||
|
||||
# ---------- 音频转写 + 翻译 + SKG 改写 + MiniMax 配音 ----------
|
||||
# ---------- 音频转写 + 翻译 + SKG 改写 + Azure OpenAI 配音 ----------
|
||||
|
||||
class TranscriptionUnavailable(RuntimeError):
|
||||
pass
|
||||
@@ -2322,18 +2305,6 @@ def _rewrite_audio_script_sync(segments: list[TranscriptSegment], target_seconds
|
||||
return fallback, f"改写失败,使用本地模板:{e}"
|
||||
|
||||
|
||||
def _minimax_tts_url() -> str:
|
||||
if MINIMAX_TTS_BASE_URL.endswith("/v1/t2a_v2"):
|
||||
return MINIMAX_TTS_BASE_URL
|
||||
return f"{MINIMAX_TTS_BASE_URL}/v1/t2a_v2"
|
||||
|
||||
|
||||
def _choose_minimax_voice_id() -> str:
|
||||
if MINIMAX_TTS_VOICE_POOL:
|
||||
return random.choice(MINIMAX_TTS_VOICE_POOL)
|
||||
return MINIMAX_TTS_VOICE_ID
|
||||
|
||||
|
||||
def _choose_azure_voice_id() -> str:
|
||||
if AZURE_TTS_VOICE_POOL:
|
||||
return random.choice(AZURE_TTS_VOICE_POOL)
|
||||
@@ -2358,53 +2329,6 @@ def _voice_speed_for(voice_id: str, target_seconds: float, text: str) -> float:
|
||||
return 0.99
|
||||
|
||||
|
||||
def _minimax_tts_sync(job_id: str, text: str, voice_id: str, target_seconds: float = 12.0) -> str:
|
||||
if not MINIMAX_API_KEY:
|
||||
raise RuntimeError("MINIMAX_API_KEY 未配置,未生成配音")
|
||||
if not text.strip():
|
||||
raise RuntimeError("改写文案为空,未生成配音")
|
||||
payload = {
|
||||
"model": MINIMAX_TTS_MODEL,
|
||||
"text": text.strip()[:9500],
|
||||
"stream": False,
|
||||
"language_boost": "English",
|
||||
"output_format": "hex",
|
||||
"voice_setting": {
|
||||
"voice_id": voice_id,
|
||||
"speed": _voice_speed_for(voice_id, target_seconds, text),
|
||||
"vol": 1,
|
||||
"pitch": 0,
|
||||
},
|
||||
"audio_setting": {
|
||||
"sample_rate": 32000,
|
||||
"bitrate": 128000,
|
||||
"format": "mp3",
|
||||
"channel": 1,
|
||||
},
|
||||
}
|
||||
resp = httpx.post(
|
||||
_minimax_tts_url(),
|
||||
headers={"Authorization": f"Bearer {MINIMAX_API_KEY}", "Content-Type": "application/json"},
|
||||
json=payload,
|
||||
timeout=90,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
base_resp = data.get("base_resp") or {}
|
||||
if int(base_resp.get("status_code", 0) or 0) != 0:
|
||||
raise RuntimeError(base_resp.get("status_msg") or "MiniMax TTS 返回失败")
|
||||
audio_hex = ((data.get("data") or {}).get("audio") or "").strip()
|
||||
if not audio_hex:
|
||||
raise RuntimeError("MiniMax TTS 未返回 audio hex")
|
||||
try:
|
||||
audio_bytes = bytes.fromhex(audio_hex)
|
||||
except ValueError as e:
|
||||
raise RuntimeError(f"MiniMax TTS audio hex 无法解析:{e}") from e
|
||||
out = job_dir(job_id) / "audio_script.mp3"
|
||||
out.write_bytes(audio_bytes)
|
||||
return f"/jobs/{job_id}/audio-script.mp3"
|
||||
|
||||
|
||||
def _azure_tts_url_for(path_value: str) -> str:
|
||||
path = path_value if path_value.startswith("/") else f"/{path_value}"
|
||||
if AZURE_OPENAI_BASE_URL.endswith(path):
|
||||
|
||||
@@ -637,7 +637,7 @@ web/app/page.tsx
|
||||
后端主链路:
|
||||
api/main.py
|
||||
-> Job / KeyFrame / KeyElement / StoryboardScene / AudioScript
|
||||
-> 下载 / 上传 / 音频提取 / ASR / 翻译 / 声音背景音分析 / 抽帧 / Vision / 清洗 / 元素提取 / 分镜保存 / 后续音频改写与 MiniMax 英文配音
|
||||
-> 下载 / 上传 / 音频提取 / ASR / 翻译 / 声音背景音分析 / 抽帧 / Vision / 清洗 / 元素提取 / 分镜保存 / 后续音频改写与 Azure OpenAI 英文配音
|
||||
-> jobs/<jobId>/state.json + 图片文件落盘</pre>
|
||||
</section>
|
||||
|
||||
@@ -745,7 +745,7 @@ api/main.py
|
||||
background_audio_profile,
|
||||
product_brief,
|
||||
rewrite_model,
|
||||
voice_provider: azure_openai | minimax,
|
||||
voice_provider: azure_openai,
|
||||
voice_model,
|
||||
voice_id,
|
||||
voice_url,
|
||||
@@ -882,7 +882,7 @@ ProductRefStateItem {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>网页登录</td><td><code>POST /auth/login</code>、<code>GET /auth/check</code>、<code>POST /auth/logout</code></td><td><code>web/app/login/page.tsx</code>、Nginx <code>auth_request</code></td><td>登录页提交账号密码到 <code>/api/auth/login</code>,后端设置 HttpOnly 会话 Cookie;生产 Nginx 对工作台和 <code>/api/</code> 调 <code>/auth/check</code> 做统一校验,未登录页面跳 <code>/login/</code>,API 返回 JSON 401。</td></tr>
|
||||
<tr><td>运行配置 / 模型标注</td><td><code>GET /health</code></td><td><code>getRuntimeHealth</code>、<code>ModelTrace</code></td><td>返回 <code>models</code>:ASR、本机 ASR、ASR fallback、翻译、GPT 改写、GPT 画面理解、产品视角识别 <code>product_view</code>、GPT 图像模型、主体 6 视图 GPT 图像模型、Azure OpenAI TTS、视频别名和 Seedance 服务商。当前 <code>REWRITE_MODEL</code>、<code>AUDIO_REWRITE_MODEL</code> 和 <code>VISION_MODEL</code> 默认使用 <code>gpt-4o</code>;如果旧环境变量仍写 <code>gemini-*</code>,后端会归一化回 <code>GPT_TEXT_MODEL</code> / <code>REWRITE_MODEL</code>。前端所有当前主路径里会调用模型的按钮旁显示模型名,点击弹出小窗口查看模型链路和输入输出逻辑;不返回 API Key 或敏感凭证。</td></tr>
|
||||
<tr><td>运行配置 / 模型标注</td><td><code>GET /health</code></td><td><code>getRuntimeHealth</code>、<code>ModelTrace</code></td><td>返回 <code>models</code>:ASR、本机 ASR、ASR fallback、翻译、GPT 改写、GPT 画面理解、产品视角识别 <code>product_view</code>、GPT 图像模型、主体 6 视图 GPT 图像模型、Azure OpenAI TTS、视频别名和 Seedance 服务商。当前 <code>REWRITE_MODEL</code>、<code>AUDIO_REWRITE_MODEL</code> 和 <code>VISION_MODEL</code> 默认使用 <code>gpt-4o</code>;如果旧环境变量仍写 <code>gemini-*</code>,后端会归一化回 <code>GPT_TEXT_MODEL</code> / <code>REWRITE_MODEL</code>。语音只走 Azure OpenAI TTS,<code>models.voice_tts_paths</code> 会回传当前尝试的语音路径,方便区分路径错误和语音服务不可用。前端所有当前主路径里会调用模型的按钮旁显示模型名,点击弹出小窗口查看模型链路和输入输出逻辑;不返回 API Key 或敏感凭证。</td></tr>
|
||||
<tr><td>历史列表</td><td><code>GET /jobs</code></td><td><code>listJobs</code></td><td>所有 job 精简列表(id/url/status/thumbnail/mtime…),按 state.json mtime 倒序。前端 URL 无 <code>?job=</code> 时拉它回填全部历史;带 <code>limit</code> 可截断。</td></tr>
|
||||
<tr><td>创建任务</td><td><code>POST /jobs</code></td><td><code>createJob</code></td><td>提交 TK 链接,后台开始下载;前端“开始”队列会在 downloaded 后自动触发音频解析。</td></tr>
|
||||
<tr><td>上传视频</td><td><code>POST /jobs/upload</code></td><td><code>uploadJob</code></td><td>保存 source.mp4,然后同样进入下载完成状态;当前上传后也加入第一步队列,下载完成后自动解析音频。</td></tr>
|
||||
@@ -891,7 +891,7 @@ ProductRefStateItem {
|
||||
<tr><td>音频文案轨</td><td><code>POST /jobs/{id}/transcribe</code></td><td><code>triggerTranscribe</code></td><td>若尚未拆轨,先从 <code>source.mp4</code> 提取 <code>audio.wav</code> 并回填 <code>source_audio_url</code>;随后用 ASR 提取原始文案,翻译成中文,写入 <code>audio_script.source_text</code>、<code>source_zh</code> 和逐句 <code>transcript</code>。远端 <code>ASR_MODEL</code> 失败后先走本机 <code>LOCAL_ASR_BIN</code>/<code>LOCAL_ASR_MODEL</code>(默认 <code>mlx_whisper</code>),再尝试 <code>ASR_FALLBACK_MODEL</code>。后端会拒绝重复文本、逐秒假字幕或覆盖率过低的结果,不再把不可听的多模态输出写进时间轴。再用 <code>ASR_FALLBACK_MODEL</code> 多模态音频分析讲话人、语速节奏、停顿、背景音乐/环境声/音效,写入 <code>speaker_profile</code>、<code>rhythm_profile</code>、<code>background_audio_profile</code>。当前第一步不默认生成 SKG 新口播和 Azure OpenAI 配音。</td></tr>
|
||||
<tr><td>分镜脚本改写</td><td><code>POST /jobs/{id}/script/rewrite</code></td><td><code>rewriteStoryboardScript</code></td><td>根据原参考文案、当前新口播、分镜角色、时间段和作者想法改写中文口播,默认主模型为 <code>AUDIO_REWRITE_MODEL=gpt-4o</code>,失败后再尝试 ASR fallback 和翻译模型。<code>mode=segment</code> 只改一段;<code>mode=all</code> 一次改完整片,要求整片前后连贯。接口只返回 <code>items[index,text]</code>,前端暂存在当前页面状态里,保存规划或生成首尾帧时写入 <code>StoryboardScene.action</code>。</td></tr>
|
||||
<tr><td>原始音频文件</td><td><code>GET /jobs/{id}/audio.wav</code></td><td><code>sourceAudioUrl</code></td><td>返回拆轨得到的 wav;当前主界面不再渲染底部吸附音频条,右侧复刻工作表会读取该文件生成参考图式横向响度波形,并和原视频、逐句时间轴联动;波形标题栏显示当前播放秒数、总时长和鼠标指针停点秒数。</td></tr>
|
||||
<tr><td>改写配音文件</td><td><code>GET /jobs/{id}/audio-script.mp3</code></td><td><code>apiAssetUrl(job.audio_script.voice_url)</code></td><td>后续新配音阶段保留的 TTS 产物;默认走 <code>VOICE_PROVIDER=azure_openai</code>,通过 <code>AZURE_OPENAI_BASE_URL</code> 的 OpenAI 协议 <code>/audio/speech</code> 生成 mp3。当前第一步不默认生成该文件。</td></tr>
|
||||
<tr><td>改写配音文件</td><td><code>GET /jobs/{id}/audio-script.mp3</code></td><td><code>apiAssetUrl(job.audio_script.voice_url)</code></td><td>后续新配音阶段保留的 TTS 产物;服务端固定走 <code>VOICE_PROVIDER=azure_openai</code>,通过 <code>AZURE_OPENAI_BASE_URL</code> 的 OpenAI 协议生成 mp3,并按 <code>AZURE_TTS_PATHS</code> 依次尝试 <code>/audio/speech</code>、<code>/v1/audio/speech</code> 等路径。MiniMax 不再作为 fallback。当前第一步不默认生成该文件。</td></tr>
|
||||
<tr><td>手动加帧</td><td><code>POST /jobs/{id}/frames?t=</code></td><td><code>addManualFrame</code></td><td>按视频时间戳抽一帧,index 递增但 frames 按 timestamp 排序。当前主界面会把原版视频播放器的播放秒数传给 <code>AudioIntakePanel</code> 标题栏右侧的“当前点抽帧”。</td></tr>
|
||||
<tr><td>删除关键帧</td><td><code>DELETE /jobs/{id}/frames/{idx}</code></td><td><code>deleteFrame</code></td><td>删除单张关键帧并清掉对应选择态;当前主界面每张缩略图右下角提供删除入口,方便手动抽错后直接修正。</td></tr>
|
||||
<tr><td>Vision 识别</td><td><code>POST /frames/{idx}/describe</code></td><td><code>describeFrame</code></td><td>调用 <code>VISION_MODEL=gpt-4o</code> 做关键帧画面理解,写入 frame.description,后续可从 objects 加候选元素。</td></tr>
|
||||
@@ -939,7 +939,7 @@ ProductRefStateItem {
|
||||
<tr>
|
||||
<td><span class="tag gray">音频条</span></td>
|
||||
<td>复刻工作表顶部触发音频解析;全文文案依据默认折叠,音频解析结果也默认折叠为辅助信息;主展示以源视频工作区为准:竖版原视频在左,音频波形和逐句时间轴在右;底部 <code>AudioStrip</code> 当前不渲染。</td>
|
||||
<td>当前第一步不要默认展示底部音频条、新配音播放器、独立原文案提取大卡片,或把 MiniMax 配音当作已完成结果。</td>
|
||||
<td>当前第一步不要默认展示底部音频条、新配音播放器、独立原文案提取大卡片,或把未生成的 Azure OpenAI 配音当作已完成结果。</td>
|
||||
<td><code>web/components/audio-strip.tsx</code>、<code>pipeline_transcribe</code>、<code>AudioScript</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -974,7 +974,7 @@ ProductRefStateItem {
|
||||
<h3>阻塞 / 占位</h3>
|
||||
<ul>
|
||||
<li>ASR:优先走当前 OpenAI-compatible 音频转写入口;如果该网关没有 <code>/audio/transcriptions</code>,自动 fallback 到 <code>ASR_FALLBACK_MODEL</code>(默认 <code>gemini-2.5-flash</code>)的多模态音频识别。</li>
|
||||
<li>Voice:当前默认语音通道是 <code>VOICE_PROVIDER=azure_openai</code>,通过 <code>AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure</code> 的 OpenAI 协议生成 TTS;第一步暂不默认调用。MiniMax 仅保留为兼容旧配置。</li>
|
||||
<li>Voice:当前语音通道固定是 <code>VOICE_PROVIDER=azure_openai</code>,通过 <code>AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure</code> 的 OpenAI 协议生成 TTS;后端按 <code>AZURE_TTS_PATHS</code> 依次尝试路径。第一步暂不默认调用,MiniMax 不再作为 fallback。</li>
|
||||
<li>Audio Product Brief:默认是通用 SKG 放松产品卖点;当前第一步只保留配置,后续分镜/新配音阶段再使用。</li>
|
||||
<li>Video Gen:当前视频通道固定优先 Seedance;<code>VIDEO_API_BASE_URL=https://ai.skg.com/doubao</code> 走 content JSON 异步任务,提交后写入候选片段并轮询到完成。</li>
|
||||
<li>Compose:还没做本地 ffmpeg 字幕/TTS 合成。</li>
|
||||
@@ -1016,6 +1016,18 @@ ProductRefStateItem {
|
||||
<h2>变更记录</h2>
|
||||
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
|
||||
<div class="changelog">
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-18 · 语音通道固定 Azure OpenAI TTS</h3>
|
||||
<span class="tag violet">API</span>
|
||||
<span class="tag cyan">Model</span>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p><strong>问题:</strong>语音生成测试失败时无法区分是 Azure OpenAI TTS 路径配置不对,还是整条语音服务不可用;同时旧 MiniMax 分支仍可能被环境变量或前端文案误导。</p>
|
||||
<p><strong>改动:</strong>后端固定 <code>VOICE_PROVIDER=azure_openai</code>,删除 MiniMax TTS 活动配置和 fallback;<code>_azure_openai_tts_sync</code> 改为按 <code>AZURE_TTS_PATHS</code> 依次尝试多个 OpenAI-compatible 语音路径,失败时返回每个 URL 的 HTTP 状态或连接错误。<code>/health</code> 新增 <code>models.voice_tts_paths</code> 和 <code>models.minimax_disabled=true</code>。</p>
|
||||
<p><strong>影响:</strong>后续排查语音时先看失败信息里的路径和状态码:如果所有路径 404/405,优先调 <code>AZURE_TTS_PATHS</code>;如果连接/鉴权/上游错误,则按 Azure 语音服务可用性或 Key 排查。前端和状态文档不再把 MiniMax 当作候选语音方案。</p>
|
||||
</div>
|
||||
</article>
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-18 · 画面理解和文案改写改用 GPT</h3>
|
||||
|
||||
Reference in New Issue
Block a user