From 261f67a67d477c054424eba858d84cea72d90569 Mon Sep 17 00:00:00 2001 From: Liu Xiaopai <73659136+liuxiaopai-ai@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:31:21 +0800 Subject: [PATCH] fix(stage-pages,stage-ui): surface provider transcription errors (#1070) * fix(hearing): surface provider transcription errors * Standardize and enhance error message reporting by introducing an `errorMessage` utility function. --------- Co-authored-by: Rin --- .../src/pages/settings/modules/hearing.vue | 16 +++++++--- .../stage-ui/src/stores/modules/hearing.ts | 29 +++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/stage-pages/src/pages/settings/modules/hearing.vue b/packages/stage-pages/src/pages/settings/modules/hearing.vue index bd35f53e6..27ebcc9e6 100644 --- a/packages/stage-pages/src/pages/settings/modules/hearing.vue +++ b/packages/stage-pages/src/pages/settings/modules/hearing.vue @@ -38,14 +38,16 @@ const { audioInputs, selectedAudioInput, stream } = storeToRefs(useSettingsAudio const { startRecord, stopRecord, onStopRecord } = useAudioRecorder(stream) const { startAnalyzer, stopAnalyzer, onAnalyzerUpdate, volumeLevel } = useAudioAnalyzer() const { audioContext } = storeToRefs(useAudioContext()) +const hearingSpeechInputPipeline = useHearingSpeechInputPipeline() const { transcribeForRecording, transcribeForMediaStream, stopStreamingTranscription, -} = useHearingSpeechInputPipeline() +} = hearingSpeechInputPipeline const { supportsStreamInput, -} = storeToRefs(useHearingSpeechInputPipeline()) + error: transcriptionPipelineError, +} = storeToRefs(hearingSpeechInputPipeline) const animationFrame = ref() @@ -269,7 +271,7 @@ onStopRecord(async (recording) => { console.info('STT test transcription result:', result) } else { - testTranscriptionError.value = 'No transcription result received' + testTranscriptionError.value = transcriptionPipelineError.value || 'No transcription result returned from provider' testStatusMessage.value = 'Transcription failed' } } @@ -290,8 +292,13 @@ onStopRecord(async (recording) => { const res = await transcribeForRecording(recording) - if (res) + if (res) { transcriptions.value.push(res) + error.value = '' + } + else if (transcriptionPipelineError.value) { + error.value = transcriptionPipelineError.value + } }) // Speech-to-Text test functions @@ -310,6 +317,7 @@ async function startSTTTest() { testTranscriptionText.value = '' testStreamingText.value = '' testStatusMessage.value = '' + error.value = '' isTestingSTT.value = true isTranscribing.value = true diff --git a/packages/stage-ui/src/stores/modules/hearing.ts b/packages/stage-ui/src/stores/modules/hearing.ts index 690289800..d03e9e259 100644 --- a/packages/stage-ui/src/stores/modules/hearing.ts +++ b/packages/stage-ui/src/stores/modules/hearing.ts @@ -15,6 +15,15 @@ import { useProvidersStore } from '../providers' import { streamAliyunTranscription } from '../providers/aliyun/stream-transcription' import { streamWebSpeechAPITranscription } from '../providers/web-speech-api' +function errorMessage(err: unknown): string { + const msg = err instanceof Error ? err.message : String(err) + // Browsers hide the real reason (CORS, timeout, DNS, …) behind this generic string. + if (msg === 'Failed to fetch' || msg === 'Load failed') { + return `${msg} — check the browser console (Network tab) for the exact reason (e.g. CORS, network timeout, DNS failure).` + } + return msg +} + export interface StreamTranscriptionFileInputOptions extends Omit { file: Blob fileName?: string @@ -348,7 +357,7 @@ export const useHearingSpeechInputPipeline = defineStore('modules:hearing:speech return text } catch (err) { - error.value = err instanceof Error ? err.message : String(err) + error.value = errorMessage(err) console.error('Error getting transcription result:', error.value) } } @@ -393,7 +402,7 @@ export const useHearingSpeechInputPipeline = defineStore('modules:hearing:speech return text } catch (err) { - error.value = err instanceof Error ? err.message : String(err) + error.value = errorMessage(err) console.error('Error generating transcription:', error.value) } } @@ -424,6 +433,8 @@ export const useHearingSpeechInputPipeline = defineStore('modules:hearing:speech return } + error.value = undefined + try { const providerId = activeTranscriptionProvider.value if (!providerId) { @@ -698,12 +709,14 @@ export const useHearingSpeechInputPipeline = defineStore('modules:hearing:speech } } catch (err) { - error.value = err instanceof Error ? err.message : String(err) + error.value = errorMessage(err) console.error('Error generating transcription:', error.value) } } async function transcribeForRecording(recording: Blob | null | undefined) { + error.value = undefined + if (!recording) return @@ -723,11 +736,17 @@ export const useHearingSpeechInputPipeline = defineStore('modules:hearing:speech model, new File([recording], 'recording.wav'), ) - return result.mode === 'stream' ? await result.text : result.text + const text = result.mode === 'stream' ? await result.text : result.text + if (!text || !text.trim()) { + error.value = 'No transcription result returned from provider' + return + } + + return text } } catch (err) { - error.value = err instanceof Error ? err.message : String(err) + error.value = errorMessage(err) console.error('Error generating transcription:', error.value) } }