@bendyline/gezel-inference 1.0.8 → 1.0.9

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 (59) hide show
  1. package/dist/catalog/chat-models/ge/gemma4-12b-q4/manifest.json +46 -3
  2. package/dist/catalog/chat-models/ge/gemma4-12b-q4/versions/1.2.1/manifest.json +141 -0
  3. package/dist/catalog/chat-models/ge/gemma4-e4b-q4/manifest.json +5 -0
  4. package/dist/catalog/chat-models/index.json +1 -1
  5. package/dist/catalog/chat-models/or/ornith1.5-9b-q4/manifest.json +5 -0
  6. package/dist/catalog/chat-models/or/ornith1.5-9b-q8/manifest.json +5 -0
  7. package/dist/catalog/chat-models/qw/qwen3.5-9b-q4/manifest.json +5 -0
  8. package/dist/catalog/chat-models/qw/qwen3.8-27b-q4/manifest.json +5 -0
  9. package/dist/catalog/chat-models/qw/qwen3.8-flash-next-q2/manifest.json +24 -7
  10. package/dist/catalog/chat-models/qw/qwen3.8-flash-next-q2/versions/1.0.3/manifest.json +47 -0
  11. package/dist/catalog/chat-models/raw-index.json +1 -1
  12. package/dist/catalog/provenance.json +1 -1
  13. package/dist/engines/electron-native-reuse.d.ts +5 -4
  14. package/dist/engines/electron-native-reuse.js +1 -1
  15. package/dist/engines/native-file-manifest.json +12344 -698
  16. package/dist/engines/native-manifest.d.ts +4 -4
  17. package/dist/engines/native-manifest.js +40 -36
  18. package/dist/engines/resolver.d.ts +18 -17
  19. package/dist/engines/resolver.js +36 -1
  20. package/dist/http/model-inventory-cache.d.ts +1 -1
  21. package/dist/http/routes/engines.js +3 -3
  22. package/dist/models/catalog-drift.d.ts +2 -0
  23. package/dist/models/catalog-drift.js +1 -1
  24. package/dist/models/catalog-payload-identity.d.ts +2 -2
  25. package/dist/models/catalog-payload-identity.js +10 -5
  26. package/dist/providers/active-install-message.js +1 -1
  27. package/dist/providers/catalog-model-config.d.ts +28 -0
  28. package/dist/providers/ds4/build-provider.js +35 -13
  29. package/dist/providers/ds4/residency.d.ts +77 -0
  30. package/dist/providers/ds4/residency.js +87 -6
  31. package/dist/providers/image/sd-cpp.js +3 -2
  32. package/dist/providers/llama-cpp/backend-health.d.ts +3 -2
  33. package/dist/providers/llama-cpp/backend-health.js +6 -3
  34. package/dist/providers/llama-cpp/backend-quarantine.js +1 -1
  35. package/dist/providers/llama-cpp/build-provider.js +168 -29
  36. package/dist/providers/llama-cpp/devices.js +4 -1
  37. package/dist/providers/llama-cpp/engine-flags.d.ts +8 -1
  38. package/dist/providers/llama-cpp/engine-flags.js +13 -8
  39. package/dist/providers/llama-cpp/gpu-startup-watch.d.ts +4 -4
  40. package/dist/providers/llama-cpp/gpu-startup-watch.js +7 -5
  41. package/dist/providers/llama-cpp/models.d.ts +10 -0
  42. package/dist/providers/llama-cpp/models.js +64 -10
  43. package/dist/providers/llama-cpp/offload-planner.d.ts +6 -0
  44. package/dist/providers/llama-cpp/offload-planner.js +1 -1
  45. package/dist/providers/llama-cpp/spec-draft.d.ts +45 -23
  46. package/dist/providers/llama-cpp/spec-draft.js +113 -24
  47. package/dist/providers/mlx/python/spec_decode.py +23 -2
  48. package/dist/providers/mlx/python/tool_args_json.py +59 -21
  49. package/dist/providers/mlx/venv.d.ts +19 -1
  50. package/dist/providers/mlx/venv.js +21 -2
  51. package/dist/providers/native/device-capacity.d.ts +4 -0
  52. package/dist/providers/native/device-capacity.js +5 -0
  53. package/dist/providers/native/local-engine-runtime.d.ts +16 -1
  54. package/dist/providers/native/local-engine-runtime.js +142 -39
  55. package/dist/providers/native/supervisor.d.ts +8 -1
  56. package/dist/providers/native/supervisor.js +16 -7
  57. package/dist/providers/types.d.ts +4 -0
  58. package/dist/remotes/types.d.ts +1 -1
  59. package/package.json +4 -4
@@ -40,12 +40,12 @@
40
40
  * but logs that it could not anchor to a baked-in digest.
41
41
  */
42
42
  /** Native release version this build pins. Placeholder until first public release. */
43
- export declare const NATIVE_ENGINE_RELEASE = "0.1.57";
43
+ export declare const NATIVE_ENGINE_RELEASE = "0.1.62";
44
44
  /** sha256 of the pinned release's `SHA256SUMS` asset. All-zeros = unpinned. */
45
- export declare const SHA256SUMS_DIGEST = "977b0ad4cdb8983ed44d7a3223267038b1f35b2806d8a1807d7d15c5c7e94f47";
46
- /** Exact SHA256 values for every engine archive published by native-v0.1.57. */
45
+ export declare const SHA256SUMS_DIGEST = "9c2e726c1cef4152b5ffb8cee8e9970cf24127786d3fafcce3582dcf0df9badb";
46
+ /** Exact SHA256 values for every engine archive published by native-v0.1.62. */
47
47
  export declare const NATIVE_ENGINE_ARCHIVE_SHA256: Readonly<Record<string, string>>;
48
- /** Download size in bytes of every engine archive published by native-v0.1.57. */
48
+ /** Download size in bytes of every engine archive published by native-v0.1.62. */
49
49
  export declare const NATIVE_ENGINE_ARCHIVE_BYTES: Readonly<Record<string, number>>;
50
50
  /**
51
51
  * True only when the pinned release's standalone macOS archives were
@@ -40,47 +40,51 @@
40
40
  * but logs that it could not anchor to a baked-in digest.
41
41
  */
42
42
  /** Native release version this build pins. Placeholder until first public release. */
43
- export const NATIVE_ENGINE_RELEASE = '0.1.57';
43
+ export const NATIVE_ENGINE_RELEASE = '0.1.62';
44
44
  /** sha256 of the pinned release's `SHA256SUMS` asset. All-zeros = unpinned. */
45
- export const SHA256SUMS_DIGEST = '977b0ad4cdb8983ed44d7a3223267038b1f35b2806d8a1807d7d15c5c7e94f47';
45
+ export const SHA256SUMS_DIGEST = '9c2e726c1cef4152b5ffb8cee8e9970cf24127786d3fafcce3582dcf0df9badb';
46
46
  // BEGIN PINNED NATIVE ARCHIVE HASHES
47
- /** Exact SHA256 values for every engine archive published by native-v0.1.57. */
47
+ /** Exact SHA256 values for every engine archive published by native-v0.1.62. */
48
48
  export const NATIVE_ENGINE_ARCHIVE_SHA256 = Object.freeze({
49
- 'gezel-native-0.1.57-darwin-arm64-metal.tar.gz': '6b97be648b032980cf73663db4424140f4cc04577972dc283b956af677ce884a',
50
- 'gezel-native-0.1.57-darwin-arm64.tar.gz': 'e8ca070c4d3961e95946ed32fe7376ddbc97e407dd3f5b78e26cab20c8fb286a',
51
- 'gezel-native-0.1.57-linux-arm64-cpu.tar.gz': 'cc87822123602a0024c63a0d65789232a665140cb2fe37b106d5bd680d12f795',
52
- 'gezel-native-0.1.57-linux-arm64-cuda.tar.gz': '315a441e5698800945f7030e9da961e20f3cf379a4b75c98e1d6ba88c8fee1c5',
53
- 'gezel-native-0.1.57-linux-arm64.tar.gz': '0684b77d23f008f8a2aa0d9d8931437bacd8569fb6c10d3ddab61a5fb25b6a06',
54
- 'gezel-native-0.1.57-linux-x64-cpu.tar.gz': 'b2100842f51c393b92667535798e49932173d52626f5ae72c40005b3408f1201',
55
- 'gezel-native-0.1.57-linux-x64-cuda.tar.gz': '2fca7720614687e8491be17366060c604551257199d395f795b27a87216bcde9',
56
- 'gezel-native-0.1.57-linux-x64-vulkan.tar.gz': 'c1014a1fe1d241ad1ee9abfa5badafcce208cbff8d38b4b4deb4779f31a95bef',
57
- 'gezel-native-0.1.57-linux-x64.tar.gz': 'a7f3dbe1941c3d03ed121c32be1bdd199aea47080b03d89989590a0f5d13c5ff',
58
- 'gezel-native-0.1.57-win32-arm64-cpu.zip': '12d66681cec1872f23b015c7d0bd45a25ed262342da6b0ecea8bea303adf2105',
59
- 'gezel-native-0.1.57-win32-arm64-cuda.zip': 'b437a9f0894673179f91a1af75d5d7d058dafa1b4f48a57eab123596f1c21ca4',
60
- 'gezel-native-0.1.57-win32-arm64.zip': '21cddb8b733f9a2669d64b1ada18002f60bb2779f2d5e2aacfb88214db00da7e',
61
- 'gezel-native-0.1.57-win32-x64-cpu.zip': 'a81f7c6754f1954a0ed5de1efc47d6194dc60d912138a9d2b8e4f992168b12c6',
62
- 'gezel-native-0.1.57-win32-x64-cuda.zip': '9e3f21713fe77c23536dd4124244ac3834f90c82d6e470cb6836a73bb4f7c592',
63
- 'gezel-native-0.1.57-win32-x64-vulkan.zip': '0d473300317b4f6cb072bdd7875ce1536a85fe50e781c435e1aef1c4cbdb08e5',
64
- 'gezel-native-0.1.57-win32-x64.zip': '89a073abcfc97e50b9a0c39f56862b51ac21139b7ca4a3e7a75105dca56292a2',
49
+ 'gezel-native-0.1.62-darwin-arm64-metal.tar.gz': '0ac76122fdc7bd33e6774fb5f62dea6ff22220c483c78026667e33ff8014936d',
50
+ 'gezel-native-0.1.62-darwin-arm64.tar.gz': '23607c3e5c3e6ac050f0594bcf4ca997cff91c57a4b65af985511e1d87843764',
51
+ 'gezel-native-0.1.62-linux-arm64-cpu.tar.gz': 'e9a69cdb07f5ad0103da8d99715e4a1bd257219c55457716ff93e34ee4a544ab',
52
+ 'gezel-native-0.1.62-linux-arm64-cuda.tar.gz': '6389e97d85a2d4b53b4a1c6735669e5cd6022f393fdccabe4c18c9957f9073fc',
53
+ 'gezel-native-0.1.62-linux-arm64.tar.gz': 'cd263de007b14be6de5b828985d3066490a339f097ef199086cd51692495b9f0',
54
+ 'gezel-native-0.1.62-linux-x64-cpu.tar.gz': '2f1b382a02082767187340f8ecd037dfa72953004977e5716ca95127429e4f0b',
55
+ 'gezel-native-0.1.62-linux-x64-cuda.tar.gz': '8f28018fdfd5a38e48dc886a92eab392f92193d3a58c322c85dcd16c6f80fd13',
56
+ 'gezel-native-0.1.62-linux-x64-rocm.tar.gz': '37b88b81bdcbe06fa3c5ebe3b07f0d0112b555449cc2d29775d5ad8373150f18',
57
+ 'gezel-native-0.1.62-linux-x64-vulkan.tar.gz': 'c39fff97dceeacb377a1948ebe9f9553a98485cf21cda9492fbfbe18dd279193',
58
+ 'gezel-native-0.1.62-linux-x64.tar.gz': '2d0c927f98379c0cd1dd05d68f2cc4b87178ede077a65e948bf36e124d560993',
59
+ 'gezel-native-0.1.62-win32-arm64-cpu.zip': 'f867d50591df2da2c2d78cbf844a5b87679da9f5abc9a29ed6b06905c5423bed',
60
+ 'gezel-native-0.1.62-win32-arm64-cuda.zip': 'd42c79845f64ee1fb5e97ea45dde44c6b4705ad6bc4cd6ce9360caffc27cf67f',
61
+ 'gezel-native-0.1.62-win32-arm64.zip': 'd4e660181cd387051d4f8728d6ae008004969786f68da15c76e44704db247af0',
62
+ 'gezel-native-0.1.62-win32-x64-cpu.zip': '85a350c01454e77d49e2a7e463089025d71b6f7cd25daa30b5b0fe8498cf3f2a',
63
+ 'gezel-native-0.1.62-win32-x64-cuda.zip': 'edddeeb820f4612453c036bccd4882c507811464520eb772dfa5bf7b4aa2658f',
64
+ 'gezel-native-0.1.62-win32-x64-rocm.zip': '8640280b49fbc4bc6152b734fcb2dd02b0b9c3375797685470da52406a46aa34',
65
+ 'gezel-native-0.1.62-win32-x64-vulkan.zip': 'a57071775f8876800d6e5689d38a3f7362c4b585e8137596ee19e45ee38b6991',
66
+ 'gezel-native-0.1.62-win32-x64.zip': '357e6c79a4d02968a2ba67299e78efed8dc2ab2d4b40a1e8f866073506b6b2b6',
65
67
  });
66
- /** Download size in bytes of every engine archive published by native-v0.1.57. */
68
+ /** Download size in bytes of every engine archive published by native-v0.1.62. */
67
69
  export const NATIVE_ENGINE_ARCHIVE_BYTES = Object.freeze({
68
- 'gezel-native-0.1.57-darwin-arm64-metal.tar.gz': 7387963,
69
- 'gezel-native-0.1.57-darwin-arm64.tar.gz': 42223786,
70
- 'gezel-native-0.1.57-linux-arm64-cpu.tar.gz': 9223249,
71
- 'gezel-native-0.1.57-linux-arm64-cuda.tar.gz': 621626283,
72
- 'gezel-native-0.1.57-linux-arm64.tar.gz': 42040003,
73
- 'gezel-native-0.1.57-linux-x64-cpu.tar.gz': 13398337,
74
- 'gezel-native-0.1.57-linux-x64-cuda.tar.gz': 1000325316,
75
- 'gezel-native-0.1.57-linux-x64-vulkan.tar.gz': 26290440,
76
- 'gezel-native-0.1.57-linux-x64.tar.gz': 57190295,
77
- 'gezel-native-0.1.57-win32-arm64-cpu.zip': 6049234,
78
- 'gezel-native-0.1.57-win32-arm64-cuda.zip': 243595554,
79
- 'gezel-native-0.1.57-win32-arm64.zip': 42236484,
80
- 'gezel-native-0.1.57-win32-x64-cpu.zip': 9131460,
81
- 'gezel-native-0.1.57-win32-x64-cuda.zip': 756439338,
82
- 'gezel-native-0.1.57-win32-x64-vulkan.zip': 21673895,
83
- 'gezel-native-0.1.57-win32-x64.zip': 55668381,
70
+ 'gezel-native-0.1.62-darwin-arm64-metal.tar.gz': 7577614,
71
+ 'gezel-native-0.1.62-darwin-arm64.tar.gz': 42917428,
72
+ 'gezel-native-0.1.62-linux-arm64-cpu.tar.gz': 9518882,
73
+ 'gezel-native-0.1.62-linux-arm64-cuda.tar.gz': 624413294,
74
+ 'gezel-native-0.1.62-linux-arm64.tar.gz': 43076642,
75
+ 'gezel-native-0.1.62-linux-x64-cpu.tar.gz': 13762290,
76
+ 'gezel-native-0.1.62-linux-x64-cuda.tar.gz': 1016218245,
77
+ 'gezel-native-0.1.62-linux-x64-rocm.tar.gz': 313875007,
78
+ 'gezel-native-0.1.62-linux-x64-vulkan.tar.gz': 26687129,
79
+ 'gezel-native-0.1.62-linux-x64.tar.gz': 58389984,
80
+ 'gezel-native-0.1.62-win32-arm64-cpu.zip': 6219263,
81
+ 'gezel-native-0.1.62-win32-arm64-cuda.zip': 245764366,
82
+ 'gezel-native-0.1.62-win32-arm64.zip': 42822090,
83
+ 'gezel-native-0.1.62-win32-x64-cpu.zip': 9357113,
84
+ 'gezel-native-0.1.62-win32-x64-cuda.zip': 761458453,
85
+ 'gezel-native-0.1.62-win32-x64-rocm.zip': 241630451,
86
+ 'gezel-native-0.1.62-win32-x64-vulkan.zip': 21898409,
87
+ 'gezel-native-0.1.62-win32-x64.zip': 56194044,
84
88
  });
85
89
  // END PINNED NATIVE ARCHIVE HASHES
86
90
  /**
@@ -24,7 +24,7 @@ export type EngineResolveEvent = NativeEngineResolveEvent | EngineExtractingEven
24
24
  export interface ResolveEngineOptions {
25
25
  engine: NativeBinaryName;
26
26
  home: string;
27
- /** GPU backend for llama-server (`cuda`/`vulkan`/`metal`/`cpu`); omit for single-variant engines. */
27
+ /** GPU backend for llama-server (`cuda`/`rocm`/`vulkan`/`metal`/`cpu`); omit for single-variant engines. */
28
28
  variant?: string;
29
29
  /** Native release version; defaults to the source pin / `GEZEL_NATIVE_ENGINE_VERSION`. */
30
30
  version?: string;
@@ -100,22 +100,23 @@ export declare function resolveEngine(opts: ResolveEngineOptions): AsyncGenerato
100
100
  */
101
101
  export declare function resolveEngineToCompletion(opts: ResolveEngineOptions, onEvent?: (e: EngineResolveEvent) => void): Promise<EngineResolveResult>;
102
102
  /**
103
- * The archive variant an engine lives in when the caller names none.
103
+ * The llama-server build an on-demand install fetches when the probe found
104
+ * `detected`: the first build down discovery's own fallback chain (ROCm and
105
+ * CUDA → Vulkan → CPU) that the pinned release publishes for this platform.
106
+ * Without this, a probe answer the release has no archive for — ROCm before
107
+ * the release that first carries it, Vulkan on Linux ARM — failed the install
108
+ * outright instead of taking the next build down.
104
109
  *
105
- * These are facts about *where the archive is*, not backend choices:
106
- *
107
- * - **ds4-server on Linux** ships only in the `-cuda` archive, sharing one
108
- * copy of the NVIDIA redistributables with llama-server's CUDA build
109
- * instead of shipping them twice (see scripts/native-payload.mjs).
110
- * - **llama-server on macOS** ships only in the `-metal` archive; there is no
111
- * `darwin-arm64-cpu` asset, and the bare `darwin-arm64` key holds
112
- * ds4/sd/whisper/uv but no llama-server. Omitting the variant there fails
113
- * the extract with "none of [gezel-llama-server, llama-server] were inside
114
- * it" — a loud error, but an avoidable one.
115
- *
116
- * Deriving both here is the point: every call site would otherwise have to
117
- * remember, and one eventually won't. Where the variant is a genuine choice
118
- * (llama-server on Linux/Windows: cuda / vulkan / cpu) this returns undefined
119
- * and the decision stays with the caller.
110
+ * ROCm also has to be worth its several hundred megabytes before anything
111
+ * downloads: `rocmWorthDownloading` (rocm-devices.ts), which says no on
112
+ * Windows. An explicit `llamaCppBackendOverride` never comes through here.
120
113
  */
114
+ export declare function llamaDownloadVariant(detected: string | undefined, opts?: {
115
+ platform?: NodeJS.Platform;
116
+ platformKey?: string | null;
117
+ version?: string;
118
+ /** Archive names the release publishes; null when unpinned (every build counts). */
119
+ archives?: Readonly<Record<string, string>> | null;
120
+ rocmWorth?: () => boolean;
121
+ }): string | undefined;
121
122
  export declare function impliedEngineVariant(engine: NativeBinaryName, platform: NodeJS.Platform): string | undefined;
@@ -3,7 +3,7 @@ import { createReadStream, existsSync, readdirSync } from 'node:fs';
3
3
  import { chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { HttpStatusError, createLogger, nowIso, resolveDistributionProfile, retryTransient, } from '@bendyline/gezel';
6
- import { DUCKDB_APPLE_TEAM_ID, DUCKDB_ARCHIVE_SHA256, DUCKDB_ASSET, DUCKDB_BINARY_SHA256, DUCKDB_VERSION, DUCKDB_WINDOWS_PUBLISHER, MXC_EXECUTOR_NAME, MXC_EXECUTOR_SHA256, MXC_LICENSE_SHA256, MXC_TARBALL_INTEGRITY, MXC_TARBALL_LICENSE_ENTRY, MXC_TARBALL_SHA256, MXC_TARBALL_URL, MXC_VERIFIED_SENTINEL, MXC_VERSION, MXC_WINDOWS_PUBLISHER, duckdbAssetUrl, duckdbBinaryName, duckdbInstallDir, duckdbPlatformKey, mxcArch, mxcInstallDir, mxcTarballEntry, resolvePlatformKey, } from '@bendyline/gezel/native';
6
+ import { DUCKDB_APPLE_TEAM_ID, DUCKDB_ARCHIVE_SHA256, DUCKDB_ASSET, DUCKDB_BINARY_SHA256, DUCKDB_VERSION, DUCKDB_WINDOWS_PUBLISHER, MXC_EXECUTOR_NAME, MXC_EXECUTOR_SHA256, MXC_LICENSE_SHA256, MXC_TARBALL_INTEGRITY, MXC_TARBALL_LICENSE_ENTRY, MXC_TARBALL_SHA256, MXC_TARBALL_URL, MXC_VERIFIED_SENTINEL, MXC_VERSION, MXC_WINDOWS_PUBLISHER, duckdbAssetUrl, duckdbBinaryName, duckdbInstallDir, duckdbPlatformKey, mxcArch, mxcInstallDir, mxcTarballEntry, resolveAvailableLlamaBinary, resolvePlatformKey, rocmWorthDownloading, } from '@bendyline/gezel/native';
7
7
  import * as tar from 'tar';
8
8
  import { downloadWithRetry, friendlyErrors, } from '../utils/download-with-retry.js';
9
9
  import { extractZip } from '../utils/extract-zip.js';
@@ -744,6 +744,41 @@ function stripVer(v) {
744
744
  * (llama-server on Linux/Windows: cuda / vulkan / cpu) this returns undefined
745
745
  * and the decision stays with the caller.
746
746
  */
747
+ const LLAMA_DOWNLOAD_BACKENDS = ['cuda', 'rocm', 'vulkan', 'metal', 'cpu'];
748
+ /**
749
+ * The llama-server build an on-demand install fetches when the probe found
750
+ * `detected`: the first build down discovery's own fallback chain (ROCm and
751
+ * CUDA → Vulkan → CPU) that the pinned release publishes for this platform.
752
+ * Without this, a probe answer the release has no archive for — ROCm before
753
+ * the release that first carries it, Vulkan on Linux ARM — failed the install
754
+ * outright instead of taking the next build down.
755
+ *
756
+ * ROCm also has to be worth its several hundred megabytes before anything
757
+ * downloads: `rocmWorthDownloading` (rocm-devices.ts), which says no on
758
+ * Windows. An explicit `llamaCppBackendOverride` never comes through here.
759
+ */
760
+ export function llamaDownloadVariant(detected, opts = {}) {
761
+ const platform = opts.platform ?? process.platform;
762
+ if (platform === 'darwin')
763
+ return impliedEngineVariant('llama-server', platform);
764
+ const preferred = LLAMA_DOWNLOAD_BACKENDS.find((backend) => backend === detected);
765
+ if (!preferred)
766
+ return detected;
767
+ const platformKey = opts.platformKey === undefined ? resolvePlatformKey() : opts.platformKey;
768
+ const version = stripVer(opts.version ?? effectiveEngineRelease());
769
+ const ext = platform === 'win32' ? 'zip' : 'tar.gz';
770
+ const archives = opts.archives === undefined
771
+ ? isPlaceholderDigest(SHA256SUMS_DIGEST) || process.env.GEZEL_NATIVE_ENGINE_VERSION
772
+ ? null
773
+ : NATIVE_ENGINE_ARCHIVE_SHA256
774
+ : opts.archives;
775
+ const rocmWorth = opts.rocmWorth ?? (() => rocmWorthDownloading({ platform }));
776
+ const resolved = resolveAvailableLlamaBinary(preferred, (backend) => {
777
+ const name = `gezel-native-${version}-${platformKey}-${backend}.${ext}`;
778
+ return !archives || archives[name] ? name : null;
779
+ }, true, (backend) => backend !== 'rocm' || rocmWorth());
780
+ return resolved?.backend ?? preferred;
781
+ }
747
782
  export function impliedEngineVariant(engine, platform) {
748
783
  if (engine === 'ds4-server' && platform === 'linux')
749
784
  return 'cuda';
@@ -4,5 +4,5 @@ export interface CacheEntry {
4
4
  at: number;
5
5
  models: ModelInfo[];
6
6
  }
7
- export declare const cache: Map<"copilot" | "openai" | "anthropic" | "anthropic-cli" | "codex-cli" | "ollama" | "llama-cpp" | "mlx" | "ds4" | "apple-foundation-models" | "android-mlkit" | "remote", CacheEntry>;
7
+ export declare const cache: Map<"copilot" | "openai" | "anthropic" | "anthropic-cli" | "codex-cli" | "ollama" | "llama-cpp" | "mlx" | "ds4" | "remote" | "apple-foundation-models" | "android-mlkit", CacheEntry>;
8
8
  export declare function invalidateModelsCache(provider?: ProviderName): void;
@@ -40,7 +40,7 @@ export function enginesRoutes(ctx) {
40
40
  */
41
41
  app.get('/binaries/status', (c) => {
42
42
  const backendRaw = process.env.GEZEL_LLAMA_SERVER_BACKEND ?? process.env.GEZEL_LLAMA_DETECTED_BACKEND;
43
- const llamaBackend = ['cuda', 'vulkan', 'metal', 'cpu'].find((value) => value === backendRaw);
43
+ const llamaBackend = ['cuda', 'rocm', 'vulkan', 'metal', 'cpu'].find((value) => value === backendRaw);
44
44
  const platformKey = resolvePlatformKey();
45
45
  return c.json({
46
46
  release: effectiveEngineRelease(),
@@ -64,7 +64,7 @@ export function enginesRoutes(ctx) {
64
64
  });
65
65
  /**
66
66
  * Start (or attach to) a download+verify of `engine` (optionally a GPU
67
- * `?variant=cuda|vulkan|metal|cpu`) and stream progress as SSE. The
67
+ * `?variant=cuda|rocm|vulkan|metal|cpu`) and stream progress as SSE. The
68
68
  * manual trigger behind Settings → On-device → "Download engine". The
69
69
  * lazy on-device-chat path uses the same registry. Idempotent — a
70
70
  * second call while one is in flight attaches to the running job.
@@ -231,7 +231,7 @@ export function enginesRoutes(ctx) {
231
231
  app.delete('/llama-cpp/quarantine/:backend', async (c) => {
232
232
  const backend = c.req.param('backend');
233
233
  if (!isLlamaBackend(backend)) {
234
- return c.json({ error: 'backend must be "cuda", "vulkan", "metal", or "cpu"' }, 400);
234
+ return c.json({ error: 'backend must be "cuda", "rocm", "vulkan", "metal", or "cpu"' }, 400);
235
235
  }
236
236
  const override = (await ctx.store.readConfig()).llamaCppBackendOverride;
237
237
  const { restored } = liftLlamaQuarantine(ctx.home, backend, {
@@ -36,6 +36,8 @@ export interface EvaluateCatalogDriftOptions {
36
36
  installedVersion?: string;
37
37
  installed: InstalledPayloadRecord;
38
38
  manifest: ChatModelManifest;
39
+ /** The backend a llama.cpp copy runs on. Defaults to the process's own. */
40
+ llamaCppBackend?: string | undefined;
39
41
  /**
40
42
  * False for models resolved out of a read-only overlay (the machine asset
41
43
  * store) and while an install is in flight for this id — the verdict still
@@ -30,7 +30,7 @@ export async function evaluateCatalogDrift(opts) {
30
30
  if (!opts.installedVersion || !currentVersion || opts.installedVersion === currentVersion) {
31
31
  return { updateAvailable: false };
32
32
  }
33
- const catalog = describeCatalogPayload(opts.manifest, opts.engine);
33
+ const catalog = describeCatalogPayload(opts.manifest, opts.engine, opts.llamaCppBackend);
34
34
  if (!catalog) {
35
35
  // The catalog no longer describes this engine's payload at all (a source
36
36
  // block was pulled). Nothing to download, so nothing to offer.
@@ -30,7 +30,7 @@
30
30
  * over what the install already recorded, and its answers steer UI, never
31
31
  * trust.
32
32
  */
33
- import type { ChatModelManifest, GezmodelEngine } from '@bendyline/gezel';
33
+ import { type ChatModelManifest, type GezmodelEngine } from '@bendyline/gezel';
34
34
  /** One file the catalog expects to land in the model directory. */
35
35
  export interface CatalogPayloadFile {
36
36
  /** Name as it lands on disk ('/'-joined for nested files, as in `fileSha256`). */
@@ -103,5 +103,5 @@ export declare function catalogPayloadFingerprint(payload: CatalogPayload): stri
103
103
  * The single place that knows each source block's shape, so callers compare
104
104
  * like for like without re-deriving filenames.
105
105
  */
106
- export declare function describeCatalogPayload(manifest: ChatModelManifest, engine: GezmodelEngine): CatalogPayload | null;
106
+ export declare function describeCatalogPayload(manifest: ChatModelManifest, engine: GezmodelEngine, llamaCppBackend?: string | undefined): CatalogPayload | null;
107
107
  export declare function comparePayloadIdentity(opts: ComparePayloadOptions): Promise<PayloadIdentityResult>;
@@ -33,6 +33,8 @@
33
33
  import { createHash } from 'node:crypto';
34
34
  import { stat } from 'node:fs/promises';
35
35
  import { basename, join } from 'node:path';
36
+ import { llamaCppDraftModelApplies, } from '@bendyline/gezel';
37
+ import { currentLlamaCppBackend } from '../providers/llama-cpp/backend-health.js';
36
38
  /**
37
39
  * Stable hash of a catalog payload description. Deliberately excludes
38
40
  * `revision`: it is a fetch coordinate, and identical hashes mean identical
@@ -53,7 +55,7 @@ export function catalogPayloadFingerprint(payload) {
53
55
  * The single place that knows each source block's shape, so callers compare
54
56
  * like for like without re-deriving filenames.
55
57
  */
56
- export function describeCatalogPayload(manifest, engine) {
58
+ export function describeCatalogPayload(manifest, engine, llamaCppBackend = currentLlamaCppBackend()) {
57
59
  if (engine === 'mlx') {
58
60
  const src = manifest.mlx;
59
61
  if (!src)
@@ -96,10 +98,13 @@ export function describeCatalogPayload(manifest, engine) {
96
98
  sizeBytes: src.approxSizeBytes,
97
99
  });
98
100
  }
99
- // Draft companions exist on both engine sources and are fetched
100
- // unconditionally by `buildDownloadPlan`, so read them off the already
101
- // engine-selected `src` rather than re-narrowing to one engine.
102
- if (src.draftModel) {
101
+ // Draft companions exist on both engine sources, so read them off the
102
+ // already engine-selected `src`. A llama.cpp drafter limited to some backends
103
+ // is no part of this install elsewhere: counting it would offer every other
104
+ // machine an update for a file it never downloads.
105
+ const draftApplies = engine !== 'llama-cpp' ||
106
+ llamaCppDraftModelApplies(manifest.llamaCpp?.draftModel, llamaCppBackend);
107
+ if (src.draftModel && draftApplies) {
103
108
  files.push({
104
109
  name: basename(src.draftModel.filename),
105
110
  sha256: src.draftModel.sha256,
@@ -37,7 +37,7 @@ export function notReadyError(message, code) {
37
37
  */
38
38
  export function noModelYetMessage(engineLabel, active) {
39
39
  if (!active) {
40
- return `${engineLabel}: no model is installed yet. Download one from Settings → Artificial Intelligence, then try again.`;
40
+ return `${engineLabel}: no model is installed yet. Download one from Settings → Artificial intelligence, then try again.`;
41
41
  }
42
42
  if (active.phase !== 'downloading') {
43
43
  const verb = active.phase === 'verifying' ? 'being verified' : 'being prepared';
@@ -59,5 +59,33 @@ export declare function resolveCatalogLlamaCppEngineConfig(catalog: CatalogServi
59
59
  mtp?: boolean | undefined;
60
60
  draftModelId?: string | undefined;
61
61
  nMax?: number | undefined;
62
+ sampling?: "greedy" | "probabilistic" | undefined;
63
+ byBackend?: {
64
+ cuda?: {
65
+ type?: "none" | "draft-mtp" | "draft-eagle3" | "draft-dflash" | "draft-simple" | "ngram-mod" | "ngram-simple" | "ngram-map-k" | "ngram-map-k4v" | "ngram-cache" | undefined;
66
+ nMax?: number | undefined;
67
+ sampling?: "greedy" | "probabilistic" | undefined;
68
+ } | undefined;
69
+ rocm?: {
70
+ type?: "none" | "draft-mtp" | "draft-eagle3" | "draft-dflash" | "draft-simple" | "ngram-mod" | "ngram-simple" | "ngram-map-k" | "ngram-map-k4v" | "ngram-cache" | undefined;
71
+ nMax?: number | undefined;
72
+ sampling?: "greedy" | "probabilistic" | undefined;
73
+ } | undefined;
74
+ vulkan?: {
75
+ type?: "none" | "draft-mtp" | "draft-eagle3" | "draft-dflash" | "draft-simple" | "ngram-mod" | "ngram-simple" | "ngram-map-k" | "ngram-map-k4v" | "ngram-cache" | undefined;
76
+ nMax?: number | undefined;
77
+ sampling?: "greedy" | "probabilistic" | undefined;
78
+ } | undefined;
79
+ metal?: {
80
+ type?: "none" | "draft-mtp" | "draft-eagle3" | "draft-dflash" | "draft-simple" | "ngram-mod" | "ngram-simple" | "ngram-map-k" | "ngram-map-k4v" | "ngram-cache" | undefined;
81
+ nMax?: number | undefined;
82
+ sampling?: "greedy" | "probabilistic" | undefined;
83
+ } | undefined;
84
+ cpu?: {
85
+ type?: "none" | "draft-mtp" | "draft-eagle3" | "draft-dflash" | "draft-simple" | "ngram-mod" | "ngram-simple" | "ngram-map-k" | "ngram-map-k4v" | "ngram-cache" | undefined;
86
+ nMax?: number | undefined;
87
+ sampling?: "greedy" | "probabilistic" | undefined;
88
+ } | undefined;
89
+ } | undefined;
62
90
  } | undefined;
63
91
  } | undefined>;
@@ -1,5 +1,5 @@
1
1
  import { basename, dirname, join } from 'node:path';
2
- import { DEFAULT_LOCAL_ENGINE_IDLE_TIMEOUT_MS, createLogger, } from '@bendyline/gezel';
2
+ import { DEFAULT_LOCAL_ENGINE_IDLE_TIMEOUT_MS, createLogger, ds4SsdStreamingSupported, } from '@bendyline/gezel';
3
3
  import { gezelPaths } from '@bendyline/gezel/paths';
4
4
  import { LlamaCppProvider } from '../llama-cpp/index.js';
5
5
  import { minViableLocalContextTokens, pressureIdleGraceMs } from '../native/capacity-broker.js';
@@ -282,7 +282,7 @@ export async function buildDs4Provider(opts) {
282
282
  // build). `shouldUseDs4SsdStreaming` still refuses a model that cannot fit,
283
283
  // which is what keeps the flip safe: overriding that on a 90.89 GiB build
284
284
  // OOM-killed the engine mid-load on a 121.63 GiB host.
285
- const { ds4VisionResidentBytes, planDs4ExpertCache, shouldUseDs4SsdStreaming } = await import('./residency.js');
285
+ const { ds4VisionResidentBytes, planDs4ExpertCache, planDs4MeasuredLaunch, shouldUseDs4SsdStreaming, } = await import('./residency.js');
286
286
  let visionEncoderSizeBytes = installedModel?.visionEncoderSizeBytes;
287
287
  if (vision.enabled && vision.visionEncoderPath && !visionEncoderSizeBytes) {
288
288
  const { stat: statVisionEncoder } = await import('node:fs/promises');
@@ -291,11 +291,10 @@ export async function buildDs4Provider(opts) {
291
291
  .catch(() => undefined);
292
292
  }
293
293
  const visionResidentBytes = ds4VisionResidentBytes(vision.enabled ? visionEncoderSizeBytes : undefined);
294
+ const streamingSupported = ds4SsdStreamingSupported(ds4Source, process.platform);
294
295
  const ssdStreaming = shouldUseDs4SsdStreaming({
295
296
  configured: config.ds4SsdStreaming,
296
- ...(ds4Source?.ssdStreamingSupported !== undefined
297
- ? { ssdStreamingSupported: ds4Source.ssdStreamingSupported }
298
- : {}),
297
+ ...(streamingSupported !== undefined ? { ssdStreamingSupported: streamingSupported } : {}),
299
298
  modelSizeBytes: residentWeightBytes,
300
299
  totalRamBytes,
301
300
  ...(visionResidentBytes > 0 ? { companionBytes: visionResidentBytes } : {}),
@@ -304,15 +303,30 @@ export async function buildDs4Provider(opts) {
304
303
  log.warn(`[ds4] ignored unsafe full-residency override for ${effectiveModelId ?? modelPath}; ` +
305
304
  `model=${modelSizeBytes ?? 'unknown'} bytes, system=${Math.round(totalRamGb)} GiB`);
306
305
  }
307
- if (config.ds4SsdStreaming === true && ds4Source?.ssdStreamingSupported === false) {
308
- log.warn(`[ds4] ignored SSD-streaming override for ${effectiveModelId ?? modelPath}; this model keeps its lookup tables on disk but does not implement routed-expert streaming`);
306
+ if (config.ds4SsdStreaming === true && streamingSupported === false) {
307
+ log.warn(`[ds4] ignored SSD-streaming override for ${effectiveModelId ?? modelPath}; ds4 cannot stream this model's routed experts on this backend`);
309
308
  }
310
- const cachePlanWith = (extraResidentBytes) => planDs4ExpertCache({
311
- configuredGb: config.ds4CacheExpertsGb,
312
- catalogCacheBytes: ds4Source?.cacheExpertsBytes,
313
- catalogResidentBytes: (ds4Source?.residentBytes ?? 0) + visionResidentBytes + extraResidentBytes,
309
+ // A measured footprint prices the launch for its mode; a measured streamed
310
+ // footprint also sizes the expert cache against the local-model budget.
311
+ const modelBudgetBytes = ssdStreaming && ds4Source?.streamingMemory
312
+ ? await (await import('../native/device-capacity.js')).deviceModelBudgetBytes(config)
313
+ : undefined;
314
+ const measuredLaunchWith = (extraResidentBytes) => planDs4MeasuredLaunch({
315
+ source: ds4Source,
316
+ numCtx,
317
+ ssdStreaming,
314
318
  totalRamBytes: totalRamGb * 1024 ** 3,
319
+ budgetBytes: modelBudgetBytes,
320
+ sidecarBytes: visionResidentBytes + extraResidentBytes,
321
+ configuredCacheGb: config.ds4CacheExpertsGb,
315
322
  });
323
+ const cachePlanWith = (extraResidentBytes) => measuredLaunchWith(extraResidentBytes)?.cache ??
324
+ planDs4ExpertCache({
325
+ configuredGb: config.ds4CacheExpertsGb,
326
+ catalogCacheBytes: ds4Source?.cacheExpertsBytes,
327
+ catalogResidentBytes: (ds4Source?.residentBytes ?? 0) + visionResidentBytes + extraResidentBytes,
328
+ totalRamBytes: totalRamGb * 1024 ** 3,
329
+ });
316
330
  // DSpark speculative decoding. Resolved here, beside the residency decision
317
331
  // it depends on: ds4 aborts at startup if an external `--mtp-model` is
318
332
  // combined with `--ssd-streaming` for any family but DeepSeek V4.1.
@@ -386,7 +400,9 @@ export async function buildDs4Provider(opts) {
386
400
  throw err;
387
401
  }
388
402
  if (cachePlan.clamped) {
389
- log.warn(`[ds4] clamped expert cache ${cachePlan.requestedGb} → ${cachePlan.cacheGb} GiB to preserve system headroom`);
403
+ log.warn(`[ds4] clamped expert cache ${cachePlan.requestedGb} → ${cachePlan.cacheGb} GiB to ${cachePlan.limit === 'model-budget'
404
+ ? "fit this device's local-model budget"
405
+ : 'preserve system headroom'}`);
390
406
  }
391
407
  const cacheExpertsGb = cachePlan.cacheGb;
392
408
  const kvDir = join(home, 'engines', 'ds4', 'kv');
@@ -433,6 +449,11 @@ export async function buildDs4Provider(opts) {
433
449
  ? 'interactive'
434
450
  : 'background',
435
451
  requirement: async () => {
452
+ // A measured launch is reserved at exactly its working set: no
453
+ // full-residency floor, and the expert cache this launch was given.
454
+ const measured = measuredLaunchWith(dspark.enabled ? dsparkCompanionBytes : 0);
455
+ if (measured)
456
+ return { bytes: measured.workingSetBytes, exclusive: !ssdStreaming };
436
457
  const { ds4AdmissionGpuBytes, ds4BaseResidentBytes, ds4ResidentLine, ds4ProjectedResidentBytes, ds4ResidentBytesForMode, } = await import('./residency.js');
437
458
  const line = ds4ResidentLine(ds4Source ?? {});
438
459
  const projected = line ? ds4ProjectedResidentBytes(line, numCtx) : ds4Source?.residentBytes;
@@ -444,7 +465,7 @@ export async function buildDs4Provider(opts) {
444
465
  modelSizeBytes: residentWeightBytes,
445
466
  });
446
467
  const workingSetBytes = base + visionResidentBytes + (dspark.enabled ? dsparkCompanionBytes : 0);
447
- const bytes = ds4ResidentBytesForMode(workingSetBytes, ssdStreaming, ds4Source?.ssdStreamingSupported);
468
+ const bytes = ds4ResidentBytesForMode(workingSetBytes, ssdStreaming, streamingSupported);
448
469
  const gpuBytes = ds4AdmissionGpuBytes({
449
470
  workingSetBytes,
450
471
  reservationBytes: bytes,
@@ -528,6 +549,7 @@ export async function buildDs4Provider(opts) {
528
549
  `backend=${backendFlag.slice(2)} ctx=${numCtx} ` +
529
550
  `prefillChunk=${ds4Source?.prefillChunk ?? 'engine-default'} ` +
530
551
  `ssdStreaming=${ssdStreaming} cacheExpertsGiB=${ssdStreaming ? cacheExpertsGb : 0} ` +
552
+ `memory=${measuredLaunchWith(0) ? 'measured' : 'catalog'} ` +
531
553
  `vision=${vision.enabled} mtp=${mtp.enabled} exactSampling=${mtp.exactSampling} (${mtp.reason}) ` +
532
554
  `dspark=${dspark.enabled} (${dspark.reason})`);
533
555
  return { command: binary, args, baseUrl: `http://127.0.0.1:${port}`, cwd: ds4BundleDir };
@@ -4,6 +4,10 @@ export { DS4_FULL_RESIDENCY_HEADROOM_BYTES };
4
4
  * Conservative floor for the full-residency working set. Exclusivity is an
5
5
  * explicit device lease at process startup, not a consequence of this number:
6
6
  * the automatic model budget can exceed 96 GiB on larger machines.
7
+ *
8
+ * Only for a launch nobody measured: an entry with `fullResidencyMemory` is
9
+ * reserved at that working set ({@link planDs4MeasuredLaunch}). Flooring a
10
+ * 45 GiB Qwen3.8 launch here put it over a 96 GiB Mac's 80 GiB budget.
7
11
  */
8
12
  export declare const DS4_FULL_RESIDENCY_RESERVATION_BYTES: number;
9
13
  /**
@@ -73,12 +77,27 @@ export interface Ds4ExpertCachePlanOptions {
73
77
  catalogCacheBytes?: number;
74
78
  catalogResidentBytes?: number;
75
79
  totalRamBytes?: number;
80
+ /**
81
+ * A measured streamed footprint at the launch window, sidecars included
82
+ * ({@link ds4StreamingFixedBytes}). With {@link budgetBytes} it replaces the
83
+ * catalog subtraction and the generic 24 GiB headroom.
84
+ */
85
+ measuredFixedBytes?: number;
86
+ /** This device's local-model budget, the ceiling the capacity broker admits under. */
87
+ budgetBytes?: number;
88
+ /** Smallest cache the entry says is worth streaming with. */
89
+ minCacheBytes?: number;
76
90
  }
77
91
  export interface Ds4ExpertCachePlan {
78
92
  cacheGb: number;
79
93
  requestedGb: number;
80
94
  clamped: boolean;
81
95
  safe: boolean;
96
+ /**
97
+ * What bounded the cache: the device's local-model budget beside a measured
98
+ * footprint, or the generic {@link DS4_FULL_RESIDENCY_HEADROOM_BYTES}.
99
+ */
100
+ limit: 'model-budget' | 'headroom';
82
101
  }
83
102
  /**
84
103
  * Pick a bounded routed-expert cache for streaming mode. Catalog guidance is
@@ -86,8 +105,66 @@ export interface Ds4ExpertCachePlan {
86
105
  * the old RAM-tier heuristic which incorrectly gave both models 64 GiB on a
87
106
  * 128 GiB Mac. Manual values are still accepted, but clamped so fixed model
88
107
  * state + the cache cannot consume the runtime/OS reserve.
108
+ *
109
+ * The 24 GiB headroom guards a footprint guessed as `residentBytes −
110
+ * cacheExpertsBytes`, which omits graph and staging memory ds4 also holds.
111
+ * An entry with a measured streamed footprint has no such gap, so its cache
112
+ * may use whatever this device's local-model budget leaves beside it. A fixed
113
+ * 24 GiB refused Qwen3.8 on 32 GiB Macs whose budget held it with room to spare.
89
114
  */
90
115
  export declare function planDs4ExpertCache(opts: Ds4ExpertCachePlanOptions): Ds4ExpertCachePlan;
116
+ /** A measured ds4 memory line from the catalog: bytes at a window, plus a per-token slope. */
117
+ export interface Ds4MeasuredLine {
118
+ bytes: number;
119
+ ctxTokens: number;
120
+ bytesPerToken: number;
121
+ }
122
+ /** Evaluate a measured line at a window, never below its context-free part. */
123
+ export declare function ds4MeasuredBytesAt(line: Ds4MeasuredLine, ctxTokens: number): number;
124
+ /** The memory fields of a catalog `ds4` block that price a launch. */
125
+ export interface Ds4MemorySource {
126
+ cacheExpertsBytes?: number | undefined;
127
+ fullResidencyMemory?: {
128
+ workingSetBytes: number;
129
+ ctxTokens: number;
130
+ bytesPerToken: number;
131
+ } | undefined;
132
+ streamingMemory?: {
133
+ fixedBytes: number;
134
+ ctxTokens: number;
135
+ bytesPerToken: number;
136
+ minCacheBytes?: number;
137
+ } | undefined;
138
+ }
139
+ /** Measured streamed footprint without the expert cache, at a window; undefined when unmeasured. */
140
+ export declare function ds4StreamingFixedBytes(source: Ds4MemorySource | undefined, ctxTokens: number): number | undefined;
141
+ export interface Ds4MeasuredLaunchOptions {
142
+ source: Ds4MemorySource | undefined;
143
+ numCtx: number;
144
+ ssdStreaming: boolean;
145
+ totalRamBytes: number;
146
+ /** Local-model budget, needed to size a streamed launch's cache. */
147
+ budgetBytes?: number | undefined;
148
+ /** Resident memory beside the model: a vision encoder, a DSpark companion. */
149
+ sidecarBytes?: number;
150
+ configuredCacheGb?: number | undefined;
151
+ }
152
+ export interface Ds4MeasuredLaunch {
153
+ /** What the launch holds, sidecars included. The broker reserves exactly this. */
154
+ workingSetBytes: number;
155
+ /** The part of {@link workingSetBytes} the window does not move. */
156
+ contextFreeBytes: number;
157
+ bytesPerToken: number;
158
+ /** Streamed launches only. */
159
+ cache?: Ds4ExpertCachePlan;
160
+ }
161
+ /**
162
+ * Price a launch from the entry's measurement for the chosen mode. Undefined
163
+ * when that mode is unmeasured (or a streamed launch has no budget to size
164
+ * against), so callers keep their catalog-`residentBytes` path, including the
165
+ * 96 GiB full-residency floor that stands in for a missing measurement.
166
+ */
167
+ export declare function planDs4MeasuredLaunch(opts: Ds4MeasuredLaunchOptions): Ds4MeasuredLaunch | undefined;
91
168
  /** Effective capacity-broker reservation for the chosen DS4 residency mode. */
92
169
  export declare function ds4ResidentBytesForMode(catalogResidentBytes: number, ssdStreaming: boolean, ssdStreamingSupported?: boolean): number;
93
170
  /** Allowance for ds4's compute and staging buffers beyond weights and KV rows. */