@qvac/sdk 0.20.1 → 0.21.0

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.
Files changed (104) hide show
  1. package/NOTICE +76 -13
  2. package/README.md +39 -5
  3. package/dist/examples/advisory-model-fit.d.ts +17 -16
  4. package/dist/examples/advisory-model-fit.js +18 -18
  5. package/dist/examples/asr/parakeet-indic-conformer-filesystem.js +3 -3
  6. package/dist/examples/asr/parakeet-unified-coreml.d.ts +2 -0
  7. package/dist/examples/asr/parakeet-unified-coreml.js +113 -0
  8. package/dist/examples/assess-model-fit.js +9 -4
  9. package/dist/examples/model-fit-ui/server.js +5 -2
  10. package/dist/examples/rpc-inference.d.ts +2 -0
  11. package/dist/examples/rpc-inference.js +51 -0
  12. package/dist/examples/rpc-server.d.ts +2 -0
  13. package/dist/examples/rpc-server.js +32 -0
  14. package/dist/examples/tools/llamacpp-deferred-tools.d.ts +2 -0
  15. package/dist/examples/tools/llamacpp-deferred-tools.js +77 -0
  16. package/dist/examples/tts/audio8.js +4 -0
  17. package/dist/examples/tts/moss-dialogue.d.ts +2 -0
  18. package/dist/examples/tts/moss-dialogue.js +63 -0
  19. package/dist/examples/tts/moss.d.ts +2 -0
  20. package/dist/examples/tts/moss.js +71 -0
  21. package/dist/scripts/contract/build-contract.d.ts +2 -1
  22. package/dist/scripts/contract/build-contract.js +4 -1
  23. package/dist/scripts/contract/build-numeric-constants.d.ts +15 -0
  24. package/dist/scripts/contract/build-numeric-constants.js +13 -0
  25. package/dist/scripts/contract/method-shapes.d.ts +3 -0
  26. package/dist/scripts/contract/method-shapes.js +3 -0
  27. package/dist/scripts/enforce-inference-versions.d.ts +18 -0
  28. package/dist/scripts/enforce-inference-versions.js +158 -0
  29. package/dist/scripts/generate-bare-kit-runtimes.d.ts +2 -0
  30. package/dist/scripts/generate-bare-kit-runtimes.js +106 -0
  31. package/dist/src/client/api/assess-model-fit.d.ts +5 -4
  32. package/dist/src/client/api/assess-model-fit.js +24 -7
  33. package/dist/src/client/api/bci-transcribe.d.ts +4 -1
  34. package/dist/src/client/api/get-system-resources.d.ts +8 -0
  35. package/dist/src/client/api/get-system-resources.js +8 -0
  36. package/dist/src/client/api/index.d.ts +1 -0
  37. package/dist/src/client/api/index.js +1 -0
  38. package/dist/src/client/api/rpc-server.d.ts +20 -0
  39. package/dist/src/client/api/rpc-server.js +45 -0
  40. package/dist/src/client/api/text-to-speech.d.ts +9 -0
  41. package/dist/src/client/api/text-to-speech.js +9 -0
  42. package/dist/src/client/api/transcribe.d.ts +21 -15
  43. package/dist/src/client/api/transcribe.js +65 -20
  44. package/dist/src/client/rpc/rpc-error.js +11 -7
  45. package/dist/src/commands/bundle/entry-gen.d.ts +2 -2
  46. package/dist/src/commands/bundle/entry-gen.js +8 -4
  47. package/dist/src/commands/bundle/index.d.ts +20 -0
  48. package/dist/src/commands/bundle/index.js +88 -12
  49. package/dist/src/commands/bundle/plugins.js +1 -1
  50. package/dist/src/commands/command-logger.d.ts +6 -0
  51. package/dist/src/commands/command-logger.js +12 -0
  52. package/dist/src/commands/host-prebuilds/index.d.ts +71 -0
  53. package/dist/src/commands/host-prebuilds/index.js +227 -0
  54. package/dist/src/commands/host-prebuilds/package-manager.d.ts +54 -0
  55. package/dist/src/commands/host-prebuilds/package-manager.js +278 -0
  56. package/dist/src/commands/index.d.ts +10 -1
  57. package/dist/src/commands/index.js +6 -0
  58. package/dist/src/commands/verify/abi.d.ts +22 -2
  59. package/dist/src/commands/verify/abi.js +49 -27
  60. package/dist/src/commands/verify/addon-source.d.ts +22 -0
  61. package/dist/src/commands/verify/addon-source.js +69 -3
  62. package/dist/src/commands/verify/bare-kit-runtime.d.ts +38 -0
  63. package/dist/src/commands/verify/bare-kit-runtime.js +201 -0
  64. package/dist/src/commands/verify/bare-kit-runtimes.d.ts +12 -0
  65. package/dist/src/commands/verify/bare-kit-runtimes.js +108 -0
  66. package/dist/src/commands/verify/bundle-source.js +16 -15
  67. package/dist/src/commands/verify/engines-advice.d.ts +73 -0
  68. package/dist/src/commands/verify/engines-advice.js +271 -0
  69. package/dist/src/commands/verify/index.d.ts +22 -0
  70. package/dist/src/commands/verify/index.js +151 -13
  71. package/dist/src/commands/verify/node-modules-source.js +43 -39
  72. package/dist/src/commands/verify/prebuilds.d.ts +19 -0
  73. package/dist/src/commands/verify/prebuilds.js +52 -13
  74. package/dist/src/expo/plugins/index.d.ts +2 -0
  75. package/dist/src/expo/plugins/withMobileBundle.d.ts +15 -5
  76. package/dist/src/expo/plugins/withMobileBundle.js +68 -42
  77. package/dist/src/expo/plugins/withQvacSDK.d.ts +7 -1
  78. package/dist/src/expo/plugins/withQvacSDK.js +6 -3
  79. package/dist/src/index.d.ts +5 -2
  80. package/dist/src/index.js +4 -2
  81. package/dist/src/schemas/sdk-errors-client.d.ts +2 -0
  82. package/dist/src/schemas/sdk-errors-client.js +16 -0
  83. package/dist/src/schemas/sdk-errors-server.d.ts +1 -0
  84. package/dist/src/schemas/sdk-errors-server.js +5 -0
  85. package/dist/src/utils/errors-client.d.ts +15 -0
  86. package/dist/src/utils/errors-client.js +26 -0
  87. package/dist/src/utils/errors-server.d.ts +1 -0
  88. package/dist/src/utils/errors-server.js +1 -0
  89. package/dist/src/utils/mcp-adapter.js +7 -1
  90. package/dist/src/utils/tool-helpers.d.ts +4 -0
  91. package/dist/src/utils/tool-helpers.js +2 -0
  92. package/dist/src/worker/ggml-rpc-server-provider.d.ts +2 -0
  93. package/dist/src/worker/ggml-rpc-server-provider.js +1 -0
  94. package/dist/src/worker/rpc-server-provider.d.ts +3 -0
  95. package/dist/src/worker/rpc-server-provider.js +1 -0
  96. package/package.json +30 -25
  97. package/dist/scripts/enforce-inference-peer-dependencies.d.ts +0 -2
  98. package/dist/scripts/enforce-inference-peer-dependencies.js +0 -39
  99. package/dist/src/deprecate.d.ts +0 -2
  100. package/dist/src/deprecate.js +0 -9
  101. package/dist/src/worker/legacy-worker-entry.d.ts +0 -2
  102. package/dist/src/worker/legacy-worker-entry.js +0 -3
  103. package/dist/src/worker/worker-core.d.ts +0 -7
  104. package/dist/src/worker/worker-core.js +0 -8
package/NOTICE CHANGED
@@ -71,6 +71,8 @@ Third-Party Model Licenses
71
71
  https://huggingface.co/Audio8/Audio8-TTS-Preview-0.6b
72
72
  bci-embedder
73
73
  https://github.com/tetherto/qvac/releases/download/bci-test-assets-v0.1.0/bci-embedder.bin
74
+ coremldata
75
+ https://huggingface.co/Audio8/Audio8-TTS-Preview-0.6b
74
76
  cosyvoice3-campplus-f32
75
77
  https://huggingface.co/FunAudioLLM/Fun-CosyVoice3-0.5B-2512
76
78
  cosyvoice3-flow-bf16
@@ -136,7 +138,7 @@ Third-Party Model Licenses
136
138
  LightOnOCR-2-1B-ocr-soup-GGUF
137
139
  https://huggingface.co/noctrex/LightOnOCR-2-1B-ocr-soup-GGUF
138
140
  Llama_3.2_1B_Intruct_Tool_Calling_V2-GGUF
139
- https://huggingface.co/mav23/Llama_3.2_1B_Intruct_Tool_Calling_V2-GGUF
141
+ https://huggingface.co/mradermacher/Llama_3.2_1B_Intruct_Tool_Calling_V2-GGUF
140
142
  MedPsy-1.7B-GGUF
141
143
  https://huggingface.co/qvac/MedPsy-1.7B-GGUF
142
144
  MedPsy-4B-GGUF
@@ -145,6 +147,14 @@ Third-Party Model Licenses
145
147
  https://huggingface.co/FunAudioLLM/Fun-CosyVoice3-0.5B-2512
146
148
  mmproj-unlimited-ocr-Q8_0
147
149
  https://huggingface.co/baidu/Unlimited-OCR
150
+ model
151
+ https://huggingface.co/Audio8/Audio8-TTS-Preview-0.6b
152
+ moss-codec-decoder-f16
153
+ https://huggingface.co/OpenMOSS-Team/MOSS-TTS
154
+ moss-codec-encoder-f16
155
+ https://huggingface.co/OpenMOSS-Team/MOSS-TTS
156
+ moss-tts-delay-f16
157
+ https://huggingface.co/OpenMOSS-Team/MOSS-TTS
148
158
  nb-tiny-ggml-model
149
159
  https://huggingface.co/NbAiLab/nb-whisper-tiny
150
160
  parler-indic-f16
@@ -211,6 +221,8 @@ Third-Party Model Licenses
211
221
  https://huggingface.co/HuggingFaceVLA/smolvla_libero
212
222
  SmolVLM2-500M-Video-Instruct-GGUF
213
223
  https://huggingface.co/ggml-org/SmolVLM2-500M-Video-Instruct-GGUF
224
+ Ternary-Bonsai-2-27B-gguf
225
+ https://huggingface.co/prism-ml/Ternary-Bonsai-2-27B-gguf
214
226
  TranslatePsy-AfriSLM-0.8B-Q4-GGUF
215
227
  https://huggingface.co/qvac/TranslatePsy-AfriSLM-0.8B-Q4-GGUF
216
228
  TranslatePsy-AfriSLM-0.8B-Q8-GGUF
@@ -251,6 +263,8 @@ Third-Party Model Licenses
251
263
  https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged
252
264
  Wan2.2-TI2V-5B-Turbo-GGUF
253
265
  https://huggingface.co/hum-ma/Wan2.2-TI2V-5B-Turbo-GGUF
266
+ weight
267
+ https://huggingface.co/Audio8/Audio8-TTS-Preview-0.6b
254
268
 
255
269
  --- bsd-3-clause (BSD 3-Clause License) ---
256
270
 
@@ -277,30 +291,58 @@ Third-Party Model Licenses
277
291
 
278
292
  AfriqueGemma-4B-GGUF
279
293
  https://huggingface.co/mradermacher/AfriqueGemma-4B-GGUF
294
+ coremldata
295
+ https://huggingface.co/nvidia/parakeet-ctc-0.6b
296
+ coremldata
297
+ https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3
298
+ coremldata
299
+ https://huggingface.co/nvidia/parakeet-tdt-1.1b
300
+ coremldata
301
+ https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
302
+ model
303
+ https://huggingface.co/nvidia/parakeet-ctc-0.6b
304
+ model
305
+ https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3
306
+ model
307
+ https://huggingface.co/nvidia/parakeet-tdt-1.1b
308
+ model
309
+ https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
310
+ parakeet_realtime_eou_120m-v1.f16
311
+ https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
312
+ parakeet_realtime_eou_120m-v1.q4_0
313
+ https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
314
+ parakeet_realtime_eou_120m-v1.q8_0
315
+ https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
280
316
  parakeet-ctc-0.6b.f16
281
317
  https://huggingface.co/nvidia/parakeet-ctc-0.6b
282
318
  parakeet-ctc-0.6b.q4_0
283
319
  https://huggingface.co/nvidia/parakeet-ctc-0.6b
284
320
  parakeet-ctc-0.6b.q8_0
285
321
  https://huggingface.co/nvidia/parakeet-ctc-0.6b
286
- parakeet-eou-120m-v1.f16
287
- https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
288
- parakeet-eou-120m-v1.q4_0
289
- https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
290
- parakeet-eou-120m-v1.q8_0
291
- https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
292
322
  parakeet-tdt-0.6b-v3.f16
293
323
  https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3
294
324
  parakeet-tdt-0.6b-v3.q4_0
295
325
  https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3
296
326
  parakeet-tdt-0.6b-v3.q8_0
297
327
  https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3
328
+ parakeet-tdt-1.1b.f16
329
+ https://huggingface.co/nvidia/parakeet-tdt-1.1b
330
+ parakeet-tdt-1.1b.q8_0
331
+ https://huggingface.co/nvidia/parakeet-tdt-1.1b
298
332
  sortformer-4spk-v1.f16
299
333
  https://huggingface.co/nvidia/diar_sortformer_4spk-v1
300
334
  sortformer-4spk-v1.q4_0
301
335
  https://huggingface.co/nvidia/diar_sortformer_4spk-v1
302
336
  sortformer-4spk-v1.q8_0
303
337
  https://huggingface.co/nvidia/diar_sortformer_4spk-v1
338
+ weight
339
+ https://huggingface.co/nvidia/parakeet-ctc-0.6b
340
+ weight
341
+ https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3
342
+ weight
343
+ https://huggingface.co/nvidia/parakeet-tdt-1.1b
344
+ weight
345
+ https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1
304
346
 
305
347
  --- gemma (Gemma Terms of Use) ---
306
348
 
@@ -362,6 +404,8 @@ Third-Party Model Licenses
362
404
  https://huggingface.co/ResembleAI/chatterbox
363
405
  chatterbox-t3-turbo
364
406
  https://huggingface.co/ResembleAI/chatterbox-turbo
407
+ coremldata
408
+ https://huggingface.co/ai4bharat/indic-conformer-600m-multilingual
365
409
  de-base-ggml-model-f16
366
410
  https://huggingface.co/aware-ai/whisper-base-german
367
411
  DeepSeek-V4-Flash-0731-GGUF
@@ -384,16 +428,18 @@ Third-Party Model Licenses
384
428
  https://huggingface.co/ChristianAzinn/gte-large-gguf
385
429
  gte-large-gguf
386
430
  https://huggingface.co/ChristianAzinn/gte-large-gguf
387
- indic-conformer-ctc.f16
431
+ indic-conformer-600m-multilingual.f16
388
432
  https://huggingface.co/ai4bharat/indic-conformer-600m-multilingual
389
- indic-conformer-ctc.q4_0
433
+ indic-conformer-600m-multilingual.q4_0
390
434
  https://huggingface.co/ai4bharat/indic-conformer-600m-multilingual
391
- indic-conformer-ctc.q8_0
435
+ indic-conformer-600m-multilingual.q8_0
392
436
  https://huggingface.co/ai4bharat/indic-conformer-600m-multilingual
393
437
  lavasr-denoiser-f16
394
438
  https://huggingface.co/spaces/YatharthS
395
439
  lavasr-enhancer-f16
396
440
  https://huggingface.co/spaces/YatharthS
441
+ model
442
+ https://huggingface.co/ai4bharat/indic-conformer-600m-multilingual
397
443
  pt-tiny-ggml-model-f16
398
444
  https://huggingface.co/DavyCosta701/whisper-tiny-pt-bpra
399
445
  taew2_2_f16
@@ -404,6 +450,8 @@ Third-Party Model Licenses
404
450
  https://voiceinput.futo.org/VoiceInput/tiny_en_acft_q8_0.bin
405
451
  vae-BF16
406
452
  https://huggingface.co/ACE-Step/Ace-Step1.5
453
+ weight
454
+ https://huggingface.co/ai4bharat/indic-conformer-600m-multilingual
407
455
  whisper-vad
408
456
  https://huggingface.co/ggml-org/whisper-vad
409
457
  whisper.cpp
@@ -618,6 +666,12 @@ Third-Party Model Licenses
618
666
 
619
667
  --- nvidia-open-model-license ---
620
668
 
669
+ coremldata
670
+ https://huggingface.co/nvidia/diar_streaming_sortformer_4spk-v2.1
671
+ coremldata
672
+ https://huggingface.co/nvidia/nemotron-3.5-asr-streaming-0.6b
673
+ coremldata
674
+ https://huggingface.co/nvidia/parakeet-unified-en-0.6b
621
675
  diar_streaming_sortformer_4spk-v2.1.f16
622
676
  https://huggingface.co/nvidia/diar_streaming_sortformer_4spk-v2.1
623
677
  diar_streaming_sortformer_4spk-v2.1.q4_0
@@ -628,6 +682,12 @@ Third-Party Model Licenses
628
682
  https://huggingface.co/nvidia/GR00T-N1.7-LIBERO
629
683
  groot-q8_vf16
630
684
  https://huggingface.co/nvidia/GR00T-N1.7-3B
685
+ model
686
+ https://huggingface.co/nvidia/diar_streaming_sortformer_4spk-v2.1
687
+ model
688
+ https://huggingface.co/nvidia/nemotron-3.5-asr-streaming-0.6b
689
+ model
690
+ https://huggingface.co/nvidia/parakeet-unified-en-0.6b
631
691
  nemotron-3.5-asr-streaming-0.6b.f16
632
692
  https://huggingface.co/nvidia/nemotron-3.5-asr-streaming-0.6b
633
693
  nemotron-3.5-asr-streaming-0.6b.q4_0
@@ -640,6 +700,12 @@ Third-Party Model Licenses
640
700
  https://huggingface.co/nvidia/parakeet-unified-en-0.6b
641
701
  parakeet-unified-en-0.6b.q8_0
642
702
  https://huggingface.co/nvidia/parakeet-unified-en-0.6b
703
+ weight
704
+ https://huggingface.co/nvidia/diar_streaming_sortformer_4spk-v2.1
705
+ weight
706
+ https://huggingface.co/nvidia/nemotron-3.5-asr-streaming-0.6b
707
+ weight
708
+ https://huggingface.co/nvidia/parakeet-unified-en-0.6b
643
709
 
644
710
  --- openrail ---
645
711
 
@@ -682,7 +748,6 @@ the underlying models.
682
748
 
683
749
  Source URLs for original weights are recorded in each model's
684
750
  metadata entry within the registry.
685
-
686
751
  =========================================================================
687
752
  JavaScript Dependencies
688
753
  =========================================================================
@@ -722,8 +787,6 @@ JavaScript Dependencies
722
787
  https://github.com/tetherto/qvac
723
788
  @qvac/logging@0.1.1
724
789
  https://github.com/tetherto/qvac
725
- @qvac/model-fit@0.12.2
726
- https://github.com/tetherto/qvac
727
790
  @qvac/ocr-ggml@0.24.3
728
791
  https://github.com/tetherto/qvac
729
792
  @qvac/rag@0.8.1
package/README.md CHANGED
@@ -22,6 +22,13 @@ For AI/LLM tools, use [https://docs.qvac.tether.io/llms-full.txt](https://docs.q
22
22
 
23
23
  See https://docs.qvac.tether.io/sdk/getting-started/installation
24
24
 
25
+ On Node.js, the SDK installs and launches its own Bare worker through `bare-runtime`.
26
+ Its dependency range must satisfy `@qvac/inference`'s `engines.bare` requirement.
27
+ When upgrading the SDK, reinstall dependencies with the existing lockfile so the
28
+ package manager can replace an older, incompatible runtime. Remove any override
29
+ or resolution that forces the worker below that requirement; installing a newer
30
+ global `bare` executable does not change the SDK's worker dependency.
31
+
25
32
  ## Quickstart
26
33
 
27
34
  1. Create the examples workspace:
@@ -143,16 +150,16 @@ initialized, the event omits the resource block.
143
150
  ## Pre-download model fit assessment
144
151
 
145
152
  Use `assessModelFit` to check, before downloading anything, whether models are
146
- likely to fit in this device's memory. It reads generated catalog metadata plus a
147
- fresh memory sample, and for a single candidate the registry's weightless
148
- description of the artifact, so the engine's own fitter can answer — no weights,
149
- no load:
153
+ likely to fit in this device's memory. Each candidate is described as `loadModel`
154
+ takes it. It reads generated catalog metadata plus a fresh memory sample, and for
155
+ a single candidate the registry's weightless description of the artifact, so the
156
+ engine's own fitter can answer — no weights, no load:
150
157
 
151
158
  ```ts
152
159
  import { assessModelFit, QWEN3_8B_INST_Q4_K_M } from '@qvac/sdk'
153
160
 
154
161
  const result = await assessModelFit({
155
- models: [{ model: QWEN3_8B_INST_Q4_K_M, workload: { kind: 'llm', contextTokens: 8192 } }],
162
+ models: [{ modelSrc: QWEN3_8B_INST_Q4_K_M, modelType: 'llm', modelConfig: { ctx_size: 8192 } }],
156
163
  execution: 'sequential',
157
164
  policy: 'interactive-v1'
158
165
  })
@@ -187,6 +194,33 @@ console.log(stats?.audioDuration, stats?.realTimeFactor)
187
194
  `session.stats` resolves to `undefined` when the engine does not report
188
195
  statistics.
189
196
 
197
+ Batch `transcribe()` calls also expose terminal statistics:
198
+
199
+ ```ts
200
+ const call = transcribe({ modelId, audioChunk })
201
+ const text = await call
202
+ const stats = await call.stats
203
+ console.log(text, stats?.encoderUsedCoreml)
204
+ ```
205
+
206
+ `call.stats` resolves to `undefined` when the engine reports no statistics and
207
+ rejects if transcription fails.
208
+
209
+ ## Parakeet Core ML encoders on Apple devices
210
+
211
+ Load a supported Parakeet registry constant as usual. On macOS and iOS, the SDK
212
+ downloads any complete, published Core ML encoder bundle with its GGUF and
213
+ places the `.mlmodelc` directory where the native addon discovers it. This is
214
+ available for TDT 0.6B v3, Unified English 0.6B, EOU 120M v1, and streaming
215
+ Sortformer v2.1. The download includes the sidecar weights, so first load takes
216
+ more time and disk space. Other platforms download only the GGUF. If the
217
+ sidecar is unavailable or cannot load, inference uses the GGUF encoder.
218
+ Sortformer v2.1 also needs a GGUF with the
219
+ `parakeet.model_variant=sortformer-streaming-v2.1-aosc` metadata. Caching both
220
+ sidecars alone does not activate Core ML. See
221
+ [`examples/asr/parakeet-unified-coreml.ts`](examples/asr/parakeet-unified-coreml.ts)
222
+ for a local check of a Unified GGUF and its encoder bundle.
223
+
190
224
  ## Examples
191
225
 
192
226
  In the `./examples` subdirectory, you will find scripts demonstrating how to use all SDK functionalities. To try any of them:
@@ -1,15 +1,16 @@
1
1
  /**
2
- * Advisory llama.cpp fit check (QVAC-22629).
2
+ * Advisory model fit check.
3
3
  *
4
- * Before a completion or embedding load, the SDK runs `@qvac/model-fit` in one
5
- * disposable Bare child and projects whether the exact configuration it is
6
- * about to load will fit in device memory.
4
+ * Before a load, the SDK runs the fitter belonging to the engine that would
5
+ * run it and projects whether the exact configuration it is about to load will
6
+ * fit in device memory. Desktop runs it in a disposable Bare child; mobile,
7
+ * which cannot spawn one, runs it on a worker thread.
7
8
  *
8
9
  * The result is ADVISORY. It never blocks a load. `does-not-fit` is logged and
9
10
  * the ordinary load path runs unchanged. Crashes, timeouts, malformed
10
11
  * responses, unsupported configurations, and internal errors all resolve to
11
- * "no evidence" and are equally non-blocking. Nothing consumes the verdict
12
- * yet — this PR only produces it.
12
+ * "no evidence" and are equally non-blocking. No verdict changes the load;
13
+ * `getLoadedModelInfo` returns it as `fitProbe`.
13
14
  *
14
15
  * The verdict is emitted on the SDK server log stream, not to stdout, so this
15
16
  * example subscribes to `loggingStream({ id: SDK_LOG_ID })` and reprints the
@@ -19,9 +20,8 @@
19
20
  * What fits on this machine (Apple M4 Pro, 24 GiB unified memory)
20
21
  * ---------------------------------------------------------------------------
21
22
  *
22
- * Measured with `@qvac/model-fit@0.8.0` (the first release carrying the
23
- * qvac-fabric#214 memory-reporting fix) at the default 1024 MiB margin. The
24
- * fitter budgets against what the machine can actually keep resident
23
+ * Measured at the default 1024 MiB margin. The fitter budgets against what the
24
+ * machine can actually keep resident
25
25
  * (total − wired − compressor: 17.4 GiB on this machine at idle), not the raw
26
26
  * RAM figure.
27
27
  *
@@ -30,10 +30,12 @@
30
30
  * gpt-oss-20B Q4_K_M 10.8 GiB @ 32k ctx
31
31
  * gte-large fp16 0.6 GiB (embedding, context pinned to 512)
32
32
  *
33
- * PROJECTED NOT TO FIT — try any of these to see a `does-not-fit` verdict
33
+ * PROJECTED NOT TO FIT — with `gpu_layers: 99` pinned
34
34
  * gpt-oss-20B Q4_K_M 10.8 GiB @ 128k ctx with an f32 KV cache ← below
35
- * Gemma 4 31B Q4_K_M 18.3 GiB @ 1k ctx ← the weights alone do not
36
- * fit with gpu_layers: 99
35
+ *
36
+ * These rows were measured with `gpu_layers` pinned at 99. Loads no longer pin
37
+ * the layer count, and the `does-not-fit` row has not been re-measured
38
+ * unpinned.
37
39
  *
38
40
  * gpt-oss-20B at 128k context FITS with the default KV cache and DOES NOT FIT
39
41
  * once `cache-type-k`/`cache-type-v` are set to `f32`. Same model, same
@@ -42,10 +44,9 @@
42
44
  *
43
45
  * Two boundaries worth understanding when reading verdicts:
44
46
  *
45
- * 1. The verdict answers for a PLACEMENT, not a model. The SDK's schema
46
- * default is `gpu_layers: 99` ("everything on the GPU"), so `does-not-fit`
47
- * means "not at this placement". Omit `gpu_layers` and the fitter is free
48
- * to move layers to the CPU side instead.
47
+ * 1. The verdict answers for a PLACEMENT, not a model. With `gpu_layers`
48
+ * unset, the fitter may move layers to the CPU side; setting it pins the
49
+ * layer count, and `does-not-fit` then means "not at this placement".
49
50
  * 2. A `does-not-fit` configuration can still RUN on macOS when the OS
50
51
  * compresses and pages hard enough — and a `fits` configuration right at
51
52
  * the boundary can still fail at first decode under memory pressure.
@@ -1,15 +1,16 @@
1
1
  /**
2
- * Advisory llama.cpp fit check (QVAC-22629).
2
+ * Advisory model fit check.
3
3
  *
4
- * Before a completion or embedding load, the SDK runs `@qvac/model-fit` in one
5
- * disposable Bare child and projects whether the exact configuration it is
6
- * about to load will fit in device memory.
4
+ * Before a load, the SDK runs the fitter belonging to the engine that would
5
+ * run it and projects whether the exact configuration it is about to load will
6
+ * fit in device memory. Desktop runs it in a disposable Bare child; mobile,
7
+ * which cannot spawn one, runs it on a worker thread.
7
8
  *
8
9
  * The result is ADVISORY. It never blocks a load. `does-not-fit` is logged and
9
10
  * the ordinary load path runs unchanged. Crashes, timeouts, malformed
10
11
  * responses, unsupported configurations, and internal errors all resolve to
11
- * "no evidence" and are equally non-blocking. Nothing consumes the verdict
12
- * yet — this PR only produces it.
12
+ * "no evidence" and are equally non-blocking. No verdict changes the load;
13
+ * `getLoadedModelInfo` returns it as `fitProbe`.
13
14
  *
14
15
  * The verdict is emitted on the SDK server log stream, not to stdout, so this
15
16
  * example subscribes to `loggingStream({ id: SDK_LOG_ID })` and reprints the
@@ -19,9 +20,8 @@
19
20
  * What fits on this machine (Apple M4 Pro, 24 GiB unified memory)
20
21
  * ---------------------------------------------------------------------------
21
22
  *
22
- * Measured with `@qvac/model-fit@0.8.0` (the first release carrying the
23
- * qvac-fabric#214 memory-reporting fix) at the default 1024 MiB margin. The
24
- * fitter budgets against what the machine can actually keep resident
23
+ * Measured at the default 1024 MiB margin. The fitter budgets against what the
24
+ * machine can actually keep resident
25
25
  * (total − wired − compressor: 17.4 GiB on this machine at idle), not the raw
26
26
  * RAM figure.
27
27
  *
@@ -30,10 +30,12 @@
30
30
  * gpt-oss-20B Q4_K_M 10.8 GiB @ 32k ctx
31
31
  * gte-large fp16 0.6 GiB (embedding, context pinned to 512)
32
32
  *
33
- * PROJECTED NOT TO FIT — try any of these to see a `does-not-fit` verdict
33
+ * PROJECTED NOT TO FIT — with `gpu_layers: 99` pinned
34
34
  * gpt-oss-20B Q4_K_M 10.8 GiB @ 128k ctx with an f32 KV cache ← below
35
- * Gemma 4 31B Q4_K_M 18.3 GiB @ 1k ctx ← the weights alone do not
36
- * fit with gpu_layers: 99
35
+ *
36
+ * These rows were measured with `gpu_layers` pinned at 99. Loads no longer pin
37
+ * the layer count, and the `does-not-fit` row has not been re-measured
38
+ * unpinned.
37
39
  *
38
40
  * gpt-oss-20B at 128k context FITS with the default KV cache and DOES NOT FIT
39
41
  * once `cache-type-k`/`cache-type-v` are set to `f32`. Same model, same
@@ -42,10 +44,9 @@
42
44
  *
43
45
  * Two boundaries worth understanding when reading verdicts:
44
46
  *
45
- * 1. The verdict answers for a PLACEMENT, not a model. The SDK's schema
46
- * default is `gpu_layers: 99` ("everything on the GPU"), so `does-not-fit`
47
- * means "not at this placement". Omit `gpu_layers` and the fitter is free
48
- * to move layers to the CPU side instead.
47
+ * 1. The verdict answers for a PLACEMENT, not a model. With `gpu_layers`
48
+ * unset, the fitter may move layers to the CPU side; setting it pins the
49
+ * layer count, and `does-not-fit` then means "not at this placement".
49
50
  * 2. A `does-not-fit` configuration can still RUN on macOS when the OS
50
51
  * compresses and pages hard enough — and a `fits` configuration right at
51
52
  * the boundary can still fail at first decode under memory pressure.
@@ -98,8 +99,7 @@ try {
98
99
  // Unloaded before the next phase, so the second verdict is measured on an
99
100
  // idle machine and stays comparable to the fixture tables above. Leaving it
100
101
  // loaded would shift the verdict: the check reserves resident weight bytes
101
- // through the fit margin, and since model-fit 0.8.0 the fit child also sees
102
- // system-wide wired memory.
102
+ // through the fit margin, and the fitter also sees system-wide wired memory.
103
103
  await unloadModel({ modelId: smallModelId, clearStorage: false });
104
104
  watchVerdicts();
105
105
  // 2. A load the fitter projects NOT to fit. The point of this example is that
@@ -4,14 +4,14 @@
4
4
  * Usage:
5
5
  * bun run examples/asr/parakeet-indic-conformer-filesystem.ts <wav-file> <language> [gguf]
6
6
  *
7
- * Loads a single GGUF checkpoint (`PARAKEET_INDIC_CONFORMER_CTC_Q8_0` by
7
+ * Loads a single GGUF checkpoint (`PARAKEET_INDIC_CONFORMER_600M_Q8_0` by
8
8
  * default) and transcribes with the batch `transcribe` API. `language` is
9
9
  * required (e.g. `hi`, `ta`) because Indic Conformer CTC masks the vocab
10
10
  * with `parakeet.ctc.lang_*` ranges. English Parakeet CTC ignores this field.
11
11
  *
12
12
  * Audio should be 16 kHz mono PCM in a WAV container.
13
13
  */
14
- import { loadModel, unloadModel, transcribe, PARAKEET_INDIC_CONFORMER_CTC_Q8_0 } from '@qvac/sdk';
14
+ import { loadModel, unloadModel, transcribe, PARAKEET_INDIC_CONFORMER_600M_Q8_0 } from '@qvac/sdk';
15
15
  const args = process.argv.slice(2);
16
16
  if (!args[0] || !args[1]) {
17
17
  console.error('Usage: bun run examples/asr/parakeet-indic-conformer-filesystem.ts ' +
@@ -22,7 +22,7 @@ if (!args[0] || !args[1]) {
22
22
  }
23
23
  const audioFilePath = args[0];
24
24
  const language = args[1];
25
- const parakeetModelSrc = args[2] ?? PARAKEET_INDIC_CONFORMER_CTC_Q8_0;
25
+ const parakeetModelSrc = args[2] ?? PARAKEET_INDIC_CONFORMER_600M_Q8_0;
26
26
  try {
27
27
  console.log('▸ Loading Indic Conformer CTC model...');
28
28
  const modelId = await loadModel({
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parakeet-unified-coreml.d.ts.map
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Transcribe a WAV file with Parakeet Unified and its Core ML encoder sidecar.
3
+ *
4
+ * From packages/sdk:
5
+ * bun run examples/asr/parakeet-unified-coreml.ts \
6
+ * /path/to/parakeet-unified-en-0.6b.q4_0.gguf [16-kHz-wav-file]
7
+ *
8
+ * The GGUF must be a regenerated Unified artifact. The five compiled Core ML
9
+ * files are downloaded through the SDK registry and staged beside the GGUF.
10
+ * The default audio is examples/audio/sample-16khz.wav.
11
+ */
12
+ import { downloadAsset, getModelInfo, loadModel, transcribe, unloadModel } from '@qvac/sdk';
13
+ import { link, mkdir, mkdtemp, open, rm, symlink } from 'node:fs/promises';
14
+ import { tmpdir } from 'node:os';
15
+ import { basename, join, resolve } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ const MODEL_STEM = 'parakeet-unified-en-0.6b';
18
+ const COREML_FILES = [
19
+ ['PARAKEET_UNIFIED_COREMLDATA', 'analytics/coremldata.bin'],
20
+ ['PARAKEET_UNIFIED_COREMLDATA_1', 'coremldata.bin'],
21
+ ['PARAKEET_UNIFIED_METADATA', 'metadata.json'],
22
+ ['PARAKEET_UNIFIED_MODEL', 'model.mil'],
23
+ ['PARAKEET_UNIFIED_WEIGHT', 'weights/weight.bin']
24
+ ];
25
+ const defaultAudioPath = fileURLToPath(new URL('../audio/sample-16khz.wav', import.meta.url));
26
+ const modelPath = process.argv[2];
27
+ const audioPath = process.argv[3] ?? defaultAudioPath;
28
+ async function checkModel(path) {
29
+ const name = basename(path);
30
+ if (!/^parakeet-unified-en-0\.6b\.(?:f16|q4_0|q8_0)\.gguf$/.test(name)) {
31
+ throw new Error(`Expected a regenerated Unified GGUF, got: ${name}`);
32
+ }
33
+ const file = await open(path, 'r');
34
+ try {
35
+ const header = Buffer.alloc(1024 * 1024);
36
+ const { bytesRead } = await file.read(header, 0, header.length, 0);
37
+ if (!header.subarray(0, bytesRead).includes('parakeet.unified.left_context_frames')) {
38
+ throw new Error(`Unified GGUF lacks the new streaming metadata: ${path}`);
39
+ }
40
+ }
41
+ finally {
42
+ await file.close();
43
+ }
44
+ }
45
+ async function stageModel(path) {
46
+ if (process.platform !== 'darwin')
47
+ throw new Error('Core ML requires macOS');
48
+ await checkModel(path);
49
+ const dir = await mkdtemp(join(tmpdir(), 'qvac-unified-coreml-'));
50
+ try {
51
+ const modelSrc = join(dir, basename(path));
52
+ await symlink(resolve(path), modelSrc);
53
+ const bundleDir = join(dir, `${MODEL_STEM}-encoder.mlmodelc`);
54
+ for (const [name, relativePath] of COREML_FILES) {
55
+ const info = await getModelInfo({ name });
56
+ if (!info.isCached) {
57
+ if (!info.registrySource || !info.registryPath) {
58
+ throw new Error(`Registry location missing for ${name}`);
59
+ }
60
+ console.log(`▸ Downloading Core ML ${relativePath}...`);
61
+ await downloadAsset({ assetSrc: `registry://${info.registrySource}/${info.registryPath}` });
62
+ }
63
+ const cached = await getModelInfo({ name });
64
+ const sourcePath = cached.cacheFiles.find((file) => file.isCached)?.path;
65
+ if (!sourcePath)
66
+ throw new Error(`Core ML file is missing from the SDK cache: ${name}`);
67
+ const targetPath = join(bundleDir, relativePath);
68
+ await mkdir(join(targetPath, '..'), { recursive: true });
69
+ await link(sourcePath, targetPath);
70
+ }
71
+ return { modelSrc, dir };
72
+ }
73
+ catch (error) {
74
+ await rm(dir, { recursive: true, force: true });
75
+ throw error;
76
+ }
77
+ }
78
+ async function main() {
79
+ if (!modelPath) {
80
+ throw new Error('Usage: bun run examples/asr/parakeet-unified-coreml.ts ' +
81
+ '<regenerated-unified-gguf> [16-kHz-wav-file]');
82
+ }
83
+ let staged;
84
+ let modelId;
85
+ try {
86
+ staged = await stageModel(modelPath);
87
+ console.log('▸ Loading Parakeet Unified with its Core ML encoder sidecar...');
88
+ modelId = await loadModel({
89
+ modelSrc: staged.modelSrc,
90
+ modelType: 'parakeet-transcription',
91
+ modelConfig: { useGPU: true }
92
+ });
93
+ console.log('▸ Transcribing audio...');
94
+ const text = await transcribe({ modelId, audioChunk: audioPath });
95
+ if (!text.trim())
96
+ throw new Error('Parakeet Unified returned an empty transcription');
97
+ console.log(text);
98
+ }
99
+ finally {
100
+ try {
101
+ if (modelId)
102
+ await unloadModel({ modelId });
103
+ }
104
+ finally {
105
+ if (staged)
106
+ await rm(staged.dir, { recursive: true, force: true });
107
+ }
108
+ }
109
+ }
110
+ main().catch((error) => {
111
+ console.error('✖', error);
112
+ process.exitCode = 1;
113
+ });
@@ -11,8 +11,9 @@
11
11
  * call either way. Show it as "can't say", never as "no".
12
12
  */
13
13
  import { assessModelFit, QWEN3_600M_INST_Q4, QWEN3_1_7B_INST_Q4, QWEN3_4B_INST_Q4_K_M, QWEN3_8B_INST_Q4_K_M, QWEN3_8_27B_MULTIMODAL_UD_Q8_K_XL } from '@qvac/sdk';
14
- // `as const` so `kind` stays the `'llm'` literal the input type asks for.
15
- const WORKLOAD = { kind: 'llm', contextTokens: 8192 };
14
+ // Sizes the assessment the way it sizes the load: the estimator reads the
15
+ // context out of the config `loadModel` would be given.
16
+ const CONTEXT_TOKENS = 8192;
16
17
  // A ladder ending well past what a laptop has, so one screen shows every verdict.
17
18
  const CANDIDATES = [
18
19
  QWEN3_600M_INST_Q4,
@@ -31,7 +32,11 @@ function gib(bytes) {
31
32
  }
32
33
  try {
33
34
  const result = await assessModelFit({
34
- models: CANDIDATES.map((model) => ({ model, workload: WORKLOAD })),
35
+ models: CANDIDATES.map((model) => ({
36
+ modelSrc: model,
37
+ modelType: 'llamacpp-completion',
38
+ modelConfig: { ctx_size: CONTEXT_TOKENS }
39
+ })),
35
40
  // Declared for aggregation only, not a scheduling instruction: 'sequential'
36
41
  // counts the largest operation peak, 'concurrent' counts one per model.
37
42
  execution: 'sequential',
@@ -49,7 +54,7 @@ try {
49
54
  const placement = result.assumptions.find((line) => line.includes('assumed to execute on'));
50
55
  if (placement)
51
56
  console.log(`▸ ${placement}`);
52
- console.log(`\n▸ At ${WORKLOAD.contextTokens} tokens of context`);
57
+ console.log(`\n▸ At ${CONTEXT_TOKENS} tokens of context`);
53
58
  for (const [index, model] of result.models.entries()) {
54
59
  const mark = VERDICT_MARK[model.verdict];
55
60
  const size = gib(CANDIDATES[index].expectedSize).padStart(9);
@@ -102,9 +102,12 @@ function sendJson(response, status, payload) {
102
102
  async function estimate(body) {
103
103
  const names = Array.isArray(body['names']) ? body['names'] : [];
104
104
  const contextTokens = Number(body['contextTokens'] ?? 8192);
105
- const workload = { kind: 'llm', contextTokens };
106
105
  return assessModelFit({
107
- models: names.map((name) => ({ model: byName(name), workload })),
106
+ models: names.map((name) => ({
107
+ modelSrc: byName(name),
108
+ modelType: 'llamacpp-completion',
109
+ modelConfig: { ctx_size: contextTokens }
110
+ })),
108
111
  // Declared for aggregation only. 'sequential' counts the largest operation
109
112
  // peak; 'concurrent' counts one per model.
110
113
  execution: 'sequential',
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rpc-inference.d.ts.map