@elizaos/plugin-local-inference 2.0.0-beta.1 → 2.0.11-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/package.json +81 -15
- package/src/actions/generate-media.d.ts +59 -0
- package/src/actions/generate-media.d.ts.map +1 -0
- package/src/actions/generate-media.ts +647 -0
- package/src/actions/identify-speaker.d.ts +23 -0
- package/src/actions/identify-speaker.d.ts.map +1 -0
- package/src/actions/identify-speaker.ts +171 -0
- package/src/adapters/capacitor-llama/__tests__/compat-behavior.test.ts +218 -0
- package/src/adapters/capacitor-llama/__tests__/index.test.ts +68 -0
- package/src/adapters/capacitor-llama/__tests__/structured-output.test.ts +215 -0
- package/src/adapters/capacitor-llama/__tests__/text-streaming.test.ts +174 -0
- package/src/adapters/capacitor-llama/environment.ts +71 -0
- package/src/adapters/capacitor-llama/index.browser.ts +83 -0
- package/src/adapters/capacitor-llama/index.ts +807 -0
- package/src/adapters/capacitor-llama/loader.ts +109 -0
- package/src/adapters/capacitor-llama/structured-output.ts +165 -0
- package/src/adapters/capacitor-llama/text-streaming.ts +227 -0
- package/src/adapters/capacitor-llama/types.ts +374 -0
- package/src/backends/apple-foundation.ts +127 -0
- package/src/index.d.ts +7 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.ts +54 -0
- package/src/local-inference-routes.d.ts +38 -0
- package/src/local-inference-routes.d.ts.map +1 -0
- package/src/local-inference-routes.test.ts +344 -0
- package/src/local-inference-routes.ts +1543 -0
- package/src/provider.d.ts +21 -0
- package/src/provider.d.ts.map +1 -0
- package/src/provider.ts +1171 -0
- package/src/routes/compat-helpers.d.ts +18 -0
- package/src/routes/compat-helpers.d.ts.map +1 -0
- package/src/routes/compat-helpers.ts +274 -0
- package/src/routes/family-member-route.d.ts +62 -0
- package/src/routes/family-member-route.d.ts.map +1 -0
- package/src/routes/family-member-route.ts +353 -0
- package/src/routes/index.d.ts +19 -0
- package/src/routes/index.d.ts.map +1 -0
- package/src/routes/index.ts +60 -0
- package/src/routes/live-diarization-route.d.ts +26 -0
- package/src/routes/live-diarization-route.d.ts.map +1 -0
- package/src/routes/live-diarization-route.test.ts +213 -0
- package/src/routes/live-diarization-route.ts +122 -0
- package/src/routes/local-inference-asr-route.d.ts +4 -0
- package/src/routes/local-inference-asr-route.d.ts.map +1 -0
- package/src/routes/local-inference-asr-route.test.ts +190 -0
- package/src/routes/local-inference-asr-route.ts +213 -0
- package/src/routes/local-inference-compat-routes.d.ts +16 -0
- package/src/routes/local-inference-compat-routes.d.ts.map +1 -0
- package/src/routes/local-inference-compat-routes.test.ts +423 -0
- package/src/routes/local-inference-compat-routes.ts +782 -0
- package/src/routes/local-inference-tts-route.d.ts +7 -0
- package/src/routes/local-inference-tts-route.d.ts.map +1 -0
- package/src/routes/local-inference-tts-route.test.ts +179 -0
- package/src/routes/local-inference-tts-route.ts +230 -0
- package/src/routes/voice-first-run-routes.d.ts +62 -0
- package/src/routes/voice-first-run-routes.d.ts.map +1 -0
- package/src/routes/voice-first-run-routes.ts +524 -0
- package/src/routes/voice-models-routes.d.ts +62 -0
- package/src/routes/voice-models-routes.d.ts.map +1 -0
- package/src/routes/voice-models-routes.ts +554 -0
- package/src/routes/voice-profile-plugin-routes.d.ts +19 -0
- package/src/routes/voice-profile-plugin-routes.d.ts.map +1 -0
- package/src/routes/voice-profile-plugin-routes.ts +138 -0
- package/src/routes/voice-profiles-management-routes.d.ts +52 -0
- package/src/routes/voice-profiles-management-routes.d.ts.map +1 -0
- package/src/routes/voice-profiles-management-routes.ts +476 -0
- package/src/routes/voice-speaker-profile-routes.d.ts +57 -0
- package/src/routes/voice-speaker-profile-routes.d.ts.map +1 -0
- package/src/routes/voice-speaker-profile-routes.ts +199 -0
- package/src/runtime/aosp-llama-loader-selection.test.ts +80 -0
- package/src/runtime/capacitor-llama.d.ts +25 -0
- package/src/runtime/embedding-manager-support.d.ts +77 -0
- package/src/runtime/embedding-manager-support.d.ts.map +1 -0
- package/src/runtime/embedding-manager-support.ts +497 -0
- package/src/runtime/embedding-presets.d.ts +16 -0
- package/src/runtime/embedding-presets.d.ts.map +1 -0
- package/src/runtime/embedding-presets.ts +81 -0
- package/src/runtime/embedding-warmup-policy.d.ts +14 -0
- package/src/runtime/embedding-warmup-policy.d.ts.map +1 -0
- package/src/runtime/embedding-warmup-policy.test.ts +53 -0
- package/src/runtime/embedding-warmup-policy.ts +48 -0
- package/src/runtime/ensure-local-inference-handler.d.ts +53 -0
- package/src/runtime/ensure-local-inference-handler.d.ts.map +1 -0
- package/src/runtime/ensure-local-inference-handler.test.ts +528 -0
- package/src/runtime/ensure-local-inference-handler.ts +1398 -0
- package/src/runtime/index.d.ts +14 -0
- package/src/runtime/index.d.ts.map +1 -0
- package/src/runtime/index.ts +27 -0
- package/src/runtime/mobile-local-inference-gate.d.ts +31 -0
- package/src/runtime/mobile-local-inference-gate.d.ts.map +1 -0
- package/src/runtime/mobile-local-inference-gate.test.ts +69 -0
- package/src/runtime/mobile-local-inference-gate.ts +44 -0
- package/src/runtime/voice-entity-binding.d.ts +103 -0
- package/src/runtime/voice-entity-binding.d.ts.map +1 -0
- package/src/runtime/voice-entity-binding.transcript.test.ts +69 -0
- package/src/runtime/voice-entity-binding.ts +328 -0
- package/src/services/README.md +71 -0
- package/src/services/__tests__/backend-selector.test.ts +101 -0
- package/src/services/__tests__/checkpoint-manager.test.ts +376 -0
- package/src/services/__tests__/gpu-autotune.test.ts +400 -0
- package/src/services/__tests__/llm-streaming-binding.test.ts +85 -0
- package/src/services/__tests__/planner-grammar.test.ts +372 -0
- package/src/services/__tests__/runtime-target.test.ts +176 -0
- package/src/services/active-model-switch-rollback.test.ts +183 -0
- package/src/services/active-model.d.ts +282 -0
- package/src/services/active-model.d.ts.map +1 -0
- package/src/services/active-model.ts +1213 -0
- package/src/services/asr/errors.d.ts +21 -0
- package/src/services/asr/errors.d.ts.map +1 -0
- package/src/services/asr/errors.ts +50 -0
- package/src/services/asr/hash.d.ts +28 -0
- package/src/services/asr/hash.d.ts.map +1 -0
- package/src/services/asr/hash.ts +49 -0
- package/src/services/asr/index.d.ts +76 -0
- package/src/services/asr/index.d.ts.map +1 -0
- package/src/services/asr/index.ts +178 -0
- package/src/services/asr/types.d.ts +91 -0
- package/src/services/asr/types.d.ts.map +1 -0
- package/src/services/asr/types.ts +95 -0
- package/src/services/assignments.d.ts +71 -0
- package/src/services/assignments.d.ts.map +1 -0
- package/src/services/assignments.test.ts +80 -0
- package/src/services/assignments.ts +230 -0
- package/src/services/backend-selector.ts +95 -0
- package/src/services/backend.d.ts +346 -0
- package/src/services/backend.d.ts.map +1 -0
- package/src/services/backend.ts +612 -0
- package/src/services/bundled-models.d.ts +34 -0
- package/src/services/bundled-models.d.ts.map +1 -0
- package/src/services/bundled-models.ts +129 -0
- package/src/services/cache-bridge.d.ts +206 -0
- package/src/services/cache-bridge.d.ts.map +1 -0
- package/src/services/cache-bridge.test.ts +516 -0
- package/src/services/cache-bridge.ts +423 -0
- package/src/services/catalog.d.ts +10 -0
- package/src/services/catalog.d.ts.map +1 -0
- package/src/services/catalog.test.ts +240 -0
- package/src/services/catalog.ts +27 -0
- package/src/services/checkpoint-client.d.ts +109 -0
- package/src/services/checkpoint-client.d.ts.map +1 -0
- package/src/services/checkpoint-client.ts +258 -0
- package/src/services/checkpoint-manager.ts +474 -0
- package/src/services/cloud-fallback.d.ts +102 -0
- package/src/services/cloud-fallback.d.ts.map +1 -0
- package/src/services/cloud-fallback.ts +230 -0
- package/src/services/conversation-registry.d.ts +142 -0
- package/src/services/conversation-registry.d.ts.map +1 -0
- package/src/services/conversation-registry.test.ts +235 -0
- package/src/services/conversation-registry.ts +264 -0
- package/src/services/desktop-fused-ffi-backend-runtime.d.ts +92 -0
- package/src/services/desktop-fused-ffi-backend-runtime.d.ts.map +1 -0
- package/src/services/desktop-fused-ffi-backend-runtime.ts +333 -0
- package/src/services/device-bridge.d.ts +188 -0
- package/src/services/device-bridge.d.ts.map +1 -0
- package/src/services/device-bridge.ts +1237 -0
- package/src/services/device-resource-metrics.d.ts +149 -0
- package/src/services/device-resource-metrics.d.ts.map +1 -0
- package/src/services/device-resource-metrics.test.ts +98 -0
- package/src/services/device-resource-metrics.ts +346 -0
- package/src/services/device-tier.d.ts +115 -0
- package/src/services/device-tier.d.ts.map +1 -0
- package/src/services/device-tier.test.ts +371 -0
- package/src/services/device-tier.ts +410 -0
- package/src/services/downloader.d.ts +82 -0
- package/src/services/downloader.d.ts.map +1 -0
- package/src/services/downloader.test.ts +724 -0
- package/src/services/downloader.ts +899 -0
- package/src/services/engine-direct-bundle.test.ts +58 -0
- package/src/services/engine-streaming.test.ts +80 -0
- package/src/services/engine.d.ts +534 -0
- package/src/services/engine.d.ts.map +1 -0
- package/src/services/engine.ts +1891 -0
- package/src/services/ensure-local-artifacts.integration.test.ts +273 -0
- package/src/services/ensure-local-artifacts.test.ts +368 -0
- package/src/services/ensure-local-artifacts.ts +351 -0
- package/src/services/external-scanner.d.ts +17 -0
- package/src/services/external-scanner.d.ts.map +1 -0
- package/src/services/external-scanner.ts +312 -0
- package/src/services/ffi-llm-mock.ts +354 -0
- package/src/services/ffi-llm-streaming-abi.ts +442 -0
- package/src/services/ffi-streaming-backend.d.ts +180 -0
- package/src/services/ffi-streaming-backend.d.ts.map +1 -0
- package/src/services/ffi-streaming-backend.ts +382 -0
- package/src/services/ffi-streaming-runner.d.ts +122 -0
- package/src/services/ffi-streaming-runner.d.ts.map +1 -0
- package/src/services/ffi-streaming-runner.test.ts +60 -0
- package/src/services/ffi-streaming-runner.ts +354 -0
- package/src/services/ffi-unload-ordering.test.ts +162 -0
- package/src/services/gpu-autotune.ts +534 -0
- package/src/services/gpu-detect.ts +139 -0
- package/src/services/handler-registry.d.ts +72 -0
- package/src/services/handler-registry.d.ts.map +1 -0
- package/src/services/handler-registry.ts +240 -0
- package/src/services/hardware.d.ts +63 -0
- package/src/services/hardware.d.ts.map +1 -0
- package/src/services/hardware.test.ts +183 -0
- package/src/services/hardware.ts +404 -0
- package/src/services/hf-search.d.ts +26 -0
- package/src/services/hf-search.d.ts.map +1 -0
- package/src/services/hf-search.test.ts +69 -0
- package/src/services/hf-search.ts +420 -0
- package/src/services/image-description-runtime.d.ts +14 -0
- package/src/services/image-description-runtime.d.ts.map +1 -0
- package/src/services/image-description-runtime.test.ts +61 -0
- package/src/services/image-description-runtime.ts +118 -0
- package/src/services/imagegen/aosp-unavailable.d.ts +134 -0
- package/src/services/imagegen/aosp-unavailable.d.ts.map +1 -0
- package/src/services/imagegen/aosp-unavailable.ts +229 -0
- package/src/services/imagegen/backend-selector.d.ts +118 -0
- package/src/services/imagegen/backend-selector.d.ts.map +1 -0
- package/src/services/imagegen/backend-selector.ts +281 -0
- package/src/services/imagegen/coreml-unavailable.d.ts +105 -0
- package/src/services/imagegen/coreml-unavailable.d.ts.map +1 -0
- package/src/services/imagegen/coreml-unavailable.ts +237 -0
- package/src/services/imagegen/errors.d.ts +16 -0
- package/src/services/imagegen/errors.d.ts.map +1 -0
- package/src/services/imagegen/errors.ts +40 -0
- package/src/services/imagegen/index.d.ts +58 -0
- package/src/services/imagegen/index.d.ts.map +1 -0
- package/src/services/imagegen/index.ts +144 -0
- package/src/services/imagegen/mflux.d.ts +74 -0
- package/src/services/imagegen/mflux.d.ts.map +1 -0
- package/src/services/imagegen/mflux.ts +313 -0
- package/src/services/imagegen/sd-cpp.d.ts +180 -0
- package/src/services/imagegen/sd-cpp.d.ts.map +1 -0
- package/src/services/imagegen/sd-cpp.ts +718 -0
- package/src/services/imagegen/tensorrt-unavailable.d.ts +83 -0
- package/src/services/imagegen/tensorrt-unavailable.d.ts.map +1 -0
- package/src/services/imagegen/tensorrt-unavailable.ts +295 -0
- package/src/services/imagegen/types.d.ts +181 -0
- package/src/services/imagegen/types.d.ts.map +1 -0
- package/src/services/imagegen/types.ts +193 -0
- package/src/services/index.d.ts +30 -0
- package/src/services/index.d.ts.map +1 -0
- package/src/services/index.ts +225 -0
- package/src/services/inference-capabilities.d.ts +132 -0
- package/src/services/inference-capabilities.d.ts.map +1 -0
- package/src/services/inference-capabilities.test.ts +75 -0
- package/src/services/inference-capabilities.ts +204 -0
- package/src/services/inference-telemetry.d.ts +59 -0
- package/src/services/inference-telemetry.d.ts.map +1 -0
- package/src/services/inference-telemetry.ts +143 -0
- package/src/services/ios-llama-streaming.ts +248 -0
- package/src/services/kv-spill.d.ts +189 -0
- package/src/services/kv-spill.d.ts.map +1 -0
- package/src/services/kv-spill.test.ts +222 -0
- package/src/services/kv-spill.ts +356 -0
- package/src/services/latency-trace.d.ts +346 -0
- package/src/services/latency-trace.d.ts.map +1 -0
- package/src/services/latency-trace.test.ts +266 -0
- package/src/services/latency-trace.ts +844 -0
- package/src/services/llama-server-metrics.ts +304 -0
- package/src/services/llm-streaming-binding.d.ts +96 -0
- package/src/services/llm-streaming-binding.d.ts.map +1 -0
- package/src/services/llm-streaming-binding.ts +136 -0
- package/src/services/load-args.d.ts +82 -0
- package/src/services/load-args.d.ts.map +1 -0
- package/src/services/load-args.ts +81 -0
- package/src/services/manifest/eliza-1.manifest.v1.json +708 -0
- package/src/services/manifest/index.d.ts +4 -0
- package/src/services/manifest/index.d.ts.map +1 -0
- package/src/services/manifest/index.ts +66 -0
- package/src/services/manifest/manifest.test.ts +693 -0
- package/src/services/manifest/schema.d.ts +715 -0
- package/src/services/manifest/schema.d.ts.map +1 -0
- package/src/services/manifest/schema.ts +655 -0
- package/src/services/manifest/types.d.ts +30 -0
- package/src/services/manifest/types.d.ts.map +1 -0
- package/src/services/manifest/types.ts +55 -0
- package/src/services/manifest/validator.d.ts +66 -0
- package/src/services/manifest/validator.d.ts.map +1 -0
- package/src/services/manifest/validator.ts +569 -0
- package/src/services/memory-arbiter.d.ts +343 -0
- package/src/services/memory-arbiter.d.ts.map +1 -0
- package/src/services/memory-arbiter.test.ts +419 -0
- package/src/services/memory-arbiter.ts +1000 -0
- package/src/services/memory-monitor.d.ts +119 -0
- package/src/services/memory-monitor.d.ts.map +1 -0
- package/src/services/memory-monitor.test.ts +208 -0
- package/src/services/memory-monitor.ts +296 -0
- package/src/services/memory-pressure.d.ts +127 -0
- package/src/services/memory-pressure.d.ts.map +1 -0
- package/src/services/memory-pressure.ts +413 -0
- package/src/services/mtp-doctor.d.ts +13 -0
- package/src/services/mtp-doctor.d.ts.map +1 -0
- package/src/services/mtp-doctor.ts +78 -0
- package/src/services/network-policy.d.ts +127 -0
- package/src/services/network-policy.d.ts.map +1 -0
- package/src/services/network-policy.ts +346 -0
- package/src/services/paths.d.ts +6 -0
- package/src/services/paths.d.ts.map +1 -0
- package/src/services/paths.ts +25 -0
- package/src/services/planner-skeleton.d.ts +124 -0
- package/src/services/planner-skeleton.d.ts.map +1 -0
- package/src/services/planner-skeleton.ts +175 -0
- package/src/services/providers.d.ts +38 -0
- package/src/services/providers.d.ts.map +1 -0
- package/src/services/providers.ts +507 -0
- package/src/services/ram-budget-cache.test.ts +163 -0
- package/src/services/ram-budget.d.ts +110 -0
- package/src/services/ram-budget.d.ts.map +1 -0
- package/src/services/ram-budget.ts +0 -0
- package/src/services/readiness.d.ts +9 -0
- package/src/services/readiness.d.ts.map +1 -0
- package/src/services/readiness.test.ts +87 -0
- package/src/services/readiness.ts +238 -0
- package/src/services/recommendation.d.ts +111 -0
- package/src/services/recommendation.d.ts.map +1 -0
- package/src/services/recommendation.ts +672 -0
- package/src/services/registry.d.ts +35 -0
- package/src/services/registry.d.ts.map +1 -0
- package/src/services/registry.ts +151 -0
- package/src/services/router-handler.d.ts +92 -0
- package/src/services/router-handler.d.ts.map +1 -0
- package/src/services/router-handler.test.ts +45 -0
- package/src/services/router-handler.ts +376 -0
- package/src/services/routing-policy.d.ts +55 -0
- package/src/services/routing-policy.d.ts.map +1 -0
- package/src/services/routing-policy.ts +228 -0
- package/src/services/routing-preferences.d.ts +8 -0
- package/src/services/routing-preferences.d.ts.map +1 -0
- package/src/services/routing-preferences.ts +15 -0
- package/src/services/runtime-target.d.ts +98 -0
- package/src/services/runtime-target.d.ts.map +1 -0
- package/src/services/runtime-target.ts +154 -0
- package/src/services/service.d.ts +128 -0
- package/src/services/service.d.ts.map +1 -0
- package/src/services/service.test.ts +223 -0
- package/src/services/service.ts +735 -0
- package/src/services/session-pool.d.ts +72 -0
- package/src/services/session-pool.d.ts.map +1 -0
- package/src/services/session-pool.ts +153 -0
- package/src/services/structured-output/deterministic-repair.d.ts +23 -0
- package/src/services/structured-output/deterministic-repair.d.ts.map +1 -0
- package/src/services/structured-output/deterministic-repair.test.ts +169 -0
- package/src/services/structured-output/deterministic-repair.ts +443 -0
- package/src/services/structured-output/index.ts +4 -0
- package/src/services/structured-output.d.ts +311 -0
- package/src/services/structured-output.d.ts.map +1 -0
- package/src/services/structured-output.test.ts +483 -0
- package/src/services/structured-output.ts +712 -0
- package/src/services/transcription-priority.test.ts +211 -0
- package/src/services/tts/errors.ts +46 -0
- package/src/services/tts/index.ts +214 -0
- package/src/services/tts/tts-audio-cache.ts +235 -0
- package/src/services/tts/types.ts +157 -0
- package/src/services/types.d.ts +19 -0
- package/src/services/types.d.ts.map +1 -0
- package/src/services/types.ts +55 -0
- package/src/services/verify-on-device.d.ts +34 -0
- package/src/services/verify-on-device.d.ts.map +1 -0
- package/src/services/verify-on-device.test.ts +87 -0
- package/src/services/verify-on-device.ts +127 -0
- package/src/services/verify.d.ts +8 -0
- package/src/services/verify.d.ts.map +1 -0
- package/src/services/verify.ts +13 -0
- package/src/services/vision/aosp-unavailable.d.ts +115 -0
- package/src/services/vision/aosp-unavailable.d.ts.map +1 -0
- package/src/services/vision/aosp-unavailable.ts +163 -0
- package/src/services/vision/capacitor-llama.d.ts +99 -0
- package/src/services/vision/capacitor-llama.d.ts.map +1 -0
- package/src/services/vision/capacitor-llama.ts +255 -0
- package/src/services/vision/cloud-fallback.d.ts +47 -0
- package/src/services/vision/cloud-fallback.d.ts.map +1 -0
- package/src/services/vision/cloud-fallback.test.ts +243 -0
- package/src/services/vision/cloud-fallback.ts +268 -0
- package/src/services/vision/fallback-chain.test.ts +86 -0
- package/src/services/vision/hash.d.ts +71 -0
- package/src/services/vision/hash.d.ts.map +1 -0
- package/src/services/vision/hash.ts +157 -0
- package/src/services/vision/index.d.ts +95 -0
- package/src/services/vision/index.d.ts.map +1 -0
- package/src/services/vision/index.ts +251 -0
- package/src/services/vision/llama-server.d.ts +73 -0
- package/src/services/vision/llama-server.d.ts.map +1 -0
- package/src/services/vision/llama-server.ts +177 -0
- package/src/services/vision/types.d.ts +153 -0
- package/src/services/vision/types.d.ts.map +1 -0
- package/src/services/vision/types.ts +154 -0
- package/src/services/vision/vast-fallback.d.ts +18 -0
- package/src/services/vision/vast-fallback.d.ts.map +1 -0
- package/src/services/vision/vast-fallback.ts +127 -0
- package/src/services/vision-embedding-cache.d.ts +98 -0
- package/src/services/vision-embedding-cache.d.ts.map +1 -0
- package/src/services/vision-embedding-cache.ts +189 -0
- package/src/services/voice/VOICE_WORKBENCH.md +88 -0
- package/src/services/voice/__test-helpers__/fake-ffi.ts +92 -0
- package/src/services/voice/__test-helpers__/synthetic-speech.ts +124 -0
- package/src/services/voice/__tests__/checkpoint-manager.test.ts +241 -0
- package/src/services/voice/__tests__/checkpoint-policy.test.ts +270 -0
- package/src/services/voice/__tests__/eager-context-builder.test.ts +257 -0
- package/src/services/voice/__tests__/eliza1-eot-scorer.test.ts +288 -0
- package/src/services/voice/__tests__/eot-classifier.test.ts +431 -0
- package/src/services/voice/__tests__/optimistic-rollback.test.ts +312 -0
- package/src/services/voice/__tests__/prefill-client.test.ts +266 -0
- package/src/services/voice/__tests__/prefix-preserving-queue.test.ts +208 -0
- package/src/services/voice/__tests__/streaming-asr.test.ts +450 -0
- package/src/services/voice/__tests__/streaming-transcriber.test.ts +339 -0
- package/src/services/voice/__tests__/turn-detector-resolver.test.ts +197 -0
- package/src/services/voice/__tests__/voice-state-machine-prefill.test.ts +275 -0
- package/src/services/voice/__tests__/voice-state-machine.test.ts +354 -0
- package/src/services/voice/audio-frame-consumer.d.ts +212 -0
- package/src/services/voice/audio-frame-consumer.d.ts.map +1 -0
- package/src/services/voice/audio-frame-consumer.test.ts +343 -0
- package/src/services/voice/audio-frame-consumer.ts +491 -0
- package/src/services/voice/barge-in.d.ts +112 -0
- package/src/services/voice/barge-in.d.ts.map +1 -0
- package/src/services/voice/barge-in.test.ts +244 -0
- package/src/services/voice/barge-in.ts +336 -0
- package/src/services/voice/cancellation-coordinator.d.ts +127 -0
- package/src/services/voice/cancellation-coordinator.d.ts.map +1 -0
- package/src/services/voice/cancellation-coordinator.test.ts +196 -0
- package/src/services/voice/cancellation-coordinator.ts +269 -0
- package/src/services/voice/checkpoint-manager.d.ts +199 -0
- package/src/services/voice/checkpoint-manager.d.ts.map +1 -0
- package/src/services/voice/checkpoint-manager.ts +401 -0
- package/src/services/voice/checkpoint-policy.ts +336 -0
- package/src/services/voice/composite-eot-classifier.test.ts +59 -0
- package/src/services/voice/e2e-harness.test.ts +182 -0
- package/src/services/voice/e2e-harness.ts +743 -0
- package/src/services/voice/eager-context-builder.d.ts +170 -0
- package/src/services/voice/eager-context-builder.d.ts.map +1 -0
- package/src/services/voice/eager-context-builder.ts +262 -0
- package/src/services/voice/eliza1-eot-scorer.d.ts +124 -0
- package/src/services/voice/eliza1-eot-scorer.d.ts.map +1 -0
- package/src/services/voice/eliza1-eot-scorer.ts +242 -0
- package/src/services/voice/embedding-server.ts +200 -0
- package/src/services/voice/embedding.d.ts +133 -0
- package/src/services/voice/embedding.d.ts.map +1 -0
- package/src/services/voice/embedding.test.ts +148 -0
- package/src/services/voice/embedding.ts +244 -0
- package/src/services/voice/emotion-attribution.d.ts +68 -0
- package/src/services/voice/emotion-attribution.d.ts.map +1 -0
- package/src/services/voice/emotion-attribution.test.ts +129 -0
- package/src/services/voice/emotion-attribution.ts +361 -0
- package/src/services/voice/engine-bridge-cancellation.test.ts +422 -0
- package/src/services/voice/engine-bridge.d.ts +746 -0
- package/src/services/voice/engine-bridge.d.ts.map +1 -0
- package/src/services/voice/engine-bridge.test.ts +384 -0
- package/src/services/voice/engine-bridge.ts +2226 -0
- package/src/services/voice/eot-classifier-ggml.d.ts +179 -0
- package/src/services/voice/eot-classifier-ggml.d.ts.map +1 -0
- package/src/services/voice/eot-classifier-ggml.ts +566 -0
- package/src/services/voice/eot-classifier.d.ts +214 -0
- package/src/services/voice/eot-classifier.d.ts.map +1 -0
- package/src/services/voice/eot-classifier.ts +533 -0
- package/src/services/voice/errors.d.ts +20 -0
- package/src/services/voice/errors.d.ts.map +1 -0
- package/src/services/voice/errors.ts +32 -0
- package/src/services/voice/expressive-tags.d.ts +158 -0
- package/src/services/voice/expressive-tags.d.ts.map +1 -0
- package/src/services/voice/expressive-tags.ts +405 -0
- package/src/services/voice/ffi-bindings.d.ts +636 -0
- package/src/services/voice/ffi-bindings.d.ts.map +1 -0
- package/src/services/voice/ffi-bindings.test.ts +671 -0
- package/src/services/voice/ffi-bindings.ts +3050 -0
- package/src/services/voice/first-line-cache.d.ts +181 -0
- package/src/services/voice/first-line-cache.d.ts.map +1 -0
- package/src/services/voice/first-line-cache.ts +725 -0
- package/src/services/voice/fused-eot-scorer.d.ts +51 -0
- package/src/services/voice/fused-eot-scorer.d.ts.map +1 -0
- package/src/services/voice/fused-eot-scorer.ts +135 -0
- package/src/services/voice/index.d.ts +91 -0
- package/src/services/voice/index.d.ts.map +1 -0
- package/src/services/voice/index.ts +481 -0
- package/src/services/voice/kokoro/__tests__/kokoro-backend.test.ts +151 -0
- package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.real.test.ts +151 -0
- package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.test.ts +60 -0
- package/src/services/voice/kokoro/__tests__/kokoro-engine-discovery.test.ts +277 -0
- package/src/services/voice/kokoro/__tests__/kokoro-ffi-runtime.test.ts +235 -0
- package/src/services/voice/kokoro/__tests__/kokoro-runtime.test.ts +95 -0
- package/src/services/voice/kokoro/__tests__/phonemizer.test.ts +53 -0
- package/src/services/voice/kokoro/__tests__/runtime-selection.test.ts +231 -0
- package/src/services/voice/kokoro/__tests__/voices.test.ts +57 -0
- package/src/services/voice/kokoro/index.ts +79 -0
- package/src/services/voice/kokoro/kokoro-backend.d.ts +72 -0
- package/src/services/voice/kokoro/kokoro-backend.d.ts.map +1 -0
- package/src/services/voice/kokoro/kokoro-backend.ts +207 -0
- package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts +58 -0
- package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts.map +1 -0
- package/src/services/voice/kokoro/kokoro-engine-discovery.ts +177 -0
- package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts +75 -0
- package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts.map +1 -0
- package/src/services/voice/kokoro/kokoro-ffi-runtime.ts +233 -0
- package/src/services/voice/kokoro/kokoro-runtime.d.ts +100 -0
- package/src/services/voice/kokoro/kokoro-runtime.d.ts.map +1 -0
- package/src/services/voice/kokoro/kokoro-runtime.ts +170 -0
- package/src/services/voice/kokoro/phoneme-stream.ts +123 -0
- package/src/services/voice/kokoro/phonemizer.d.ts +50 -0
- package/src/services/voice/kokoro/phonemizer.d.ts.map +1 -0
- package/src/services/voice/kokoro/phonemizer.ts +344 -0
- package/src/services/voice/kokoro/pick-runtime.d.ts +61 -0
- package/src/services/voice/kokoro/pick-runtime.d.ts.map +1 -0
- package/src/services/voice/kokoro/pick-runtime.test.ts +91 -0
- package/src/services/voice/kokoro/pick-runtime.ts +130 -0
- package/src/services/voice/kokoro/runtime-selection.d.ts +92 -0
- package/src/services/voice/kokoro/runtime-selection.d.ts.map +1 -0
- package/src/services/voice/kokoro/runtime-selection.ts +237 -0
- package/src/services/voice/kokoro/types.d.ts +82 -0
- package/src/services/voice/kokoro/types.d.ts.map +1 -0
- package/src/services/voice/kokoro/types.ts +95 -0
- package/src/services/voice/kokoro/voice-presets.d.ts +23 -0
- package/src/services/voice/kokoro/voice-presets.d.ts.map +1 -0
- package/src/services/voice/kokoro/voice-presets.ts +129 -0
- package/src/services/voice/kokoro/voices.d.ts +30 -0
- package/src/services/voice/kokoro/voices.d.ts.map +1 -0
- package/src/services/voice/kokoro/voices.ts +64 -0
- package/src/services/voice/lifecycle.d.ts +135 -0
- package/src/services/voice/lifecycle.d.ts.map +1 -0
- package/src/services/voice/lifecycle.test.ts +315 -0
- package/src/services/voice/lifecycle.ts +301 -0
- package/src/services/voice/live-diarization-session.d.ts +96 -0
- package/src/services/voice/live-diarization-session.d.ts.map +1 -0
- package/src/services/voice/live-diarization-session.ts +289 -0
- package/src/services/voice/mic-source.d.ts +136 -0
- package/src/services/voice/mic-source.d.ts.map +1 -0
- package/src/services/voice/mic-source.test.ts +210 -0
- package/src/services/voice/mic-source.ts +503 -0
- package/src/services/voice/optimistic-policy.d.ts +109 -0
- package/src/services/voice/optimistic-policy.d.ts.map +1 -0
- package/src/services/voice/optimistic-policy.test.ts +101 -0
- package/src/services/voice/optimistic-policy.ts +192 -0
- package/src/services/voice/optimistic-rollback.ts +343 -0
- package/src/services/voice/partial-stabilizer.d.ts +73 -0
- package/src/services/voice/partial-stabilizer.d.ts.map +1 -0
- package/src/services/voice/partial-stabilizer.test.ts +68 -0
- package/src/services/voice/partial-stabilizer.ts +140 -0
- package/src/services/voice/phoneme-tokenizer.d.ts +49 -0
- package/src/services/voice/phoneme-tokenizer.d.ts.map +1 -0
- package/src/services/voice/phoneme-tokenizer.ts +158 -0
- package/src/services/voice/phrase-cache.d.ts +76 -0
- package/src/services/voice/phrase-cache.d.ts.map +1 -0
- package/src/services/voice/phrase-cache.test.ts +242 -0
- package/src/services/voice/phrase-cache.ts +186 -0
- package/src/services/voice/phrase-chunker.d.ts +62 -0
- package/src/services/voice/phrase-chunker.d.ts.map +1 -0
- package/src/services/voice/phrase-chunker.test.ts +239 -0
- package/src/services/voice/phrase-chunker.ts +281 -0
- package/src/services/voice/pipeline-impls.d.ts +151 -0
- package/src/services/voice/pipeline-impls.d.ts.map +1 -0
- package/src/services/voice/pipeline-impls.l6.test.ts +110 -0
- package/src/services/voice/pipeline-impls.test.ts +292 -0
- package/src/services/voice/pipeline-impls.ts +315 -0
- package/src/services/voice/pipeline.d.ts +216 -0
- package/src/services/voice/pipeline.d.ts.map +1 -0
- package/src/services/voice/pipeline.ts +505 -0
- package/src/services/voice/prefill-client.d.ts +123 -0
- package/src/services/voice/prefill-client.d.ts.map +1 -0
- package/src/services/voice/prefill-client.ts +316 -0
- package/src/services/voice/prefix-preserving-queue.d.ts +113 -0
- package/src/services/voice/prefix-preserving-queue.d.ts.map +1 -0
- package/src/services/voice/prefix-preserving-queue.ts +162 -0
- package/src/services/voice/profile-store.d.ts +248 -0
- package/src/services/voice/profile-store.d.ts.map +1 -0
- package/src/services/voice/profile-store.ts +887 -0
- package/src/services/voice/ring-buffer.d.ts +40 -0
- package/src/services/voice/ring-buffer.d.ts.map +1 -0
- package/src/services/voice/ring-buffer.ts +105 -0
- package/src/services/voice/rollback-queue.d.ts +24 -0
- package/src/services/voice/rollback-queue.d.ts.map +1 -0
- package/src/services/voice/rollback-queue.ts +74 -0
- package/src/services/voice/samantha-preset-placeholder.d.ts +67 -0
- package/src/services/voice/samantha-preset-placeholder.d.ts.map +1 -0
- package/src/services/voice/samantha-preset-placeholder.test.ts +97 -0
- package/src/services/voice/samantha-preset-placeholder.ts +148 -0
- package/src/services/voice/samantha-preset-regenerator.d.ts +87 -0
- package/src/services/voice/samantha-preset-regenerator.d.ts.map +1 -0
- package/src/services/voice/samantha-preset-regenerator.ts +393 -0
- package/src/services/voice/scheduler.d.ts +146 -0
- package/src/services/voice/scheduler.d.ts.map +1 -0
- package/src/services/voice/scheduler.t2.test.ts +141 -0
- package/src/services/voice/scheduler.ts +927 -0
- package/src/services/voice/shared-resources.d.ts +190 -0
- package/src/services/voice/shared-resources.d.ts.map +1 -0
- package/src/services/voice/shared-resources.ts +320 -0
- package/src/services/voice/speaker/attribution-pipeline.d.ts +74 -0
- package/src/services/voice/speaker/attribution-pipeline.d.ts.map +1 -0
- package/src/services/voice/speaker/attribution-pipeline.ts +386 -0
- package/src/services/voice/speaker/diarizer-fused.d.ts +59 -0
- package/src/services/voice/speaker/diarizer-fused.d.ts.map +1 -0
- package/src/services/voice/speaker/diarizer-fused.real.test.ts +100 -0
- package/src/services/voice/speaker/diarizer-fused.ts +154 -0
- package/src/services/voice/speaker/diarizer.d.ts +75 -0
- package/src/services/voice/speaker/diarizer.d.ts.map +1 -0
- package/src/services/voice/speaker/diarizer.ts +218 -0
- package/src/services/voice/speaker/encoder-fused.d.ts +60 -0
- package/src/services/voice/speaker/encoder-fused.d.ts.map +1 -0
- package/src/services/voice/speaker/encoder-fused.real.test.ts +113 -0
- package/src/services/voice/speaker/encoder-fused.ts +138 -0
- package/src/services/voice/speaker/encoder-ggml.d.ts +33 -0
- package/src/services/voice/speaker/encoder-ggml.d.ts.map +1 -0
- package/src/services/voice/speaker/encoder-ggml.ts +79 -0
- package/src/services/voice/speaker/encoder.d.ts +37 -0
- package/src/services/voice/speaker/encoder.d.ts.map +1 -0
- package/src/services/voice/speaker/encoder.ts +105 -0
- package/src/services/voice/speaker-imprint.d.ts +83 -0
- package/src/services/voice/speaker-imprint.d.ts.map +1 -0
- package/src/services/voice/speaker-imprint.test.ts +185 -0
- package/src/services/voice/speaker-imprint.ts +312 -0
- package/src/services/voice/speaker-preset-cache.d.ts +77 -0
- package/src/services/voice/speaker-preset-cache.d.ts.map +1 -0
- package/src/services/voice/speaker-preset-cache.test.ts +154 -0
- package/src/services/voice/speaker-preset-cache.ts +195 -0
- package/src/services/voice/streaming-asr/streaming-pipeline-adapter.ts +292 -0
- package/src/services/voice/system-audio-sink.d.ts +73 -0
- package/src/services/voice/system-audio-sink.d.ts.map +1 -0
- package/src/services/voice/system-audio-sink.test.ts +29 -0
- package/src/services/voice/system-audio-sink.ts +366 -0
- package/src/services/voice/transcriber.d.ts +244 -0
- package/src/services/voice/transcriber.d.ts.map +1 -0
- package/src/services/voice/transcriber.test.ts +392 -0
- package/src/services/voice/transcriber.ts +704 -0
- package/src/services/voice/turn-controller.d.ts +183 -0
- package/src/services/voice/turn-controller.d.ts.map +1 -0
- package/src/services/voice/turn-controller.test.ts +575 -0
- package/src/services/voice/turn-controller.ts +596 -0
- package/src/services/voice/types.d.ts +643 -0
- package/src/services/voice/types.d.ts.map +1 -0
- package/src/services/voice/types.ts +699 -0
- package/src/services/voice/vad.d.ts +282 -0
- package/src/services/voice/vad.d.ts.map +1 -0
- package/src/services/voice/vad.test.ts +480 -0
- package/src/services/voice/vad.ts +827 -0
- package/src/services/voice/vad.v1-v4.test.ts +222 -0
- package/src/services/voice/voice-budget.d.ts +241 -0
- package/src/services/voice/voice-budget.d.ts.map +1 -0
- package/src/services/voice/voice-budget.test.ts +420 -0
- package/src/services/voice/voice-budget.ts +656 -0
- package/src/services/voice/voice-duet.test.ts +375 -0
- package/src/services/voice/voice-emotion-classifier.d.ts +95 -0
- package/src/services/voice/voice-emotion-classifier.d.ts.map +1 -0
- package/src/services/voice/voice-emotion-classifier.test.ts +210 -0
- package/src/services/voice/voice-emotion-classifier.ts +273 -0
- package/src/services/voice/voice-preset-format.d.ts +158 -0
- package/src/services/voice/voice-preset-format.d.ts.map +1 -0
- package/src/services/voice/voice-preset-format.ts +700 -0
- package/src/services/voice/voice-preset-generator.test.ts +89 -0
- package/src/services/voice/voice-profile-artifact.d.ts +116 -0
- package/src/services/voice/voice-profile-artifact.d.ts.map +1 -0
- package/src/services/voice/voice-profile-artifact.test.ts +138 -0
- package/src/services/voice/voice-profile-artifact.ts +518 -0
- package/src/services/voice/voice-profile-routes.d.ts +83 -0
- package/src/services/voice/voice-profile-routes.d.ts.map +1 -0
- package/src/services/voice/voice-profile-routes.test.ts +429 -0
- package/src/services/voice/voice-profile-routes.ts +425 -0
- package/src/services/voice/voice-scenario.ts +154 -0
- package/src/services/voice/voice-settings.d.ts +82 -0
- package/src/services/voice/voice-settings.d.ts.map +1 -0
- package/src/services/voice/voice-settings.ts +172 -0
- package/src/services/voice/voice-state-machine.d.ts +364 -0
- package/src/services/voice/voice-state-machine.d.ts.map +1 -0
- package/src/services/voice/voice-state-machine.ts +727 -0
- package/src/services/voice/voice-workbench-report.test.ts +168 -0
- package/src/services/voice/voice-workbench-report.ts +326 -0
- package/src/services/voice/voice-workbench.test.ts +158 -0
- package/src/services/voice/voice.test.ts +1070 -0
- package/src/services/voice/wake-word-ggml.d.ts +101 -0
- package/src/services/voice/wake-word-ggml.d.ts.map +1 -0
- package/src/services/voice/wake-word-ggml.ts +320 -0
- package/src/services/voice/wake-word.d.ts +255 -0
- package/src/services/voice/wake-word.d.ts.map +1 -0
- package/src/services/voice/wake-word.test.ts +298 -0
- package/src/services/voice/wake-word.ts +554 -0
- package/src/services/voice/wrap-with-first-line-cache.d.ts +70 -0
- package/src/services/voice/wrap-with-first-line-cache.d.ts.map +1 -0
- package/src/services/voice/wrap-with-first-line-cache.ts +267 -0
- package/src/services/voice-model-updater.d.ts +240 -0
- package/src/services/voice-model-updater.d.ts.map +1 -0
- package/src/services/voice-model-updater.ts +724 -0
- package/src/services/voice-prewarm.d.ts +3 -0
- package/src/services/voice-prewarm.d.ts.map +1 -0
- package/src/services/voice-prewarm.ts +51 -0
- package/dist/index.d.ts +0 -37
- package/dist/index.js +0 -1098
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `PrefixPreservingQueue` — prefix-preserving TTS barge-in rollback.
|
|
3
|
+
*
|
|
4
|
+
* Core invariant: given a divergencePoint N, after `rollbackAt(N)`:
|
|
5
|
+
* - chunks with tokenRange[1] <= N are RETAINED
|
|
6
|
+
* - chunks with tokenRange[0] > N are DROPPED
|
|
7
|
+
* - chunks that straddle N are RETAINED (best-effort, phrase granularity)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
import {
|
|
12
|
+
PrefixPreservingQueue,
|
|
13
|
+
type TaggedAudioChunk,
|
|
14
|
+
} from "../prefix-preserving-queue";
|
|
15
|
+
|
|
16
|
+
function chunk(start: number, end: number, durationMs = 50): TaggedAudioChunk {
|
|
17
|
+
const pcm = new Float32Array(Math.ceil((durationMs / 1000) * 24_000));
|
|
18
|
+
return { pcm, tokenRange: [start, end], durationMs };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("PrefixPreservingQueue — basic queue operations", () => {
|
|
22
|
+
it("starts empty", () => {
|
|
23
|
+
const q = new PrefixPreservingQueue();
|
|
24
|
+
expect(q.size).toBe(0);
|
|
25
|
+
expect(q.snapshot()).toHaveLength(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("size tracks enqueued chunks", () => {
|
|
29
|
+
const q = new PrefixPreservingQueue();
|
|
30
|
+
q.enqueue(chunk(0, 2));
|
|
31
|
+
expect(q.size).toBe(1);
|
|
32
|
+
q.enqueue(chunk(3, 5));
|
|
33
|
+
expect(q.size).toBe(2);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("clear() empties the queue and returns all chunks", () => {
|
|
37
|
+
const q = new PrefixPreservingQueue();
|
|
38
|
+
const c1 = chunk(0, 2);
|
|
39
|
+
const c2 = chunk(3, 5);
|
|
40
|
+
q.enqueue(c1);
|
|
41
|
+
q.enqueue(c2);
|
|
42
|
+
const cleared = q.clear();
|
|
43
|
+
expect(cleared).toHaveLength(2);
|
|
44
|
+
expect(cleared[0]).toBe(c1);
|
|
45
|
+
expect(cleared[1]).toBe(c2);
|
|
46
|
+
expect(q.size).toBe(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("snapshot() does not mutate the queue", () => {
|
|
50
|
+
const q = new PrefixPreservingQueue();
|
|
51
|
+
q.enqueue(chunk(0, 4));
|
|
52
|
+
const snap = q.snapshot();
|
|
53
|
+
expect(snap).toHaveLength(1);
|
|
54
|
+
expect(q.size).toBe(1);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("PrefixPreservingQueue — rollbackAt", () => {
|
|
59
|
+
it("retains chunks whose tokenRange[1] <= divergencePoint", () => {
|
|
60
|
+
const q = new PrefixPreservingQueue();
|
|
61
|
+
const c0 = chunk(0, 3); // end=3, fully before N=5
|
|
62
|
+
const c1 = chunk(4, 5); // end=5, exactly at N=5
|
|
63
|
+
q.enqueue(c0);
|
|
64
|
+
q.enqueue(c1);
|
|
65
|
+
const result = q.rollbackAt(5);
|
|
66
|
+
expect(result.retained).toContain(c0);
|
|
67
|
+
expect(result.retained).toContain(c1);
|
|
68
|
+
expect(result.dropped).toHaveLength(0);
|
|
69
|
+
expect(result.straddled).toHaveLength(0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("drops chunks whose tokenRange[0] > divergencePoint", () => {
|
|
73
|
+
const q = new PrefixPreservingQueue();
|
|
74
|
+
const c0 = chunk(0, 3); // retained
|
|
75
|
+
const c1 = chunk(6, 10); // start=6 > N=5 → dropped
|
|
76
|
+
q.enqueue(c0);
|
|
77
|
+
q.enqueue(c1);
|
|
78
|
+
const result = q.rollbackAt(5);
|
|
79
|
+
expect(result.retained).toContain(c0);
|
|
80
|
+
expect(result.dropped).toContain(c1);
|
|
81
|
+
expect(result.straddled).toHaveLength(0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("keeps straddling chunks (start<=N, end>N) and records them separately", () => {
|
|
85
|
+
const q = new PrefixPreservingQueue();
|
|
86
|
+
const c0 = chunk(0, 2); // fully before N=4 → retained
|
|
87
|
+
const c1 = chunk(3, 7); // straddles N=4 (start=3<=4, end=7>4) → retained+straddled
|
|
88
|
+
const c2 = chunk(8, 12); // fully after N=4 → dropped
|
|
89
|
+
q.enqueue(c0);
|
|
90
|
+
q.enqueue(c1);
|
|
91
|
+
q.enqueue(c2);
|
|
92
|
+
const result = q.rollbackAt(4);
|
|
93
|
+
expect(result.retained).toContain(c0);
|
|
94
|
+
expect(result.retained).toContain(c1);
|
|
95
|
+
expect(result.straddled).toContain(c1);
|
|
96
|
+
expect(result.dropped).toContain(c2);
|
|
97
|
+
expect(result.straddled).not.toContain(c0);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("clears the queue after rollback", () => {
|
|
101
|
+
const q = new PrefixPreservingQueue();
|
|
102
|
+
q.enqueue(chunk(0, 3));
|
|
103
|
+
q.enqueue(chunk(4, 8));
|
|
104
|
+
q.rollbackAt(5);
|
|
105
|
+
expect(q.size).toBe(0);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("empty queue returns empty result", () => {
|
|
109
|
+
const q = new PrefixPreservingQueue();
|
|
110
|
+
const result = q.rollbackAt(10);
|
|
111
|
+
expect(result.retained).toHaveLength(0);
|
|
112
|
+
expect(result.dropped).toHaveLength(0);
|
|
113
|
+
expect(result.straddled).toHaveLength(0);
|
|
114
|
+
expect(result.retainedDurationMs).toBe(0);
|
|
115
|
+
expect(result.droppedDurationMs).toBe(0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("divergencePoint=0 retains only chunks ending at 0, drops the rest", () => {
|
|
119
|
+
const q = new PrefixPreservingQueue();
|
|
120
|
+
const c0 = chunk(0, 0, 20); // end=0, retained
|
|
121
|
+
const c1 = chunk(1, 5, 40); // start=1 > 0, dropped
|
|
122
|
+
q.enqueue(c0);
|
|
123
|
+
q.enqueue(c1);
|
|
124
|
+
const result = q.rollbackAt(0);
|
|
125
|
+
expect(result.retained).toContain(c0);
|
|
126
|
+
expect(result.dropped).toContain(c1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("sums durations correctly", () => {
|
|
130
|
+
const q = new PrefixPreservingQueue();
|
|
131
|
+
q.enqueue(chunk(0, 2, 100)); // retained
|
|
132
|
+
q.enqueue(chunk(3, 5, 80)); // retained
|
|
133
|
+
q.enqueue(chunk(6, 9, 60)); // dropped
|
|
134
|
+
const result = q.rollbackAt(5);
|
|
135
|
+
expect(result.retainedDurationMs).toBe(180);
|
|
136
|
+
expect(result.droppedDurationMs).toBe(60);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("all chunks retained when divergencePoint is very large", () => {
|
|
140
|
+
const q = new PrefixPreservingQueue();
|
|
141
|
+
q.enqueue(chunk(0, 5));
|
|
142
|
+
q.enqueue(chunk(6, 10));
|
|
143
|
+
q.enqueue(chunk(11, 20));
|
|
144
|
+
const result = q.rollbackAt(999);
|
|
145
|
+
expect(result.retained).toHaveLength(3);
|
|
146
|
+
expect(result.dropped).toHaveLength(0);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("all chunks dropped when divergencePoint is -1", () => {
|
|
150
|
+
const q = new PrefixPreservingQueue();
|
|
151
|
+
q.enqueue(chunk(0, 5));
|
|
152
|
+
q.enqueue(chunk(6, 10));
|
|
153
|
+
const result = q.rollbackAt(-1);
|
|
154
|
+
// tokenRange[0]=0 > -1 → both dropped
|
|
155
|
+
expect(result.retained).toHaveLength(0);
|
|
156
|
+
expect(result.dropped).toHaveLength(2);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("multiple rollbacks each clear the queue independently", () => {
|
|
160
|
+
const q = new PrefixPreservingQueue();
|
|
161
|
+
q.enqueue(chunk(0, 3));
|
|
162
|
+
q.enqueue(chunk(4, 7));
|
|
163
|
+
const r1 = q.rollbackAt(5);
|
|
164
|
+
expect(r1.retained).toHaveLength(2); // both at or straddle N=5
|
|
165
|
+
expect(q.size).toBe(0);
|
|
166
|
+
|
|
167
|
+
// Second rollback with fresh chunks.
|
|
168
|
+
q.enqueue(chunk(0, 2));
|
|
169
|
+
q.enqueue(chunk(10, 15));
|
|
170
|
+
const r2 = q.rollbackAt(8);
|
|
171
|
+
expect(r2.retained).toHaveLength(1);
|
|
172
|
+
expect(r2.dropped).toHaveLength(1);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("PrefixPreservingQueue — token-range boundary conditions", () => {
|
|
177
|
+
it("chunk ending exactly at N is retained (boundary inclusive)", () => {
|
|
178
|
+
const q = new PrefixPreservingQueue();
|
|
179
|
+
const c = chunk(3, 7);
|
|
180
|
+
q.enqueue(c);
|
|
181
|
+
const result = q.rollbackAt(7);
|
|
182
|
+
expect(result.retained).toContain(c);
|
|
183
|
+
expect(result.dropped).toHaveLength(0);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("chunk starting exactly at N+1 is dropped (post-divergence)", () => {
|
|
187
|
+
const q = new PrefixPreservingQueue();
|
|
188
|
+
const c = chunk(8, 12);
|
|
189
|
+
q.enqueue(c);
|
|
190
|
+
const result = q.rollbackAt(7);
|
|
191
|
+
expect(result.dropped).toContain(c);
|
|
192
|
+
expect(result.retained).toHaveLength(0);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("single-token chunk at exactly N is retained", () => {
|
|
196
|
+
const q = new PrefixPreservingQueue();
|
|
197
|
+
const c = chunk(5, 5);
|
|
198
|
+
q.enqueue(c);
|
|
199
|
+
expect(q.rollbackAt(5).retained).toContain(c);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("single-token chunk one past N is dropped", () => {
|
|
203
|
+
const q = new PrefixPreservingQueue();
|
|
204
|
+
const c = chunk(6, 6);
|
|
205
|
+
q.enqueue(c);
|
|
206
|
+
expect(q.rollbackAt(5).dropped).toContain(c);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming-ASR tests: LocalAgreement-2 word-level stabilization, sliding-window
|
|
3
|
+
* chunking (FfiBatchTranscriber mock), speech-pause → drafter wiring, and
|
|
4
|
+
* flush()-after-speech-end committed final.
|
|
5
|
+
*
|
|
6
|
+
* No real ASR model or native library is used. Tests that would require a live
|
|
7
|
+
* model are guarded with `it.skipIf(true, ...)` per the task spec (no models >2B).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
import { MockCheckpointManager } from "../checkpoint-manager";
|
|
12
|
+
import type {
|
|
13
|
+
ElizaInferenceContextHandle,
|
|
14
|
+
ElizaInferenceFfi,
|
|
15
|
+
ElizaInferenceRegion,
|
|
16
|
+
} from "../ffi-bindings";
|
|
17
|
+
import { LocalAgreementBuffer } from "../streaming-asr/streaming-pipeline-adapter";
|
|
18
|
+
import { ASR_SAMPLE_RATE, FfiBatchTranscriber } from "../transcriber";
|
|
19
|
+
import type { PcmFrame, TranscriberEvent } from "../types";
|
|
20
|
+
import type {
|
|
21
|
+
DrafterAbortReason,
|
|
22
|
+
DrafterHandle,
|
|
23
|
+
StartDrafterFn,
|
|
24
|
+
} from "../voice-state-machine";
|
|
25
|
+
import { VoiceStateMachine } from "../voice-state-machine";
|
|
26
|
+
|
|
27
|
+
/* ======================================================================
|
|
28
|
+
* Helpers
|
|
29
|
+
* ====================================================================== */
|
|
30
|
+
|
|
31
|
+
function makePcmFrame(samples: number, tsMs = 0): PcmFrame {
|
|
32
|
+
return {
|
|
33
|
+
pcm: new Float32Array(samples).fill(0.05),
|
|
34
|
+
sampleRate: ASR_SAMPLE_RATE,
|
|
35
|
+
timestampMs: tsMs,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build a minimal fake `ElizaInferenceFfi` backed by a scripted batch
|
|
41
|
+
* transcriber. Only `asrTranscribe` is used by `FfiBatchTranscriber`;
|
|
42
|
+
* every other method throws or returns a sentinel so accidental calls
|
|
43
|
+
* are caught immediately.
|
|
44
|
+
*/
|
|
45
|
+
function makeBatchFfi(
|
|
46
|
+
transcribeFn: (pcm: Float32Array) => string,
|
|
47
|
+
): ElizaInferenceFfi {
|
|
48
|
+
return {
|
|
49
|
+
libraryPath: "/tmp/fake-batch",
|
|
50
|
+
libraryAbiVersion: "1",
|
|
51
|
+
create: (): ElizaInferenceContextHandle => 1n,
|
|
52
|
+
destroy: () => {},
|
|
53
|
+
mmapAcquire: (
|
|
54
|
+
_c: ElizaInferenceContextHandle,
|
|
55
|
+
_r: ElizaInferenceRegion,
|
|
56
|
+
) => {},
|
|
57
|
+
mmapEvict: (
|
|
58
|
+
_c: ElizaInferenceContextHandle,
|
|
59
|
+
_r: ElizaInferenceRegion,
|
|
60
|
+
) => {},
|
|
61
|
+
ttsSynthesize: () => {
|
|
62
|
+
throw new Error("not used");
|
|
63
|
+
},
|
|
64
|
+
asrTranscribe: ({ pcm }) => transcribeFn(pcm),
|
|
65
|
+
ttsStreamSupported: () => false,
|
|
66
|
+
ttsSynthesizeStream: () => {
|
|
67
|
+
throw new Error("not used");
|
|
68
|
+
},
|
|
69
|
+
cancelTts: () => {},
|
|
70
|
+
setVerifierCallback: () => ({ close: () => {} }),
|
|
71
|
+
vadSupported: () => false,
|
|
72
|
+
vadOpen: () => {
|
|
73
|
+
throw new Error("not used");
|
|
74
|
+
},
|
|
75
|
+
vadProcess: () => {
|
|
76
|
+
throw new Error("not used");
|
|
77
|
+
},
|
|
78
|
+
vadReset: () => {},
|
|
79
|
+
vadClose: () => {},
|
|
80
|
+
asrStreamSupported: () => false,
|
|
81
|
+
asrStreamOpen: () => {
|
|
82
|
+
throw new Error("not used");
|
|
83
|
+
},
|
|
84
|
+
asrStreamFeed: () => {
|
|
85
|
+
throw new Error("not used");
|
|
86
|
+
},
|
|
87
|
+
asrStreamPartial: () => {
|
|
88
|
+
throw new Error("not used");
|
|
89
|
+
},
|
|
90
|
+
asrStreamFinish: () => {
|
|
91
|
+
throw new Error("not used");
|
|
92
|
+
},
|
|
93
|
+
asrStreamClose: () => {
|
|
94
|
+
throw new Error("not used");
|
|
95
|
+
},
|
|
96
|
+
close: () => {},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ======================================================================
|
|
101
|
+
* 1. LocalAgreement-2 word-level stabilization — unit tests
|
|
102
|
+
* ====================================================================== */
|
|
103
|
+
|
|
104
|
+
describe("LocalAgreementBuffer (word-level LocalAgreement-2)", () => {
|
|
105
|
+
it("returns empty on the first hypothesis (need 2 consecutive matches)", () => {
|
|
106
|
+
const buf = new LocalAgreementBuffer();
|
|
107
|
+
const stable = buf.stable(["hello", "there"]);
|
|
108
|
+
expect(stable).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("commits the matching prefix when two consecutive hypotheses agree on leading words", () => {
|
|
112
|
+
const buf = new LocalAgreementBuffer();
|
|
113
|
+
buf.stable(["hello", "there", "world"]);
|
|
114
|
+
const stable = buf.stable(["hello", "there", "how"]);
|
|
115
|
+
// First two words match across consecutive hypotheses → committed.
|
|
116
|
+
expect(stable).toEqual(["hello", "there"]);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("extends the committed prefix as additional words stabilize", () => {
|
|
120
|
+
const buf = new LocalAgreementBuffer();
|
|
121
|
+
buf.stable(["hello", "there"]);
|
|
122
|
+
buf.stable(["hello", "there", "world"]);
|
|
123
|
+
const stable = buf.stable(["hello", "there", "world"]);
|
|
124
|
+
// "world" appeared in the 2nd and 3rd hypotheses → now committed.
|
|
125
|
+
expect(stable).toEqual(["hello", "there", "world"]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("does NOT roll back a committed word if a later hypothesis diverges", () => {
|
|
129
|
+
const buf = new LocalAgreementBuffer();
|
|
130
|
+
buf.stable(["the", "cat"]);
|
|
131
|
+
buf.stable(["the", "cat", "sat"]); // commits ["the", "cat"]
|
|
132
|
+
// New hypothesis changes "cat" → but committed must stay
|
|
133
|
+
const stable = buf.stable(["the", "dog"]);
|
|
134
|
+
expect(stable).toEqual(["the", "cat"]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("returns empty array for empty hypotheses", () => {
|
|
138
|
+
const buf = new LocalAgreementBuffer();
|
|
139
|
+
buf.stable([]);
|
|
140
|
+
const stable = buf.stable([]);
|
|
141
|
+
expect(stable).toEqual([]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("reset() clears all committed state", () => {
|
|
145
|
+
const buf = new LocalAgreementBuffer();
|
|
146
|
+
buf.stable(["hi"]);
|
|
147
|
+
buf.stable(["hi", "there"]); // commits ["hi"]
|
|
148
|
+
expect(buf.getCommitted()).toEqual(["hi"]);
|
|
149
|
+
buf.reset();
|
|
150
|
+
expect(buf.getCommitted()).toEqual([]);
|
|
151
|
+
// After reset, single hypothesis again commits nothing.
|
|
152
|
+
expect(buf.stable(["hi"])).toEqual([]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("commits nothing when the two consecutive hypotheses share no common prefix", () => {
|
|
156
|
+
const buf = new LocalAgreementBuffer();
|
|
157
|
+
buf.stable(["apple", "banana"]);
|
|
158
|
+
const stable = buf.stable(["cherry", "date"]);
|
|
159
|
+
expect(stable).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("respects n=3: requires three consecutive identical leading words", () => {
|
|
163
|
+
const buf = new LocalAgreementBuffer(3);
|
|
164
|
+
buf.stable(["a", "b"]);
|
|
165
|
+
buf.stable(["a", "b", "c"]);
|
|
166
|
+
// Only two so far — still nothing.
|
|
167
|
+
expect(buf.getCommitted()).toEqual([]);
|
|
168
|
+
buf.stable(["a", "b", "d"]);
|
|
169
|
+
// Third hypothesis agrees on ["a", "b"] with the window.
|
|
170
|
+
expect(buf.getCommitted()).toEqual(["a", "b"]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("rejects invalid n values", () => {
|
|
174
|
+
expect(() => new LocalAgreementBuffer(0)).toThrow();
|
|
175
|
+
expect(() => new LocalAgreementBuffer(-1)).toThrow();
|
|
176
|
+
expect(() => new LocalAgreementBuffer(Number.NaN)).toThrow();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/* ======================================================================
|
|
181
|
+
* 2. Sliding-window chunking via FfiBatchTranscriber — incremental partials
|
|
182
|
+
* ====================================================================== */
|
|
183
|
+
|
|
184
|
+
describe("FfiBatchTranscriber sliding-window — incremental partial emission", () => {
|
|
185
|
+
it("emits partial events incrementally and stays within the window bound per decode call", async () => {
|
|
186
|
+
const decodedWindowSizes: number[] = [];
|
|
187
|
+
let decodeCallCount = 0;
|
|
188
|
+
|
|
189
|
+
const ffi = makeBatchFfi((pcm) => {
|
|
190
|
+
decodedWindowSizes.push(pcm.length);
|
|
191
|
+
decodeCallCount++;
|
|
192
|
+
return `word${decodeCallCount}`;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const transcriber = new FfiBatchTranscriber({
|
|
196
|
+
ffi,
|
|
197
|
+
getContext: () => 1n,
|
|
198
|
+
// Tiny window so multiple commits are forced across a ~2.4 s audio feed.
|
|
199
|
+
windowSeconds: 0.8,
|
|
200
|
+
overlapSeconds: 0.15,
|
|
201
|
+
stepSeconds: 0.4,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const events: TranscriberEvent[] = [];
|
|
205
|
+
transcriber.on((e) => events.push(e));
|
|
206
|
+
|
|
207
|
+
// Feed ~2.4 s worth of audio in 0.6 s frames.
|
|
208
|
+
const frameSamples = Math.round(0.6 * ASR_SAMPLE_RATE);
|
|
209
|
+
for (let i = 0; i < 4; i++) {
|
|
210
|
+
transcriber.feed(makePcmFrame(frameSamples, i * 600));
|
|
211
|
+
// Let the serial decode chain drain.
|
|
212
|
+
// eslint-disable-next-line no-await-in-loop
|
|
213
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// At least one partial was emitted during the feed phase.
|
|
217
|
+
const partials = events.filter((e) => e.kind === "partial");
|
|
218
|
+
expect(partials.length).toBeGreaterThanOrEqual(1);
|
|
219
|
+
// Each partial has isFinal: false while the segment is still open.
|
|
220
|
+
for (const p of partials) {
|
|
221
|
+
expect(p.kind).toBe("partial");
|
|
222
|
+
if (p.kind === "partial") {
|
|
223
|
+
expect(p.update.isFinal).toBe(false);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Every batch decode call was bounded by window+overlap (no full-buffer re-decode).
|
|
228
|
+
const maxWindow = Math.round((0.8 + 0.15) * ASR_SAMPLE_RATE) + 50; // +50 rounding slack
|
|
229
|
+
for (const n of decodedWindowSizes) {
|
|
230
|
+
expect(n).toBeLessThanOrEqual(maxWindow);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// flush() force-finalizes and emits a final event.
|
|
234
|
+
const final = await transcriber.flush();
|
|
235
|
+
expect(final.isFinal).toBe(true);
|
|
236
|
+
expect(typeof final.partial).toBe("string");
|
|
237
|
+
|
|
238
|
+
const finalEvents = events.filter((e) => e.kind === "final");
|
|
239
|
+
expect(finalEvents.length).toBe(1);
|
|
240
|
+
|
|
241
|
+
transcriber.dispose();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("flush() without any prior feed returns an empty final transcript", async () => {
|
|
245
|
+
const ffi = makeBatchFfi(() => "");
|
|
246
|
+
const transcriber = new FfiBatchTranscriber({
|
|
247
|
+
ffi,
|
|
248
|
+
getContext: () => 1n,
|
|
249
|
+
});
|
|
250
|
+
const final = await transcriber.flush();
|
|
251
|
+
expect(final.isFinal).toBe(true);
|
|
252
|
+
expect(final.partial).toBe("");
|
|
253
|
+
transcriber.dispose();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("multiple flush() calls each commit independently (segment reset between)", async () => {
|
|
257
|
+
let callIdx = 0;
|
|
258
|
+
const scripts = ["first utterance", "second utterance"];
|
|
259
|
+
const ffi = makeBatchFfi(() => scripts[callIdx++ % scripts.length] ?? "");
|
|
260
|
+
const transcriber = new FfiBatchTranscriber({
|
|
261
|
+
ffi,
|
|
262
|
+
getContext: () => 1n,
|
|
263
|
+
stepSeconds: 0.01,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Feed and flush for utterance 1.
|
|
267
|
+
transcriber.feed(makePcmFrame(Math.round(0.5 * ASR_SAMPLE_RATE), 0));
|
|
268
|
+
const final1 = await transcriber.flush();
|
|
269
|
+
expect(final1.isFinal).toBe(true);
|
|
270
|
+
|
|
271
|
+
// Feed and flush for utterance 2 — the transcriber must reset between segments.
|
|
272
|
+
transcriber.feed(makePcmFrame(Math.round(0.5 * ASR_SAMPLE_RATE), 500));
|
|
273
|
+
const final2 = await transcriber.flush();
|
|
274
|
+
expect(final2.isFinal).toBe(true);
|
|
275
|
+
// The second flush should not carry state from the first segment.
|
|
276
|
+
expect(final2.partial.length).toBeGreaterThanOrEqual(0); // just verify no throw
|
|
277
|
+
|
|
278
|
+
transcriber.dispose();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
/* ======================================================================
|
|
283
|
+
* 3. speech-pause with available partial starts the drafter immediately
|
|
284
|
+
* ====================================================================== */
|
|
285
|
+
|
|
286
|
+
interface DrafterCall {
|
|
287
|
+
turnId: string;
|
|
288
|
+
partial: string;
|
|
289
|
+
aborted: DrafterAbortReason | null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function fakeDrafter(): { fn: StartDrafterFn; calls: DrafterCall[] } {
|
|
293
|
+
const calls: DrafterCall[] = [];
|
|
294
|
+
const fn: StartDrafterFn = ({ turnId, partialTranscript }) => {
|
|
295
|
+
const record: DrafterCall = {
|
|
296
|
+
turnId,
|
|
297
|
+
partial: partialTranscript,
|
|
298
|
+
aborted: null,
|
|
299
|
+
};
|
|
300
|
+
calls.push(record);
|
|
301
|
+
const handle: DrafterHandle = {
|
|
302
|
+
abort(reason) {
|
|
303
|
+
if (record.aborted === null) record.aborted = reason;
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
return handle;
|
|
307
|
+
};
|
|
308
|
+
return { fn, calls };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
describe("VoiceStateMachine — speech-pause feeds partial transcript to drafter", () => {
|
|
312
|
+
it("on speech-pause the drafter is started synchronously with the current partial transcript", async () => {
|
|
313
|
+
const drafter = fakeDrafter();
|
|
314
|
+
const machine = new VoiceStateMachine({
|
|
315
|
+
slotId: "test-slot",
|
|
316
|
+
checkpointManager: new MockCheckpointManager(),
|
|
317
|
+
startDrafter: drafter.fn,
|
|
318
|
+
pauseHangoverMs: 200,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
await machine.dispatch({ type: "speech-start", timestampMs: 0 });
|
|
322
|
+
// Provide a partial transcript — this is what the streaming ASR adapter
|
|
323
|
+
// would have accumulated via LocalAgreementBuffer by speech-pause time.
|
|
324
|
+
const partialAtPause = "how do I configure the";
|
|
325
|
+
await machine.dispatch({
|
|
326
|
+
type: "speech-pause",
|
|
327
|
+
timestampMs: 1500,
|
|
328
|
+
partialTranscript: partialAtPause,
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
expect(machine.getState()).toBe("PAUSE_TENTATIVE");
|
|
332
|
+
// Drafter was started immediately with the streaming partial (not waiting for flush()).
|
|
333
|
+
expect(drafter.calls).toHaveLength(1);
|
|
334
|
+
expect(drafter.calls[0]?.partial).toBe(partialAtPause);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("speech-pause with empty partial still starts the drafter (drafter handles empty input)", async () => {
|
|
338
|
+
const drafter = fakeDrafter();
|
|
339
|
+
const machine = new VoiceStateMachine({
|
|
340
|
+
slotId: "test-slot",
|
|
341
|
+
checkpointManager: new MockCheckpointManager(),
|
|
342
|
+
startDrafter: drafter.fn,
|
|
343
|
+
pauseHangoverMs: 200,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
await machine.dispatch({ type: "speech-start", timestampMs: 0 });
|
|
347
|
+
await machine.dispatch({
|
|
348
|
+
type: "speech-pause",
|
|
349
|
+
timestampMs: 500,
|
|
350
|
+
partialTranscript: "",
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
expect(drafter.calls).toHaveLength(1);
|
|
354
|
+
expect(drafter.calls[0]?.partial).toBe("");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("drafter is aborted if speech resumes within the rollback window (partial was premature)", async () => {
|
|
358
|
+
const drafter = fakeDrafter();
|
|
359
|
+
const machine = new VoiceStateMachine({
|
|
360
|
+
slotId: "test-slot",
|
|
361
|
+
checkpointManager: new MockCheckpointManager(),
|
|
362
|
+
startDrafter: drafter.fn,
|
|
363
|
+
pauseHangoverMs: 200,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
await machine.dispatch({ type: "speech-start", timestampMs: 0 });
|
|
367
|
+
await machine.dispatch({
|
|
368
|
+
type: "speech-pause",
|
|
369
|
+
timestampMs: 1000,
|
|
370
|
+
partialTranscript: "I was going to",
|
|
371
|
+
});
|
|
372
|
+
// Speech-active within 2×hangover (400ms) → rollback: drafter aborted.
|
|
373
|
+
await machine.dispatch({ type: "speech-active", timestampMs: 1200 });
|
|
374
|
+
|
|
375
|
+
expect(machine.getState()).toBe("LISTENING");
|
|
376
|
+
expect(drafter.calls[0]?.aborted).toBe("resumed");
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
/* ======================================================================
|
|
381
|
+
* 4. flush() after speech-end returns the committed final transcript
|
|
382
|
+
* ====================================================================== */
|
|
383
|
+
|
|
384
|
+
describe("FfiBatchTranscriber — flush() returns committed final on speech-end", () => {
|
|
385
|
+
it("flush() drains the pending tail into committed and returns it with isFinal: true", async () => {
|
|
386
|
+
const ffi = makeBatchFfi(() => "the quick brown fox");
|
|
387
|
+
const transcriber = new FfiBatchTranscriber({
|
|
388
|
+
ffi,
|
|
389
|
+
getContext: () => 1n,
|
|
390
|
+
stepSeconds: 0.01,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
transcriber.feed(makePcmFrame(Math.round(0.5 * ASR_SAMPLE_RATE), 0));
|
|
394
|
+
const final = await transcriber.flush();
|
|
395
|
+
|
|
396
|
+
expect(final.isFinal).toBe(true);
|
|
397
|
+
// The decoder returned "the quick brown fox" → committed final contains it.
|
|
398
|
+
expect(final.partial).toContain("the quick brown fox");
|
|
399
|
+
transcriber.dispose();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("flush() on an untouched transcriber returns empty string (no audio, no transcript)", async () => {
|
|
403
|
+
const ffi = makeBatchFfi(() => "should not be called");
|
|
404
|
+
const transcriber = new FfiBatchTranscriber({
|
|
405
|
+
ffi,
|
|
406
|
+
getContext: () => 1n,
|
|
407
|
+
});
|
|
408
|
+
const final = await transcriber.flush();
|
|
409
|
+
expect(final.isFinal).toBe(true);
|
|
410
|
+
expect(final.partial).toBe("");
|
|
411
|
+
transcriber.dispose();
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("flush() resolves in order even when multiple decode passes are enqueued (serial chain)", async () => {
|
|
415
|
+
let decodes = 0;
|
|
416
|
+
const ffi = makeBatchFfi((_pcm) => {
|
|
417
|
+
decodes++;
|
|
418
|
+
return `segment${decodes}`;
|
|
419
|
+
});
|
|
420
|
+
const transcriber = new FfiBatchTranscriber({
|
|
421
|
+
ffi,
|
|
422
|
+
getContext: () => 1n,
|
|
423
|
+
// Very small step so many decode passes queue up.
|
|
424
|
+
stepSeconds: 0.01,
|
|
425
|
+
windowSeconds: 0.5,
|
|
426
|
+
overlapSeconds: 0.1,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Feed ~1.5 s to force multiple decode passes.
|
|
430
|
+
for (let i = 0; i < 3; i++) {
|
|
431
|
+
transcriber.feed(
|
|
432
|
+
makePcmFrame(Math.round(0.5 * ASR_SAMPLE_RATE), i * 500),
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
const final = await transcriber.flush();
|
|
436
|
+
expect(final.isFinal).toBe(true);
|
|
437
|
+
// All enqueued decodes ran before flush() resolved.
|
|
438
|
+
expect(decodes).toBeGreaterThan(0);
|
|
439
|
+
transcriber.dispose();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Guard: real model tests are skipped — no models >2B are loaded.
|
|
443
|
+
it.skipIf(true)(
|
|
444
|
+
"SKIP — real Qwen3-ASR model: flush() of a live stream returns the full utterance",
|
|
445
|
+
async () => {
|
|
446
|
+
// This test requires a real libelizainference build with a bundled
|
|
447
|
+
// ASR model. It is always skipped in CI to avoid loading large models.
|
|
448
|
+
},
|
|
449
|
+
);
|
|
450
|
+
});
|