fix(stage-ui): make streaming transcription requests half-duplex (#2283)

This commit is contained in:
RainbowBird
2026-08-14 19:21:48 +08:00
committed by GitHub
parent 497546e02f
commit f45bb2e678
2 changed files with 37 additions and 1 deletions
@@ -3,6 +3,38 @@ import { describe, expect, it } from 'vitest'
import { streamTranscription } from './index' import { streamTranscription } from './index'
describe('streamTranscription', () => { describe('streamTranscription', () => {
it('sets half-duplex transport for the browser audio upload', async () => {
// ROOT CAUSE:
//
// Browser fetch requires `duplex: 'half'` when the request body is a
// ReadableStream. The official provider set this in its fetch wrapper,
// but that wrapper does not own this adapter's stream transport.
//
// Report: T-3, Official provider transcription does not work reliably.
let requestInit: RequestInit | undefined
const audioStream = new ReadableStream<ArrayBuffer>({
start(controller) {
controller.close()
},
})
const result = streamTranscription({
baseURL: 'https://example.invalid/transcription',
fetch: async (_input: RequestInfo | URL, init?: RequestInit) => {
requestInit = init
return new Response(new ReadableStream<Uint8Array>({
start(controller) {
controller.close()
},
}))
},
inputAudioStream: audioStream,
})
await expect(result.text).resolves.toBe('')
expect((requestInit as RequestInit & { duplex?: string }).duplex).toBe('half')
})
it('parses split SSE chunks and joins transcription deltas', async () => { it('parses split SSE chunks and joins transcription deltas', async () => {
const encoder = new TextEncoder() const encoder = new TextEncoder()
const responseBody = new ReadableStream<Uint8Array>({ const responseBody = new ReadableStream<Uint8Array>({
@@ -123,10 +123,14 @@ export function streamTranscription(options: StreamTranscriptionOptions): AIRISt
: new URL(typeof options.baseURL === 'string' ? options.baseURL : 'http://localhost') : new URL(typeof options.baseURL === 'string' ? options.baseURL : 'http://localhost')
const response = await fetcher(requestTarget, { const response = await fetcher(requestTarget, {
body: audioStream, body: audioStream,
// Browser fetch requires half-duplex mode for a ReadableStream body.
// Keep this at the transport boundary so every SSE transcription
// provider receives the required request option.
duplex: 'half',
headers: options.headers, headers: options.headers,
method: 'POST', method: 'POST',
signal: options.abortSignal, signal: options.abortSignal,
}) } as RequestInit & { duplex: 'half' })
if (!response.ok) if (!response.ok)
throw new Error(`Streaming transcription request failed with status ${response.status}`) throw new Error(`Streaming transcription request failed with status ${response.status}`)