diff --git a/app/api/insert-beat/route.ts b/app/api/insert-beat/route.ts index e40450a..2366eb0 100644 --- a/app/api/insert-beat/route.ts +++ b/app/api/insert-beat/route.ts @@ -24,7 +24,7 @@ export async function POST(req: Request) { try { const base = loadEngineConfig(req.headers); // See StartRequest.clientTts — BYO clients synth in-browser, so drop server TTS. - const config = body.clientTts ? { ...base, tts: undefined } : base; + const config = body.clientTts === true ? { ...base, tts: undefined } : base; const result = await requestInsertBeat(config, body); return NextResponse.json(result); } catch (err) { diff --git a/app/api/scene/route.ts b/app/api/scene/route.ts index e7a8127..c3b177b 100644 --- a/app/api/scene/route.ts +++ b/app/api/scene/route.ts @@ -25,7 +25,7 @@ export async function POST(req: Request) { try { const base = loadEngineConfig(req.headers); // See StartRequest.clientTts — BYO clients synth in-browser, so drop server TTS. - const config = body.clientTts ? { ...base, tts: undefined } : base; + const config = body.clientTts === true ? { ...base, tts: undefined } : base; const result = await requestScene(config, body); return NextResponse.json(result); } catch (err) { diff --git a/app/api/start/route.ts b/app/api/start/route.ts index 5c9af7c..167c097 100644 --- a/app/api/start/route.ts +++ b/app/api/start/route.ts @@ -45,7 +45,7 @@ export async function POST(req: Request) { // BYO key: the browser provisions + synths voices directly against Xiaomi // (key never reaches us), so strip server-side TTS so the engine skips all // provisioning + synth. See StartRequest.clientTts. - const config = body.clientTts ? { ...base, tts: undefined } : base; + const config = body.clientTts === true ? { ...base, tts: undefined } : base; const result = await startSession(config, body); return NextResponse.json(result); } catch (err) { diff --git a/components/TtsKeyModal.tsx b/components/TtsKeyModal.tsx index bcdd7cd..1713d15 100644 --- a/components/TtsKeyModal.tsx +++ b/components/TtsKeyModal.tsx @@ -48,6 +48,14 @@ export function TtsKeyModal({ const [showKey, setShowKey] = useState(false); const [shown, setShown] = useState(false); const alreadyConfigured = initial != null; + // Soft guard: tp- keys belong to Token Plan, sk- to pay-as-you-go. A + // mismatched pairing hits the wrong endpoint → guaranteed auth failure → + // silent playback (the very symptom BYO exists to kill). Warn, but never + // block: prefix conventions could change and a hard gate would lock out an + // otherwise-valid key. + const expectedPrefix = keyType === "payg" ? "sk-" : "tp-"; + const prefixMismatch = + apiKey.trim().length > 0 && !apiKey.trim().startsWith(expectedPrefix); useEffect(() => { const id = requestAnimationFrame(() => setShown(true)); @@ -214,6 +222,14 @@ export function TtsKeyModal({ /> + {prefixMismatch && ( + + + 此 Key 不是 {expectedPrefix} 开头,可能与所选「 + {keyType === "payg" ? "按量付费 Pay-as-you-go" : "套餐 Token Plan"} + 」类型不符,请确认是否填错。 + + )}