Skip to content

Commit 51a44c1

Browse files
committed
fix: disable SSML when not support
1 parent c3f2beb commit 51a44c1

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

packages/stage-ui/src/stores/modules/speech.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,39 @@ describe('speech store helpers', () => {
126126
expect(request.input).toContain('volume="-5%"')
127127
})
128128

129+
/**
130+
* @example
131+
* speechStore.resolveVoicePackSpeechInput({ text, voice, forceSSML: true, supportsSSML: false, supportsAdapterProsody: true })
132+
*/
133+
it('keeps official adapter-backed speech input as plain text when global SSML is enabled', () => {
134+
const speechStore = useSpeechStore()
135+
const voice = {
136+
id: 'voice-1',
137+
name: 'Voice 1',
138+
provider: OFFICIAL_SPEECH_PROVIDER_ID,
139+
languages: [{ code: 'en-US', title: 'English' }],
140+
gender: 'neutral',
141+
}
142+
143+
// ROOT CAUSE:
144+
//
145+
// Auto TTS can enable global SSML before the server routes the official
146+
// speech provider to DashScope CosyVoice. DashScope rejects `<speak>...`
147+
// payloads with `SSML text is not supported at the moment!`, so providers
148+
// that apply prosody through adapter options must keep the text field plain.
149+
const request = speechStore.resolveVoicePackSpeechInput({
150+
text: 'hello',
151+
voice,
152+
providerConfig: { pitch: 0 },
153+
forceSSML: true,
154+
supportsSSML: false,
155+
supportsAdapterProsody: true,
156+
})
157+
158+
expect(request.input).toBe('hello')
159+
expect(request.input).not.toContain('<speak')
160+
})
161+
129162
/**
130163
* @example
131164
* speechStore.resolveVoicePackSpeechInput({ text, voice, params: { pitch: '+20%' }, supportsAdapterProsody: true })

packages/stage-ui/src/stores/modules/speech.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,10 +473,11 @@ export const useSpeechStore = defineStore('speech', () => {
473473

474474
function resolveVoicePackSpeechInput(options: VoicePackSpeechInputOptions): VoicePackSpeechInput {
475475
const providerConfig = { ...options.providerConfig }
476+
const canUseSSML = options.supportsSSML === true
476477

477478
if (!options.params) {
478479
return {
479-
input: options.forceSSML
480+
input: options.forceSSML === true && canUseSSML
480481
? generateSSML(options.text, options.voice, providerConfig)
481482
: options.text,
482483
providerConfig,
@@ -493,6 +494,8 @@ export const useSpeechStore = defineStore('speech', () => {
493494
if (speed != null)
494495
providerConfig.speed = speed
495496

497+
const shouldUseSSML = canUseSSML && (options.forceSSML === true || (needsProsody && !options.supportsAdapterProsody))
498+
496499
if (options.voicePack) {
497500
providerConfig.extraBody = {
498501
...(providerConfig.extraBody as Record<string, unknown> | undefined),
@@ -511,11 +514,11 @@ export const useSpeechStore = defineStore('speech', () => {
511514
voice_pack: { pitch, volume },
512515
}
513516
}
514-
else if (needsProsody && !options.forceSSML && !options.supportsSSML) {
517+
else if (needsProsody && !options.supportsAdapterProsody && !shouldUseSSML) {
515518
throw new Error('Voice Pack pitch and volume parameters require an SSML-capable speech provider.')
516519
}
517520

518-
if (!options.forceSSML && (!needsProsody || options.supportsAdapterProsody)) {
521+
if (!shouldUseSSML && (!needsProsody || options.supportsAdapterProsody)) {
519522
return {
520523
input: options.text,
521524
providerConfig,

0 commit comments

Comments
 (0)