@qvac/ocr-ggml 0.25.1 → 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
@@ -149,7 +149,7 @@ bare examples/backend-device.js --backend metal
149
149
  | `params.lowConfidenceThreshold` | `number` | | `0.4` | retry threshold (`easyocr` only) |
150
150
  | `params.recognizerBatchSize` | `number` | | `32` | recognizer batch size (`easyocr` only) |
151
151
  | `params.nThreads` | `number` | | `0` (auto) | CPU thread count for GGML; `<0` leaves the GGML default |
152
- | `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/` |
153
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) |
154
154
  | `params.main-gpu` / `params.main_gpu` | `number` \| `string` | | _prefer dedicated_ | Raw ggml registry index or strict GPU class; requires GPU `backendDevice`. See below. |
155
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) |
@@ -234,7 +234,7 @@ Behaviour and expectations:
234
234
  `backendIsGpu` stat). It never silently does the wrong thing.
235
235
  - **Required backend libs.** Vulkan execution needs the `libggml-vulkan`
236
236
  backend shared library (`libggml-vulkan.so` / `.dll` / `.dylib`) present in
237
- `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
238
238
  Vulkan driver/ICD and a Vulkan-capable GPU on the host. **OpenCL** likewise
239
239
  needs the `libggml-opencl` backend shared library plus a working OpenCL
240
240
  runtime (`libOpenCL.so`); it is built primarily for **Android** (the `opencl`
@@ -667,7 +667,7 @@ omission for v1: `--debug-png` (annotated overlay) — print boxes via
667
667
 
668
668
  | Script | Purpose |
669
669
  |---|---|
670
- | [`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 |
671
671
  | [`scripts/pth_to_gguf.py`](./scripts/pth_to_gguf.py) | Weight converter: EasyOCR PyTorch `.pth` → `.gguf` (optionally `Q8_0` / `Q4_K` quantized) |
672
672
  | [`scripts/convert-model.sh`](./scripts/convert-model.sh) | Wrapper around the converter (`npm run convert-model`) — venv discovery, sanity checks |
673
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.1",
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": {
@@ -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.1"
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.1"
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.1"
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.1"
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.1"
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']) {