auto-save 2026-05-18 14:46 (~7)

This commit is contained in:
2026-05-18 14:46:24 +08:00
parent fde94f4698
commit e6387cf7af
7 changed files with 74 additions and 65 deletions

View File

@@ -65,12 +65,10 @@
- `IMAGE_BASE_URL` / `IMAGE_API_KEY` / `IMAGE_MODEL`OpenAI 兼容生图网关;当前所有生图入口一律强制使用 `gpt-image-2`,不做其他图片模型 fallback - `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` - `GPT_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODELS`:保留兼容旧环境变量名,但服务端会强制主体 6 视图和所有其他生图入口都只使用 `gpt-image-2`
- `AI_HTTP_PROXY` / `IMAGE_HTTP_PROXY`:可选的 AI 网关出站代理;本地 launchd 后台进程不一定继承 shell 的 `http_proxy/https_proxy`,如生图报 DNS / ConnectError可在本地 `api/.env` 配置后重启后端。`/health` 只回传是否配置代理,不回传代理地址。 - `AI_HTTP_PROXY` / `IMAGE_HTTP_PROXY`:可选的 AI 网关出站代理;本地 launchd 后台进程不一定继承 shell 的 `http_proxy/https_proxy`,如生图报 DNS / ConnectError可在本地 `api/.env` 配置后重启后端。`/health` 只回传是否配置代理,不回传代理地址。
- `VOICE_PROVIDER`:配音通道,当前固定使用 `azure_openai` - `VOICE_PROVIDER`:配音通道,服务端固定使用 `azure_openai`;旧环境若写 `minimax` 会被忽略
- `AZURE_OPENAI_BASE_URL` / `AZURE_OPENAI_API_KEY`:微软 Azure OpenAI 协议配音网关;本地未单独配置 Key 时回退复用 `LLM_API_KEY` - `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 协议语音路径 - `AZURE_TTS_MODEL` / `AZURE_TTS_VOICE_ID` / `AZURE_TTS_VOICE_POOL` / `AZURE_TTS_PATH` / `AZURE_TTS_PATHS`Azure OpenAI TTS 模型、默认音色、音色池和 OpenAI 协议语音路径;后端会按 `AZURE_TTS_PATHS` 依次尝试,便于区分路径不对和整条语音服务不可用
- `MINIMAX_API_KEY`MiniMax T2A 配音 Key只能放本地 `api/.env`,不能入库;当前第一步暂不默认调用 - MiniMax TTS 不再作为语音 fallback不要新增或依赖 `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只能放本地环境变量 - `POE_API_KEY` / `VIDEO_API_KEY`:视频生成通道 Key只能放本地环境变量
- `WEB_AUTH_USERNAME` / `WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET`:生产网页登录和会话签名配置;密码和 session secret 只放服务器环境变量,不入库 - `WEB_AUTH_USERNAME` / `WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET`:生产网页登录和会话签名配置;密码和 session secret 只放服务器环境变量,不入库
- `FFMPEG_BIN` / `FFPROBE_BIN`:可选本地媒体二进制路径;本机 Homebrew ffmpeg 动态库损坏时,后端会自动跳过不可用的 PATH 版本并尝试本机静态 ffmpeg 备选,生产仍建议使用系统 ffmpeg/ffprobe - `FFMPEG_BIN` / `FFPROBE_BIN`:可选本地媒体二进制路径;本机 Homebrew ffmpeg 动态库损坏时,后端会自动跳过不可用的 PATH 版本并尝试本机静态 ffmpeg 备选,生产仍建议使用系统 ffmpeg/ffprobe

View File

@@ -37,6 +37,7 @@ VIDEO_MODEL_VEO3=veo-3.1-fast
# 音频文案改写 + Azure OpenAI 配音 # 音频文案改写 + Azure OpenAI 配音
AUDIO_REWRITE_MODEL=gpt-4o AUDIO_REWRITE_MODEL=gpt-4o
AUDIO_PRODUCT_BRIEF="SKG 智能按摩产品,主打日常肩颈、腰背、眼部、膝盖或足部放松;广告表达要高级、干净、可信,不做医疗疗效承诺。" AUDIO_PRODUCT_BRIEF="SKG 智能按摩产品,主打日常肩颈、腰背、眼部、膝盖或足部放松;广告表达要高级、干净、可信,不做医疗疗效承诺。"
# 语音通道服务端固定为 azure_openai旧 VOICE_PROVIDER=minimax 会被忽略。
VOICE_PROVIDER=azure_openai VOICE_PROVIDER=azure_openai
AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure
AZURE_OPENAI_API_KEY= AZURE_OPENAI_API_KEY=
@@ -44,13 +45,7 @@ AZURE_TTS_MODEL=gpt-4o-mini-tts
AZURE_TTS_VOICE_ID=alloy AZURE_TTS_VOICE_ID=alloy
AZURE_TTS_VOICE_POOL=alloy,verse,shimmer AZURE_TTS_VOICE_POOL=alloy,verse,shimmer
AZURE_TTS_PATH=/audio/speech AZURE_TTS_PATH=/audio/speech
AZURE_TTS_PATHS=/audio/speech,/v1/audio/speech
# MiniMax 旧配音通道,保留兼容;默认不走
MINIMAX_API_KEY=
MINIMAX_TTS_BASE_URL=https://api.minimax.io
MINIMAX_TTS_MODEL=speech-2.8-turbo
MINIMAX_TTS_VOICE_ID=English_expressive_narrator
MINIMAX_TTS_VOICE_POOL=English_magnetic_voiced_man,English_Upbeat_Woman,English_MaturePartner
# Poe 视频 API优先用于 Seedance / Kling / Veo # Poe 视频 API优先用于 Seedance / Kling / Veo
POE_API_BASE_URL=https://api.poe.com/v1 POE_API_BASE_URL=https://api.poe.com/v1

View File

@@ -105,7 +105,9 @@ MINIMAX_TTS_VOICE_POOL = [
for v in os.getenv("MINIMAX_TTS_VOICE_POOL", ",".join(DEFAULT_MINIMAX_TTS_VOICE_POOL)).split(",") for v in os.getenv("MINIMAX_TTS_VOICE_POOL", ",".join(DEFAULT_MINIMAX_TTS_VOICE_POOL)).split(",")
if v.strip() if v.strip()
] ]
VOICE_PROVIDER = os.getenv("VOICE_PROVIDER", "azure_openai").strip().lower() or "azure_openai" # 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"
AZURE_OPENAI_BASE_URL = os.getenv("AZURE_OPENAI_BASE_URL", "https://ai.skg.com/azure").strip().rstrip("/") AZURE_OPENAI_BASE_URL = os.getenv("AZURE_OPENAI_BASE_URL", "https://ai.skg.com/azure").strip().rstrip("/")
AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY", LLM_API_KEY).strip() AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY", LLM_API_KEY).strip()
AZURE_TTS_MODEL = os.getenv("AZURE_TTS_MODEL", "gpt-4o-mini-tts").strip() or "gpt-4o-mini-tts" AZURE_TTS_MODEL = os.getenv("AZURE_TTS_MODEL", "gpt-4o-mini-tts").strip() or "gpt-4o-mini-tts"
@@ -117,6 +119,11 @@ AZURE_TTS_VOICE_POOL = [
if v.strip() if v.strip()
] ]
AZURE_TTS_PATH = os.getenv("AZURE_TTS_PATH", "/audio/speech").strip() or "/audio/speech" AZURE_TTS_PATH = os.getenv("AZURE_TTS_PATH", "/audio/speech").strip() or "/audio/speech"
AZURE_TTS_PATHS = [
p.strip()
for p in os.getenv("AZURE_TTS_PATHS", f"{AZURE_TTS_PATH},/audio/speech,/v1/audio/speech").split(",")
if p.strip()
]
POE_API_BASE_URL = os.getenv("POE_API_BASE_URL", "https://api.poe.com/v1").strip() or "https://api.poe.com/v1" POE_API_BASE_URL = os.getenv("POE_API_BASE_URL", "https://api.poe.com/v1").strip() or "https://api.poe.com/v1"
POE_API_KEY = os.getenv("POE_API_KEY", "").strip() POE_API_KEY = os.getenv("POE_API_KEY", "").strip()
@@ -2334,9 +2341,7 @@ def _choose_azure_voice_id() -> str:
def _choose_tts_voice_id() -> str: def _choose_tts_voice_id() -> str:
if VOICE_PROVIDER == "azure_openai": return _choose_azure_voice_id()
return _choose_azure_voice_id()
return _choose_minimax_voice_id()
def _voice_speed_for(voice_id: str, target_seconds: float, text: str) -> float: def _voice_speed_for(voice_id: str, target_seconds: float, text: str) -> float:
@@ -2400,13 +2405,22 @@ def _minimax_tts_sync(job_id: str, text: str, voice_id: str, target_seconds: flo
return f"/jobs/{job_id}/audio-script.mp3" return f"/jobs/{job_id}/audio-script.mp3"
def _azure_tts_url() -> str: def _azure_tts_url_for(path_value: str) -> str:
path = AZURE_TTS_PATH if AZURE_TTS_PATH.startswith("/") else f"/{AZURE_TTS_PATH}" path = path_value if path_value.startswith("/") else f"/{path_value}"
if AZURE_OPENAI_BASE_URL.endswith(path): if AZURE_OPENAI_BASE_URL.endswith(path):
return AZURE_OPENAI_BASE_URL return AZURE_OPENAI_BASE_URL
return f"{AZURE_OPENAI_BASE_URL}{path}" return f"{AZURE_OPENAI_BASE_URL}{path}"
def _azure_tts_urls() -> list[str]:
urls: list[str] = []
for path in AZURE_TTS_PATHS or [AZURE_TTS_PATH]:
url = _azure_tts_url_for(path)
if url not in urls:
urls.append(url)
return urls
def _azure_openai_tts_sync(job_id: str, text: str, voice_id: str, target_seconds: float = 12.0) -> str: def _azure_openai_tts_sync(job_id: str, text: str, voice_id: str, target_seconds: float = 12.0) -> str:
if not AZURE_OPENAI_API_KEY: if not AZURE_OPENAI_API_KEY:
raise RuntimeError("AZURE_OPENAI_API_KEY 或 LLM_API_KEY 未配置,未生成配音") raise RuntimeError("AZURE_OPENAI_API_KEY 或 LLM_API_KEY 未配置,未生成配音")
@@ -2419,18 +2433,32 @@ def _azure_openai_tts_sync(job_id: str, text: str, voice_id: str, target_seconds
"response_format": "mp3", "response_format": "mp3",
"speed": _voice_speed_for(voice_id, target_seconds, text), "speed": _voice_speed_for(voice_id, target_seconds, text),
} }
resp = httpx.post( headers = {
_azure_tts_url(), "Authorization": f"Bearer {AZURE_OPENAI_API_KEY}",
headers={ "api-key": AZURE_OPENAI_API_KEY,
"Authorization": f"Bearer {AZURE_OPENAI_API_KEY}", "Content-Type": "application/json",
"api-key": AZURE_OPENAI_API_KEY, }
"Content-Type": "application/json", resp: httpx.Response | None = None
}, errors: list[str] = []
json=payload, with ai_http_client(timeout=120) as client:
timeout=120, for url in _azure_tts_urls():
) try:
current = client.post(url, headers=headers, json=payload)
except Exception as e:
errors.append(f"{url}: {type(e).__name__}: {e}")
continue
if current.status_code < 400:
resp = current
break
errors.append(f"{url}: HTTP {current.status_code}: {current.text[:180]}")
if current.status_code not in {404, 405}:
resp = current
break
if resp is None:
raise RuntimeError("Azure OpenAI TTS 不可用;已尝试 " + " | ".join(errors))
if resp.status_code >= 400: if resp.status_code >= 400:
raise RuntimeError(f"Azure OpenAI TTS HTTP {resp.status_code}: {resp.text[:300]}") detail = " | ".join(errors) or resp.text[:300]
raise RuntimeError(f"Azure OpenAI TTS HTTP {resp.status_code}: {detail[:600]}")
audio_bytes = resp.content audio_bytes = resp.content
if not audio_bytes: if not audio_bytes:
raise RuntimeError("Azure OpenAI TTS 未返回音频内容") raise RuntimeError("Azure OpenAI TTS 未返回音频内容")
@@ -2447,9 +2475,7 @@ def _azure_openai_tts_sync(job_id: str, text: str, voice_id: str, target_seconds
def _tts_sync(job_id: str, text: str, voice_id: str, target_seconds: float = 12.0) -> tuple[str, str, str]: def _tts_sync(job_id: str, text: str, voice_id: str, target_seconds: float = 12.0) -> tuple[str, str, str]:
if VOICE_PROVIDER == "azure_openai": return _azure_openai_tts_sync(job_id, text, voice_id, target_seconds), "azure_openai", AZURE_TTS_MODEL
return _azure_openai_tts_sync(job_id, text, voice_id, target_seconds), "azure_openai", AZURE_TTS_MODEL
return _minimax_tts_sync(job_id, text, voice_id, target_seconds), "minimax", MINIMAX_TTS_MODEL
def _build_audio_script_sync(job_id: str, segments: list[TranscriptSegment], target_seconds: float = 12.0) -> AudioScript: def _build_audio_script_sync(job_id: str, segments: list[TranscriptSegment], target_seconds: float = 12.0) -> AudioScript:
@@ -2461,8 +2487,8 @@ def _build_audio_script_sync(job_id: str, segments: list[TranscriptSegment], tar
speaker_profile, rhythm_profile = _audio_delivery_profile(segments, duration, selected_voice_id) speaker_profile, rhythm_profile = _audio_delivery_profile(segments, duration, selected_voice_id)
voice_url = "" voice_url = ""
voice_error = "" voice_error = ""
voice_provider = "azure_openai" if VOICE_PROVIDER == "azure_openai" else "minimax" voice_provider = "azure_openai"
voice_model = AZURE_TTS_MODEL if voice_provider == "azure_openai" else MINIMAX_TTS_MODEL voice_model = AZURE_TTS_MODEL
try: try:
voice_url, voice_provider, voice_model = _tts_sync(job_id, rewritten, selected_voice_id, duration) voice_url, voice_provider, voice_model = _tts_sync(job_id, rewritten, selected_voice_id, duration)
except Exception as e: except Exception as e:
@@ -3060,7 +3086,7 @@ def health() -> dict:
"auth_configured": WEB_AUTH_CONFIGURED, "auth_configured": WEB_AUTH_CONFIGURED,
"base_url": LLM_BASE_URL or "openai-default", "base_url": LLM_BASE_URL or "openai-default",
"image_base_url": IMAGE_BASE_URL or LLM_BASE_URL or "openai-default", "image_base_url": IMAGE_BASE_URL or LLM_BASE_URL or "openai-default",
"voice_base_url": AZURE_OPENAI_BASE_URL if VOICE_PROVIDER == "azure_openai" else MINIMAX_TTS_BASE_URL, "voice_base_url": AZURE_OPENAI_BASE_URL,
"models": { "models": {
"asr": ASR_MODEL, "asr": ASR_MODEL,
"local_asr": LOCAL_ASR_MODEL, "local_asr": LOCAL_ASR_MODEL,
@@ -3077,15 +3103,13 @@ def health() -> dict:
"subject_image": SUBJECT_ASSET_IMAGE_MODEL, "subject_image": SUBJECT_ASSET_IMAGE_MODEL,
"subject_image_fallbacks": SUBJECT_ASSET_IMAGE_MODELS, "subject_image_fallbacks": SUBJECT_ASSET_IMAGE_MODELS,
"voice_provider": VOICE_PROVIDER, "voice_provider": VOICE_PROVIDER,
"voice_base_url": AZURE_OPENAI_BASE_URL if VOICE_PROVIDER == "azure_openai" else MINIMAX_TTS_BASE_URL, "voice_base_url": AZURE_OPENAI_BASE_URL,
"voice_tts": AZURE_TTS_MODEL if VOICE_PROVIDER == "azure_openai" else MINIMAX_TTS_MODEL, "voice_tts": AZURE_TTS_MODEL,
"voice_id": AZURE_TTS_VOICE_ID if VOICE_PROVIDER == "azure_openai" else MINIMAX_TTS_VOICE_ID, "voice_tts_paths": AZURE_TTS_PATHS,
"voice_pool": AZURE_TTS_VOICE_POOL if VOICE_PROVIDER == "azure_openai" else (MINIMAX_TTS_VOICE_POOL or [MINIMAX_TTS_VOICE_ID]), "voice_id": AZURE_TTS_VOICE_ID,
"voice_configured": bool(AZURE_OPENAI_API_KEY) if VOICE_PROVIDER == "azure_openai" else bool(MINIMAX_API_KEY), "voice_pool": AZURE_TTS_VOICE_POOL,
"minimax_tts": MINIMAX_TTS_MODEL, "voice_configured": bool(AZURE_OPENAI_API_KEY),
"minimax_voice": MINIMAX_TTS_VOICE_ID, "minimax_disabled": True,
"minimax_voice_pool": MINIMAX_TTS_VOICE_POOL or [MINIMAX_TTS_VOICE_ID],
"minimax_configured": bool(MINIMAX_API_KEY),
"video": VIDEO_MODEL, "video": VIDEO_MODEL,
"video_aliases": VIDEO_MODEL_ALIASES, "video_aliases": VIDEO_MODEL_ALIASES,
"video_provider": video_provider_name(), "video_provider": video_provider_name(),

View File

@@ -38,6 +38,7 @@ AI_HTTP_PROXY=
# Audio rewrite and Azure OpenAI TTS # Audio rewrite and Azure OpenAI TTS
AUDIO_REWRITE_MODEL=gpt-4o AUDIO_REWRITE_MODEL=gpt-4o
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." 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 is fixed to Azure OpenAI in the backend; legacy VOICE_PROVIDER=minimax is ignored.
VOICE_PROVIDER=azure_openai VOICE_PROVIDER=azure_openai
AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure
AZURE_OPENAI_API_KEY= AZURE_OPENAI_API_KEY=
@@ -45,13 +46,7 @@ AZURE_TTS_MODEL=gpt-4o-mini-tts
AZURE_TTS_VOICE_ID=alloy AZURE_TTS_VOICE_ID=alloy
AZURE_TTS_VOICE_POOL=alloy,verse,shimmer AZURE_TTS_VOICE_POOL=alloy,verse,shimmer
AZURE_TTS_PATH=/audio/speech AZURE_TTS_PATH=/audio/speech
AZURE_TTS_PATHS=/audio/speech,/v1/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
MINIMAX_TTS_VOICE_ID=English_expressive_narrator
MINIMAX_TTS_VOICE_POOL=English_magnetic_voiced_man,English_Upbeat_Woman,English_MaturePartner
# Video generation. Use SKG Doubao / Seedance gateway in production. # Video generation. Use SKG Doubao / Seedance gateway in production.
POE_API_BASE_URL=https://api.poe.com/v1 POE_API_BASE_URL=https://api.poe.com/v1

View File

@@ -641,15 +641,15 @@ export const Dashboard = forwardRef<DashboardHandle, Props>(function Dashboard({
</div> </div>
</KanbanCard> </KanbanCard>
<KanbanCard tone="green" tags={["配音"]} title={job?.audio_script?.voice_model || "MiniMax T2A"}> <KanbanCard tone="green" tags={["配音"]} title={job?.audio_script?.voice_model || "Azure OpenAI TTS"}>
{job?.audio_script?.voice_url ? ( {job?.audio_script?.voice_url ? (
<audio controls className="h-8 w-full" src={apiAssetUrl(job.audio_script.voice_url)} /> <audio controls className="h-8 w-full" src={apiAssetUrl(job.audio_script.voice_url)} />
) : ( ) : (
<div className="text-[11px] text-[var(--text-soft)]"> <div className="text-[11px] text-[var(--text-soft)]">
{job?.audio_script?.error || "配置 MiniMax 后自动生成配音文件"} {job?.audio_script?.error || "配置 Azure OpenAI TTS 后自动生成配音文件"}
</div> </div>
)} )}
<div className="kanban-meta">{job?.audio_script?.voice_id || "random English voice"}</div> <div className="kanban-meta">{job?.audio_script?.voice_id || "Azure voice"}</div>
</KanbanCard> </KanbanCard>
</> </>
)} )}

View File

@@ -2102,7 +2102,7 @@ export function RewriteNode({ data, selected }: any) {
} }
/* ============================================================ /* ============================================================
5b. AudioNode — 合并 ASR + 翻译 + 改写 + MiniMax 配音 5b. AudioNode — 合并 ASR + 翻译 + 改写 + Azure OpenAI 配音
============================================================ */ ============================================================ */
export function AudioNode({ data, selected }: any) { export function AudioNode({ data, selected }: any) {
const d: NodeData = data const d: NodeData = data
@@ -2152,9 +2152,9 @@ export function AudioNode({ data, selected }: any) {
}} }}
> >
<div> <div>
/ SKG MiniMax <br /> / SKG Azure OpenAI <br />
<span className="text-[var(--text-faint)] font-mono"> <span className="text-[var(--text-faint)] font-mono">
{audioScript?.rewrite_model || "AUDIO_REWRITE_MODEL"} {audioScript?.voice_model || "MiniMax T2A"} {audioScript?.rewrite_model || "AUDIO_REWRITE_MODEL"} {audioScript?.voice_model || "Azure OpenAI TTS"}
</span> </span>
</div> </div>
{job && ( {job && (
@@ -2195,7 +2195,7 @@ export function AudioNode({ data, selected }: any) {
)} )}
</div> </div>
)} )}
{voiceUrl && <div className="text-[10.5px] text-emerald-200/85">MiniMax natural English voice ready · </div>} {voiceUrl && <div className="text-[10.5px] text-emerald-200/85">Azure OpenAI English voice ready · </div>}
{isRewriting && ( {isRewriting && (
<div className="text-[10.5px] text-[var(--text-faint)]"></div> <div className="text-[10.5px] text-[var(--text-faint)]"></div>
)} )}

View File

@@ -172,10 +172,8 @@ export interface RuntimeModels {
voice_id?: string voice_id?: string
voice_pool?: string[] voice_pool?: string[]
voice_configured?: boolean voice_configured?: boolean
minimax_tts?: string voice_tts_paths?: string[]
minimax_voice?: string minimax_disabled?: boolean
minimax_voice_pool?: string[]
minimax_configured?: boolean
video?: string video?: string
video_aliases?: Record<string, string> video_aliases?: Record<string, string>
video_provider?: string video_provider?: string
@@ -601,9 +599,8 @@ export interface BackendHealth {
translate?: string translate?: string
rewrite?: string rewrite?: string
audio_rewrite?: string audio_rewrite?: string
minimax_tts?: string voice_tts_paths?: string[]
minimax_voice?: string minimax_disabled?: boolean
minimax_configured?: boolean
video?: string video?: string
video_aliases?: Record<string, string> video_aliases?: Record<string, string>
video_base_url?: string video_base_url?: string