@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,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mic capture → `PcmRingBuffer` + VAD tee.
|
|
3
|
+
*
|
|
4
|
+
* The `MicSource` interface (see `./types`) is the only seam the rest of the
|
|
5
|
+
* voice loop sees. The first concrete implementation, `DesktopMicSource`,
|
|
6
|
+
* shells out to the platform's standard PCM-capable recorder, emits 16 kHz
|
|
7
|
+
* mono `PcmFrame`s, and lets callers tee them anywhere:
|
|
8
|
+
*
|
|
9
|
+
* mic → DesktopMicSource ─┬─→ PcmRingBuffer (ASR reads PCM from here)
|
|
10
|
+
* └─→ VadDetector (speech / barge-in signals)
|
|
11
|
+
*
|
|
12
|
+
* Per-platform recorder selection (in priority order):
|
|
13
|
+
* - Linux: `arecord` (alsa-utils), else `parec` (PulseAudio), else `sox`/`rec`.
|
|
14
|
+
* - macOS: `sox`/`rec` (`sox -d`), else `ffmpeg -f avfoundation`.
|
|
15
|
+
* - Windows: `ffmpeg -f dshow` (DirectShow capture — bundled with most
|
|
16
|
+
* Windows installs of ffmpeg; the renderer's `getUserMedia` path
|
|
17
|
+
* feeding `PushMicSource` is the no-ffmpeg route).
|
|
18
|
+
*
|
|
19
|
+
* Connectors that already have a decoded PCM stream (Discord voice, the
|
|
20
|
+
* Electrobun renderer's `getUserMedia` path, a mobile capture callback —
|
|
21
|
+
* the Capacitor `Microphone` plugin on iOS/Android) implement `MicSource`
|
|
22
|
+
* over `PushMicSource` instead of spawning a process.
|
|
23
|
+
*
|
|
24
|
+
* No fallback sludge: if no recorder binary is on PATH (and no override
|
|
25
|
+
* argv was given), `start()` throws — the caller surfaces "no mic backend
|
|
26
|
+
* available", it does not pretend to capture silence.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { type ChildProcess, spawn } from "node:child_process";
|
|
30
|
+
import { existsSync } from "node:fs";
|
|
31
|
+
import path from "node:path";
|
|
32
|
+
import process from "node:process";
|
|
33
|
+
import { PcmRingBuffer } from "./ring-buffer";
|
|
34
|
+
import type { AudioSink, MicSource, PcmFrame } from "./types";
|
|
35
|
+
|
|
36
|
+
/** Resolve an executable on PATH (with the Windows extension list). */
|
|
37
|
+
function whichBin(bin: string): string | null {
|
|
38
|
+
const pathEnv = process.env.PATH ?? "";
|
|
39
|
+
const exts =
|
|
40
|
+
process.platform === "win32" ? [".exe", ".cmd", ".bat", ""] : [""];
|
|
41
|
+
for (const dir of pathEnv.split(path.delimiter)) {
|
|
42
|
+
if (!dir) continue;
|
|
43
|
+
for (const ext of exts) {
|
|
44
|
+
const candidate = path.join(dir, bin + ext);
|
|
45
|
+
if (existsSync(candidate)) return candidate;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Pick the recorder that streams raw signed 16-bit LE mono PCM at
|
|
53
|
+
* `sampleRate` on stdout for the host platform. Returns `null` when none is
|
|
54
|
+
* available — the caller throws an actionable error or uses `PushMicSource`.
|
|
55
|
+
* Exported so the cross-platform preflight (`voice:interactive
|
|
56
|
+
* --platform-report`) can report which recorder the host would use.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveDesktopRecorder(
|
|
59
|
+
sampleRate: number,
|
|
60
|
+
device?: string,
|
|
61
|
+
): { program: string; argv: string[] } | null {
|
|
62
|
+
const sr = String(sampleRate);
|
|
63
|
+
if (process.platform === "linux") {
|
|
64
|
+
if (whichBin("arecord")) {
|
|
65
|
+
return {
|
|
66
|
+
program: "arecord",
|
|
67
|
+
argv: [
|
|
68
|
+
"-q",
|
|
69
|
+
"-D",
|
|
70
|
+
device ?? "default",
|
|
71
|
+
"-f",
|
|
72
|
+
"S16_LE",
|
|
73
|
+
"-r",
|
|
74
|
+
sr,
|
|
75
|
+
"-c",
|
|
76
|
+
"1",
|
|
77
|
+
"-t",
|
|
78
|
+
"raw",
|
|
79
|
+
"-",
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (whichBin("parec")) {
|
|
84
|
+
// PulseAudio / PipeWire capture, raw 16-bit LE mono on stdout.
|
|
85
|
+
return {
|
|
86
|
+
program: "parec",
|
|
87
|
+
argv: ["--raw", "--format=s16le", `--rate=${sr}`, "--channels=1"],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const soxLinux = whichBin("rec") ? "rec" : whichBin("sox") ? "sox" : null;
|
|
91
|
+
if (soxLinux) {
|
|
92
|
+
return {
|
|
93
|
+
program: soxLinux,
|
|
94
|
+
argv: [
|
|
95
|
+
"-q",
|
|
96
|
+
...(soxLinux === "sox" ? ["-d"] : []),
|
|
97
|
+
"-r",
|
|
98
|
+
sr,
|
|
99
|
+
"-c",
|
|
100
|
+
"1",
|
|
101
|
+
"-b",
|
|
102
|
+
"16",
|
|
103
|
+
"-e",
|
|
104
|
+
"signed-integer",
|
|
105
|
+
"-t",
|
|
106
|
+
"raw",
|
|
107
|
+
"-",
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (process.platform === "darwin") {
|
|
114
|
+
const soxMac = whichBin("sox") ? "sox" : whichBin("rec") ? "rec" : null;
|
|
115
|
+
if (soxMac) {
|
|
116
|
+
return {
|
|
117
|
+
program: soxMac,
|
|
118
|
+
argv: [
|
|
119
|
+
"-q",
|
|
120
|
+
...(soxMac === "sox" ? ["-d"] : []),
|
|
121
|
+
"-r",
|
|
122
|
+
sr,
|
|
123
|
+
"-c",
|
|
124
|
+
"1",
|
|
125
|
+
"-b",
|
|
126
|
+
"16",
|
|
127
|
+
"-e",
|
|
128
|
+
"signed-integer",
|
|
129
|
+
"-t",
|
|
130
|
+
"raw",
|
|
131
|
+
"-",
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (whichBin("ffmpeg")) {
|
|
136
|
+
// avfoundation default audio device → raw PCM16-LE mono on stdout.
|
|
137
|
+
return {
|
|
138
|
+
program: "ffmpeg",
|
|
139
|
+
argv: [
|
|
140
|
+
"-loglevel",
|
|
141
|
+
"error",
|
|
142
|
+
"-f",
|
|
143
|
+
"avfoundation",
|
|
144
|
+
"-i",
|
|
145
|
+
device ?? ":default",
|
|
146
|
+
"-ac",
|
|
147
|
+
"1",
|
|
148
|
+
"-ar",
|
|
149
|
+
sr,
|
|
150
|
+
"-f",
|
|
151
|
+
"s16le",
|
|
152
|
+
"pipe:1",
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (process.platform === "win32") {
|
|
159
|
+
if (whichBin("ffmpeg")) {
|
|
160
|
+
// DirectShow default microphone → raw PCM16-LE mono on stdout. The
|
|
161
|
+
// device name is `audio="<friendly name>"`; `device` overrides it.
|
|
162
|
+
// Without a name ffmpeg's dshow demuxer can't pick a default, so we
|
|
163
|
+
// require either an explicit device or fall back to the common
|
|
164
|
+
// "Microphone" alias; callers that know the device name pass it.
|
|
165
|
+
const dshowDevice = device
|
|
166
|
+
? `audio=${device}`
|
|
167
|
+
: "audio=Microphone (Realtek(R) Audio)";
|
|
168
|
+
return {
|
|
169
|
+
program: "ffmpeg",
|
|
170
|
+
argv: [
|
|
171
|
+
"-loglevel",
|
|
172
|
+
"error",
|
|
173
|
+
"-f",
|
|
174
|
+
"dshow",
|
|
175
|
+
"-i",
|
|
176
|
+
dshowDevice,
|
|
177
|
+
"-ac",
|
|
178
|
+
"1",
|
|
179
|
+
"-ar",
|
|
180
|
+
sr,
|
|
181
|
+
"-f",
|
|
182
|
+
"s16le",
|
|
183
|
+
"pipe:1",
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const DEFAULT_SAMPLE_RATE = 16_000;
|
|
193
|
+
const DEFAULT_FRAME_MS = 32; // 512 samples @ 16 kHz — matches Silero's window.
|
|
194
|
+
|
|
195
|
+
function frameSamplesFor(sampleRate: number, frameMs: number): number {
|
|
196
|
+
return Math.round((sampleRate * frameMs) / 1000);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function now(): number {
|
|
200
|
+
return typeof performance !== "undefined" && performance.now
|
|
201
|
+
? performance.now()
|
|
202
|
+
: Date.now();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
abstract class BaseMicSource implements MicSource {
|
|
206
|
+
readonly sampleRate: number;
|
|
207
|
+
readonly frameSamples: number;
|
|
208
|
+
protected readonly frameListeners = new Set<(frame: PcmFrame) => void>();
|
|
209
|
+
protected readonly errorListeners = new Set<(error: Error) => void>();
|
|
210
|
+
protected _running = false;
|
|
211
|
+
|
|
212
|
+
constructor(sampleRate: number, frameSamples: number) {
|
|
213
|
+
this.sampleRate = sampleRate;
|
|
214
|
+
this.frameSamples = frameSamples;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
get running(): boolean {
|
|
218
|
+
return this._running;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
abstract start(): Promise<void>;
|
|
222
|
+
abstract stop(): Promise<void>;
|
|
223
|
+
|
|
224
|
+
onFrame(listener: (frame: PcmFrame) => void): () => void {
|
|
225
|
+
this.frameListeners.add(listener);
|
|
226
|
+
return () => this.frameListeners.delete(listener);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
onError(listener: (error: Error) => void): () => void {
|
|
230
|
+
this.errorListeners.add(listener);
|
|
231
|
+
return () => this.errorListeners.delete(listener);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
protected emitFrame(pcm: Float32Array, timestampMs: number): void {
|
|
235
|
+
const frame: PcmFrame = { pcm, sampleRate: this.sampleRate, timestampMs };
|
|
236
|
+
for (const l of this.frameListeners) l(frame);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
protected emitError(error: Error): void {
|
|
240
|
+
this._running = false;
|
|
241
|
+
for (const l of this.errorListeners) l(error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface DesktopMicSourceOptions {
|
|
246
|
+
/** Capture sample rate. Default 16 kHz (what the VAD + Qwen3-ASR expect). */
|
|
247
|
+
sampleRate?: number;
|
|
248
|
+
/** Frame duration in ms. Default 32 ms (one Silero window @ 16 kHz). */
|
|
249
|
+
frameMs?: number;
|
|
250
|
+
/** Recorder program. Default: auto-resolved per platform (see module doc). */
|
|
251
|
+
program?: string;
|
|
252
|
+
/** Override the recorder argv. When set, `program` is the executable and
|
|
253
|
+
* these are the args (must produce raw little-endian signed 16-bit mono
|
|
254
|
+
* PCM at `sampleRate` on stdout). */
|
|
255
|
+
argv?: string[];
|
|
256
|
+
/** Capture device — ALSA name (Linux `arecord -D`), avfoundation index
|
|
257
|
+
* (macOS, e.g. `:0`), or DirectShow friendly name (Windows). */
|
|
258
|
+
device?: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* `MicSource` backed by a recorder subprocess. The recorder is auto-resolved
|
|
263
|
+
* per platform (Linux: `arecord`/`parec`/`sox`; macOS: `sox -d`/`ffmpeg -f
|
|
264
|
+
* avfoundation`; Windows: `ffmpeg -f dshow`); all stream raw PCM16 mono to
|
|
265
|
+
* stdout, which this class re-frames into fixed-size `Float32Array` frames
|
|
266
|
+
* in [-1, 1].
|
|
267
|
+
*
|
|
268
|
+
* When no recorder is available `start()` throws — the renderer's
|
|
269
|
+
* `getUserMedia` path (or the Capacitor `Microphone` plugin on mobile),
|
|
270
|
+
* both feeding a `PushMicSource`, are the no-CLI route.
|
|
271
|
+
*/
|
|
272
|
+
export class DesktopMicSource extends BaseMicSource {
|
|
273
|
+
private readonly program: string;
|
|
274
|
+
private readonly argv: string[];
|
|
275
|
+
private proc: ChildProcess | null = null;
|
|
276
|
+
// Carry-over bytes that didn't complete a frame on the last `data` chunk.
|
|
277
|
+
private readonly carry: number[] = [];
|
|
278
|
+
private readonly bytesPerFrame: number;
|
|
279
|
+
|
|
280
|
+
constructor(opts: DesktopMicSourceOptions = {}) {
|
|
281
|
+
const sampleRate = opts.sampleRate ?? DEFAULT_SAMPLE_RATE;
|
|
282
|
+
const frameMs = opts.frameMs ?? DEFAULT_FRAME_MS;
|
|
283
|
+
const frameSamples = frameSamplesFor(sampleRate, frameMs);
|
|
284
|
+
super(sampleRate, frameSamples);
|
|
285
|
+
this.bytesPerFrame = frameSamples * 2;
|
|
286
|
+
|
|
287
|
+
if (opts.program && opts.argv) {
|
|
288
|
+
this.program = opts.program;
|
|
289
|
+
this.argv = opts.argv;
|
|
290
|
+
} else {
|
|
291
|
+
const resolved = resolveDesktopRecorder(sampleRate, opts.device);
|
|
292
|
+
if (resolved && !opts.program) {
|
|
293
|
+
this.program = resolved.program;
|
|
294
|
+
this.argv = resolved.argv;
|
|
295
|
+
} else if (opts.program) {
|
|
296
|
+
// Caller named a program but not argv — give it the resolved argv if
|
|
297
|
+
// the resolved program matches, else an empty argv (it must be a
|
|
298
|
+
// recorder that defaults to raw-PCM-on-stdout, e.g. a wrapper).
|
|
299
|
+
this.program = opts.program;
|
|
300
|
+
this.argv =
|
|
301
|
+
resolved && resolved.program === opts.program ? resolved.argv : [];
|
|
302
|
+
} else {
|
|
303
|
+
this.program = "";
|
|
304
|
+
this.argv = [];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async start(): Promise<void> {
|
|
310
|
+
if (this._running) return;
|
|
311
|
+
if (!this.program) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`[voice] No CLI mic recorder available on platform '${process.platform}'. ` +
|
|
314
|
+
`Install one (Linux: alsa-utils/pulseaudio/sox; macOS: sox or ffmpeg; ` +
|
|
315
|
+
`Windows: ffmpeg) or feed PCM via PushMicSource (the renderer's ` +
|
|
316
|
+
`getUserMedia path, or the Capacitor Microphone plugin on mobile).`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
let proc: ChildProcess;
|
|
320
|
+
try {
|
|
321
|
+
proc = spawn(this.program, this.argv, {
|
|
322
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
323
|
+
});
|
|
324
|
+
} catch (err) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
`[voice] Failed to spawn mic recorder '${this.program}': ${
|
|
327
|
+
err instanceof Error ? err.message : String(err)
|
|
328
|
+
}. Install it (Linux: alsa-utils/sox; macOS: sox or ffmpeg; Windows: ffmpeg) or use PushMicSource.`,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
this.proc = proc;
|
|
332
|
+
|
|
333
|
+
const stderrChunks: Buffer[] = [];
|
|
334
|
+
proc.stderr?.on("data", (b: Buffer) => {
|
|
335
|
+
stderrChunks.push(b);
|
|
336
|
+
if (stderrChunks.length > 64) stderrChunks.shift();
|
|
337
|
+
});
|
|
338
|
+
proc.stdout?.on("data", (chunk: Buffer) => this.ingest(chunk));
|
|
339
|
+
proc.on("error", (err) => {
|
|
340
|
+
this.emitError(
|
|
341
|
+
new Error(
|
|
342
|
+
`[voice] Mic recorder '${this.program}' error: ${err.message}`,
|
|
343
|
+
),
|
|
344
|
+
);
|
|
345
|
+
});
|
|
346
|
+
proc.on("exit", (code, signal) => {
|
|
347
|
+
this.proc = null;
|
|
348
|
+
if (this._running) {
|
|
349
|
+
// Exited while we expected it to be running.
|
|
350
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
|
|
351
|
+
this.emitError(
|
|
352
|
+
new Error(
|
|
353
|
+
`[voice] Mic recorder '${this.program}' exited unexpectedly (code=${code} signal=${signal})${
|
|
354
|
+
stderr ? `: ${stderr}` : ""
|
|
355
|
+
}`,
|
|
356
|
+
),
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Confirm the process is alive and producing audio: arecord/sox emit
|
|
362
|
+
// their first PCM chunk within a few hundred ms; if it dies immediately
|
|
363
|
+
// (bad device, missing binary masquerading) the `exit` handler above
|
|
364
|
+
// already fired. We just need to flip `_running` so callers can tee.
|
|
365
|
+
this._running = true;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async stop(): Promise<void> {
|
|
369
|
+
this._running = false;
|
|
370
|
+
const proc = this.proc;
|
|
371
|
+
this.proc = null;
|
|
372
|
+
this.carry.length = 0;
|
|
373
|
+
if (proc && proc.exitCode === null) {
|
|
374
|
+
proc.kill("SIGTERM");
|
|
375
|
+
// Best-effort hard kill if it ignores SIGTERM.
|
|
376
|
+
await new Promise<void>((resolve) => {
|
|
377
|
+
const t = setTimeout(() => {
|
|
378
|
+
if (proc.exitCode === null) proc.kill("SIGKILL");
|
|
379
|
+
resolve();
|
|
380
|
+
}, 250);
|
|
381
|
+
proc.once("exit", () => {
|
|
382
|
+
clearTimeout(t);
|
|
383
|
+
resolve();
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private ingest(chunk: Buffer): void {
|
|
390
|
+
const ts = now();
|
|
391
|
+
// Accumulate raw bytes, slice into whole frames.
|
|
392
|
+
for (let i = 0; i < chunk.length; i++) this.carry.push(chunk[i]);
|
|
393
|
+
while (this.carry.length >= this.bytesPerFrame) {
|
|
394
|
+
const bytes = this.carry.splice(0, this.bytesPerFrame);
|
|
395
|
+
const pcm = new Float32Array(this.frameSamples);
|
|
396
|
+
for (let s = 0; s < this.frameSamples; s++) {
|
|
397
|
+
const lo = bytes[s * 2];
|
|
398
|
+
const hi = bytes[s * 2 + 1];
|
|
399
|
+
let v = (hi << 8) | lo;
|
|
400
|
+
if (v >= 0x8000) v -= 0x10000;
|
|
401
|
+
pcm[s] = v / 0x8000;
|
|
402
|
+
}
|
|
403
|
+
this.emitFrame(pcm, ts);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* `MicSource` driven by an external producer (Discord opus-decoded PCM, the
|
|
410
|
+
* Electrobun renderer's `getUserMedia` chunks, a mobile capture callback,
|
|
411
|
+
* or a test). The producer calls `push(pcm)` (any sample count, mono,
|
|
412
|
+
* already at `sampleRate`); this class re-frames it to `frameSamples`-long
|
|
413
|
+
* frames and emits them. `start()` / `stop()` just toggle the gate.
|
|
414
|
+
*/
|
|
415
|
+
export class PushMicSource extends BaseMicSource {
|
|
416
|
+
// Pending samples that didn't complete a frame.
|
|
417
|
+
private pending: Float32Array = new Float32Array(0);
|
|
418
|
+
private pendingStartTimestampMs = 0;
|
|
419
|
+
|
|
420
|
+
constructor(
|
|
421
|
+
opts: { sampleRate?: number; frameMs?: number; frameSamples?: number } = {},
|
|
422
|
+
) {
|
|
423
|
+
const sampleRate = opts.sampleRate ?? DEFAULT_SAMPLE_RATE;
|
|
424
|
+
const frameSamples =
|
|
425
|
+
opts.frameSamples ??
|
|
426
|
+
frameSamplesFor(sampleRate, opts.frameMs ?? DEFAULT_FRAME_MS);
|
|
427
|
+
super(sampleRate, frameSamples);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async start(): Promise<void> {
|
|
431
|
+
this._running = true;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async stop(): Promise<void> {
|
|
435
|
+
this._running = false;
|
|
436
|
+
this.pending = new Float32Array(0);
|
|
437
|
+
this.pendingStartTimestampMs = 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Feed mono PCM in [-1, 1] at `sampleRate`. Re-frames and emits. The
|
|
442
|
+
* timestamp is the first sample's timestamp; emitted frames advance by
|
|
443
|
+
* their sample offset so a large pushed buffer still presents a real audio
|
|
444
|
+
* timeline to VAD/ASR.
|
|
445
|
+
*/
|
|
446
|
+
push(pcm: Float32Array, timestampMs = now()): void {
|
|
447
|
+
if (!this._running) return;
|
|
448
|
+
const mergedStartTimestampMs =
|
|
449
|
+
this.pending.length > 0 ? this.pendingStartTimestampMs : timestampMs;
|
|
450
|
+
const merged = new Float32Array(this.pending.length + pcm.length);
|
|
451
|
+
merged.set(this.pending, 0);
|
|
452
|
+
merged.set(pcm, this.pending.length);
|
|
453
|
+
let offset = 0;
|
|
454
|
+
while (merged.length - offset >= this.frameSamples) {
|
|
455
|
+
const frame = merged.slice(offset, offset + this.frameSamples);
|
|
456
|
+
const frameTimestampMs =
|
|
457
|
+
mergedStartTimestampMs + (offset / this.sampleRate) * 1000;
|
|
458
|
+
offset += this.frameSamples;
|
|
459
|
+
this.emitFrame(frame, frameTimestampMs);
|
|
460
|
+
}
|
|
461
|
+
this.pending = merged.slice(offset);
|
|
462
|
+
this.pendingStartTimestampMs =
|
|
463
|
+
this.pending.length > 0
|
|
464
|
+
? mergedStartTimestampMs + (offset / this.sampleRate) * 1000
|
|
465
|
+
: 0;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** Feed mono PCM16 little-endian bytes (Discord / browser path). */
|
|
469
|
+
pushPcm16(bytes: Uint8Array, timestampMs = now()): void {
|
|
470
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
471
|
+
const n = Math.floor(bytes.byteLength / 2);
|
|
472
|
+
const out = new Float32Array(n);
|
|
473
|
+
for (let i = 0; i < n; i++) out[i] = view.getInt16(i * 2, true) / 0x8000;
|
|
474
|
+
this.push(out, timestampMs);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/** Surface a fatal producer-side error to subscribers. */
|
|
478
|
+
fail(error: Error): void {
|
|
479
|
+
this.emitError(error);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Wire a `MicSource` to a `PcmRingBuffer` (the buffer the ASR reads PCM
|
|
485
|
+
* from). Returns the ring buffer and an unsubscribe function. The ring
|
|
486
|
+
* buffer's `onOverflow` is forwarded so callers can apply backpressure.
|
|
487
|
+
*/
|
|
488
|
+
export function pipeMicToRingBuffer(
|
|
489
|
+
source: MicSource,
|
|
490
|
+
sink: AudioSink,
|
|
491
|
+
opts: {
|
|
492
|
+
/** Ring buffer capacity in samples. Default 8 s at the source rate. */
|
|
493
|
+
capacitySamples?: number;
|
|
494
|
+
onOverflow?: (droppedSamples: number) => void;
|
|
495
|
+
} = {},
|
|
496
|
+
): { ringBuffer: PcmRingBuffer; unsubscribe: () => void } {
|
|
497
|
+
const capacity = opts.capacitySamples ?? source.sampleRate * 8;
|
|
498
|
+
const ringBuffer = new PcmRingBuffer(capacity, source.sampleRate, sink, {
|
|
499
|
+
onOverflow: opts.onOverflow,
|
|
500
|
+
});
|
|
501
|
+
const off = source.onFrame((frame) => ringBuffer.write(frame.pcm));
|
|
502
|
+
return { ringBuffer, unsubscribe: off };
|
|
503
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimistic-generation policy — Wave 3 W3-9.
|
|
3
|
+
*
|
|
4
|
+
* Decides whether the LM should fire optimistically the moment the turn
|
|
5
|
+
* detector crosses the EOT threshold (before the audio buffer is fully
|
|
6
|
+
* drained), or wait for the full hangover. Per the user spec:
|
|
7
|
+
*
|
|
8
|
+
* > "We want to optimize to start running and checking basically the
|
|
9
|
+
* second there is no audio / voice detected, and then we start
|
|
10
|
+
* optimistically processing, and if the user starts talking again we
|
|
11
|
+
* kill and add to the conversation and start reprocessing."
|
|
12
|
+
*
|
|
13
|
+
* Default heuristic:
|
|
14
|
+
* - Plugged-in (or power state unknown): optimistic = true.
|
|
15
|
+
* - On battery: optimistic = false (the extra forward pass burns
|
|
16
|
+
* joules every false-positive EOT).
|
|
17
|
+
*
|
|
18
|
+
* Callers (notably `VoiceStateMachine`) read `shouldStartOptimisticLm()`
|
|
19
|
+
* each time they consider firing the speculative drafter. The policy can be
|
|
20
|
+
* mutated at runtime via `setOptimisticOverride()` so a Settings UI toggle
|
|
21
|
+
* or a per-session env var can pin the value without restarting the engine.
|
|
22
|
+
*/
|
|
23
|
+
/** Power-source signal. `unknown` is treated as "plugged in" for the
|
|
24
|
+
* default policy (a desktop without battery telemetry). */
|
|
25
|
+
export type PowerSourceState = "plugged-in" | "battery" | "unknown";
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the current power-source state.
|
|
28
|
+
*
|
|
29
|
+
* Production callers (engine bridge) prime the optimistic policy with this
|
|
30
|
+
* once at session start. There is no canonical power-source accessor in
|
|
31
|
+
* `device-tier.ts` (the hardware probe records RAM / GPU / cores but no
|
|
32
|
+
* battery telemetry), so this resolver consults two cheap signals:
|
|
33
|
+
*
|
|
34
|
+
* 1. `ELIZA_VOICE_POWER_SOURCE` env var (`"plugged-in" | "battery" |
|
|
35
|
+
* "unknown"`). Wins outright when set — operators / Settings can pin
|
|
36
|
+
* the policy without restarting the engine.
|
|
37
|
+
* 2. Linux `/sys/class/power_supply` AC online status when available
|
|
38
|
+
* synchronously. Returns `"plugged-in"` when any AC adapter reports
|
|
39
|
+
* `online`, `"battery"` when only batteries are present and none
|
|
40
|
+
* report online, `"unknown"` otherwise.
|
|
41
|
+
*
|
|
42
|
+
* macOS / Windows / mobile go through `"unknown"` here — the desktop probe
|
|
43
|
+
* surfaces those via the Electrobun side and Settings overrides it through
|
|
44
|
+
* (1). The default heuristic (`unknown → enabled`) treats those as
|
|
45
|
+
* plugged-in for the optimistic gate, which is the right default for
|
|
46
|
+
* desktop / dev (battery-aware mobile builds set the override explicitly).
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolvePowerSourceState(): PowerSourceState;
|
|
49
|
+
export interface OptimisticPolicyOptions {
|
|
50
|
+
/**
|
|
51
|
+
* Default value when no override is set. Resolved from the device tier
|
|
52
|
+
* at construction time (per the user spec: true on plugged-in,
|
|
53
|
+
* false on battery). Defaults to `true` when no power signal is given.
|
|
54
|
+
*/
|
|
55
|
+
defaultEnabled?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The threshold the turn detector's EOT probability must clear before
|
|
58
|
+
* the optimistic LM start fires. Mirrors `EOT_TENTATIVE_THRESHOLD`
|
|
59
|
+
* (the existing default) but is configurable per-policy so tier-aware
|
|
60
|
+
* deployments can tighten it on slower devices.
|
|
61
|
+
*/
|
|
62
|
+
eotThreshold?: number;
|
|
63
|
+
}
|
|
64
|
+
export interface ResolveOptimisticPolicyArgs {
|
|
65
|
+
powerSource?: PowerSourceState;
|
|
66
|
+
/**
|
|
67
|
+
* Explicit override. When set, wins over both the default and the
|
|
68
|
+
* power-source heuristic. Sourced from `voice.optimisticGenerationOnEot`
|
|
69
|
+
* in user settings (Wave 3C).
|
|
70
|
+
*/
|
|
71
|
+
override?: boolean;
|
|
72
|
+
}
|
|
73
|
+
export declare const DEFAULT_OPTIMISTIC_EOT_THRESHOLD = 0.6;
|
|
74
|
+
/**
|
|
75
|
+
* Pure resolver. Takes the power source + an optional override and returns
|
|
76
|
+
* whether optimistic LM start should fire.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveOptimisticPolicyEnabled(args: ResolveOptimisticPolicyArgs): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Mutable runtime policy. The voice state machine asks
|
|
81
|
+
* `policy.shouldStartOptimisticLm(eotProb)` before firing the drafter.
|
|
82
|
+
*
|
|
83
|
+
* Reasoning for a class rather than a free function: at runtime we need
|
|
84
|
+
* the override + the power source to be hot-swappable from Settings or a
|
|
85
|
+
* device-event listener (battery state change) without re-plumbing the
|
|
86
|
+
* machine.
|
|
87
|
+
*/
|
|
88
|
+
export declare class OptimisticGenerationPolicy {
|
|
89
|
+
private overrideValue;
|
|
90
|
+
private powerSource;
|
|
91
|
+
private readonly defaultEnabled;
|
|
92
|
+
private readonly eotThreshold;
|
|
93
|
+
constructor(opts?: OptimisticPolicyOptions);
|
|
94
|
+
/** Update the power source (called by the device-tier observer). */
|
|
95
|
+
setPowerSource(state: PowerSourceState): void;
|
|
96
|
+
/** Set / clear the user override. */
|
|
97
|
+
setOverride(value: boolean | undefined): void;
|
|
98
|
+
/** Resolve "should we be optimistic right now?". */
|
|
99
|
+
enabled(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Combined gate: the policy must be enabled AND the EOT probability
|
|
102
|
+
* must clear the policy's threshold. This is the canonical check the
|
|
103
|
+
* voice state machine calls before firing the drafter on a partial
|
|
104
|
+
* transcript.
|
|
105
|
+
*/
|
|
106
|
+
shouldStartOptimisticLm(eotProb: number): boolean;
|
|
107
|
+
get threshold(): number;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=optimistic-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimistic-policy.d.ts","sourceRoot":"","sources":["optimistic-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;4DAC4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,IAAI,gBAAgB,CAyC1D;AAED,MAAM,WAAW,uBAAuB;IACvC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,2BAA2B;IAC3C,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,8BAA8B,CAC7C,IAAI,EAAE,2BAA2B,GAC/B,OAAO,CAMT;AAED;;;;;;;;GAQG;AACH,qBAAa,0BAA0B;IACtC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,IAAI,GAAE,uBAA4B;IAM9C,oEAAoE;IACpE,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAI7C,qCAAqC;IACrC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI;IAI7C,oDAAoD;IACpD,OAAO,IAAI,OAAO;IAWlB;;;;;OAKG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKjD,IAAI,SAAS,IAAI,MAAM,CAEtB;CACD"}
|