@elizaos/plugin-local-inference 2.0.0-beta.1 → 2.0.3-beta.2
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 +82 -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/actions/transcription-control.d.ts +29 -0
- package/src/actions/transcription-control.d.ts.map +1 -0
- package/src/actions/transcription-control.test.ts +100 -0
- package/src/actions/transcription-control.ts +127 -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 +8 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.ts +62 -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 +1082 -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 +205 -0
- package/src/routes/local-inference-asr-route.ts +163 -0
- package/src/routes/local-inference-asr-transcribe.d.ts +20 -0
- package/src/routes/local-inference-asr-transcribe.d.ts.map +1 -0
- package/src/routes/local-inference-asr-transcribe.test.ts +118 -0
- package/src/routes/local-inference-asr-transcribe.ts +97 -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 +485 -0
- package/src/routes/local-inference-compat-routes.ts +808 -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/transcript-audio-store.d.ts +15 -0
- package/src/routes/transcript-audio-store.d.ts.map +1 -0
- package/src/routes/transcript-audio-store.ts +27 -0
- package/src/routes/transcripts-routes.d.ts +36 -0
- package/src/routes/transcripts-routes.d.ts.map +1 -0
- package/src/routes/transcripts-routes.test.ts +144 -0
- package/src/routes/transcripts-routes.ts +159 -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 +62 -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 +1448 -0
- package/src/runtime/index.d.ts +15 -0
- package/src/runtime/index.d.ts.map +1 -0
- package/src/runtime/index.ts +33 -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/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/bionic-host-loader.d.ts +46 -0
- package/src/services/bionic-host-loader.d.ts.map +1 -0
- package/src/services/bionic-host-loader.test.ts +133 -0
- package/src/services/bionic-host-loader.ts +180 -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 +238 -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 +95 -0
- package/src/services/desktop-fused-ffi-backend-runtime.d.ts.map +1 -0
- package/src/services/desktop-fused-ffi-backend-runtime.ts +339 -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 +747 -0
- package/src/services/downloader.ts +925 -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 +540 -0
- package/src/services/engine.d.ts.map +1 -0
- package/src/services/engine.ts +1909 -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.d.ts +56 -0
- package/src/services/gpu-detect.d.ts.map +1 -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 +231 -0
- package/src/services/hardware.ts +410 -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 +277 -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 +29 -0
- package/src/services/index.d.ts.map +1 -0
- package/src/services/index.ts +211 -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 +689 -0
- package/src/services/manifest/schema.d.ts +713 -0
- package/src/services/manifest/schema.d.ts.map +1 -0
- package/src/services/manifest/schema.ts +653 -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 +567 -0
- package/src/services/memory-arbiter.d.ts +318 -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 +925 -0
- package/src/services/memory-monitor.d.ts +122 -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 +297 -0
- package/src/services/memory-pressure.d.ts +130 -0
- package/src/services/memory-pressure.d.ts.map +1 -0
- package/src/services/memory-pressure.ts +414 -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 +671 -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 +407 -0
- package/src/services/routing-policy.d.ts +69 -0
- package/src/services/routing-policy.d.ts.map +1 -0
- package/src/services/routing-policy.test.ts +164 -0
- package/src/services/routing-policy.ts +297 -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 +17 -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/system-memory.d.ts +33 -0
- package/src/services/system-memory.d.ts.map +1 -0
- package/src/services/system-memory.test.ts +47 -0
- package/src/services/system-memory.ts +67 -0
- package/src/services/transcription-priority.test.ts +211 -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 +94 -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 +195 -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/asr-timed.real.test.ts +141 -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 +131 -0
- package/src/services/voice/embedding.ts +243 -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 +759 -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 +2302 -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 +674 -0
- package/src/services/voice/ffi-bindings.d.ts.map +1 -0
- package/src/services/voice/ffi-bindings.test.ts +728 -0
- package/src/services/voice/ffi-bindings.ts +3225 -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/real-audio-decode.test.ts +148 -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.test.ts +129 -0
- package/src/services/voice/ring-buffer.ts +123 -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/transcript-knowledge.d.ts +37 -0
- package/src/services/voice/transcript-knowledge.d.ts.map +1 -0
- package/src/services/voice/transcript-knowledge.test.ts +68 -0
- package/src/services/voice/transcript-knowledge.ts +75 -0
- package/src/services/voice/transcript-service.d.ts +41 -0
- package/src/services/voice/transcript-service.d.ts.map +1 -0
- package/src/services/voice/transcript-service.test.ts +137 -0
- package/src/services/voice/transcript-service.ts +141 -0
- package/src/services/voice/transcript-store.d.ts +53 -0
- package/src/services/voice/transcript-store.d.ts.map +1 -0
- package/src/services/voice/transcript-store.test.ts +153 -0
- package/src/services/voice/transcript-store.ts +132 -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 +418 -0
- package/src/services/voice/voice-budget.ts +635 -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,239 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { type ClockMs, chunkTokens, PhraseChunker } from "./phrase-chunker";
|
|
3
|
+
import type { TextToken } from "./types";
|
|
4
|
+
|
|
5
|
+
function tokens(parts: string[]): TextToken[] {
|
|
6
|
+
return parts.map((text, index) => ({ index, text }));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe("PhraseChunker punctuation boundaries", () => {
|
|
10
|
+
it("flushes on semicolon and colon boundaries for faster first audio", () => {
|
|
11
|
+
const phrases = chunkTokens(tokens(["First:", " second;", " third"]), {});
|
|
12
|
+
|
|
13
|
+
expect(phrases.map((phrase) => phrase.text)).toEqual([
|
|
14
|
+
"First:",
|
|
15
|
+
" second;",
|
|
16
|
+
" third",
|
|
17
|
+
]);
|
|
18
|
+
expect(phrases.map((phrase) => phrase.terminator)).toEqual([
|
|
19
|
+
"punctuation",
|
|
20
|
+
"punctuation",
|
|
21
|
+
"max-cap",
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("PhraseChunker T3 time-budget flush", () => {
|
|
27
|
+
it("force-flushes once the time budget elapses on a slow producer", () => {
|
|
28
|
+
let now = 0;
|
|
29
|
+
const clock: ClockMs = () => now;
|
|
30
|
+
const chunker = new PhraseChunker(
|
|
31
|
+
// Pin first-phrase budget == full budget so these mechanism tests
|
|
32
|
+
// exercise the uniform 200ms path (first-phrase shortening is
|
|
33
|
+
// covered separately below).
|
|
34
|
+
{
|
|
35
|
+
maxAccumulationMs: 200,
|
|
36
|
+
firstPhraseMaxAccumulationMs: 200,
|
|
37
|
+
maxTokensPerPhrase: 100,
|
|
38
|
+
},
|
|
39
|
+
null,
|
|
40
|
+
clock,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
expect(chunker.push({ index: 0, text: "hello", acceptedAt: 0 })).toBeNull();
|
|
44
|
+
now = 100;
|
|
45
|
+
expect(
|
|
46
|
+
chunker.push({ index: 1, text: " there", acceptedAt: 0 }),
|
|
47
|
+
).toBeNull();
|
|
48
|
+
now = 220;
|
|
49
|
+
const flushed = chunker.push({ index: 2, text: " friend", acceptedAt: 0 });
|
|
50
|
+
expect(flushed).not.toBeNull();
|
|
51
|
+
expect(flushed?.text).toBe("hello there friend");
|
|
52
|
+
expect(flushed?.terminator).toBe("max-cap");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("does not flush before the budget elapses", () => {
|
|
56
|
+
let now = 0;
|
|
57
|
+
const clock: ClockMs = () => now;
|
|
58
|
+
const chunker = new PhraseChunker(
|
|
59
|
+
// Pin first-phrase budget == full budget so these mechanism tests
|
|
60
|
+
// exercise the uniform 200ms path (first-phrase shortening is
|
|
61
|
+
// covered separately below).
|
|
62
|
+
{
|
|
63
|
+
maxAccumulationMs: 200,
|
|
64
|
+
firstPhraseMaxAccumulationMs: 200,
|
|
65
|
+
maxTokensPerPhrase: 100,
|
|
66
|
+
},
|
|
67
|
+
null,
|
|
68
|
+
clock,
|
|
69
|
+
);
|
|
70
|
+
expect(chunker.push({ index: 0, text: "a", acceptedAt: 0 })).toBeNull();
|
|
71
|
+
now = 50;
|
|
72
|
+
expect(chunker.push({ index: 1, text: "b", acceptedAt: 0 })).toBeNull();
|
|
73
|
+
now = 150;
|
|
74
|
+
expect(chunker.push({ index: 2, text: "c", acceptedAt: 0 })).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("flushIfTimeBudgetExceeded triggers on caller poll without a new token", () => {
|
|
78
|
+
let now = 0;
|
|
79
|
+
const clock: ClockMs = () => now;
|
|
80
|
+
const chunker = new PhraseChunker(
|
|
81
|
+
// Pin first-phrase budget == full budget so these mechanism tests
|
|
82
|
+
// exercise the uniform 200ms path (first-phrase shortening is
|
|
83
|
+
// covered separately below).
|
|
84
|
+
{
|
|
85
|
+
maxAccumulationMs: 200,
|
|
86
|
+
firstPhraseMaxAccumulationMs: 200,
|
|
87
|
+
maxTokensPerPhrase: 100,
|
|
88
|
+
},
|
|
89
|
+
null,
|
|
90
|
+
clock,
|
|
91
|
+
);
|
|
92
|
+
chunker.push({ index: 0, text: "x", acceptedAt: 0 });
|
|
93
|
+
now = 100;
|
|
94
|
+
expect(chunker.flushIfTimeBudgetExceeded()).toBeNull();
|
|
95
|
+
now = 250;
|
|
96
|
+
const phrase = chunker.flushIfTimeBudgetExceeded();
|
|
97
|
+
expect(phrase?.text).toBe("x");
|
|
98
|
+
expect(phrase?.terminator).toBe("max-cap");
|
|
99
|
+
expect(chunker.flushIfTimeBudgetExceeded()).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("msUntilTimeBudget reports infinity for an empty buffer or disabled budget", () => {
|
|
103
|
+
let now = 0;
|
|
104
|
+
const clock: ClockMs = () => now;
|
|
105
|
+
const chunker = new PhraseChunker(
|
|
106
|
+
// Pin first-phrase budget == full budget so these mechanism tests
|
|
107
|
+
// exercise the uniform 200ms path (first-phrase shortening is
|
|
108
|
+
// covered separately below).
|
|
109
|
+
{
|
|
110
|
+
maxAccumulationMs: 200,
|
|
111
|
+
firstPhraseMaxAccumulationMs: 200,
|
|
112
|
+
maxTokensPerPhrase: 100,
|
|
113
|
+
},
|
|
114
|
+
null,
|
|
115
|
+
clock,
|
|
116
|
+
);
|
|
117
|
+
expect(chunker.msUntilTimeBudget()).toBe(Number.POSITIVE_INFINITY);
|
|
118
|
+
chunker.push({ index: 0, text: "x", acceptedAt: 0 });
|
|
119
|
+
expect(chunker.msUntilTimeBudget()).toBe(200);
|
|
120
|
+
now = 75;
|
|
121
|
+
expect(chunker.msUntilTimeBudget()).toBe(125);
|
|
122
|
+
|
|
123
|
+
const disabled = new PhraseChunker(
|
|
124
|
+
{ maxAccumulationMs: 0, maxTokensPerPhrase: 100 },
|
|
125
|
+
null,
|
|
126
|
+
clock,
|
|
127
|
+
);
|
|
128
|
+
disabled.push({ index: 0, text: "x", acceptedAt: 0 });
|
|
129
|
+
expect(disabled.msUntilTimeBudget()).toBe(Number.POSITIVE_INFINITY);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("disabled budget never time-flushes", () => {
|
|
133
|
+
let now = 0;
|
|
134
|
+
const clock: ClockMs = () => now;
|
|
135
|
+
const chunker = new PhraseChunker(
|
|
136
|
+
{ maxAccumulationMs: 0, maxTokensPerPhrase: 100 },
|
|
137
|
+
null,
|
|
138
|
+
clock,
|
|
139
|
+
);
|
|
140
|
+
chunker.push({ index: 0, text: "a", acceptedAt: 0 });
|
|
141
|
+
now = 10_000;
|
|
142
|
+
expect(chunker.push({ index: 1, text: " b", acceptedAt: 0 })).toBeNull();
|
|
143
|
+
expect(chunker.flushIfTimeBudgetExceeded()).toBeNull();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe("PhraseChunker first-phrase budget (TTFA)", () => {
|
|
148
|
+
it("flushes the first phrase on the shorter budget, later phrases on the full one", () => {
|
|
149
|
+
let now = 0;
|
|
150
|
+
const clock: ClockMs = () => now;
|
|
151
|
+
const chunker = new PhraseChunker(
|
|
152
|
+
{
|
|
153
|
+
maxAccumulationMs: 700,
|
|
154
|
+
firstPhraseMaxAccumulationMs: 300,
|
|
155
|
+
maxTokensPerPhrase: 100,
|
|
156
|
+
},
|
|
157
|
+
null,
|
|
158
|
+
clock,
|
|
159
|
+
);
|
|
160
|
+
// First phrase: silent producer, no punctuation — flushes at 300ms.
|
|
161
|
+
expect(chunker.push({ index: 0, text: "hello", acceptedAt: 0 })).toBeNull();
|
|
162
|
+
expect(chunker.msUntilTimeBudget()).toBe(300);
|
|
163
|
+
now = 300;
|
|
164
|
+
const first = chunker.flushIfTimeBudgetExceeded();
|
|
165
|
+
expect(first?.text).toBe("hello");
|
|
166
|
+
expect(first?.terminator).toBe("max-cap");
|
|
167
|
+
|
|
168
|
+
// Second phrase now uses the FULL 700ms budget (no fragmentation).
|
|
169
|
+
now = 1000;
|
|
170
|
+
expect(
|
|
171
|
+
chunker.push({ index: 1, text: " there", acceptedAt: 0 }),
|
|
172
|
+
).toBeNull();
|
|
173
|
+
expect(chunker.msUntilTimeBudget()).toBe(700);
|
|
174
|
+
now = 1300; // 300ms in — would have flushed the first phrase, not this one
|
|
175
|
+
expect(chunker.flushIfTimeBudgetExceeded()).toBeNull();
|
|
176
|
+
now = 1700; // full 700ms elapsed
|
|
177
|
+
expect(chunker.flushIfTimeBudgetExceeded()?.text).toBe(" there");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("derives the first-phrase budget from maxAccumulationMs when unset (half, capped 350)", () => {
|
|
181
|
+
const now = 0;
|
|
182
|
+
const clock: ClockMs = () => now;
|
|
183
|
+
// 700ms full → first-phrase budget = min(350, 350) = 350.
|
|
184
|
+
const chunker = new PhraseChunker(
|
|
185
|
+
{ maxAccumulationMs: 700, maxTokensPerPhrase: 100 },
|
|
186
|
+
null,
|
|
187
|
+
clock,
|
|
188
|
+
);
|
|
189
|
+
chunker.push({ index: 0, text: "x", acceptedAt: 0 });
|
|
190
|
+
expect(chunker.msUntilTimeBudget()).toBe(350);
|
|
191
|
+
|
|
192
|
+
// 400ms full → half = 200 (below the 350 cap).
|
|
193
|
+
const small = new PhraseChunker(
|
|
194
|
+
{ maxAccumulationMs: 400, maxTokensPerPhrase: 100 },
|
|
195
|
+
null,
|
|
196
|
+
clock,
|
|
197
|
+
);
|
|
198
|
+
small.push({ index: 0, text: "x", acceptedAt: 0 });
|
|
199
|
+
expect(small.msUntilTimeBudget()).toBe(200);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("resets the first-phrase gate on reset() so each reply gets fast first audio", () => {
|
|
203
|
+
let now = 0;
|
|
204
|
+
const clock: ClockMs = () => now;
|
|
205
|
+
const chunker = new PhraseChunker(
|
|
206
|
+
{
|
|
207
|
+
maxAccumulationMs: 700,
|
|
208
|
+
firstPhraseMaxAccumulationMs: 300,
|
|
209
|
+
maxTokensPerPhrase: 100,
|
|
210
|
+
},
|
|
211
|
+
null,
|
|
212
|
+
clock,
|
|
213
|
+
);
|
|
214
|
+
chunker.push({ index: 0, text: "first", acceptedAt: 0 });
|
|
215
|
+
now = 300;
|
|
216
|
+
expect(chunker.flushIfTimeBudgetExceeded()).not.toBeNull(); // phrase #1 flushed
|
|
217
|
+
chunker.reset();
|
|
218
|
+
now = 1000;
|
|
219
|
+
chunker.push({ index: 0, text: "again", acceptedAt: 0 });
|
|
220
|
+
// Back to the short first-phrase budget after reset.
|
|
221
|
+
expect(chunker.msUntilTimeBudget()).toBe(300);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("clamps an explicit first-phrase budget to the full budget", () => {
|
|
225
|
+
const now = 0;
|
|
226
|
+
const clock: ClockMs = () => now;
|
|
227
|
+
const chunker = new PhraseChunker(
|
|
228
|
+
{
|
|
229
|
+
maxAccumulationMs: 200,
|
|
230
|
+
firstPhraseMaxAccumulationMs: 999,
|
|
231
|
+
maxTokensPerPhrase: 100,
|
|
232
|
+
},
|
|
233
|
+
null,
|
|
234
|
+
clock,
|
|
235
|
+
);
|
|
236
|
+
chunker.push({ index: 0, text: "x", acceptedAt: 0 });
|
|
237
|
+
expect(chunker.msUntilTimeBudget()).toBe(200);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import type { PhonemeTokenizer } from "./phoneme-tokenizer";
|
|
2
|
+
import type {
|
|
3
|
+
AcceptedToken,
|
|
4
|
+
Phrase,
|
|
5
|
+
PhraseChunkerConfig,
|
|
6
|
+
TextToken,
|
|
7
|
+
} from "./types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default phrase boundaries: end-of-clause punctuation plus the three
|
|
11
|
+
* sentence-final marks. Per `packages/inference/AGENTS.md` §4 / the
|
|
12
|
+
* voice-swarm brief item A6 — "the first segment delimited by punctuation
|
|
13
|
+
* OR the first 30 words, whichever comes first". Cutting a phrase at the
|
|
14
|
+
* first comma/semicolon/colon hands TTS something to say without waiting
|
|
15
|
+
* for a sentence-final mark.
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_TERMINATORS: ReadonlySet<string> = new Set([
|
|
18
|
+
",",
|
|
19
|
+
".",
|
|
20
|
+
"!",
|
|
21
|
+
"?",
|
|
22
|
+
";",
|
|
23
|
+
":",
|
|
24
|
+
]);
|
|
25
|
+
const DEFAULT_PHONEMES_PER_CHUNK = 8;
|
|
26
|
+
/** Default hard word cap when a caller doesn't supply `maxTokensPerPhrase` (the brief's "first 30 words"). */
|
|
27
|
+
const DEFAULT_MAX_TOKENS_PER_PHRASE = 30;
|
|
28
|
+
/**
|
|
29
|
+
* T3 — default time budget in milliseconds for the time-budget phrase
|
|
30
|
+
* flush. When a phrase has been accumulating in the buffer for this long
|
|
31
|
+
* without hitting a punctuation / phoneme / cap boundary, force a flush
|
|
32
|
+
* so the next phrase reaches TTS instead of stalling behind a slow
|
|
33
|
+
* producer. Override via `ELIZA_PHRASE_FLUSH_MS` env var.
|
|
34
|
+
*
|
|
35
|
+
* The default is deliberately phrase-sized. A 200ms budget was fast on paper
|
|
36
|
+
* but split slow token streams into word fragments, which made OmniVoice
|
|
37
|
+
* produce filler-like audio and degraded the downstream ASR loop.
|
|
38
|
+
*/
|
|
39
|
+
function resolveDefaultMaxAccumulationMs(): number {
|
|
40
|
+
const raw = process.env.ELIZA_PHRASE_FLUSH_MS?.trim();
|
|
41
|
+
if (raw) {
|
|
42
|
+
const v = Number.parseInt(raw, 10);
|
|
43
|
+
if (Number.isFinite(v) && v > 0) return v;
|
|
44
|
+
}
|
|
45
|
+
return 700;
|
|
46
|
+
}
|
|
47
|
+
const DEFAULT_MAX_ACCUMULATION_MS = resolveDefaultMaxAccumulationMs();
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* First-audio (TTFA) optimization: the FIRST phrase of a reply uses a shorter
|
|
51
|
+
* time budget than the rest. First-audio latency is the dominant voice-UX
|
|
52
|
+
* metric, and a punctuation-sparse opening otherwise waits the full
|
|
53
|
+
* {@link DEFAULT_MAX_ACCUMULATION_MS} before any sound plays. Once audio is
|
|
54
|
+
* flowing, later phrases keep the full budget so the bulk of the reply is not
|
|
55
|
+
* fragmented into word-sized chunks (the failure mode the 700ms default fixed).
|
|
56
|
+
* Override via `ELIZA_PHRASE_FLUSH_FIRST_MS`; defaults to half the full budget,
|
|
57
|
+
* capped at 350ms. A non-positive full budget disables both.
|
|
58
|
+
*/
|
|
59
|
+
function resolveFirstPhraseMs(fullBudgetMs: number): number {
|
|
60
|
+
if (fullBudgetMs <= 0) return 0;
|
|
61
|
+
const raw = process.env.ELIZA_PHRASE_FLUSH_FIRST_MS?.trim();
|
|
62
|
+
if (raw) {
|
|
63
|
+
const v = Number.parseInt(raw, 10);
|
|
64
|
+
if (Number.isFinite(v) && v > 0) return Math.min(v, fullBudgetMs);
|
|
65
|
+
}
|
|
66
|
+
return Math.min(350, Math.ceil(fullBudgetMs / 2));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Wall-clock source the chunker uses. Tests inject a deterministic clock. */
|
|
70
|
+
export type ClockMs = () => number;
|
|
71
|
+
|
|
72
|
+
const DEFAULT_CLOCK: ClockMs = () => globalThis.performance.now();
|
|
73
|
+
|
|
74
|
+
export class PhraseChunker {
|
|
75
|
+
private buffer: AcceptedToken[] = [];
|
|
76
|
+
private nextPhraseId = 0;
|
|
77
|
+
private readonly terminators: ReadonlySet<string>;
|
|
78
|
+
private readonly chunkOn: "punctuation" | "phoneme-stream";
|
|
79
|
+
private readonly phonemesPerChunk: number;
|
|
80
|
+
private readonly maxTokensPerPhrase: number;
|
|
81
|
+
private readonly tokenizer: PhonemeTokenizer | null;
|
|
82
|
+
private phonemeCount = 0;
|
|
83
|
+
/**
|
|
84
|
+
* T3 — time-budget flush. `firstTokenAtMs` is captured on the first
|
|
85
|
+
* `push()` after an empty buffer; once `clock() - firstTokenAtMs >=
|
|
86
|
+
* maxAccumulationMs` the chunker force-flushes even without a
|
|
87
|
+
* punctuation / phoneme / cap boundary. `maxAccumulationMs <= 0`
|
|
88
|
+
* disables the time budget.
|
|
89
|
+
*/
|
|
90
|
+
private readonly maxAccumulationMs: number;
|
|
91
|
+
/** Shorter budget applied only while no phrase has flushed yet this reply. */
|
|
92
|
+
private readonly firstPhraseMaxAccumulationMs: number;
|
|
93
|
+
private readonly clock: ClockMs;
|
|
94
|
+
private firstTokenAtMs = 0;
|
|
95
|
+
/** Phrases emitted since the last {@link reset}; gates the first-phrase budget. */
|
|
96
|
+
private phrasesEmitted = 0;
|
|
97
|
+
|
|
98
|
+
constructor(
|
|
99
|
+
config: PhraseChunkerConfig,
|
|
100
|
+
tokenizer: PhonemeTokenizer | null = null,
|
|
101
|
+
clock: ClockMs = DEFAULT_CLOCK,
|
|
102
|
+
) {
|
|
103
|
+
this.terminators = config.sentenceTerminators ?? DEFAULT_TERMINATORS;
|
|
104
|
+
this.chunkOn = config.chunkOn ?? "punctuation";
|
|
105
|
+
this.phonemesPerChunk = Math.max(
|
|
106
|
+
1,
|
|
107
|
+
config.phonemesPerChunk ?? DEFAULT_PHONEMES_PER_CHUNK,
|
|
108
|
+
);
|
|
109
|
+
this.maxTokensPerPhrase = Math.max(
|
|
110
|
+
1,
|
|
111
|
+
config.maxTokensPerPhrase ?? DEFAULT_MAX_TOKENS_PER_PHRASE,
|
|
112
|
+
);
|
|
113
|
+
this.maxAccumulationMs =
|
|
114
|
+
config.maxAccumulationMs !== undefined
|
|
115
|
+
? Math.max(0, config.maxAccumulationMs)
|
|
116
|
+
: DEFAULT_MAX_ACCUMULATION_MS;
|
|
117
|
+
this.firstPhraseMaxAccumulationMs =
|
|
118
|
+
config.firstPhraseMaxAccumulationMs !== undefined
|
|
119
|
+
? Math.min(
|
|
120
|
+
this.maxAccumulationMs,
|
|
121
|
+
Math.max(0, config.firstPhraseMaxAccumulationMs),
|
|
122
|
+
)
|
|
123
|
+
: resolveFirstPhraseMs(this.maxAccumulationMs);
|
|
124
|
+
this.clock = clock;
|
|
125
|
+
this.tokenizer = tokenizer;
|
|
126
|
+
if (this.chunkOn === "phoneme-stream" && this.tokenizer === null) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"PhraseChunker: chunkOn='phoneme-stream' requires a PhonemeTokenizer",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
push(token: AcceptedToken): Phrase | null {
|
|
134
|
+
if (this.buffer.length === 0) {
|
|
135
|
+
this.firstTokenAtMs = this.clock();
|
|
136
|
+
}
|
|
137
|
+
this.buffer.push(token);
|
|
138
|
+
|
|
139
|
+
// Punctuation always wins — a `, . ! ?` boundary forces a flush even
|
|
140
|
+
// in phoneme-stream mode.
|
|
141
|
+
if (this.endsWithTerminator(token.text)) {
|
|
142
|
+
return this.flushAs("punctuation");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (this.chunkOn === "phoneme-stream" && this.tokenizer !== null) {
|
|
146
|
+
const phonemes = this.tokenizer.tokenize(token.text, token.index);
|
|
147
|
+
this.phonemeCount += phonemes.length;
|
|
148
|
+
if (this.phonemeCount >= this.phonemesPerChunk) {
|
|
149
|
+
return this.flushAs("phoneme-stream");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (this.buffer.length >= this.maxTokensPerPhrase) {
|
|
154
|
+
return this.flushAs("max-cap");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// T3 — time-budget flush. Re-uses the `"max-cap"` terminator because
|
|
158
|
+
// adding a new terminator value would require editing the shared
|
|
159
|
+
// `Phrase` type in `types.ts`. Structurally "the chunker forced a
|
|
160
|
+
// flush" is what max-cap already means.
|
|
161
|
+
const budget = this.currentBudgetMs();
|
|
162
|
+
if (budget > 0 && this.clock() - this.firstTokenAtMs >= budget) {
|
|
163
|
+
return this.flushAs("max-cap");
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Active time budget: the shorter first-phrase budget until the reply's
|
|
169
|
+
* first phrase has flushed, then the full budget. */
|
|
170
|
+
private currentBudgetMs(): number {
|
|
171
|
+
return this.phrasesEmitted === 0
|
|
172
|
+
? this.firstPhraseMaxAccumulationMs
|
|
173
|
+
: this.maxAccumulationMs;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* T3 — caller-driven check. Returns a phrase when the time budget has
|
|
178
|
+
* elapsed for the current buffer, otherwise null. The scheduler polls
|
|
179
|
+
* this from a `setTimeout` so even a producer that goes silent before
|
|
180
|
+
* pushing the next token still gets its in-flight phrase flushed.
|
|
181
|
+
*/
|
|
182
|
+
flushIfTimeBudgetExceeded(): Phrase | null {
|
|
183
|
+
if (this.buffer.length === 0) return null;
|
|
184
|
+
const budget = this.currentBudgetMs();
|
|
185
|
+
if (budget <= 0) return null;
|
|
186
|
+
if (this.clock() - this.firstTokenAtMs < budget) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
return this.flushAs("max-cap");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* T3 — milliseconds remaining until the time budget elapses for the
|
|
194
|
+
* current buffer. Negative when the budget has already been exceeded;
|
|
195
|
+
* `Number.POSITIVE_INFINITY` when the buffer is empty or the budget is
|
|
196
|
+
* disabled. Callers compute their flush timer off this.
|
|
197
|
+
*/
|
|
198
|
+
msUntilTimeBudget(): number {
|
|
199
|
+
if (this.buffer.length === 0) return Number.POSITIVE_INFINITY;
|
|
200
|
+
const budget = this.currentBudgetMs();
|
|
201
|
+
if (budget <= 0) return Number.POSITIVE_INFINITY;
|
|
202
|
+
return this.firstTokenAtMs + budget - this.clock();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
flushPending(): Phrase | null {
|
|
206
|
+
if (this.buffer.length === 0) return null;
|
|
207
|
+
return this.flushAs("max-cap");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Drop buffered tokens that have not flushed whose token index is ≥
|
|
212
|
+
* `fromIndex`. Used by the pipeline's rollback path: when the target
|
|
213
|
+
* verifier rejects a draft tail, any draft tokens still sitting in the
|
|
214
|
+
* chunker's buffer before phrase packing MUST be discarded so
|
|
215
|
+
* the verifier's correction does not get glued onto stale text.
|
|
216
|
+
* Phonemes are recounted from scratch over what remains.
|
|
217
|
+
*/
|
|
218
|
+
dropPendingFrom(fromIndex: number): void {
|
|
219
|
+
const kept = this.buffer.filter((t) => t.index < fromIndex);
|
|
220
|
+
if (kept.length === this.buffer.length) return;
|
|
221
|
+
this.buffer = kept;
|
|
222
|
+
this.phonemeCount = 0;
|
|
223
|
+
if (this.buffer.length === 0) {
|
|
224
|
+
this.firstTokenAtMs = 0;
|
|
225
|
+
}
|
|
226
|
+
if (this.chunkOn === "phoneme-stream" && this.tokenizer !== null) {
|
|
227
|
+
for (const t of this.buffer) {
|
|
228
|
+
this.phonemeCount += this.tokenizer.tokenize(t.text, t.index).length;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
reset(): void {
|
|
234
|
+
this.buffer = [];
|
|
235
|
+
this.phonemeCount = 0;
|
|
236
|
+
this.firstTokenAtMs = 0;
|
|
237
|
+
this.phrasesEmitted = 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private endsWithTerminator(text: string): boolean {
|
|
241
|
+
if (text.length === 0) return false;
|
|
242
|
+
const last = text[text.length - 1];
|
|
243
|
+
return this.terminators.has(last);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private flushAs(terminator: Phrase["terminator"]): Phrase {
|
|
247
|
+
const tokens = this.buffer;
|
|
248
|
+
this.buffer = [];
|
|
249
|
+
this.phonemeCount = 0;
|
|
250
|
+
this.firstTokenAtMs = 0;
|
|
251
|
+
this.phrasesEmitted++;
|
|
252
|
+
const fromIndex = tokens[0].index;
|
|
253
|
+
const toIndex = tokens[tokens.length - 1].index;
|
|
254
|
+
const text = tokens.map((t) => t.text).join("");
|
|
255
|
+
const phrase: Phrase = {
|
|
256
|
+
id: this.nextPhraseId++,
|
|
257
|
+
text,
|
|
258
|
+
fromIndex,
|
|
259
|
+
toIndex,
|
|
260
|
+
terminator,
|
|
261
|
+
};
|
|
262
|
+
return phrase;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function chunkTokens(
|
|
267
|
+
tokens: TextToken[],
|
|
268
|
+
config: PhraseChunkerConfig,
|
|
269
|
+
acceptedAt = 0,
|
|
270
|
+
tokenizer: PhonemeTokenizer | null = null,
|
|
271
|
+
): Phrase[] {
|
|
272
|
+
const chunker = new PhraseChunker(config, tokenizer);
|
|
273
|
+
const phrases: Phrase[] = [];
|
|
274
|
+
for (const t of tokens) {
|
|
275
|
+
const p = chunker.push({ ...t, acceptedAt });
|
|
276
|
+
if (p) phrases.push(p);
|
|
277
|
+
}
|
|
278
|
+
const tail = chunker.flushPending();
|
|
279
|
+
if (tail) phrases.push(tail);
|
|
280
|
+
return phrases;
|
|
281
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete implementations of the `VoicePipeline` seams (`pipeline.ts`).
|
|
3
|
+
*
|
|
4
|
+
* `pipeline.ts` defines two interfaces — `DraftProposer`, `TargetVerifier`
|
|
5
|
+
* — plus the ASR contract (it consumes the live `StreamingTranscriber`
|
|
6
|
+
* from `voice/types.ts` directly) and an overlapped scheduler that drives
|
|
7
|
+
* the fused mic→speech graph from `packages/inference/AGENTS.md` §4. This
|
|
8
|
+
* module fills those interfaces against the live runtime:
|
|
9
|
+
*
|
|
10
|
+
* - `MissingAsrTranscriber` — a `StreamingTranscriber` that hard-fails
|
|
11
|
+
* when no ASR backend is available (AGENTS.md §3 — no silent cloud
|
|
12
|
+
* fallback). The bridge's `resolveTranscriber()` returns this instead
|
|
13
|
+
* of throwing eagerly so the failure surfaces at turn time.
|
|
14
|
+
* - `MtpDraftProposer` — proposes a small MTP draft window from the
|
|
15
|
+
* running in-process llama.cpp runtime; honours `cancel.cancelled`
|
|
16
|
+
* between kernel ticks.
|
|
17
|
+
* - `MtpTargetVerifier` — verifies against the text model's KV cache
|
|
18
|
+
* and consumes exact verifier accept/reject events when the runtime
|
|
19
|
+
* emits them.
|
|
20
|
+
*
|
|
21
|
+
* Hard-fail discipline (AGENTS.md §3 + §9): a missing fused ASR region in
|
|
22
|
+
* voice mode is a thrown `VoiceStartupError`, never a silent cloud
|
|
23
|
+
* fallback, never log-and-continue.
|
|
24
|
+
*
|
|
25
|
+
* Why a separate file from `pipeline.ts`: `pipeline.ts` stays
|
|
26
|
+
* dependency-light (it is the streaming contract, importable by text-only
|
|
27
|
+
* callers). The runtime wiring lives here so
|
|
28
|
+
* the contract module does not drag it in.
|
|
29
|
+
*/
|
|
30
|
+
import type { GenerateArgs } from "../backend";
|
|
31
|
+
import { type DraftProposer, type TargetVerifier } from "./pipeline";
|
|
32
|
+
import type { PcmFrame, StreamingTranscriber, TextToken, TranscriptUpdate, VerifierStreamEvent } from "./types";
|
|
33
|
+
/**
|
|
34
|
+
* A `StreamingTranscriber` that hard-fails: used when no ASR backend is
|
|
35
|
+
* available (no fused streaming decoder, no fused batch decoder, no
|
|
36
|
+
* bundled ASR region) but a voice turn was requested. AGENTS.md §3 —
|
|
37
|
+
* missing required voice backend in voice mode is a thrown
|
|
38
|
+
* `VoiceStartupError`, never a silent fallback. The bridge returns this
|
|
39
|
+
* from `resolveTranscriber()` so the failure surfaces when the pipeline
|
|
40
|
+
* actually feeds audio rather than at bridge construction.
|
|
41
|
+
*/
|
|
42
|
+
export declare class MissingAsrTranscriber implements StreamingTranscriber {
|
|
43
|
+
private readonly reason;
|
|
44
|
+
constructor(reason: string);
|
|
45
|
+
feed(_frame: PcmFrame): void;
|
|
46
|
+
flush(): Promise<TranscriptUpdate>;
|
|
47
|
+
on(): () => void;
|
|
48
|
+
dispose(): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Minimal surface of the running MTP text runtime the draft/verify
|
|
52
|
+
* adapters need. Kept structural so tests can pass a fake without
|
|
53
|
+
* standing up a real runtime.
|
|
54
|
+
*/
|
|
55
|
+
export interface MtpTextRunner {
|
|
56
|
+
/** True when MTP speculative decoding is enabled. */
|
|
57
|
+
hasDrafter(): boolean;
|
|
58
|
+
generateWithVerifierEvents(args: GenerateArgs & {
|
|
59
|
+
onVerifierEvent: (event: VerifierStreamEvent) => void | Promise<void>;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
text: string;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/** Adapt a local-inference backend onto `MtpTextRunner`. */
|
|
65
|
+
export declare function mtpTextRunner(runner: {
|
|
66
|
+
mtpEnabled(): boolean;
|
|
67
|
+
generateWithUsage(args: GenerateArgs & {
|
|
68
|
+
onVerifierEvent: (event: VerifierStreamEvent) => void | Promise<void>;
|
|
69
|
+
}): Promise<{
|
|
70
|
+
text: string;
|
|
71
|
+
}>;
|
|
72
|
+
}): MtpTextRunner;
|
|
73
|
+
/**
|
|
74
|
+
* `DraftProposer` over native MTP. The runtime draft window already bounds
|
|
75
|
+
* proposals; this adapter additionally
|
|
76
|
+
* clamps to the pipeline's `maxDraft` and stops early on
|
|
77
|
+
* `cancel.cancelled`. GPU dispatch is N=1 (the fork's voice profile
|
|
78
|
+
* disables command-buffer batching — ledger §2 "Keep voice dispatch
|
|
79
|
+
* unbatched") so a barge-in lands at the next kernel boundary.
|
|
80
|
+
*
|
|
81
|
+
* Until the fork exposes a "draft only, return the proposed tokens"
|
|
82
|
+
* endpoint, the proposer issues a short low-temperature completion
|
|
83
|
+
* (`maxTokens = maxDraft`) and treats the produced tokens as the draft
|
|
84
|
+
* window. The verifier then re-checks them against the target's KV — the
|
|
85
|
+
* standard speculative-decoding contract, just with the draft sourced
|
|
86
|
+
* from the same server.
|
|
87
|
+
*/
|
|
88
|
+
export declare class MtpDraftProposer implements DraftProposer {
|
|
89
|
+
private readonly runner;
|
|
90
|
+
constructor(runner: MtpTextRunner);
|
|
91
|
+
propose(args: {
|
|
92
|
+
prefix: ReadonlyArray<TextToken>;
|
|
93
|
+
maxDraft: number;
|
|
94
|
+
cancel: {
|
|
95
|
+
cancelled: boolean;
|
|
96
|
+
};
|
|
97
|
+
}): Promise<TextToken[]>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* `TargetVerifier` over the text model via MTP. Runs one
|
|
101
|
+
* autoregressive verify step against the runtime KV cache: it sends the
|
|
102
|
+
* accepted prefix and reads back the model's own continuation. The
|
|
103
|
+
* leading tokens that match the supplied `draft` are "accepted from
|
|
104
|
+
* draft"; the first mismatch is the correction; the model's `done` /
|
|
105
|
+
* stop is propagated.
|
|
106
|
+
*
|
|
107
|
+
* When the native fork emits exact verifier reject ranges, the
|
|
108
|
+
* `onVerifierEvent` callback already carries `kind: "reject"` events with
|
|
109
|
+
* the rejected token positions — this adapter records both and trusts the
|
|
110
|
+
* server's accept/reject split rather than re-deriving it.
|
|
111
|
+
*/
|
|
112
|
+
export declare class MtpTargetVerifier implements TargetVerifier {
|
|
113
|
+
private readonly runner;
|
|
114
|
+
private readonly maxStep;
|
|
115
|
+
constructor(runner: MtpTextRunner, opts?: {
|
|
116
|
+
maxStep?: number;
|
|
117
|
+
});
|
|
118
|
+
verify(args: {
|
|
119
|
+
prefix: ReadonlyArray<TextToken>;
|
|
120
|
+
draft: ReadonlyArray<TextToken>;
|
|
121
|
+
cancel: {
|
|
122
|
+
cancelled: boolean;
|
|
123
|
+
};
|
|
124
|
+
}): Promise<{
|
|
125
|
+
accepted: TextToken[];
|
|
126
|
+
done: boolean;
|
|
127
|
+
}>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Bridge a `{cancelled: boolean}` flag (the pipeline's cancellation
|
|
131
|
+
* primitive — checked between kernel ticks) onto an `AbortSignal` so the
|
|
132
|
+
* local generation aborts when a barge-in fires.
|
|
133
|
+
*
|
|
134
|
+
* L6 — event-driven cancellation. The cancel token may expose an
|
|
135
|
+
* `onCancel(listener)` hook (set by the scheduler / pipeline when it
|
|
136
|
+
* wires the token up); when present, we fire `controller.abort()`
|
|
137
|
+
* synchronously from that hook and skip polling entirely. When the
|
|
138
|
+
* token is the plain `{cancelled: boolean}` POJO with no hook, we fall
|
|
139
|
+
* back to a coarse poll so a barge-in still lands in bounded time —
|
|
140
|
+
* but the hook-driven path is what voice barge-ins use in production.
|
|
141
|
+
*/
|
|
142
|
+
export interface CancelTokenWithSignal {
|
|
143
|
+
cancelled: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Optional hook the token's owner fires synchronously when `cancelled`
|
|
146
|
+
* flips from false to true. The listener returns nothing; calling it
|
|
147
|
+
* after `cancelled` has already been set is a harmless no-op.
|
|
148
|
+
*/
|
|
149
|
+
onCancel?: (listener: () => void) => () => void;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=pipeline-impls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-impls.d.ts","sourceRoot":"","sources":["pipeline-impls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,cAAc,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACX,QAAQ,EACR,oBAAoB,EACpB,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,MAAM,SAAS,CAAC;AAMjB;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,YAAW,oBAAoB;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAC3C,IAAI,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI;IAGtB,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAGxC,EAAE,IAAI,MAAM,IAAI;IAGhB,OAAO,IAAI,IAAI;CACf;AAMD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC7B,qDAAqD;IACrD,UAAU,IAAI,OAAO,CAAC;IACtB,0BAA0B,CACzB,IAAI,EAAE,YAAY,GAAG;QACpB,eAAe,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACtE,GACC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7B;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACrC,UAAU,IAAI,OAAO,CAAC;IACtB,iBAAiB,CAChB,IAAI,EAAE,YAAY,GAAG;QACpB,eAAe,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACtE,GACC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7B,GAAG,aAAa,CAUhB;AAaD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,YAAW,aAAa;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,MAAM,EAAE,aAAa;IAI3B,OAAO,CAAC,IAAI,EAAE;QACnB,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE;YAAE,SAAS,EAAE,OAAO,CAAA;SAAE,CAAC;KAC/B,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;CA6BxB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,aAAa,EAAE,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAK5D,MAAM,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QACjC,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QAChC,MAAM,EAAE;YAAE,SAAS,EAAE,OAAO,CAAA;SAAE,CAAC;KAC/B,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;CA2CrD;AAMD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;CAChD"}
|