fix(stage-ui): keep wake word preparation when an equal catalog arrives during load (#3104)

This commit is contained in:
CyberSprout
2026-10-28 21:12:16 +08:00
committed by GitHub
parent a19cdf05fb
commit a2bef067be
3 changed files with 24 additions and 9 deletions
@@ -252,6 +252,27 @@ describe('wakeWordDetector', () => {
expect(harness.spotter.setKeywords).not.toHaveBeenCalled()
})
it('becomes ready when the same catalog arrives again while the spotter is loading', async () => {
let finishLoad!: () => void
const loaded = new Promise<void>(resolve => finishLoad = resolve)
const harness = createHarness()
harness.createSpotter.mockImplementationOnce(async () => {
await loaded
return harness.spotter
})
const catalog = () => pronunciations([{ characterId: 'alice', text: 'Alice', tokens: [['AE1', 'L', 'IH0', 'S']] }])
// A reactive catalog can emit an equal list again, for example after the card store hydrates.
const first = harness.detector.setPronunciations(catalog())
const second = harness.detector.setPronunciations(catalog())
finishLoad()
await Promise.all([first, second])
expect(harness.createSpotter).toHaveBeenCalledTimes(1)
expect(harness.detector.ready).toBe(true)
expect(harness.onPreparationChange).toHaveBeenLastCalledWith('ready')
})
it('does not load a model for pronunciations of another model', async () => {
const harness = createHarness()
@@ -152,7 +152,8 @@ export class WakeWordDetector {
const keywords = this.keywordsOf([...next.values()])
const update = this.updates.then(async () => {
// A newer list or a stop replaced this one while an earlier update ran.
if (generation !== this.generation || next !== this.pronunciations)
// An equal list replaces `pronunciations` without a new generation, so only the generation tells a newer list.
if (generation !== this.generation)
return
if (keywords.length === 0) {
@@ -184,7 +185,7 @@ export class WakeWordDetector {
// A keyword update starts a new stream in the Worker.
this.streamSampleRate = undefined
this.resetStream = false
if (generation !== this.generation || next !== this.pronunciations)
if (generation !== this.generation)
return
this.keywordsReady = true
this.options.onPreparationChange?.('ready')
-7
View File
@@ -37,13 +37,6 @@ const audioTestAPI = createAudioTestAPI<
}]
},
async execute({ plan, task, invokeHandler, runPreflight }) {
// NOTICE:
// CI wake word cases time out while preparation stays `preparing`. The root cause is not confirmed.
// Context: audio-web wake word cases time out in the CI Unit Test job.
// Remove this skip after the preparation failure has a verified fix on CI.
if (env.CI && plan.definition.pipelines?.includes('kws'))
task.context.skip(true, 'Wake word preparation times out on CI. The root cause is not confirmed.')
// Electron bundles the wake word model. Web downloads it, so the download must finish before the recording starts.
if (plan.definition.pipelines?.includes('kws') && inject('fakemicRuntime').kind === 'web')
await prepareWakeWordModel()