From bc94ba480b849d9087bc1631a75cf38404e4042a Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 13 Apr 2026 13:55:00 +0800 Subject: [PATCH] fix(stage-pages): allow clearing OpenAI-compatible speech model field (#1644) --- .../speech/openai-compatible-audio-speech.vue | 57 +++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/packages/stage-pages/src/pages/settings/providers/speech/openai-compatible-audio-speech.vue b/packages/stage-pages/src/pages/settings/providers/speech/openai-compatible-audio-speech.vue index 07979c648..92079d23e 100644 --- a/packages/stage-pages/src/pages/settings/providers/speech/openai-compatible-audio-speech.vue +++ b/packages/stage-pages/src/pages/settings/providers/speech/openai-compatible-audio-speech.vue @@ -27,6 +27,7 @@ const defaultVoiceSettings = { // Get provider metadata const providerId = 'openai-compatible-audio-speech' const defaultModel = 'tts-1' +const defaultVoice = 'alloy' // Initialize speed from provider config or default const speed = ref( @@ -35,9 +36,12 @@ const speed = ref( || defaultVoiceSettings.speed, ) -// Model selection +// Model selection (store raw string; empty string is valid while editing — default is applied only at API call) const model = computed({ - get: () => providers.value[providerId]?.model as string | undefined || defaultModel, + get: () => { + const raw = providers.value[providerId]?.model as string | undefined | null + return raw ?? '' + }, set: (value) => { if (!providers.value[providerId]) providers.value[providerId] = {} @@ -46,7 +50,10 @@ const model = computed({ }) const voice = computed({ - get: () => providers.value[providerId]?.voice || 'alloy', + get: () => { + const raw = providers.value[providerId]?.voice as string | undefined | null + return raw ?? '' + }, set: (value) => { if (!providers.value[providerId]) providers.value[providerId] = {} @@ -66,19 +73,19 @@ watch( if (Math.abs(speed.value - newSpeed) > 0.001) // Use small epsilon for float comparison speed.value = newSpeed - // Sync model if it was reset - if (!config.model && model.value !== defaultModel) + // Sync model if property was cleared externally (undefined/null), not when user sets empty string + if (config.model == null && model.value !== defaultModel) model.value = defaultModel - // Sync voice if it was reset - if (!config.voice && voice.value !== 'alloy') - voice.value = 'alloy' + // Sync voice if property was cleared externally (undefined/null), not when user sets empty string + if (config.voice == null && voice.value !== defaultVoice) + voice.value = defaultVoice } else { // Provider config was reset, reset our local refs to defaults speed.value = defaultVoiceSettings.speed model.value = defaultModel - voice.value = 'alloy' + voice.value = defaultVoice } }, { deep: true, immediate: true }, @@ -89,16 +96,10 @@ const apiKeyConfigured = computed(() => !!providers.value[providerId]?.apiKey) // Ensure provider config is initialized on mount onMounted(() => { - if (!providers.value[providerId]) { - providers.value[providerId] = {} - } - // Initialize model and voice if they don't exist - if (!providers.value[providerId].model) { - providers.value[providerId].model = defaultModel - } - if (!providers.value[providerId].voice) { - providers.value[providerId].voice = 'alloy' - } + providers.value[providerId] ??= {} + // Defaults only when unset (null/undefined); empty strings are kept intentionally + providers.value[providerId].model ??= defaultModel + providers.value[providerId].voice ??= defaultVoice }) // Generate speech with OpenAI-compatible parameters @@ -118,7 +119,7 @@ async function handleGenerateSpeech(input: string, voiceId: string, _useSSML: bo provider, modelToUse, input, - voiceId || (voice.value as string), + voiceId || voice.value || defaultVoice, { ...providerConfig, ...defaultVoiceSettings, @@ -133,22 +134,6 @@ watch(speed, async () => { providers.value[providerId].speed = speed.value }) -watch(model, () => { - // Ensure provider config exists - if (!providers.value[providerId]) - providers.value[providerId] = {} - // Save model to provider config (this persists to localStorage automatically) - providers.value[providerId].model = model.value -}) - -watch(voice, () => { - // Ensure provider config exists - if (!providers.value[providerId]) - providers.value[providerId] = {} - // Save voice to provider config (this persists to localStorage automatically) - providers.value[providerId].voice = voice.value -}) - // Use the composable to get validation logic and state const { isValidating,