feat(stage-ui): introduce helpers for stream and tts chunking (#335)

This commit is contained in:
Makito
2025-07-30 11:07:20 +08:00
committed by GitHub
parent 76ed0ff315
commit 43a1360a2f
3 changed files with 34 additions and 18 deletions
+5 -18
View File
@@ -4,7 +4,8 @@ import type { UseQueueReturn } from './queue'
import { sleep } from '@moeru/std'
import { EMOTION_VALUES } from '../constants/emotions'
import { chunkTTSInput } from '../utils/tts'
import { createControllableStream } from '../utils/stream'
import { chunkToTTSQueue } from '../utils/tts'
import { useQueue } from './queue'
export function useEmotionsMessageQueue(emotionsQueue: UseQueueReturn<Emotion>) {
@@ -102,28 +103,14 @@ export function useDelayMessageQueue() {
export function useMessageContentQueue(ttsQueue: UseQueueReturn<string>) {
const encoder = new TextEncoder()
let enqueue: (data: Uint8Array) => void
const stream = new ReadableStream<Uint8Array>({
start(controller) {
enqueue = data => controller.enqueue(data)
},
});
const { stream, controller } = createControllableStream<Uint8Array>()
(async () => {
try {
for await (const chunk of chunkTTSInput(stream.getReader())) {
await ttsQueue.add(chunk.text)
}
}
catch (e) {
console.error('Error chunking input stream for TTS:', e)
}
})()
chunkToTTSQueue(stream.getReader(), ttsQueue)
return useQueue<string>({
handlers: [
async (ctx) => {
enqueue(encoder.encode(ctx.data))
controller.enqueue(encoder.encode(ctx.data))
},
],
})
+16
View File
@@ -0,0 +1,16 @@
export interface ControllableStream<R = any> {
stream: ReadableStream<R>
controller: ReadableStreamDefaultController<R>
}
export function createControllableStream<R = any>(): ControllableStream<R> {
// WHY!: ReadableStream.start is called synchronously and immediately
let controller!: ReadableStreamDefaultController<R>
const stream = new ReadableStream<R>({
start(ctrl) {
controller = ctrl
},
})
return { stream, controller }
}
+13
View File
@@ -1,5 +1,7 @@
import type { ReaderLike } from 'clustr'
import type { UseQueueReturn } from '../composables/queue'
import { readGraphemeClusters } from 'clustr'
// A special character to instruct the TTS pipeline to flush
@@ -144,3 +146,14 @@ export async function* chunkTTSInput(input: string | ReaderLike, options?: TTSIn
}
}
}
export async function chunkToTTSQueue(reader: ReaderLike, queue: UseQueueReturn<string>) {
try {
for await (const chunk of chunkTTSInput(reader)) {
await queue.add(chunk.text)
}
}
catch (e) {
console.error('Error chunking stream to TTS queue:', e)
}
}