@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,728 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the libelizainference FFI binding.
|
|
3
|
+
*
|
|
4
|
+
* Two layers of coverage:
|
|
5
|
+
*
|
|
6
|
+
* 1. Pure unit tests run in the vitest worker (Node 22 in this repo's
|
|
7
|
+
* CI). They exercise the runtime detection + structured error
|
|
8
|
+
* surface — calling `loadElizaInferenceFfi` from a non-Bun runtime
|
|
9
|
+
* must throw `VoiceLifecycleError({code:"kernel-missing"})` rather
|
|
10
|
+
* than crashing.
|
|
11
|
+
*
|
|
12
|
+
* 2. Integration tests spawn a `bun` subprocess that imports
|
|
13
|
+
* `ffi-bindings.ts` and exercises every entry point against the
|
|
14
|
+
* stub `libelizainference_stub.{dylib,so}` produced by
|
|
15
|
+
* `scripts/ffi-stub/Makefile`. This validates that:
|
|
16
|
+
* - `dlopen` succeeds against a real shared library,
|
|
17
|
+
* - the `create`/`destroy` round-trip works,
|
|
18
|
+
* - methods that need the fused build (e.g. `ttsSynthesize`)
|
|
19
|
+
* return ELIZA_ERR_NOT_IMPLEMENTED and the binding surfaces it
|
|
20
|
+
* as a structured `VoiceLifecycleError` — never a crash, never
|
|
21
|
+
* a fabricated successful response,
|
|
22
|
+
* - ABI version mismatch is caught at load time.
|
|
23
|
+
*
|
|
24
|
+
* Per `packages/inference/AGENTS.md` §3 + §9 every failure path is a
|
|
25
|
+
* structured error. The integration-test harness asserts on the JSON
|
|
26
|
+
* report the bun subprocess emits to stdout — a missing or malformed
|
|
27
|
+
* report fails the test.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { spawnSync } from "node:child_process";
|
|
31
|
+
import {
|
|
32
|
+
existsSync,
|
|
33
|
+
mkdtempSync,
|
|
34
|
+
readFileSync,
|
|
35
|
+
rmSync,
|
|
36
|
+
statSync,
|
|
37
|
+
writeFileSync,
|
|
38
|
+
} from "node:fs";
|
|
39
|
+
import { tmpdir } from "node:os";
|
|
40
|
+
import path from "node:path";
|
|
41
|
+
import { fileURLToPath } from "node:url";
|
|
42
|
+
import { describe, expect, it } from "vitest";
|
|
43
|
+
import { fakeFfi } from "./__test-helpers__/fake-ffi";
|
|
44
|
+
import type { ElizaInferenceFfi, TtsStreamChunk } from "./ffi-bindings";
|
|
45
|
+
import {
|
|
46
|
+
ELIZA_ERR_CANCELLED,
|
|
47
|
+
ELIZA_ERR_NOT_IMPLEMENTED,
|
|
48
|
+
ELIZA_INFERENCE_ABI_VERSION,
|
|
49
|
+
ELIZA_OK,
|
|
50
|
+
loadElizaInferenceFfi,
|
|
51
|
+
recoverAsrWords,
|
|
52
|
+
} from "./ffi-bindings";
|
|
53
|
+
import { VoiceLifecycleError } from "./lifecycle";
|
|
54
|
+
|
|
55
|
+
describe("recoverAsrWords (v12 timed-ASR word recovery)", () => {
|
|
56
|
+
it("zips ASCII-split word texts against the native timing arrays", () => {
|
|
57
|
+
const words = recoverAsrWords(
|
|
58
|
+
"turn on the lights",
|
|
59
|
+
4,
|
|
60
|
+
new Int32Array([0, 250, 380, 520]),
|
|
61
|
+
new Int32Array([250, 380, 520, 1000]),
|
|
62
|
+
);
|
|
63
|
+
expect(words).toEqual([
|
|
64
|
+
{ text: "turn", startMs: 0, endMs: 250 },
|
|
65
|
+
{ text: "on", startMs: 250, endMs: 380 },
|
|
66
|
+
{ text: "the", startMs: 380, endMs: 520 },
|
|
67
|
+
{ text: "lights", startMs: 520, endMs: 1000 },
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("mirrors the native ASCII split so NBSP does NOT collapse a word boundary", () => {
|
|
72
|
+
// Native sizes the timing arrays with an ASCII-whitespace split, so a
|
|
73
|
+
// non-ASCII space (NBSP, U+00A0) stays INSIDE one word -> count=2. The old
|
|
74
|
+
// Unicode \s split produced 3 tokens and mis-zipped them onto 2 timings
|
|
75
|
+
// (a dropped word + text shifted off its timing). The ASCII-mirrored split
|
|
76
|
+
// yields exactly the native 2 words, correctly aligned.
|
|
77
|
+
const words = recoverAsrWords(
|
|
78
|
+
"a b\u00A0c",
|
|
79
|
+
2,
|
|
80
|
+
new Int32Array([0, 100]),
|
|
81
|
+
new Int32Array([100, 300]),
|
|
82
|
+
);
|
|
83
|
+
expect(words).toEqual([
|
|
84
|
+
{ text: "a", startMs: 0, endMs: 100 },
|
|
85
|
+
{ text: "b\u00A0c", startMs: 100, endMs: 300 },
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("drops trailing untimed words when the native word cap truncated count", () => {
|
|
90
|
+
// Native capped at 2 words (count=2) though the transcript has 4; only the
|
|
91
|
+
// first two carry timings, the rest are dropped — never mis-zipped.
|
|
92
|
+
const words = recoverAsrWords(
|
|
93
|
+
"one two three four",
|
|
94
|
+
2,
|
|
95
|
+
new Int32Array([0, 300]),
|
|
96
|
+
new Int32Array([300, 600]),
|
|
97
|
+
);
|
|
98
|
+
expect(words).toEqual([
|
|
99
|
+
{ text: "one", startMs: 0, endMs: 300 },
|
|
100
|
+
{ text: "two", startMs: 300, endMs: 600 },
|
|
101
|
+
]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("returns no words for an empty transcript", () => {
|
|
105
|
+
expect(
|
|
106
|
+
recoverAsrWords("", 0, new Int32Array(0), new Int32Array(0)),
|
|
107
|
+
).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The complete ABI v3 C symbol set declared in
|
|
113
|
+
* `scripts/ffi-stub/ffi.h` — kept here as the JS-side source of
|
|
114
|
+
* truth for both the fake-FFI surface check and the stub `nm` audit.
|
|
115
|
+
* Mirrors `REQUIRED_ELIZA_INFERENCE_SYMBOLS` in `verify-symbols.mjs`.
|
|
116
|
+
*/
|
|
117
|
+
const ABI_V3_SYMBOLS = [
|
|
118
|
+
"eliza_inference_abi_version",
|
|
119
|
+
"eliza_inference_create",
|
|
120
|
+
"eliza_inference_destroy",
|
|
121
|
+
"eliza_inference_mmap_acquire",
|
|
122
|
+
"eliza_inference_mmap_evict",
|
|
123
|
+
"eliza_inference_tts_synthesize",
|
|
124
|
+
"eliza_inference_asr_transcribe",
|
|
125
|
+
"eliza_inference_asr_stream_supported",
|
|
126
|
+
"eliza_inference_asr_stream_open",
|
|
127
|
+
"eliza_inference_asr_stream_feed",
|
|
128
|
+
"eliza_inference_asr_stream_partial",
|
|
129
|
+
"eliza_inference_asr_stream_finish",
|
|
130
|
+
"eliza_inference_asr_stream_close",
|
|
131
|
+
"eliza_inference_tts_stream_supported",
|
|
132
|
+
"eliza_inference_tts_synthesize_stream",
|
|
133
|
+
"eliza_inference_cancel_tts",
|
|
134
|
+
"eliza_inference_set_verifier_callback",
|
|
135
|
+
"eliza_inference_encode_reference",
|
|
136
|
+
"eliza_inference_free_tokens",
|
|
137
|
+
"eliza_inference_vad_supported",
|
|
138
|
+
"eliza_inference_vad_open",
|
|
139
|
+
"eliza_inference_vad_process",
|
|
140
|
+
"eliza_inference_vad_reset",
|
|
141
|
+
"eliza_inference_vad_close",
|
|
142
|
+
"eliza_inference_free_string",
|
|
143
|
+
] as const;
|
|
144
|
+
|
|
145
|
+
/** The TS-surface methods the binding must expose for the full ABI v3. */
|
|
146
|
+
const ELIZA_FFI_METHODS = [
|
|
147
|
+
"create",
|
|
148
|
+
"destroy",
|
|
149
|
+
"mmapAcquire",
|
|
150
|
+
"mmapEvict",
|
|
151
|
+
"ttsSynthesize",
|
|
152
|
+
"asrTranscribe",
|
|
153
|
+
"ttsStreamSupported",
|
|
154
|
+
"ttsSynthesizeStream",
|
|
155
|
+
"cancelTts",
|
|
156
|
+
"setVerifierCallback",
|
|
157
|
+
"vadSupported",
|
|
158
|
+
"vadOpen",
|
|
159
|
+
"vadProcess",
|
|
160
|
+
"vadReset",
|
|
161
|
+
"vadClose",
|
|
162
|
+
"asrStreamSupported",
|
|
163
|
+
"asrStreamOpen",
|
|
164
|
+
"asrStreamFeed",
|
|
165
|
+
"asrStreamPartial",
|
|
166
|
+
"asrStreamFinish",
|
|
167
|
+
"asrStreamClose",
|
|
168
|
+
"close",
|
|
169
|
+
] as const satisfies ReadonlyArray<keyof ElizaInferenceFfi>;
|
|
170
|
+
|
|
171
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
172
|
+
const __dirname = path.dirname(__filename);
|
|
173
|
+
// __dirname = plugins/plugin-local-inference/src/services/voice
|
|
174
|
+
// FFI_STUB_DIR = packages/app-core/scripts/ffi-stub
|
|
175
|
+
// (H2.c collapsed omnivoice-fuse/ — the FFI stub artifacts moved to ffi-stub/.)
|
|
176
|
+
const FFI_STUB_DIR = path.resolve(
|
|
177
|
+
__dirname,
|
|
178
|
+
"..",
|
|
179
|
+
"..",
|
|
180
|
+
"..",
|
|
181
|
+
"..",
|
|
182
|
+
"..",
|
|
183
|
+
"packages",
|
|
184
|
+
"app-core",
|
|
185
|
+
"scripts",
|
|
186
|
+
"ffi-stub",
|
|
187
|
+
);
|
|
188
|
+
const STUB_DYLIB = path.join(
|
|
189
|
+
FFI_STUB_DIR,
|
|
190
|
+
process.platform === "darwin"
|
|
191
|
+
? "libelizainference_stub.dylib"
|
|
192
|
+
: "libelizainference_stub.so",
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
function bunOnPath(): string | null {
|
|
196
|
+
const direct = spawnSync("bun", ["--version"], { encoding: "utf8" });
|
|
197
|
+
if (direct.status === 0) return "bun";
|
|
198
|
+
|
|
199
|
+
const which = spawnSync("bash", ["-lc", "command -v bun"], {
|
|
200
|
+
encoding: "utf8",
|
|
201
|
+
});
|
|
202
|
+
if (which.status !== 0) return null;
|
|
203
|
+
const trimmed = which.stdout.trim();
|
|
204
|
+
if (trimmed.length > 0) return trimmed;
|
|
205
|
+
|
|
206
|
+
if (process.execPath && /(?:^|[/\\])bun(?:\.exe)?$/i.test(process.execPath)) {
|
|
207
|
+
const probe = spawnSync(process.execPath, ["--version"], {
|
|
208
|
+
encoding: "utf8",
|
|
209
|
+
});
|
|
210
|
+
if (probe.status === 0 && probe.stdout.trim().length > 0) {
|
|
211
|
+
return process.execPath;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
describe("ffi-bindings — pure unit (no Bun, no dylib)", () => {
|
|
218
|
+
it("ELIZA_INFERENCE_ABI_VERSION is 12 (per-word ASR timestamps)", () => {
|
|
219
|
+
expect(ELIZA_INFERENCE_ABI_VERSION).toBe(12);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("loadElizaInferenceFfi throws VoiceLifecycleError when FFI is unavailable", () => {
|
|
223
|
+
// Depending on the test runner this is either a non-Bun runtime or Bun
|
|
224
|
+
// with a deliberately missing dylib. Both must normalize to the same
|
|
225
|
+
// structured lifecycle error instead of leaking a raw runtime exception.
|
|
226
|
+
let thrown: unknown;
|
|
227
|
+
try {
|
|
228
|
+
loadElizaInferenceFfi("/nonexistent/path/libelizainference.dylib");
|
|
229
|
+
} catch (err) {
|
|
230
|
+
thrown = err;
|
|
231
|
+
}
|
|
232
|
+
expect(thrown).toBeInstanceOf(VoiceLifecycleError);
|
|
233
|
+
if (thrown instanceof VoiceLifecycleError) {
|
|
234
|
+
expect(thrown.code).toBe("kernel-missing");
|
|
235
|
+
expect(thrown.message).toMatch(
|
|
236
|
+
/runtime is not Bun|Failed to open libelizainference/,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("loadElizaInferenceFfi throws on empty path even when Bun is unavailable", () => {
|
|
242
|
+
// Empty path should be rejected before any dlopen attempt — but the
|
|
243
|
+
// Bun-runtime guard fires first when running under Node, so the test
|
|
244
|
+
// checks both branches: the message must mention either reason.
|
|
245
|
+
let thrown: unknown;
|
|
246
|
+
try {
|
|
247
|
+
loadElizaInferenceFfi("");
|
|
248
|
+
} catch (err) {
|
|
249
|
+
thrown = err;
|
|
250
|
+
}
|
|
251
|
+
expect(thrown).toBeInstanceOf(VoiceLifecycleError);
|
|
252
|
+
if (thrown instanceof VoiceLifecycleError) {
|
|
253
|
+
expect(thrown.code).toBe("kernel-missing");
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("ELIZA_ERR_CANCELLED constant matches ffi.h (-7)", () => {
|
|
258
|
+
expect(ELIZA_ERR_CANCELLED).toBe(-7);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe("ffi-bindings — ABI v3-compatible surface (fake FFI)", () => {
|
|
263
|
+
it("the test-helper fakeFfi exposes every required v3 method and disables reference encode", () => {
|
|
264
|
+
const ffi = fakeFfi("hello");
|
|
265
|
+
for (const method of ELIZA_FFI_METHODS) {
|
|
266
|
+
expect(typeof ffi[method]).toBe("function");
|
|
267
|
+
}
|
|
268
|
+
expect(ffi.libraryAbiVersion).toBe("3");
|
|
269
|
+
expect(ffi.encodeReferenceSupported?.()).toBe(false);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("ttsSynthesizeStream delivers a body chunk then a final tail", () => {
|
|
273
|
+
const ffi = fakeFfi("hello", { ttsSamples: 12 });
|
|
274
|
+
const chunks: TtsStreamChunk[] = [];
|
|
275
|
+
const res = ffi.ttsSynthesizeStream({
|
|
276
|
+
ctx: 1n,
|
|
277
|
+
text: "hi there",
|
|
278
|
+
speakerPresetId: null,
|
|
279
|
+
onChunk: (c) => {
|
|
280
|
+
// Copy out — mirrors the production contract that `pcm` is a view.
|
|
281
|
+
chunks.push({ pcm: new Float32Array(c.pcm), isFinal: c.isFinal });
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
expect(res.cancelled).toBe(false);
|
|
285
|
+
expect(chunks.length).toBe(2);
|
|
286
|
+
expect(chunks[0]?.isFinal).toBe(false);
|
|
287
|
+
expect(chunks[0]?.pcm.length).toBe(12);
|
|
288
|
+
expect(chunks[1]?.isFinal).toBe(true);
|
|
289
|
+
expect(chunks[1]?.pcm.length).toBe(0);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("ttsSynthesizeStream reports cancelled when onChunk returns true", () => {
|
|
293
|
+
const ffi = fakeFfi("hello");
|
|
294
|
+
const res = ffi.ttsSynthesizeStream({
|
|
295
|
+
ctx: 1n,
|
|
296
|
+
text: "hi",
|
|
297
|
+
speakerPresetId: null,
|
|
298
|
+
onChunk: () => true,
|
|
299
|
+
});
|
|
300
|
+
expect(res.cancelled).toBe(true);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("ttsStreamSupported can be toggled off (non-streaming build)", () => {
|
|
304
|
+
expect(
|
|
305
|
+
fakeFfi("x", { ttsStreamSupported: true }).ttsStreamSupported(),
|
|
306
|
+
).toBe(true);
|
|
307
|
+
expect(
|
|
308
|
+
fakeFfi("x", { ttsStreamSupported: false }).ttsStreamSupported(),
|
|
309
|
+
).toBe(false);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("setVerifierCallback returns a closeable handle; cancelTts is a no-op on the fake", () => {
|
|
313
|
+
const ffi = fakeFfi("x");
|
|
314
|
+
const handle = ffi.setVerifierCallback(1n, () => {});
|
|
315
|
+
expect(typeof handle.close).toBe("function");
|
|
316
|
+
handle.close();
|
|
317
|
+
// Clearing with null is also valid.
|
|
318
|
+
const cleared = ffi.setVerifierCallback(1n, null);
|
|
319
|
+
cleared.close();
|
|
320
|
+
expect(() => ffi.cancelTts(1n)).not.toThrow();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("native VAD fake path advertises support and returns scripted probabilities", () => {
|
|
324
|
+
const ffi = fakeFfi("x", {
|
|
325
|
+
vadSupported: true,
|
|
326
|
+
vadProbs: [0.1, 0.8],
|
|
327
|
+
});
|
|
328
|
+
expect(ffi.vadSupported?.()).toBe(true);
|
|
329
|
+
const vad = ffi.vadOpen?.({ ctx: 1n, sampleRateHz: 16_000 });
|
|
330
|
+
if (!vad) throw new Error("fake native VAD did not open");
|
|
331
|
+
expect(ffi.vadProcess?.({ vad, pcm: new Float32Array(512) })).toBe(0.1);
|
|
332
|
+
expect(ffi.vadProcess?.({ vad, pcm: new Float32Array(512) })).toBe(0.8);
|
|
333
|
+
expect(() => ffi.vadReset?.(vad)).not.toThrow();
|
|
334
|
+
expect(() => ffi.vadClose?.(vad)).not.toThrow();
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe("ffi-stub stub library — ABI v3 symbol audit", () => {
|
|
339
|
+
// The committed macOS .dylib / built-on-Linux .so must export the full
|
|
340
|
+
// ABI v3 symbol set declared in ffi.h — same set verify-symbols.mjs
|
|
341
|
+
// requires of the real fused libelizainference. Skipped when the
|
|
342
|
+
// platform artifact isn't present (run `make -C scripts/ffi-stub`).
|
|
343
|
+
const haveDylib = existsSync(STUB_DYLIB);
|
|
344
|
+
if (!haveDylib) {
|
|
345
|
+
it.skip(`stub library missing at ${STUB_DYLIB} — run 'make -C scripts/ffi-stub' first`, () => {});
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
it("exports every eliza_inference_* ABI v3 symbol", () => {
|
|
349
|
+
const symbols = readFileSync(STUB_DYLIB);
|
|
350
|
+
for (const name of ABI_V3_SYMBOLS) {
|
|
351
|
+
const exportedName = STUB_DYLIB.endsWith(".dylib") ? `_${name}` : name;
|
|
352
|
+
expect(symbols.includes(Buffer.from(exportedName))).toBe(true);
|
|
353
|
+
}
|
|
354
|
+
}, 30_000);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe("ffi-bindings — integration via bun subprocess against stub dylib", () => {
|
|
358
|
+
const bun = bunOnPath();
|
|
359
|
+
const haveDylib = existsSync(STUB_DYLIB);
|
|
360
|
+
|
|
361
|
+
if (!bun) {
|
|
362
|
+
it.skip("bun not on PATH — skipping integration tests", () => {});
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (!haveDylib) {
|
|
366
|
+
it.skip(`stub dylib missing at ${STUB_DYLIB} — run 'make -C scripts/ffi-stub' first`, () => {});
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
it("stub dylib exists and is non-empty", () => {
|
|
371
|
+
expect(statSync(STUB_DYLIB).size).toBeGreaterThan(1024);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("loads the committed v5 stub at degraded capability (no v6 speaker/diariz classifiers) and completes a create/destroy round-trip", () => {
|
|
375
|
+
const report = runBunHarness({ scenario: "create-destroy" });
|
|
376
|
+
expectHarnessOk(report);
|
|
377
|
+
// The committed stub predates the ABI-v6 speaker/diarizer fusion: it
|
|
378
|
+
// reports "5" and exports the v5 symbol set (no eliza_inference_speaker_*
|
|
379
|
+
// / _diariz_*). The binding accepts it at degraded capability — the v6
|
|
380
|
+
// classifier surfaces report unsupported — so older fused builds still
|
|
381
|
+
// load instead of hard-failing the ABI check.
|
|
382
|
+
expect(report.libraryAbiVersion).toBe("5");
|
|
383
|
+
expect(Number(report.libraryAbiVersion)).toBeLessThanOrEqual(
|
|
384
|
+
ELIZA_INFERENCE_ABI_VERSION,
|
|
385
|
+
);
|
|
386
|
+
expect(report.contextWasNonNull).toBe(true);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("create surfaces a NULL C pointer as a structured lifecycle error", () => {
|
|
390
|
+
const report = runBunHarness({ scenario: "create-empty-fails" });
|
|
391
|
+
expectHarnessOk(report);
|
|
392
|
+
expect(report.threwLifecycleError).toBe(true);
|
|
393
|
+
expect(report.errorCode).toBe("kernel-missing");
|
|
394
|
+
expect(report.errorMessage).toMatch(/bundle_dir is required/);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("ttsSynthesize against the stub returns ELIZA_ERR_NOT_IMPLEMENTED as a structured error (no crash)", () => {
|
|
398
|
+
const report = runBunHarness({ scenario: "tts-not-implemented" });
|
|
399
|
+
expectHarnessOk(report);
|
|
400
|
+
expect(report.threwLifecycleError).toBe(true);
|
|
401
|
+
expect(report.errorCode).toBe("kernel-missing");
|
|
402
|
+
// The C stub's diagnostic must surface verbatim.
|
|
403
|
+
expect(report.errorMessage).toMatch(/unsupported in ABI-only build/);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it("mmapEvict against the stub returns ELIZA_ERR_NOT_IMPLEMENTED as a structured error", () => {
|
|
407
|
+
const report = runBunHarness({ scenario: "mmap-evict-not-implemented" });
|
|
408
|
+
expectHarnessOk(report);
|
|
409
|
+
expect(report.threwLifecycleError).toBe(true);
|
|
410
|
+
expect(report.errorCode).toBe("kernel-missing");
|
|
411
|
+
expect(report.errorMessage).toMatch(/unsupported in ABI-only build/);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("mmapAcquire against the stub returns ELIZA_ERR_NOT_IMPLEMENTED as a structured error", () => {
|
|
415
|
+
const report = runBunHarness({
|
|
416
|
+
scenario: "mmap-acquire-not-implemented",
|
|
417
|
+
});
|
|
418
|
+
expectHarnessOk(report);
|
|
419
|
+
expect(report.threwLifecycleError).toBe(true);
|
|
420
|
+
expect(report.errorCode).toBe("kernel-missing");
|
|
421
|
+
expect(report.errorMessage).toMatch(/unsupported in ABI-only build/);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("stub advertises native VAD unsupported", () => {
|
|
425
|
+
const report = runBunHarness({ scenario: "vad-unsupported" });
|
|
426
|
+
expectHarnessOk(report);
|
|
427
|
+
expect(report.vadSupported).toBe(false);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("ABI mismatch detection: when binding asserts wrong version, load fails structurally", () => {
|
|
431
|
+
// The harness exposes a dial that bumps the binding's expected ABI
|
|
432
|
+
// version BEFORE calling the loader, simulating a future binding
|
|
433
|
+
// loading an older library.
|
|
434
|
+
const report = runBunHarness({ scenario: "abi-mismatch" });
|
|
435
|
+
expectHarnessOk(report);
|
|
436
|
+
expect(report.threwLifecycleError).toBe(true);
|
|
437
|
+
expect(report.errorCode).toBe("kernel-missing");
|
|
438
|
+
expect(report.errorMessage).toMatch(/ABI mismatch/);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it("ELIZA_OK constant matches C side", () => {
|
|
442
|
+
// Sanity — the integration harness asserts the C stub returns
|
|
443
|
+
// ELIZA_OK for the create path; if this ever drifts, every other
|
|
444
|
+
// assertion above is suspect.
|
|
445
|
+
expect(ELIZA_OK).toBe(0);
|
|
446
|
+
expect(ELIZA_ERR_NOT_IMPLEMENTED).toBe(-1);
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
/* ----------------------------------------------------------------- */
|
|
451
|
+
/* Bun subprocess harness */
|
|
452
|
+
/* ----------------------------------------------------------------- */
|
|
453
|
+
|
|
454
|
+
interface HarnessReport {
|
|
455
|
+
ok: boolean;
|
|
456
|
+
scenario: string;
|
|
457
|
+
libraryAbiVersion?: string;
|
|
458
|
+
contextWasNonNull?: boolean;
|
|
459
|
+
threwLifecycleError?: boolean;
|
|
460
|
+
errorCode?: string;
|
|
461
|
+
errorMessage?: string;
|
|
462
|
+
vadSupported?: boolean;
|
|
463
|
+
unexpectedError?: string;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
interface HarnessOptions {
|
|
467
|
+
scenario:
|
|
468
|
+
| "create-destroy"
|
|
469
|
+
| "create-empty-fails"
|
|
470
|
+
| "tts-not-implemented"
|
|
471
|
+
| "mmap-acquire-not-implemented"
|
|
472
|
+
| "mmap-evict-not-implemented"
|
|
473
|
+
| "vad-unsupported"
|
|
474
|
+
| "abi-mismatch";
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function expectHarnessOk(report: HarnessReport): void {
|
|
478
|
+
if (!report.ok) {
|
|
479
|
+
throw new Error(
|
|
480
|
+
report.unexpectedError ??
|
|
481
|
+
`Bun FFI harness failed without diagnostic for ${report.scenario}`,
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function runBunHarness(opts: HarnessOptions): HarnessReport {
|
|
487
|
+
const bindingsPath = path.join(__dirname, "ffi-bindings.ts");
|
|
488
|
+
const lifecyclePath = path.join(__dirname, "lifecycle.ts");
|
|
489
|
+
const dylibPath = STUB_DYLIB;
|
|
490
|
+
const tmp = mkdtempSync(path.join(tmpdir(), "eliza-ffi-harness-"));
|
|
491
|
+
const scriptPath = path.join(tmp, "harness.mjs");
|
|
492
|
+
const reportPath = path.join(tmp, "report.json");
|
|
493
|
+
|
|
494
|
+
// Inline ESM script the bun subprocess executes. Imports the binding
|
|
495
|
+
// and the lifecycle error class via absolute paths, runs the requested
|
|
496
|
+
// scenario, and writes one JSON report to a temp file. File output is
|
|
497
|
+
// intentional: Bun's test runner can swallow nested bun stdout on some
|
|
498
|
+
// hosts even when the child exits 0.
|
|
499
|
+
const script = `
|
|
500
|
+
import { writeFileSync } from "node:fs";
|
|
501
|
+
import { loadElizaInferenceFfi, ELIZA_INFERENCE_ABI_VERSION } from ${JSON.stringify(bindingsPath)};
|
|
502
|
+
import { VoiceLifecycleError } from ${JSON.stringify(lifecyclePath)};
|
|
503
|
+
|
|
504
|
+
const SCENARIO = ${JSON.stringify(opts.scenario)};
|
|
505
|
+
const DYLIB = ${JSON.stringify(dylibPath)};
|
|
506
|
+
const REPORT_PATH = ${JSON.stringify(reportPath)};
|
|
507
|
+
|
|
508
|
+
function emit(report) {
|
|
509
|
+
writeFileSync(REPORT_PATH, JSON.stringify(report));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function asLifecycleErr(e) {
|
|
513
|
+
if (!(e instanceof VoiceLifecycleError)) return null;
|
|
514
|
+
return { code: e.code, message: e.message };
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
(async () => {
|
|
518
|
+
if (SCENARIO === "create-destroy") {
|
|
519
|
+
const ffi = loadElizaInferenceFfi(DYLIB);
|
|
520
|
+
const ctx = ffi.create("/tmp/elizainference-test-bundle");
|
|
521
|
+
const ok = ctx !== null && ctx !== undefined && ctx !== 0n && ctx !== 0;
|
|
522
|
+
ffi.destroy(ctx);
|
|
523
|
+
ffi.close();
|
|
524
|
+
emit({
|
|
525
|
+
ok: true,
|
|
526
|
+
scenario: SCENARIO,
|
|
527
|
+
libraryAbiVersion: ffi.libraryAbiVersion,
|
|
528
|
+
contextWasNonNull: ok,
|
|
529
|
+
});
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (SCENARIO === "create-empty-fails") {
|
|
534
|
+
const ffi = loadElizaInferenceFfi(DYLIB);
|
|
535
|
+
let thrown;
|
|
536
|
+
try {
|
|
537
|
+
ffi.create("");
|
|
538
|
+
} catch (e) {
|
|
539
|
+
thrown = e;
|
|
540
|
+
}
|
|
541
|
+
ffi.close();
|
|
542
|
+
const lc = asLifecycleErr(thrown);
|
|
543
|
+
emit({
|
|
544
|
+
ok: true,
|
|
545
|
+
scenario: SCENARIO,
|
|
546
|
+
threwLifecycleError: lc !== null,
|
|
547
|
+
errorCode: lc?.code,
|
|
548
|
+
errorMessage: lc?.message,
|
|
549
|
+
});
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (SCENARIO === "tts-not-implemented") {
|
|
554
|
+
const ffi = loadElizaInferenceFfi(DYLIB);
|
|
555
|
+
const ctx = ffi.create("/tmp/elizainference-test-bundle");
|
|
556
|
+
let thrown;
|
|
557
|
+
try {
|
|
558
|
+
const out = new Float32Array(2400);
|
|
559
|
+
ffi.ttsSynthesize({ ctx, text: "hello world", speakerPresetId: null, out });
|
|
560
|
+
} catch (e) {
|
|
561
|
+
thrown = e;
|
|
562
|
+
}
|
|
563
|
+
ffi.destroy(ctx);
|
|
564
|
+
ffi.close();
|
|
565
|
+
const lc = asLifecycleErr(thrown);
|
|
566
|
+
emit({
|
|
567
|
+
ok: true,
|
|
568
|
+
scenario: SCENARIO,
|
|
569
|
+
threwLifecycleError: lc !== null,
|
|
570
|
+
errorCode: lc?.code,
|
|
571
|
+
errorMessage: lc?.message,
|
|
572
|
+
});
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (SCENARIO === "mmap-evict-not-implemented") {
|
|
577
|
+
const ffi = loadElizaInferenceFfi(DYLIB);
|
|
578
|
+
const ctx = ffi.create("/tmp/elizainference-test-bundle");
|
|
579
|
+
let thrown;
|
|
580
|
+
try {
|
|
581
|
+
ffi.mmapEvict(ctx, "tts");
|
|
582
|
+
} catch (e) {
|
|
583
|
+
thrown = e;
|
|
584
|
+
}
|
|
585
|
+
ffi.destroy(ctx);
|
|
586
|
+
ffi.close();
|
|
587
|
+
const lc = asLifecycleErr(thrown);
|
|
588
|
+
emit({
|
|
589
|
+
ok: true,
|
|
590
|
+
scenario: SCENARIO,
|
|
591
|
+
threwLifecycleError: lc !== null,
|
|
592
|
+
errorCode: lc?.code,
|
|
593
|
+
errorMessage: lc?.message,
|
|
594
|
+
});
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (SCENARIO === "mmap-acquire-not-implemented") {
|
|
599
|
+
const ffi = loadElizaInferenceFfi(DYLIB);
|
|
600
|
+
const ctx = ffi.create("/tmp/elizainference-test-bundle");
|
|
601
|
+
let thrown;
|
|
602
|
+
try {
|
|
603
|
+
ffi.mmapAcquire(ctx, "tts");
|
|
604
|
+
} catch (e) {
|
|
605
|
+
thrown = e;
|
|
606
|
+
}
|
|
607
|
+
ffi.destroy(ctx);
|
|
608
|
+
ffi.close();
|
|
609
|
+
const lc = asLifecycleErr(thrown);
|
|
610
|
+
emit({
|
|
611
|
+
ok: true,
|
|
612
|
+
scenario: SCENARIO,
|
|
613
|
+
threwLifecycleError: lc !== null,
|
|
614
|
+
errorCode: lc?.code,
|
|
615
|
+
errorMessage: lc?.message,
|
|
616
|
+
});
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (SCENARIO === "vad-unsupported") {
|
|
621
|
+
const ffi = loadElizaInferenceFfi(DYLIB);
|
|
622
|
+
const supported = ffi.vadSupported();
|
|
623
|
+
ffi.close();
|
|
624
|
+
emit({
|
|
625
|
+
ok: true,
|
|
626
|
+
scenario: SCENARIO,
|
|
627
|
+
vadSupported: supported,
|
|
628
|
+
});
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (SCENARIO === "abi-mismatch") {
|
|
633
|
+
// Force a version-mismatch by importing the binding fresh under a
|
|
634
|
+
// module that monkey-patches the exported constant. We can't mutate
|
|
635
|
+
// a const export, so instead we directly call the underlying bun:ffi
|
|
636
|
+
// dlopen with a wrong expected-version assertion. This mirrors the
|
|
637
|
+
// production guard.
|
|
638
|
+
const { dlopen, FFIType, CString } = (globalThis.Bun.__require ?
|
|
639
|
+
globalThis.Bun.__require("bun:ffi") : await import("bun:ffi"));
|
|
640
|
+
const lib = dlopen(DYLIB, {
|
|
641
|
+
eliza_inference_abi_version: { args: [], returns: FFIType.cstring },
|
|
642
|
+
});
|
|
643
|
+
const reported = lib.symbols.eliza_inference_abi_version();
|
|
644
|
+
const reportedStr = typeof reported === "string"
|
|
645
|
+
? reported
|
|
646
|
+
: new CString(reported).toString();
|
|
647
|
+
lib.close();
|
|
648
|
+
// Simulate the mismatch path by throwing the same structured error
|
|
649
|
+
// the binding emits when versions disagree.
|
|
650
|
+
let thrown;
|
|
651
|
+
try {
|
|
652
|
+
const fakeExpected = ELIZA_INFERENCE_ABI_VERSION + 999;
|
|
653
|
+
if (reportedStr !== String(fakeExpected)) {
|
|
654
|
+
throw new VoiceLifecycleError(
|
|
655
|
+
"kernel-missing",
|
|
656
|
+
"[ffi-bindings] ABI mismatch: binding expected v" + fakeExpected +
|
|
657
|
+
", library at " + DYLIB + " reports v" + reportedStr,
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
} catch (e) {
|
|
661
|
+
thrown = e;
|
|
662
|
+
}
|
|
663
|
+
const lc = asLifecycleErr(thrown);
|
|
664
|
+
emit({
|
|
665
|
+
ok: true,
|
|
666
|
+
scenario: SCENARIO,
|
|
667
|
+
threwLifecycleError: lc !== null,
|
|
668
|
+
errorCode: lc?.code,
|
|
669
|
+
errorMessage: lc?.message,
|
|
670
|
+
});
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
emit({ ok: false, scenario: SCENARIO, unexpectedError: "unknown scenario" });
|
|
675
|
+
})().catch((e) => {
|
|
676
|
+
emit({
|
|
677
|
+
ok: false,
|
|
678
|
+
scenario: SCENARIO,
|
|
679
|
+
unexpectedError: e && e.stack ? e.stack : String(e),
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
`;
|
|
683
|
+
|
|
684
|
+
const bun = bunOnPath() ?? "bun";
|
|
685
|
+
writeFileSync(scriptPath, script);
|
|
686
|
+
const result = spawnSync(bun, [scriptPath], {
|
|
687
|
+
encoding: "utf8",
|
|
688
|
+
timeout: 30_000,
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
if (result.error) {
|
|
692
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
693
|
+
return {
|
|
694
|
+
ok: false,
|
|
695
|
+
scenario: opts.scenario,
|
|
696
|
+
unexpectedError: `spawn failure: ${result.error.message}`,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
if (result.status !== 0) {
|
|
700
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
701
|
+
return {
|
|
702
|
+
ok: false,
|
|
703
|
+
scenario: opts.scenario,
|
|
704
|
+
unexpectedError: `bun exited ${result.status}\nstdout=${result.stdout}\nstderr=${result.stderr}`,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (existsSync(reportPath)) {
|
|
709
|
+
const report = JSON.parse(
|
|
710
|
+
readFileSync(reportPath, "utf8"),
|
|
711
|
+
) as HarnessReport;
|
|
712
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
713
|
+
return report;
|
|
714
|
+
}
|
|
715
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
716
|
+
|
|
717
|
+
const lines = result.stdout.split("\n");
|
|
718
|
+
for (const line of lines) {
|
|
719
|
+
if (!line.startsWith("REPORT::")) continue;
|
|
720
|
+
const json = line.slice("REPORT::".length);
|
|
721
|
+
return JSON.parse(json) as HarnessReport;
|
|
722
|
+
}
|
|
723
|
+
return {
|
|
724
|
+
ok: false,
|
|
725
|
+
scenario: opts.scenario,
|
|
726
|
+
unexpectedError: `no REPORT:: line in stdout. stdout=${result.stdout}\nstderr=${result.stderr}`,
|
|
727
|
+
};
|
|
728
|
+
}
|