## Problem The WebGPU inference pipeline had several structural issues: 1. **No unified protocol** — Kokoro TTS, Whisper ASR, and Background Removal workers each used their own ad-hoc message formats. Adding a new model meant reinventing worker communication from scratch. 2. **Infrastructure existed but was disconnected** — `GPUResourceCoordinator`, `LoadQueue`, `InferenceWorkerManager`, and `protocol.ts` were all implemented but had zero consumers. The adapters duplicated the same lifecycle/timeout/mutex patterns independently. 3. **Performance gaps** — Kokoro only offered fp32 on WebGPU (no fp16), Whisper warm-up compiled shaders for 187.5s of dummy audio, audio transfer went through unnecessary WAV blob encode/decode, and `listVoices` reloaded the model every time. 4. **Silent failures** — Whisper worker's `generate()` had no try-catch; errors were swallowed and the main thread waited until timeout. 5. **No graceful degradation** — Whisper and Background Removal workers hardcoded `device: 'webgpu'` with no WASM fallback. 6. **No observability** — Only Kokoro had performance tracing. No adapter reported status to `useInferenceStatus`. No cache management UI existed. 7. **Dead code accumulation** — Old `KokoroWorkerManager` (232 lines), legacy Whisper message types, and scattered duplicate constants. ## Changes ### Phase 0 — Critical Performance & Bugs - Add `fp16-webgpu` dtype for Kokoro TTS (~2x inference speed on supported GPUs) - Fix Whisper warm-up tensor from `[1, 128, 3000]` → `[1, 128, 1]` (minimal shader compilation) - Fix Whisper worker silent error bug (add try-catch to `generate()` and `load()`) ### Phase 1 — Data Transfer & Caching - Switch Kokoro audio to Float32Array transferable (skip WAV blob encode in worker, lightweight WAV encode on main thread) - Cache `listVoices` results (skip redundant model reload when adapter state is `ready`) - Normalize progress reporting to 0-100 across all adapters, differentiate `warmup` phase ### Phase 2 — Protocol Unification & Infrastructure - Migrate all 3 workers + 3 adapters to unified `protocol.ts` message types (`load-model`, `run-inference`, `model-ready`, `inference-result`, `progress`, `error`) - Wire `GPUResourceCoordinator` into all adapters (VRAM allocation tracking, LRU ordering, memory pressure events) - Wire `LoadQueue` into all adapters (priority-based sequential model loading: TTS=10 > ASR=5 > BG_REMOVAL=1) - Add `coordinator.ts` global singleton for GPU coordinator + load queue - Add WebGPU detection + WASM fallback in Whisper and Background Removal workers ### Phase 3 — Error Recovery & Observability - Add restart logic with exponential backoff to Whisper adapter (matching Kokoro's existing pattern) - Integrate `classifyError()` (OOM / DEVICE_LOST / TIMEOUT classification) in Whisper adapter - Extend `defaultPerfTracer` to Whisper `transcribe()` and Background Removal `processImage()` - Wire `useInferenceStatus` into all 3 adapters (downloading → ready → terminated lifecycle) ### Phase 4 — Tests - Add unit tests for `AsyncMutex` (4 tests), `LoadQueue` (4 tests), `GPUResourceCoordinator` (7 tests) — all 15 passing ### Phase 5 — Cleanup & Features - Delete old `KokoroWorkerManager` (232 lines, zero consumers) - Delete orphaned `libs/workers/types.ts` (old Whisper message types) - Clean up `workers/kokoro/types.ts` (remove legacy message types, keep domain types) - Create centralized `constants.ts` (MODEL_IDS, MODEL_NAMES, TIMEOUTS, MAX_RESTARTS) - Remove hardcoded WebGPU check from background-removal devtools pages (worker auto-detects) - Add `useModelPreload` composable for generic idle-time preloading - Add `useInferencePreload` composable that reads provider config and preloads configured local models - Wire preloading into both `stage-web` and `stage-tamagotchi` App.vue (Kokoro TTS preloads 3s after init) - Add `ModelCacheManager.vue` settings component (cache size display, per-model status, clear cache) - Document GPU Device isolation architecture in protocol.ts ## After This PR - All inference workers speak the same protocol → adding a new model adapter is straightforward - GPU memory is tracked across all models with automatic pressure warnings at 80%/95% of VRAM budget - Models load sequentially via priority queue → no bandwidth/VRAM contention - Workers auto-detect WebGPU and fall back to WASM → works on browsers without WebGPU - Kokoro TTS preloads during idle time → "instant" first use for configured users - All adapters auto-restart on worker crashes (max 3 attempts, exponential backoff) - 15 unit tests cover core infrastructure (mutex, queue, coordinator) - Zero dead code remains in the inference pipeline ## Test Plan - [x] `pnpm exec vitest run packages/stage-ui/src/libs/inference/` — 15 tests pass - [x] `pnpm -F @proj-airi/stage-ui exec tsc --noEmit` — no TypeScript errors - [x] `pnpm lint:fix` — no lint errors in changed files - [ ] Manual: verify Kokoro TTS works with fp16-webgpu on a supported browser - [ ] Manual: verify Whisper ASR loads and transcribes correctly - [ ] Manual: verify Background Removal works in devtools page - [ ] Manual: verify preloading triggers in console (`[Preload] Loading kokoro-...`) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
212 lines
5.7 KiB
YAML
212 lines
5.7 KiB
YAML
catalogMode: prefer
|
|
shellEmulator: true
|
|
packages:
|
|
- packages/**
|
|
- plugins/**
|
|
- integrations/**
|
|
- services/**
|
|
- examples/**
|
|
- docs/**
|
|
- apps/**
|
|
- '!**/dist/**'
|
|
overrides:
|
|
array-flatten: npm:@nolyfill/array-flatten@^1.0.44
|
|
axios: npm:feaxios@^0.0.23
|
|
is-core-module: npm:@nolyfill/is-core-module@^1.0.39
|
|
isarray: npm:@nolyfill/isarray@^1.0.44
|
|
onnxruntime-web: npm:onnxruntime-web@^1.24.3
|
|
safe-buffer: npm:@nolyfill/safe-buffer@^1.0.44
|
|
safer-buffer: npm:@nolyfill/safer-buffer@^1.0.44
|
|
side-channel: npm:@nolyfill/side-channel@^1.0.44
|
|
string.prototype.matchall: npm:@nolyfill/string.prototype.matchall@^1.0.44
|
|
patchedDependencies:
|
|
'@mediapipe/tasks-vision': patches/@mediapipe__tasks-vision.patch
|
|
'@xsai/generate-text@0.5.0-beta.2': patches/@xsai__generate-text@0.5.0-beta.2.patch
|
|
'@xsai/shared-chat@0.5.0-beta.2': patches/@xsai__shared-chat@0.5.0-beta.2.patch
|
|
'@xsai/stream-text@0.5.0-beta.2': patches/@xsai__stream-text@0.5.0-beta.2.patch
|
|
crossws@0.4.4: patches/crossws@0.4.4.patch
|
|
mineflayer-pathfinder: patches/mineflayer-pathfinder.patch
|
|
# mineflayer@4.33.0: patches/mineflayer@4.33.0.patch
|
|
pixi-live2d-display: patches/pixi-live2d-display.patch
|
|
catalog:
|
|
'@better-auth/oauth-provider': 1.5.6
|
|
'@capacitor/android': ^8.3.0
|
|
'@capacitor/barcode-scanner': ^3.0.2
|
|
'@capacitor/cli': ^8.3.0
|
|
'@capacitor/core': ^8.3.0
|
|
'@capacitor/ios': ^8.3.0
|
|
'@capacitor/local-notifications': ^8.0.2
|
|
'@electric-sql/pglite': ^0.4.4
|
|
'@electron-toolkit/preload': ^3.0.2
|
|
'@electron/notarize': ^3.1.1
|
|
'@fontsource-variable/nunito': ^5.2.7
|
|
'@guiiai/logg': ^1.2.11
|
|
'@histoire/plugin-vue': 1.0.0-beta.1
|
|
'@hono/node-ws': ^1.3.0
|
|
'@iconify-json/line-md': ^1.2.16
|
|
'@iconify-json/logos': ^1.2.11
|
|
'@iconify-json/material-symbols': ^1.2.66
|
|
'@iconify-json/mdi': ^1.2.3
|
|
'@iconify-json/tabler': ^1.2.33
|
|
'@intlify/core': ^11.3.2
|
|
'@modelcontextprotocol/sdk': ^1.29.0
|
|
'@moeru/eslint-config': 0.1.0-beta.15
|
|
'@moeru/eventa': 1.0.0-beta.3
|
|
'@moeru/std': 0.1.0-beta.17
|
|
'@napi-rs/image': ^1.12.0
|
|
'@nekopaw/tempora': 0.4.0-alpha.1
|
|
'@pinia/testing': ^1.0.3
|
|
'@proj-airi/drizzle-duckdb-wasm': ^0.4.29
|
|
'@proj-airi/iconify-meteocons': ^0.1.4
|
|
'@proj-airi/unplugin-drizzle-orm-migrations': ^0.1.6
|
|
'@proj-airi/unplugin-fetch': ^0.2.2
|
|
'@shopify/draggable': ^1.2.1
|
|
'@takumi-rs/image-response': 1.0.0-beta.20
|
|
'@types/audioworklet': ^0.0.97
|
|
'@types/d3': ^7.4.3
|
|
'@types/hast': ^3.0.4
|
|
'@types/splitpanes': ^2.2.6
|
|
'@types/unist': ^3.0.3
|
|
'@types/ws': ^8.18.1
|
|
'@vueuse/core': 14.1.0
|
|
'@xsai-ext/providers': 0.5.0-beta.2
|
|
'@xsai/embed': 0.5.0-beta.2
|
|
'@xsai/generate-speech': 0.5.0-beta.2
|
|
'@xsai/generate-text': 0.5.0-beta.2
|
|
'@xsai/generate-transcription': 0.5.0-beta.2
|
|
'@xsai/model': 0.5.0-beta.2
|
|
'@xsai/shared': 0.5.0-beta.2
|
|
'@xsai/shared-chat': 0.5.0-beta.2
|
|
'@xsai/stream-text': 0.5.0-beta.2
|
|
'@xsai/stream-transcription': 0.5.0-beta.2
|
|
'@xsai/tool': 0.5.0-beta.2
|
|
'@xsai/utils-chat': 0.5.0-beta.2
|
|
alien-signals: ^3.1.2
|
|
async-mutex: 0.5.0
|
|
better-auth: ^1.6.2
|
|
better-call: ^2.0.3
|
|
builder-util-runtime: ^9.5.1
|
|
cac: ^7.0.0
|
|
capacitor-native-settings: ^8.1.0
|
|
crossws: ^0.4.4
|
|
d3: 7.9.0
|
|
date-fns: ^4.1.0
|
|
drizzle-kit: ^0.31.10
|
|
drizzle-orm: ^0.45.2
|
|
drizzle-valibot: ^0.4.2
|
|
electron: ^40.8.5
|
|
embla-carousel-vue: 9.0.0-rc02
|
|
es-toolkit: 1.43.0
|
|
get-port-please: ^3.2.0
|
|
histoire: 1.0.0-beta.1
|
|
hono: 4.11.3
|
|
hono-rate-limiter: ^0.5.3
|
|
idb-keyval: ^6.2.2
|
|
injeca: ^0.1.8
|
|
is-network-error: ^1.3.1
|
|
isolated-vm: ^6.1.2
|
|
jose: ^6.2.2
|
|
knip: ^6.3.1
|
|
meow: ^14.1.0
|
|
mkcert: ^3.2.0
|
|
nano-staged: ^1.0.2
|
|
nanoid: 5.1.6
|
|
node-pty: ^1.1.0
|
|
ofetch: ^1.5.1
|
|
oxc-minify: ^0.124.0
|
|
pinia: ^3.0.4
|
|
posthog-js: 1.306.1
|
|
splitpanes: ^4.0.4
|
|
std-env: ^4.0.0
|
|
superjson: ^2.2.6
|
|
tsdown: ^0.21.7
|
|
tsx: ^4.21.0
|
|
uncrypto: ^0.1.3
|
|
unplugin-info: ^1.3.2
|
|
unstorage: ^1.17.5
|
|
uqr: ^0.1.3
|
|
valibot: 1.2.0
|
|
vite: ^8.0.8
|
|
vite-plugin-inspect: 12.0.0-beta.1
|
|
vite-plugin-mkcert: ^2.0.0
|
|
vue: ^3.5.32
|
|
vue-sonner: 2.0.9
|
|
web-haptics: ^0.0.6
|
|
ws: ^8.20.0
|
|
xsschema: 0.5.0-beta.2
|
|
yaml: ^2.8.3
|
|
zod: ^4.3.6
|
|
catalogs:
|
|
vitest:
|
|
'@vitest/browser-playwright': ^4.1.4
|
|
'@vitest/coverage-v8': ^4.1.4
|
|
vitest: ^4.1.4
|
|
xsai:
|
|
unspeech: ^0.1.11
|
|
|
|
ignoredBuiltDependencies:
|
|
- '@prisma/client'
|
|
- better-sqlite3
|
|
- simple-git-hooks # [workaround] postinstall script bundled in simple-git-hooks fails to execute with `enableGlobalVirtualStore: true`. Using local postinstall script to run `npx simple-git-hooks` instead.
|
|
|
|
onlyBuiltDependencies:
|
|
- '@discordjs/opus'
|
|
- '@ffmpeg-installer/darwin-arm64'
|
|
- '@ffmpeg-installer/linux-x64'
|
|
- '@napi-rs/image'
|
|
- '@parcel/watcher'
|
|
- bufferutil
|
|
- canvas
|
|
- core-js
|
|
- electron
|
|
- electron-click-drag-plugin
|
|
- electron-winstaller
|
|
- es5-ext
|
|
- esbuild
|
|
- ffmpeg-static
|
|
- isolated-vm
|
|
- less
|
|
- msw
|
|
- node-pty
|
|
- onnxruntime-node
|
|
- protobufjs
|
|
- sharp
|
|
- spawn-sync
|
|
- utf-8-validate
|
|
- vue-demi
|
|
packageExtensions:
|
|
'@formkit/auto-animate':
|
|
peerDependencies:
|
|
vue: '*'
|
|
'@huggingface/transformers':
|
|
optionalDependencies:
|
|
'onnxruntime-common': '*'
|
|
'@intlify/unplugin-vue-i18n':
|
|
peerDependencies:
|
|
vite: '*'
|
|
'@masknet/eslint-plugin':
|
|
peerDependencies:
|
|
typescript: '*'
|
|
'@pixiv/three-vrm-animation':
|
|
peerDependencies:
|
|
'@types/three': '*'
|
|
'@pixiv/three-vrm-core':
|
|
peerDependencies:
|
|
'@types/three': '*'
|
|
'@pmndrs/pointer-events':
|
|
peerDependencies:
|
|
three: '*'
|
|
'@tresjs/core':
|
|
peerDependencies:
|
|
'@types/three': '*'
|
|
'@unocss/eslint-plugin':
|
|
peerDependencies:
|
|
'@typescript-eslint/types': '*'
|
|
'vitepress':
|
|
peerDependencies:
|
|
vite: '*'
|
|
vue: '*'
|
|
'vue-sonner':
|
|
peerDependencies:
|
|
vue: '^3.2.0'
|