fix(stage-ui): share streaming transcription sessions (#2260)

This commit is contained in:
Neko
2026-08-12 00:36:26 +08:00
committed by GitHub
parent 9f34877bab
commit 788a509940
9 changed files with 203 additions and 72 deletions
@@ -20,6 +20,7 @@ function createMockStore() {
const mockTranscribedContent = 'test content'
function createMockPipeline() {
return {
removeStreamingTranscriptionConsumer: vi.fn(),
transcribeForMediaStream: vi.fn().mockImplementation((_stream, options: { onSentenceEnd: (delta: string) => void }) => {
options.onSentenceEnd(mockTranscribedContent)
}),
@@ -330,6 +331,7 @@ describe('useTranscriptions', () => {
await nextTick()
expect(isListening.value).toBe(false)
expect(mockHearingPipeline.stopStreamingTranscription).toHaveBeenCalledWith(true)
expect(mockHearingPipeline.removeStreamingTranscriptionConsumer).toHaveBeenCalledOnce()
})
it('should stop streaming on unmount', async () => {
@@ -351,6 +353,7 @@ describe('useTranscriptions', () => {
app.unmount()
await nextTick()
expect(mockHearingPipeline.stopStreamingTranscription).toHaveBeenCalled()
expect(mockHearingPipeline.removeStreamingTranscriptionConsumer).toHaveBeenCalled()
})
})
@@ -5,7 +5,7 @@ import { useProviderStore } from '@proj-airi/stage-ui/stores/providers/provider'
import { useSettingsAudioDevice } from '@proj-airi/stage-ui/stores/settings'
import { until } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { nextTick, onScopeDispose, ref, toValue, watch } from 'vue'
import { nextTick, onScopeDispose, ref, toValue, useId, watch } from 'vue'
interface TranscriptionOptions {
messageInputRef: Ref<string>
@@ -19,7 +19,7 @@ export function useTranscriptions(options: TranscriptionOptions) {
const hearingStore = useHearingStore()
const audioDeviceSettingsStore = useSettingsAudioDevice()
const hearingPipeline = useHearingSpeechInputPipeline()
const { transcribeForMediaStream, stopStreamingTranscription } = hearingPipeline
const { removeStreamingTranscriptionConsumer, transcribeForMediaStream, stopStreamingTranscription } = hearingPipeline
const { supportsStreamInput } = storeToRefs(hearingPipeline)
const { configured: hearingConfigured, autoSendEnabled, autoSendDelay } = storeToRefs(hearingStore)
const { enabled: hearingEnabled, stream } = storeToRefs(audioDeviceSettingsStore)
@@ -27,6 +27,7 @@ export function useTranscriptions(options: TranscriptionOptions) {
const { askPermission, startStream } = audioDeviceSettingsStore
const isListening = ref(false)
const transcriptionConsumerId = `interactive-area:${useId()}`
// Auto-send logic
let autoSendTimeout: ReturnType<typeof setTimeout> | undefined
@@ -58,6 +59,8 @@ export function useTranscriptions(options: TranscriptionOptions) {
}
const stopStreaming = async () => {
removeStreamingTranscriptionConsumer(transcriptionConsumerId)
if (!isListening.value)
return
@@ -176,6 +179,7 @@ export function useTranscriptions(options: TranscriptionOptions) {
// Set listening state AFTER successful call
try {
await transcribeForMediaStream(stream.value, {
consumerId: transcriptionConsumerId,
onSentenceEnd: (delta) => {
if (delta && delta.trim()) {
console.info('Received transcription delta:', delta, { source: 'useTranscriptions' })