@qvac/ocr-ggml 0.25.0 → 0.26.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.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @qvac/ocr-ggml
2
2
 
3
3
  GGML-backed OCR addon for [QVAC](https://github.com/tetherto/qvac).
4
+
5
+ EasyOCR model loading rejects detector output shapes, recognizer class counts, malformed prediction bias vectors, and convolution bias sizes that do not match the expected tensors.
4
6
  Provides two inference pipelines on **`ggml` / `.gguf`** — no Python, no
5
7
  PyTorch, and no ONNX Runtime at runtime:
6
8
 
@@ -147,7 +149,7 @@ bare examples/backend-device.js --backend metal
147
149
  | `params.lowConfidenceThreshold` | `number` | | `0.4` | retry threshold (`easyocr` only) |
148
150
  | `params.recognizerBatchSize` | `number` | | `32` | recognizer batch size (`easyocr` only) |
149
151
  | `params.nThreads` | `number` | | `0` (auto) | CPU thread count for GGML; `<0` leaves the GGML default |
150
- | `params.backendsDir` | `string` | | `<package>/prebuilds` | directory holding `libggml-*.so` backend shared libs |
152
+ | `params.backendsDir` | `string` | | fabric backends root | directory holding `libqvac-ggml-*.so` backend shared libs. On desktop, the root `@qvac/fabric/backends` resolves; on mobile, this package's `prebuilds/` |
151
153
  | `params.backendDevice` | `'cpu'` \| `'vulkan'` \| `'metal'` \| `'opencl'` | | `'cpu'` | ggml backend device. `'vulkan'` (Linux/Windows/Android), `'metal'` (Apple) and `'opencl'` (Android/Adreno) opt in to GPU inference with transparent CPU fallback — see [Backend device](#backend-device-cpu--vulkan--metal--opencl) |
152
154
  | `params.main-gpu` / `params.main_gpu` | `number` \| `string` | | _prefer dedicated_ | Raw ggml registry index or strict GPU class; requires GPU `backendDevice`. See below. |
153
155
  | `params.gpuDevice` | `number` | | _prefer discrete_ | 0-based index into the matching GPU/iGPU devices for `'vulkan'`/`'metal'`/`'opencl'`; out-of-range → CPU fallback — see [Selecting a specific GPU](#selecting-a-specific-gpu-gpudevice) |
@@ -232,7 +234,7 @@ Behaviour and expectations:
232
234
  `backendIsGpu` stat). It never silently does the wrong thing.
233
235
  - **Required backend libs.** Vulkan execution needs the `libggml-vulkan`
234
236
  backend shared library (`libggml-vulkan.so` / `.dll` / `.dylib`) present in
235
- `backendsDir` (default `<package>/prebuilds/<target>/`), plus a working
237
+ `backendsDir` (on desktop, the root `@qvac/fabric/backends` resolves; on mobile, this package's `prebuilds/<target>/`), plus a working
236
238
  Vulkan driver/ICD and a Vulkan-capable GPU on the host. **OpenCL** likewise
237
239
  needs the `libggml-opencl` backend shared library plus a working OpenCL
238
240
  runtime (`libOpenCL.so`); it is built primarily for **Android** (the `opencl`
@@ -665,7 +667,7 @@ omission for v1: `--debug-png` (annotated overlay) — print boxes via
665
667
 
666
668
  | Script | Purpose |
667
669
  |---|---|
668
- | [`scripts/check_ggml_backends.sh`](./scripts/check_ggml_backends.sh) | Probe shipped ggml backends + BLAS/Vulkan/OpenCL paths in `prebuilds/` |
670
+ | [`scripts/check_ggml_backends.sh`](./scripts/check_ggml_backends.sh) | Probe shipped ggml backends + BLAS/Vulkan/OpenCL paths in the installed `@qvac/fabric` runtime |
669
671
  | [`scripts/pth_to_gguf.py`](./scripts/pth_to_gguf.py) | Weight converter: EasyOCR PyTorch `.pth` → `.gguf` (optionally `Q8_0` / `Q4_K` quantized) |
670
672
  | [`scripts/convert-model.sh`](./scripts/convert-model.sh) | Wrapper around the converter (`npm run convert-model`) — venv discovery, sanity checks |
671
673
  | [`scripts/setup-venv.sh`](./scripts/setup-venv.sh) | One-time provisioning of the converter venv (`npm run setup:venv`) |
package/index.d.ts CHANGED
@@ -59,8 +59,8 @@ export interface OcrGgmlParams {
59
59
  */
60
60
  nThreads?: number;
61
61
  /**
62
- * Directory holding ggml backend shared libraries. Default: `@qvac/fabric`'s
63
- * `prebuilds/` (desktop), falling back to this package's `prebuilds/` on
62
+ * Directory holding ggml backend shared libraries. Default: the root
63
+ * `@qvac/fabric/backends` resolves (desktop), falling back to this package's `prebuilds/` on
64
64
  * mobile where the package tree isn't resolvable from the packed worklet.
65
65
  */
66
66
  backendsDir?: string;
package/index.js CHANGED
@@ -5,6 +5,7 @@ exports.ERR_CODES = exports.QvacErrorAddonOcrGgml = exports.OcrGgml = void 0;
5
5
  const path = require("bare-path");
6
6
  const fs = require("bare-fs");
7
7
  const QvacLogger = require("@qvac/logging");
8
+ const fabricBackends = require("@qvac/fabric/backends");
8
9
  /* eslint-enable @typescript-eslint/no-require-imports */
9
10
  const infer_base_1 = require("@qvac/infer-base");
10
11
  const ocr_ggml_1 = require("./ocr-ggml");
@@ -20,24 +21,20 @@ const DOCTR_INTERNAL_LANG_LIST = ["en"];
20
21
  * create-time failures to ERR_CODES.UNSUPPORTED_LANGUAGE.
21
22
  */
22
23
  const NATIVE_LANGUAGE_ERROR = /unsupported languages|only compatible with english/i;
23
- // The ggml compute backends (GGML_BACKEND_DL modules) ship exactly once, in the
24
- // @qvac/fabric dependency (prebuilds/<host>/qvac__fabric). We deliberately do
25
- // not copy them into this addon to avoid duplicating tens of MB per fabric
26
- // consumer. On desktop, resolve the single @qvac/fabric install and load the
27
- // backends from there. On mobile the package tree isn't resolvable at runtime
28
- // (the worklet runs from a packed bundle), so fall back to this addon's own
29
- // prebuilds, where the mobile packaging stages the backends. The native side
24
+ // The ggml compute backends (GGML_BACKEND_DL modules) ship exactly once, next to
25
+ // the @qvac/fabric runtime (<root>/<host>/qvac__fabric). We deliberately do not
26
+ // copy them into this addon to avoid duplicating tens of MB per fabric
27
+ // consumer. On desktop, @qvac/fabric/backends resolves that root in whichever
28
+ // package holds the runtime. On mobile the package tree isn't resolvable at
29
+ // runtime (the worklet runs from a packed bundle), so fall back to this addon's
30
+ // own prebuilds, where the mobile packaging stages the backends. The native side
30
31
  // appends BACKENDS_SUBDIR ("<host>/qvac__fabric") to whichever root we return.
31
32
  function resolveBackendsDir() {
32
- try {
33
- const fabricPkg = require.resolve("@qvac/fabric/package");
34
- const fabricPrebuilds = path.join(path.dirname(fabricPkg), "prebuilds");
35
- if (fs.existsSync(fabricPrebuilds))
36
- return fabricPrebuilds;
37
- }
38
- catch {
39
- // Mobile worklets cannot resolve the @qvac/fabric package tree.
40
- }
33
+ // fabric's resolver only checks that the platform package resolves, not that
34
+ // its prebuilds are on disk.
35
+ const fabricRoot = fabricBackends.resolveBackendsDir();
36
+ if (fabricRoot !== null && fs.existsSync(fabricRoot))
37
+ return fabricRoot;
41
38
  return path.join(__dirname, "prebuilds");
42
39
  }
43
40
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/ocr-ggml",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "GGML-backed OCR addon for qvac (EasyOCR and DocTR pipelines on GGUF weights)",
5
5
  "addon": true,
6
6
  "engines": {
@@ -33,8 +33,8 @@
33
33
  "test": "npm run lint && npm run test:types && npm run test:unit && npm run test:integration",
34
34
  "test:types": "npm run typecheck && npm run check:generated",
35
35
  "test:dts": "npm run test:types",
36
- "test:cpp:build": "bare-make generate -D BUILD_TESTING=ON && bare-make build --target addon-test",
37
- "test:cpp:run": "cd build/addon/tests/ && ./addon-test --gtest_output=xml:cpp-test-results.xml",
36
+ "test:cpp:build": "bare-make generate -D BUILD_TESTING=ON && bare-make build --target addon-test ocr-prediction-bias-gguf-test",
37
+ "test:cpp:run": "cd build/addon/tests/ && ./addon-test --gtest_output=xml:cpp-test-results.xml && ./ocr-prediction-bias-gguf-test --gtest_output=xml:prediction-bias-gguf-test-results.xml",
38
38
  "test:cpp": "npm run test:cpp:build && npm run test:cpp:run",
39
39
  "setup:venv": "bash scripts/setup-venv.sh",
40
40
  "convert-model": "bash scripts/convert-model.sh",
@@ -75,6 +75,8 @@
75
75
  "bugs": "https://github.com/tetherto/qvac/issues",
76
76
  "homepage": "https://qvac.tether.io",
77
77
  "devDependencies": {
78
+ "@qvac/fabric-android-arm64": "^0.18.0",
79
+ "@qvac/fabric-ios": "^0.18.0",
78
80
  "@qvac/registry-client": "^0.4.0",
79
81
  "@types/node": "^24.2.1",
80
82
  "bare-stream": "^2.7.0",
@@ -95,7 +97,7 @@
95
97
  },
96
98
  "dependencies": {
97
99
  "@qvac/error": "^0.1.0",
98
- "@qvac/fabric": "^0.17.0",
100
+ "@qvac/fabric": "^0.18.0",
99
101
  "@qvac/infer-base": "^0.6.2",
100
102
  "@qvac/logging": "^0.1.0",
101
103
  "bare-fetch": "^3.0.1",
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.25"
10
+ "version": "0.26"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -3214,12 +3214,10 @@
3214
3214
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface9setLoggerEP8js_env_sP18js_callback_info_s",
3215
3215
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16jsOutputCallbackEP10uv_async_s",
3216
3216
  "__ZN23qvac_lib_infer_ocr_ggml6runJobEP8js_env_sP18js_callback_info_s",
3217
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm1EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3218
3217
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface6cancelEP8js_env_sP18js_callback_info_s",
3219
3218
  "__ZZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16drainAndTeardownEPNS0_5StateEbbENUlP11uv_handle_sE_8__invokeES4_",
3220
3219
  "__ZN28qvac_lib_inference_addon_cpp2js11JsAsyncTask10onCompleteEP10uv_async_s",
3221
3220
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface15destroyInstanceEP8js_env_sP18js_callback_info_s",
3222
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm0EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3223
3221
  "__ZN28qvac_lib_inference_addon_cpp6logger8JsLogger13onEnvTeardownEPv",
3224
3222
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface11loadWeightsEP8js_env_sP18js_callback_info_s",
3225
3223
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs13onEnvTeardownEPv",
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.25"
10
+ "version": "0.26"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -3246,12 +3246,10 @@
3246
3246
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface9setLoggerEP8js_env_sP18js_callback_info_s",
3247
3247
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16jsOutputCallbackEP10uv_async_s",
3248
3248
  "__ZN23qvac_lib_infer_ocr_ggml6runJobEP8js_env_sP18js_callback_info_s",
3249
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm1EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3250
3249
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface6cancelEP8js_env_sP18js_callback_info_s",
3251
3250
  "__ZZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16drainAndTeardownEPNS0_5StateEbbENUlP11uv_handle_sE_8__invokeES4_",
3252
3251
  "__ZN28qvac_lib_inference_addon_cpp2js11JsAsyncTask10onCompleteEP10uv_async_s",
3253
3252
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface15destroyInstanceEP8js_env_sP18js_callback_info_s",
3254
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm0EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3255
3253
  "__ZN28qvac_lib_inference_addon_cpp6logger8JsLogger13onEnvTeardownEPv",
3256
3254
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface11loadWeightsEP8js_env_sP18js_callback_info_s",
3257
3255
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs13onEnvTeardownEPv",
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.25"
10
+ "version": "0.26"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -3203,12 +3203,10 @@
3203
3203
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface9setLoggerEP8js_env_sP18js_callback_info_s",
3204
3204
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16jsOutputCallbackEP10uv_async_s",
3205
3205
  "__ZN23qvac_lib_infer_ocr_ggml6runJobEP8js_env_sP18js_callback_info_s",
3206
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm1EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3207
3206
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface6cancelEP8js_env_sP18js_callback_info_s",
3208
3207
  "__ZZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16drainAndTeardownEPNS0_5StateEbbENUlP11uv_handle_sE_8__invokeES4_",
3209
3208
  "__ZN28qvac_lib_inference_addon_cpp2js11JsAsyncTask10onCompleteEP10uv_async_s",
3210
3209
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface15destroyInstanceEP8js_env_sP18js_callback_info_s",
3211
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm0EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3212
3210
  "__ZN28qvac_lib_inference_addon_cpp6logger8JsLogger13onEnvTeardownEPv",
3213
3211
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface11loadWeightsEP8js_env_sP18js_callback_info_s",
3214
3212
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs13onEnvTeardownEPv",
@@ -3233,7 +3231,7 @@
3233
3231
  ],
3234
3232
  "target_info": [
3235
3233
  {
3236
- "min_deployment": "14",
3234
+ "min_deployment": "15",
3237
3235
  "target": "arm64-ios"
3238
3236
  }
3239
3237
  ]
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.25"
10
+ "version": "0.26"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -3203,12 +3203,10 @@
3203
3203
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface9setLoggerEP8js_env_sP18js_callback_info_s",
3204
3204
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16jsOutputCallbackEP10uv_async_s",
3205
3205
  "__ZN23qvac_lib_infer_ocr_ggml6runJobEP8js_env_sP18js_callback_info_s",
3206
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm1EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3207
3206
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface6cancelEP8js_env_sP18js_callback_info_s",
3208
3207
  "__ZZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16drainAndTeardownEPNS0_5StateEbbENUlP11uv_handle_sE_8__invokeES4_",
3209
3208
  "__ZN28qvac_lib_inference_addon_cpp2js11JsAsyncTask10onCompleteEP10uv_async_s",
3210
3209
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface15destroyInstanceEP8js_env_sP18js_callback_info_s",
3211
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm0EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3212
3210
  "__ZN28qvac_lib_inference_addon_cpp6logger8JsLogger13onEnvTeardownEPv",
3213
3211
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface11loadWeightsEP8js_env_sP18js_callback_info_s",
3214
3212
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs13onEnvTeardownEPv",
@@ -3233,7 +3231,7 @@
3233
3231
  ],
3234
3232
  "target_info": [
3235
3233
  {
3236
- "min_deployment": "14",
3234
+ "min_deployment": "15",
3237
3235
  "target": "arm64-ios-simulator"
3238
3236
  }
3239
3237
  ]
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.25"
10
+ "version": "0.26"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -3239,12 +3239,10 @@
3239
3239
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface9setLoggerEP8js_env_sP18js_callback_info_s",
3240
3240
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16jsOutputCallbackEP10uv_async_s",
3241
3241
  "__ZN23qvac_lib_infer_ocr_ggml6runJobEP8js_env_sP18js_callback_info_s",
3242
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm1EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3243
3242
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface6cancelEP8js_env_sP18js_callback_info_s",
3244
3243
  "__ZZN28qvac_lib_inference_addon_cpp16OutputCallBackJs16drainAndTeardownEPNS0_5StateEbbENUlP11uv_handle_sE_8__invokeES4_",
3245
3244
  "__ZN28qvac_lib_inference_addon_cpp2js11JsAsyncTask10onCompleteEP10uv_async_s",
3246
3245
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface15destroyInstanceEP8js_env_sP18js_callback_info_s",
3247
- "__ZNSt3__116__variant_detail12__visitation6__base12__dispatcherIJLm0EEE10__dispatchB8ne190102IONS1_9__variant15__value_visitorIZZN28qvac_lib_inference_addon_cpp9out_handl27JsRuntimeStatsOutputHandlerC1EvENKUlRKNS_6vectorINS_4pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_7variantIJdxEEEEENSG_ISL_EEEEE_clESP_EUlOT_E_EEJRKNS0_6__baseILNS0_6_TraitE0EJdxEEEEEEDcSR_DpT0_",
3248
3246
  "__ZN28qvac_lib_inference_addon_cpp6logger8JsLogger13onEnvTeardownEPv",
3249
3247
  "__ZN28qvac_lib_inference_addon_cpp11JsInterface11loadWeightsEP8js_env_sP18js_callback_info_s",
3250
3248
  "__ZN28qvac_lib_inference_addon_cpp16OutputCallBackJs13onEnvTeardownEPv",
@@ -3269,7 +3267,7 @@
3269
3267
  ],
3270
3268
  "target_info": [
3271
3269
  {
3272
- "min_deployment": "14",
3270
+ "min_deployment": "15",
3273
3271
  "target": "x86_64-ios-simulator"
3274
3272
  }
3275
3273
  ]
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { OcrGgml } = require('../..')
4
4
  const test = require('brittle')
5
- const path = require('bare-path')
6
5
  const { MIN_MAIN_GPU_INDEX, MAX_MAIN_GPU_INDEX } = require('@qvac/ocr-ggml/lib/main-gpu')
7
6
  const {
8
7
  isMobile,
@@ -20,8 +19,9 @@ const {
20
19
  // (covered by the rest of the suite), so this test only exercises the Vulkan
21
20
  // opt-in path.
22
21
  //
23
- // The Vulkan execution path can only be validated where a `libggml-vulkan`
24
- // backend shared library was shipped into prebuilds/. We gate on that file so
22
+ // The Vulkan execution path can only be validated where a `libqvac-ggml-vulkan`
23
+ // backend shared library ships, which on desktop is next to the @qvac/fabric
24
+ // runtime rather than in this package's prebuilds/. We gate on that file so
25
25
  // the test skips cleanly on hosts that never built the Vulkan backend (e.g.
26
26
  // plain desktop CI) instead of failing. On a host that ships the lib but has
27
27
  // no Vulkan-capable GPU, the selection falls back to CPU and we assert the
@@ -29,7 +29,11 @@ const {
29
29
 
30
30
  const TEST_TIMEOUT = 120 * 1000
31
31
 
32
- const vulkanBackendLib = findVulkanBackendLib(PREBUILDS_DIR)
32
+ const vulkanBackendLib = findVulkanBackendLib(
33
+ isMobile
34
+ ? PREBUILDS_DIR
35
+ : (require('@qvac/fabric/backends').resolveBackendsDir() ?? PREBUILDS_DIR)
36
+ )
33
37
 
34
38
  // Skip on mobile (prebuilds layout / device provisioning differ) and on any
35
39
  // host that did not ship a Vulkan backend lib.
@@ -37,7 +41,11 @@ const shouldSkip = isMobile || !vulkanBackendLib
37
41
 
38
42
  function nativeBackendsDir() {
39
43
  if (isMobile) return PREBUILDS_DIR
40
- return path.join(path.dirname(require.resolve('@qvac/fabric/package')), 'prebuilds')
44
+ const dir = require('@qvac/fabric/backends').resolveBackendsDir()
45
+ if (dir === null) {
46
+ throw new Error('@qvac/fabric backends not found; is @qvac/fabric-<host> installed?')
47
+ }
48
+ return dir
41
49
  }
42
50
 
43
51
  for (const key of ['main-gpu', 'main_gpu']) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { OcrGgml, QvacErrorAddonOcrGgml, ERR_CODES, binding } = require('../..')
4
4
  const test = require('brittle')
5
- const { isMobile, ensureModelPath, getImagePath } = require('./utils')
5
+ const { isMobile, ensureModelPath, getImagePath, OCR_TEST_THREADS } = require('./utils')
6
6
 
7
7
  const MOBILE_TIMEOUT = 600 * 1000
8
8
  const DESKTOP_TIMEOUT = 120 * 1000
@@ -26,7 +26,8 @@ async function loadOcr() {
26
26
  params: {
27
27
  pathDetector: detectorPath,
28
28
  pathRecognizer: recognizerPath,
29
- langList: ['en']
29
+ langList: ['en'],
30
+ nThreads: OCR_TEST_THREADS
30
31
  }
31
32
  })
32
33
 
@@ -164,7 +165,8 @@ test(
164
165
  pathRecognizer: recognizerPath,
165
166
  langList: ['en'],
166
167
  defaultRotationAngles: [90, 180, 270],
167
- contrastRetry: true
168
+ contrastRetry: true,
169
+ nThreads: OCR_TEST_THREADS
168
170
  }
169
171
  })
170
172
 
@@ -352,6 +352,7 @@ try {
352
352
  const platform = os.platform()
353
353
  const isMobile = platform === 'ios' || platform === 'android'
354
354
  const isWindows = platform === 'win32'
355
+ const OCR_TEST_THREADS = 4
355
356
 
356
357
  function _envInt(key, fallback) {
357
358
  let raw = ''
@@ -488,7 +489,7 @@ function getBackendDevice() {
488
489
  function createOcrGgml(params = {}, opts) {
489
490
  const { OcrGgml } = require('../..')
490
491
  const instance = new OcrGgml({
491
- params: { backendDevice: getBackendDevice(), ...params },
492
+ params: { backendDevice: getBackendDevice(), nThreads: OCR_TEST_THREADS, ...params },
492
493
  opts
493
494
  })
494
495
  // After load() resolves the backend, record the actual GPU/backend name on
@@ -1063,7 +1064,7 @@ async function runDoctrOCR(t, params, imagePath) {
1063
1064
  params: {
1064
1065
  langList: ['en'],
1065
1066
  pipelineType: 'doctr',
1066
- nThreads: 4,
1067
+ nThreads: OCR_TEST_THREADS,
1067
1068
  backendDevice: getBackendDevice(),
1068
1069
  ...params
1069
1070
  },
@@ -1351,7 +1352,7 @@ async function runDoctrWarmProfile(t, cfg) {
1351
1352
  params: {
1352
1353
  langList: ['en'],
1353
1354
  pipelineType: 'doctr',
1354
- nThreads: 4,
1355
+ nThreads: OCR_TEST_THREADS,
1355
1356
  backendDevice: 'vulkan',
1356
1357
  ...params
1357
1358
  },
@@ -1439,6 +1440,7 @@ module.exports = {
1439
1440
  runDoctrWarmProfile,
1440
1441
  isWindows,
1441
1442
  platform,
1443
+ OCR_TEST_THREADS,
1442
1444
  PERF_RUNS,
1443
1445
  PREBUILDS_DIR,
1444
1446
  findVulkanBackendLib,