@atomic-chat/core 0.13.2 → 0.13.6
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/dist/cli/main.d.ts +1 -1
- package/dist/core/atomic-core.d.ts +3 -1
- package/dist/core/atomic-core.js +1 -0
- package/dist/core/atomic-core.js.map +1 -1
- package/dist/core/create.js +6 -0
- package/dist/core/create.js.map +1 -1
- package/dist/core/prism.d.ts +2 -0
- package/dist/core/prism.js +1 -0
- package/dist/core/prism.js.map +1 -1
- package/dist/core/public-server.js +3 -2
- package/dist/core/public-server.js.map +1 -1
- package/dist/core/tensorrt-llm.d.ts +169 -0
- package/dist/core/tensorrt-llm.js +428 -0
- package/dist/core/tensorrt-llm.js.map +1 -0
- package/dist/core/types.d.ts +16 -0
- package/dist/core/types.js.map +1 -1
- package/dist/decision/process.d.ts +4 -0
- package/dist/decision/process.js +10 -1
- package/dist/decision/process.js.map +1 -1
- package/dist/diffusion/args.d.ts +3 -2
- package/dist/diffusion/args.js +21 -4
- package/dist/diffusion/args.js.map +1 -1
- package/dist/diffusion/compat.d.ts +1 -1
- package/dist/diffusion/compat.js +6 -2
- package/dist/diffusion/compat.js.map +1 -1
- package/dist/diffusion/jobs.js +4 -0
- package/dist/diffusion/jobs.js.map +1 -1
- package/dist/diffusion/tracker.d.ts +6 -0
- package/dist/diffusion/tracker.js +11 -0
- package/dist/diffusion/tracker.js.map +1 -1
- package/dist/diffusion/validate.js +2 -2
- package/dist/diffusion/validate.js.map +1 -1
- package/dist/diffusion/workflow.d.ts +8 -2
- package/dist/diffusion/workflow.js +13 -5
- package/dist/diffusion/workflow.js.map +1 -1
- package/dist/downloads/downloader.d.ts +22 -0
- package/dist/downloads/downloader.js +57 -2
- package/dist/downloads/downloader.js.map +1 -1
- package/dist/downloads/segmented.d.ts +40 -0
- package/dist/downloads/segmented.js +170 -0
- package/dist/downloads/segmented.js.map +1 -0
- package/dist/embedding/process.d.ts +4 -0
- package/dist/embedding/process.js +10 -1
- package/dist/embedding/process.js.map +1 -1
- package/dist/models/compatibility/inspect.d.ts +11 -2
- package/dist/models/compatibility/inspect.js +44 -13
- package/dist/models/compatibility/inspect.js.map +1 -1
- package/dist/models/compatibility/service.d.ts +3 -0
- package/dist/models/compatibility/service.js +1 -0
- package/dist/models/compatibility/service.js.map +1 -1
- package/dist/models/gguf/tensors.d.ts +1 -0
- package/dist/models/gguf/tensors.js +4 -1
- package/dist/models/gguf/tensors.js.map +1 -1
- package/dist/runtime/managed-text/adapter.d.ts +17 -0
- package/dist/runtime/managed-text/adapter.js.map +1 -1
- package/dist/runtime/managed-text/lifecycle.d.ts +8 -0
- package/dist/runtime/managed-text/lifecycle.js +38 -1
- package/dist/runtime/managed-text/lifecycle.js.map +1 -1
- package/dist/runtime/managed-text/readiness.d.ts +21 -1
- package/dist/runtime/managed-text/readiness.js +64 -0
- package/dist/runtime/managed-text/readiness.js.map +1 -1
- package/dist/runtime/shared/ports.d.ts +10 -0
- package/dist/runtime/shared/ports.js +30 -0
- package/dist/runtime/shared/ports.js.map +1 -1
- package/dist/runtime/tensorrt-llm/adapter.d.ts +1 -4
- package/dist/runtime/tensorrt-llm/adapter.js +28 -11
- package/dist/runtime/tensorrt-llm/adapter.js.map +1 -1
- package/dist/runtime/tensorrt-llm/check.js +1 -0
- package/dist/runtime/tensorrt-llm/check.js.map +1 -1
- package/dist/runtime/tensorrt-llm/compatibility.d.ts +30 -6
- package/dist/runtime/tensorrt-llm/compatibility.js +41 -9
- package/dist/runtime/tensorrt-llm/compatibility.js.map +1 -1
- package/dist/runtime/tensorrt-llm/cuda-graphs.d.ts +32 -0
- package/dist/runtime/tensorrt-llm/cuda-graphs.js +33 -0
- package/dist/runtime/tensorrt-llm/cuda-graphs.js.map +1 -0
- package/dist/runtime/tensorrt-llm/delete.d.ts +15 -0
- package/dist/runtime/tensorrt-llm/delete.js +60 -0
- package/dist/runtime/tensorrt-llm/delete.js.map +1 -0
- package/dist/runtime/tensorrt-llm/engine.js +11 -5
- package/dist/runtime/tensorrt-llm/engine.js.map +1 -1
- package/dist/runtime/tensorrt-llm/guest-files.d.ts +8 -0
- package/dist/runtime/tensorrt-llm/guest-files.js +49 -0
- package/dist/runtime/tensorrt-llm/guest-files.js.map +1 -0
- package/dist/runtime/tensorrt-llm/kv-cache.d.ts +16 -0
- package/dist/runtime/tensorrt-llm/kv-cache.js +21 -1
- package/dist/runtime/tensorrt-llm/kv-cache.js.map +1 -1
- package/dist/runtime/tensorrt-llm/location.d.ts +20 -0
- package/dist/runtime/tensorrt-llm/location.js +48 -0
- package/dist/runtime/tensorrt-llm/location.js.map +1 -0
- package/dist/runtime/tensorrt-llm/model-dir.d.ts +38 -0
- package/dist/runtime/tensorrt-llm/model-dir.js +137 -0
- package/dist/runtime/tensorrt-llm/model-dir.js.map +1 -0
- package/dist/runtime/tensorrt-llm/quant-format.d.ts +65 -0
- package/dist/runtime/tensorrt-llm/quant-format.js +189 -0
- package/dist/runtime/tensorrt-llm/quant-format.js.map +1 -0
- package/dist/runtime/tensorrt-llm/registry.d.ts +26 -0
- package/dist/runtime/tensorrt-llm/registry.js +68 -0
- package/dist/runtime/tensorrt-llm/registry.js.map +1 -0
- package/dist/runtime/tensorrt-llm/runtime.d.ts +130 -0
- package/dist/runtime/tensorrt-llm/runtime.js +395 -0
- package/dist/runtime/tensorrt-llm/runtime.js.map +1 -0
- package/dist/server/control/router.js +1 -1
- package/dist/server/control/router.js.map +1 -1
- package/dist/server/control/types.d.ts +5 -1
- package/dist/server/control/types.js.map +1 -1
- package/dist/server/public/gates.d.ts +6 -1
- package/dist/server/public/gates.js +7 -3
- package/dist/server/public/gates.js.map +1 -1
- package/dist/server/public/index.d.ts +1 -0
- package/dist/server/public/index.js +9 -6
- package/dist/server/public/index.js.map +1 -1
- package/dist/server/public/types.d.ts +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files of `tensorrt-llm` models and engine caches on Windows (change `add-tensorrt-llm-windows`,
|
|
3
|
+
* task 2.8, design D5): they live in the WSL guest's ext4, and core sees them through
|
|
4
|
+
* `\\wsl.localhost`. Listing and reading a `model.yml` through that path is fine; sizing or deleting
|
|
5
|
+
* tens of gigabytes through it is not — thousands of 9p round trips for what `du` and `rm` do in the
|
|
6
|
+
* guest at once. So sizes and removals run as commands in the guest, as root, with the guest paths
|
|
7
|
+
* behind the UNC ones (`guestPathFor`, which refuses anything outside the distribution). One `du`
|
|
8
|
+
* call for every path counts a hard-linked file once, as the Linux walk does.
|
|
9
|
+
*/
|
|
10
|
+
import { AtomicCoreError } from '../../contracts/index.js';
|
|
11
|
+
import { WSL_LOCALHOST_MOUNT } from '../wsl/index.js';
|
|
12
|
+
const TIMEOUT_MS = 30 * 60_000;
|
|
13
|
+
export function guestModelFiles(transport, mount = WSL_LOCALHOST_MOUNT) {
|
|
14
|
+
const toGuest = (paths) => paths.map((path) => mount.guestPath(transport.name, path));
|
|
15
|
+
return {
|
|
16
|
+
sizes: async (paths) => {
|
|
17
|
+
const sizes = new Map(paths.map((path) => [path, 0]));
|
|
18
|
+
if (paths.length === 0)
|
|
19
|
+
return sizes;
|
|
20
|
+
const guest = toGuest(paths);
|
|
21
|
+
const answer = await transport.exec(['du', '-s', '-b', '--', ...guest], {
|
|
22
|
+
user: 'root',
|
|
23
|
+
timeoutMs: TIMEOUT_MS,
|
|
24
|
+
});
|
|
25
|
+
// `du` exits 1 when one path is missing, and still prints every other.
|
|
26
|
+
for (const line of answer.stdout.split('\n')) {
|
|
27
|
+
const match = /^(\d+)\t(.+)$/.exec(line);
|
|
28
|
+
if (match === null)
|
|
29
|
+
continue;
|
|
30
|
+
const index = guest.indexOf(match[2]);
|
|
31
|
+
if (index !== -1)
|
|
32
|
+
sizes.set(paths[index], Number(match[1]));
|
|
33
|
+
}
|
|
34
|
+
return sizes;
|
|
35
|
+
},
|
|
36
|
+
remove: async (paths) => {
|
|
37
|
+
if (paths.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
const answer = await transport.exec(['rm', '-rf', '--', ...toGuest(paths)], {
|
|
40
|
+
user: 'root',
|
|
41
|
+
timeoutMs: TIMEOUT_MS,
|
|
42
|
+
});
|
|
43
|
+
if (answer.code !== 0) {
|
|
44
|
+
throw new AtomicCoreError('IO_ERROR', 'The files could not be removed in the Atomic Chat distribution.', answer.stderr.trim());
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=guest-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guest-files.js","sourceRoot":"","sources":["../../../src/runtime/tensorrt-llm/guest-files.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAkD,MAAM,iBAAiB,CAAA;AASrG,MAAM,UAAU,GAAG,EAAE,GAAG,MAAM,CAAA;AAE9B,MAAM,UAAU,eAAe,CAC7B,SAAmC,EACnC,QAAoB,mBAAmB;IAEvC,MAAM,OAAO,GAAG,CAAC,KAAe,EAAY,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;IACzG,OAAO;QACL,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACrB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAiB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;YACrE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;YACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;YAC5B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,EAAE;gBACtE,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,UAAU;aACtB,CAAC,CAAA;YACF,uEAAuE;YACvE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7C,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACxC,IAAI,KAAK,KAAK,IAAI;oBAAE,SAAQ;gBAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAA;gBAC/C,IAAI,KAAK,KAAK,CAAC,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACvE,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAM;YAC9B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE;gBAC1E,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,UAAU;aACtB,CAAC,CAAA;YACF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,eAAe,CACvB,UAAU,EACV,iEAAiE,EACjE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CACrB,CAAA;YACH,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -16,6 +16,22 @@ export declare const TENSORRT_LLM_DEFAULT_KV_CACHE_FREE_FRACTION = 0.8;
|
|
|
16
16
|
* unified-memory-kv-cache-bounded-by-tokens.md).
|
|
17
17
|
*/
|
|
18
18
|
export declare const TENSORRT_LLM_UNIFIED_KV_CONTEXTS = 2;
|
|
19
|
+
/**
|
|
20
|
+
* Tokens in one KV-cache block: `KvCacheConfig.tokens_per_block`'s default in 1.3.0rc29, logged by
|
|
21
|
+
* both cache managers ("tokens per block=32", "KVCacheV2Scheduler: tokens_per_block=32"). The engine
|
|
22
|
+
* turns `kv_cache_config.max_tokens` into whole blocks, rounding down: 2096 tokens became 65 blocks,
|
|
23
|
+
* a 2080-token sequence for a 2096-token context, and the readiness check refused the load as out of
|
|
24
|
+
* memory on a card with 17 GiB free (RTX 5090 Laptop, 2026-10-09). Re-check with each engine image.
|
|
25
|
+
*/
|
|
26
|
+
export declare const TENSORRT_LLM_TOKENS_PER_BLOCK = 32;
|
|
27
|
+
/** `tokens` rounded up to whole KV-cache blocks: what the engine keeps when asked for at least that many. */
|
|
28
|
+
export declare function tensorrtLlmWholeBlockTokens(tokens: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* The `kv_cache_config.max_tokens` that holds `sequences` sequences at full context at once: each
|
|
31
|
+
* sequence takes whole blocks of its own, so the blocks are counted per sequence before multiplying
|
|
32
|
+
* (2 × 2096 tokens is 131 blocks, but two 2096-token sequences need 66 each).
|
|
33
|
+
*/
|
|
34
|
+
export declare function tensorrtLlmKvTokens(contextLength: number, sequences: number): number;
|
|
19
35
|
/**
|
|
20
36
|
* The `kv_cache_config.max_tokens` a unified-memory launch writes, and the token count its memory
|
|
21
37
|
* check reserves KV for. On a unified-memory card (GB10/DGX Spark) "free GPU memory" is the system's
|
|
@@ -16,6 +16,26 @@ export const TENSORRT_LLM_DEFAULT_KV_CACHE_FREE_FRACTION = 0.8;
|
|
|
16
16
|
* unified-memory-kv-cache-bounded-by-tokens.md).
|
|
17
17
|
*/
|
|
18
18
|
export const TENSORRT_LLM_UNIFIED_KV_CONTEXTS = 2;
|
|
19
|
+
/**
|
|
20
|
+
* Tokens in one KV-cache block: `KvCacheConfig.tokens_per_block`'s default in 1.3.0rc29, logged by
|
|
21
|
+
* both cache managers ("tokens per block=32", "KVCacheV2Scheduler: tokens_per_block=32"). The engine
|
|
22
|
+
* turns `kv_cache_config.max_tokens` into whole blocks, rounding down: 2096 tokens became 65 blocks,
|
|
23
|
+
* a 2080-token sequence for a 2096-token context, and the readiness check refused the load as out of
|
|
24
|
+
* memory on a card with 17 GiB free (RTX 5090 Laptop, 2026-10-09). Re-check with each engine image.
|
|
25
|
+
*/
|
|
26
|
+
export const TENSORRT_LLM_TOKENS_PER_BLOCK = 32;
|
|
27
|
+
/** `tokens` rounded up to whole KV-cache blocks: what the engine keeps when asked for at least that many. */
|
|
28
|
+
export function tensorrtLlmWholeBlockTokens(tokens) {
|
|
29
|
+
return Math.ceil(tokens / TENSORRT_LLM_TOKENS_PER_BLOCK) * TENSORRT_LLM_TOKENS_PER_BLOCK;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The `kv_cache_config.max_tokens` that holds `sequences` sequences at full context at once: each
|
|
33
|
+
* sequence takes whole blocks of its own, so the blocks are counted per sequence before multiplying
|
|
34
|
+
* (2 × 2096 tokens is 131 blocks, but two 2096-token sequences need 66 each).
|
|
35
|
+
*/
|
|
36
|
+
export function tensorrtLlmKvTokens(contextLength, sequences) {
|
|
37
|
+
return sequences * tensorrtLlmWholeBlockTokens(contextLength);
|
|
38
|
+
}
|
|
19
39
|
/**
|
|
20
40
|
* The `kv_cache_config.max_tokens` a unified-memory launch writes, and the token count its memory
|
|
21
41
|
* check reserves KV for. On a unified-memory card (GB10/DGX Spark) "free GPU memory" is the system's
|
|
@@ -23,6 +43,6 @@ export const TENSORRT_LLM_UNIFIED_KV_CONTEXTS = 2;
|
|
|
23
43
|
* the KV cache whatever the context length; this bounds it to what the configured context needs.
|
|
24
44
|
*/
|
|
25
45
|
export function tensorrtLlmUnifiedKvMaxTokens(contextLength) {
|
|
26
|
-
return contextLength
|
|
46
|
+
return tensorrtLlmKvTokens(contextLength, TENSORRT_LLM_UNIFIED_KV_CONTEXTS);
|
|
27
47
|
}
|
|
28
48
|
//# sourceMappingURL=kv-cache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kv-cache.js","sourceRoot":"","sources":["../../../src/runtime/tensorrt-llm/kv-cache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;8LAK8L;AAC9L,MAAM,CAAC,MAAM,2CAA2C,GAAG,GAAG,CAAA;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,aAAqB;IACjE,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"kv-cache.js","sourceRoot":"","sources":["../../../src/runtime/tensorrt-llm/kv-cache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;8LAK8L;AAC9L,MAAM,CAAC,MAAM,2CAA2C,GAAG,GAAG,CAAA;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAA;AAEjD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,CAAA;AAE/C,6GAA6G;AAC7G,MAAM,UAAU,2BAA2B,CAAC,MAAc;IACxD,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,6BAA6B,CAAC,GAAG,6BAA6B,CAAA;AAC1F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,aAAqB,EAAE,SAAiB;IAC1E,OAAO,SAAS,GAAG,2BAA2B,CAAC,aAAa,CAAC,CAAA;AAC/D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,aAAqB;IACjE,OAAO,mBAAmB,CAAC,aAAa,EAAE,gCAAgC,CAAC,CAAA;AAC7E,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TensorrtLlmModelLocation } from '../../contracts/index.js';
|
|
2
|
+
import type { WindowsEnvironmentRecord } from '../environment/index.js';
|
|
3
|
+
import { type GuestMount, type WslDistributionTransport } from '../wsl/index.js';
|
|
4
|
+
/** Linux: `<data>/tensorrt-llm/models`, with the free space of the volume it is (or will be) on. */
|
|
5
|
+
export declare function linuxModelLocation(root: string, freeBytes?: (path: string) => Promise<number | null>): Promise<TensorrtLlmModelLocation>;
|
|
6
|
+
export interface WindowsModelLocationDeps {
|
|
7
|
+
records: {
|
|
8
|
+
read(): Promise<WindowsEnvironmentRecord | null>;
|
|
9
|
+
};
|
|
10
|
+
/** This scope's `scope_key` (`readOrCreateGuestScopeKey`). */
|
|
11
|
+
scopeKey: () => Promise<string>;
|
|
12
|
+
transport: (distribution: string) => WslDistributionTransport;
|
|
13
|
+
/** Free bytes on the Windows volume holding `path` (`WindowsHost.freeDiskBytes`). */
|
|
14
|
+
volumeFreeBytes: (path: string) => Promise<number | null>;
|
|
15
|
+
/** How core reaches the guest's files; `\\wsl.localhost` unless a test says otherwise. */
|
|
16
|
+
mount?: GuestMount;
|
|
17
|
+
}
|
|
18
|
+
/** The guest folder of this scope's `tensorrt-llm` models. */
|
|
19
|
+
export declare function guestModelsRoot(scopeKey: string): string;
|
|
20
|
+
export declare function windowsModelLocation(deps: WindowsModelLocationDeps): Promise<TensorrtLlmModelLocation>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where `tensorrt-llm` models live, as clients must use it (change `add-tensorrt-llm-windows`, task
|
|
3
|
+
* 2.8, design D6; spec `tensorrt-llm-models` "Core сообщает расположение моделей"). One answer, from
|
|
4
|
+
* core, on every platform, so a client never derives the folder itself: on Linux the data folder's own
|
|
5
|
+
* `<data>/tensorrt-llm/models` as before; on Windows the scope's folder in Atomic Chat's WSL
|
|
6
|
+
* distribution, `\\wsl.localhost\<distribution>\var\lib\atomic-chat\scopes\<scope_key>\models\tensorrt-llm`,
|
|
7
|
+
* created (and handed to uid 1000) before a client writes into it. The free space on Windows is the
|
|
8
|
+
* smaller of the guest's own and the Windows volume's that holds the distribution's disk image: the
|
|
9
|
+
* guest's disk can grow only as far as that volume lets it. Before the distribution exists there is no
|
|
10
|
+
* such folder: `MANAGED_ADAPTER_UNAVAILABLE`, and no download starts.
|
|
11
|
+
*/
|
|
12
|
+
import { AtomicCoreError } from '../../contracts/index.js';
|
|
13
|
+
import { freeBytesAtNearest, parseDfAvail } from '../environment/index.js';
|
|
14
|
+
import { ensureGuestScope, guestScopeRoot, WSL_LOCALHOST_MOUNT, } from '../wsl/index.js';
|
|
15
|
+
/** Linux: `<data>/tensorrt-llm/models`, with the free space of the volume it is (or will be) on. */
|
|
16
|
+
export async function linuxModelLocation(root, freeBytes = freeBytesAtNearest) {
|
|
17
|
+
return { root, free_bytes: await freeBytes(root).catch(() => null) };
|
|
18
|
+
}
|
|
19
|
+
/** The guest folder of this scope's `tensorrt-llm` models. */
|
|
20
|
+
export function guestModelsRoot(scopeKey) {
|
|
21
|
+
return `${guestScopeRoot(scopeKey)}/models/tensorrt-llm`;
|
|
22
|
+
}
|
|
23
|
+
export async function windowsModelLocation(deps) {
|
|
24
|
+
const record = await deps.records.read();
|
|
25
|
+
if (record === null) {
|
|
26
|
+
throw new AtomicCoreError('MANAGED_ADAPTER_UNAVAILABLE', 'TensorRT-LLM models live in Atomic Chat’s WSL distribution, which is not set up yet.');
|
|
27
|
+
}
|
|
28
|
+
const key = await deps.scopeKey();
|
|
29
|
+
const transport = deps.transport(record.distribution.name);
|
|
30
|
+
const guestRoot = guestModelsRoot(key);
|
|
31
|
+
// A distribution unregistered behind the app's back is no environment at all for a client.
|
|
32
|
+
await ensureGuestScope(transport, key).catch((error) => {
|
|
33
|
+
throw new AtomicCoreError('MANAGED_ADAPTER_UNAVAILABLE', 'Atomic Chat’s WSL distribution does not answer; set the environment up again.', error instanceof Error ? error.message : String(error));
|
|
34
|
+
});
|
|
35
|
+
const [guest, volume] = await Promise.all([
|
|
36
|
+
transport
|
|
37
|
+
.exec(['df', '--output=avail', '-B1', guestRoot], { user: 'root', timeoutMs: 120_000 })
|
|
38
|
+
.then(parseDfAvail)
|
|
39
|
+
.catch(() => null),
|
|
40
|
+
deps.volumeFreeBytes(record.distribution.path).catch(() => null),
|
|
41
|
+
]);
|
|
42
|
+
const known = [guest, volume].filter((value) => value !== null);
|
|
43
|
+
return {
|
|
44
|
+
root: (deps.mount ?? WSL_LOCALHOST_MOUNT).hostPath(record.distribution.name, guestRoot),
|
|
45
|
+
free_bytes: known.length === 0 ? null : Math.min(...known),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=location.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"location.js","sourceRoot":"","sources":["../../../src/runtime/tensorrt-llm/location.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE1E,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,mBAAmB,GAGpB,MAAM,iBAAiB,CAAA;AAExB,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAY,EACZ,YAAsD,kBAAkB;IAExE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAA;AACtE,CAAC;AAaD,8DAA8D;AAC9D,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC,sBAAsB,CAAA;AAC1D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAA8B;IAE9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;IACxC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,eAAe,CACvB,6BAA6B,EAC7B,sFAAsF,CACvF,CAAA;IACH,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAA;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;IAC1D,MAAM,SAAS,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;IACtC,2FAA2F;IAC3F,MAAM,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QAC9D,MAAM,IAAI,eAAe,CACvB,6BAA6B,EAC7B,+EAA+E,EAC/E,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAA;IACH,CAAC,CAAC,CAAA;IACF,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACxC,SAAS;aACN,IAAI,CAAC,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;aACtF,IAAI,CAAC,YAAY,CAAC;aAClB,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;QACpB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;KACjE,CAAC,CAAA;IACF,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAA;IAChF,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,mBAAmB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC;QACvF,UAAU,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;KAC3D,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CheckpointFile } from './compatibility.js';
|
|
2
|
+
/** `model.yml`'s own fields, parsed and typed; nothing here is derived. */
|
|
3
|
+
export interface TensorrtLlmModelYmlDocument {
|
|
4
|
+
/** `null` when `model.yml` does not carry one (tolerated: `readTensorrtLlmModel`'s own contract). */
|
|
5
|
+
repository: string | null;
|
|
6
|
+
revision: string | null;
|
|
7
|
+
architectures: string[];
|
|
8
|
+
quantization: string | null;
|
|
9
|
+
files: CheckpointFile[];
|
|
10
|
+
}
|
|
11
|
+
export interface TensorrtLlmModel {
|
|
12
|
+
id: string;
|
|
13
|
+
/** The checkpoint directory as core sees it; mounted read-only into the container. */
|
|
14
|
+
dir: string;
|
|
15
|
+
repository: string | null;
|
|
16
|
+
revision: string | null;
|
|
17
|
+
/** `architectures[0]`: the Hugging Face class name the descriptor's `model_families` is keyed by. */
|
|
18
|
+
architecture: string | null;
|
|
19
|
+
quantization: string | null;
|
|
20
|
+
/** The revision's file listing exactly as `model.yml` recorded it (the pre-launch check's input). */
|
|
21
|
+
files: CheckpointFile[];
|
|
22
|
+
/** Weight bytes of `files` (`weightBytes`), 0 when `model.yml` lists none. */
|
|
23
|
+
weightBytes: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* `model.yml`'s codec (`parse` only — the app and the CLI write it, core never does, spec
|
|
27
|
+
* `tensorrt-llm-models`): `MANAGED_METADATA_INVALID` when `text` is not valid YAML or not a mapping.
|
|
28
|
+
* `repository`/`revision`/`architectures`/`quantization` tolerate absence or the wrong type by
|
|
29
|
+
* reading as `null`/`[]`, the same way `readTensorrtLlmModel`'s own tests already expect for a bare
|
|
30
|
+
* `model.yml` — a model missing optional metadata is still a model, not a broken one. `files` is the
|
|
31
|
+
* one field held to a stricter rule (task 2.16w round 1, finding 4): the array itself may be absent
|
|
32
|
+
* (`[]`), but once present every entry must be well-formed (`fileList`/`parseFileEntry`) —
|
|
33
|
+
* silently dropping a malformed entry would leave a shard unverified, a checkpoint's weight bytes
|
|
34
|
+
* under-counted, or an `hf_quant_config.json` entry quietly lost.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseTensorrtLlmModelYml(text: string, path: string): TensorrtLlmModelYmlDocument;
|
|
37
|
+
/** `MODEL_NOT_FOUND` without a readable `model.yml`; `MANAGED_METADATA_INVALID` when it is not a mapping. */
|
|
38
|
+
export declare function readTensorrtLlmModel(modelsDir: string, modelId: string): Promise<TensorrtLlmModel>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading an installed `tensorrt-llm` model's directory under `<data>/tensorrt-llm/models/`: its
|
|
3
|
+
* `model.yml` (`parseTensorrtLlmModelYml`, task 2.16 — the TRT-specific schema documented in
|
|
4
|
+
* `docs/contracts.md`: `repository`, `revision`, `architectures`, `quantization`, `files` with
|
|
5
|
+
* `path`/`size`/`sha256`) and the few derived facts a load needs — the first architecture (for the
|
|
6
|
+
* descriptor's `model_families` entry) and the weight bytes (for the readiness timeout). The app and
|
|
7
|
+
* the CLI write the directory and its `model.yml` last (spec `tensorrt-llm-models`); a directory
|
|
8
|
+
* without one is a download still in progress and is not a model.
|
|
9
|
+
*
|
|
10
|
+
* `readTensorrtLlmModel` is the single-id lookup `TensorrtLlmRuntime.load` uses (task 2.14); the full
|
|
11
|
+
* `ModelRegistry` scan across every installed model (`registry.ts`, task 2.16) shares
|
|
12
|
+
* `parseTensorrtLlmModelYml` with it, so both agree about what `model.yml` means.
|
|
13
|
+
*/
|
|
14
|
+
import { readFile } from 'node:fs/promises';
|
|
15
|
+
import { join, relative, isAbsolute } from 'node:path';
|
|
16
|
+
import { parse } from 'yaml';
|
|
17
|
+
import { AtomicCoreError } from '../../contracts/index.js';
|
|
18
|
+
import { MODEL_YML, modelDirFromId } from '../../config/index.js';
|
|
19
|
+
import { weightBytes } from './compatibility.js';
|
|
20
|
+
/** An id is one or more `/`-separated path segments inside the models directory, never out of it. */
|
|
21
|
+
function assertModelId(modelsDir, modelId) {
|
|
22
|
+
const segments = modelId.split('/');
|
|
23
|
+
if (modelId === '' || segments.some((s) => s === '' || s === '.' || s === '..' || s.includes('\\'))) {
|
|
24
|
+
throw new AtomicCoreError('INVALID_ARGUMENT', 'Not a tensorrt-llm model id.', modelId);
|
|
25
|
+
}
|
|
26
|
+
const dir = modelDirFromId(modelsDir, modelId);
|
|
27
|
+
const inside = relative(modelsDir, dir);
|
|
28
|
+
if (inside === '' || inside.startsWith('..') || isAbsolute(inside)) {
|
|
29
|
+
throw new AtomicCoreError('INVALID_ARGUMENT', 'Not a tensorrt-llm model id.', modelId);
|
|
30
|
+
}
|
|
31
|
+
return dir;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A relative file path safe to join onto the model directory and mount read-only into a container:
|
|
35
|
+
* forward-slash separated, never absolute, no empty/`.`/`..` segment and no backslash (which some
|
|
36
|
+
* shells would treat as an escape on the file's own leaf name).
|
|
37
|
+
*/
|
|
38
|
+
function isSafeRelativePath(path) {
|
|
39
|
+
if (path === '' || path.startsWith('/') || path.includes('\\'))
|
|
40
|
+
return false;
|
|
41
|
+
return path.split('/').every((segment) => segment !== '' && segment !== '.' && segment !== '..');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One `model.yml` `files[]` entry, strictly: `MANAGED_METADATA_INVALID` for anything malformed
|
|
45
|
+
* (task 2.16w round 1, finding 4) — a non-string or unsafe `path`, a `size` that is not a finite
|
|
46
|
+
* non-negative integer, a `sha256` that is neither a string nor `null`. The previous, lenient
|
|
47
|
+
* parsing silently dropped a malformed entry instead: an unverified shard the pre-launch check would
|
|
48
|
+
* never even look for, a checkpoint's weight bytes under-counted, or an `hf_quant_config.json` entry
|
|
49
|
+
* quietly lost — every one of those is worse than refusing the model outright and naming why.
|
|
50
|
+
*/
|
|
51
|
+
function parseFileEntry(raw, index, path) {
|
|
52
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
53
|
+
throw new AtomicCoreError('MANAGED_METADATA_INVALID', `${path}: files[${index}] is not a mapping.`, JSON.stringify(raw));
|
|
54
|
+
}
|
|
55
|
+
const entry = raw;
|
|
56
|
+
if (typeof entry.path !== 'string' || !isSafeRelativePath(entry.path)) {
|
|
57
|
+
throw new AtomicCoreError('MANAGED_METADATA_INVALID', `${path}: files[${index}].path is not a safe relative path.`, String(entry.path));
|
|
58
|
+
}
|
|
59
|
+
if (typeof entry.size !== 'number' || !Number.isInteger(entry.size) || entry.size < 0) {
|
|
60
|
+
throw new AtomicCoreError('MANAGED_METADATA_INVALID', `${path}: files[${index}].size must be a non-negative integer.`, String(entry.size));
|
|
61
|
+
}
|
|
62
|
+
if (entry.sha256 !== undefined && entry.sha256 !== null && typeof entry.sha256 !== 'string') {
|
|
63
|
+
throw new AtomicCoreError('MANAGED_METADATA_INVALID', `${path}: files[${index}].sha256 must be a string or null.`, String(entry.sha256));
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
path: entry.path,
|
|
67
|
+
size: entry.size,
|
|
68
|
+
sha256: typeof entry.sha256 === 'string' ? entry.sha256 : null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** `files` absent entirely is tolerated as `[]` (a bare `model.yml` is still a model); once present, every entry must be well-formed. */
|
|
72
|
+
function fileList(raw, path) {
|
|
73
|
+
if (raw === undefined)
|
|
74
|
+
return [];
|
|
75
|
+
if (!Array.isArray(raw)) {
|
|
76
|
+
throw new AtomicCoreError('MANAGED_METADATA_INVALID', `${path}: files must be an array.`, JSON.stringify(raw));
|
|
77
|
+
}
|
|
78
|
+
return raw.map((entry, index) => parseFileEntry(entry, index, path));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* `model.yml`'s codec (`parse` only — the app and the CLI write it, core never does, spec
|
|
82
|
+
* `tensorrt-llm-models`): `MANAGED_METADATA_INVALID` when `text` is not valid YAML or not a mapping.
|
|
83
|
+
* `repository`/`revision`/`architectures`/`quantization` tolerate absence or the wrong type by
|
|
84
|
+
* reading as `null`/`[]`, the same way `readTensorrtLlmModel`'s own tests already expect for a bare
|
|
85
|
+
* `model.yml` — a model missing optional metadata is still a model, not a broken one. `files` is the
|
|
86
|
+
* one field held to a stricter rule (task 2.16w round 1, finding 4): the array itself may be absent
|
|
87
|
+
* (`[]`), but once present every entry must be well-formed (`fileList`/`parseFileEntry`) —
|
|
88
|
+
* silently dropping a malformed entry would leave a shard unverified, a checkpoint's weight bytes
|
|
89
|
+
* under-counted, or an `hf_quant_config.json` entry quietly lost.
|
|
90
|
+
*/
|
|
91
|
+
export function parseTensorrtLlmModelYml(text, path) {
|
|
92
|
+
let yml;
|
|
93
|
+
try {
|
|
94
|
+
yml = parse(text);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
throw new AtomicCoreError('MANAGED_METADATA_INVALID', `${path} is not valid YAML.`, String(error));
|
|
98
|
+
}
|
|
99
|
+
if (yml === null || typeof yml !== 'object' || Array.isArray(yml)) {
|
|
100
|
+
throw new AtomicCoreError('MANAGED_METADATA_INVALID', `${path} does not describe a model.`, path);
|
|
101
|
+
}
|
|
102
|
+
const doc = yml;
|
|
103
|
+
const architectures = Array.isArray(doc['architectures'])
|
|
104
|
+
? doc['architectures'].filter((entry) => typeof entry === 'string' && entry !== '')
|
|
105
|
+
: [];
|
|
106
|
+
return {
|
|
107
|
+
repository: typeof doc['repository'] === 'string' ? doc['repository'] : null,
|
|
108
|
+
revision: typeof doc['revision'] === 'string' ? doc['revision'] : null,
|
|
109
|
+
architectures,
|
|
110
|
+
quantization: typeof doc['quantization'] === 'string' ? doc['quantization'] : null,
|
|
111
|
+
files: fileList(doc['files'], path),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** `MODEL_NOT_FOUND` without a readable `model.yml`; `MANAGED_METADATA_INVALID` when it is not a mapping. */
|
|
115
|
+
export async function readTensorrtLlmModel(modelsDir, modelId) {
|
|
116
|
+
const dir = assertModelId(modelsDir, modelId);
|
|
117
|
+
const path = join(dir, MODEL_YML);
|
|
118
|
+
let text;
|
|
119
|
+
try {
|
|
120
|
+
text = await readFile(path, 'utf8');
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
throw new AtomicCoreError('MODEL_NOT_FOUND', `The tensorrt-llm model '${modelId}' is not installed.`, path);
|
|
124
|
+
}
|
|
125
|
+
const yml = parseTensorrtLlmModelYml(text, path);
|
|
126
|
+
return {
|
|
127
|
+
id: modelId,
|
|
128
|
+
dir,
|
|
129
|
+
repository: yml.repository,
|
|
130
|
+
revision: yml.revision,
|
|
131
|
+
architecture: yml.architectures[0] ?? null,
|
|
132
|
+
quantization: yml.quantization,
|
|
133
|
+
files: yml.files,
|
|
134
|
+
weightBytes: weightBytes(yml.files),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=model-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-dir.js","sourceRoot":"","sources":["../../../src/runtime/tensorrt-llm/model-dir.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quantization-format naming rule: a fixed checkpoint-metadata → format-name mapping that
|
|
3
|
+
* `RuntimeDescriptor.quantization[].format` (`src/contracts/environment.ts`) is keyed by. Ported
|
|
4
|
+
* verbatim from `atomic-chat-conf`'s README, "Runtime descriptors (`runtimes/`)" →
|
|
5
|
+
* `quantization[].format` names — this file *is* the core side of that conf↔core contract, so any
|
|
6
|
+
* change to the rule has to change both places together.
|
|
7
|
+
*
|
|
8
|
+
* The inputs are a checkpoint's `config.json` and, when the repository carries the file, its
|
|
9
|
+
* `hf_quant_config.json`. A checkpoint can be quantized with no `quantization_config` in
|
|
10
|
+
* `config.json` at all (e.g. `nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-FP8`,
|
|
11
|
+
* `nvidia/Llama-3.3-70B-Instruct-NVFP4`), so `hf_quant_config.json` must always be checked before
|
|
12
|
+
* `dtype`/`torch_dtype` is trusted. Steps apply in order, stopping at the first that matches; `null`
|
|
13
|
+
* means the naming rule does not recognise the checkpoint (an unsupported `quant_method` such as
|
|
14
|
+
* `awq`/`gptq`, an `fp8` checkpoint without the block size this engine loads, or a `dtype` other
|
|
15
|
+
* than `bfloat16`/`float16`) — the caller rejects that as an unsupported format, never guesses.
|
|
16
|
+
*
|
|
17
|
+
* Pure and network-free, like every file in this module (design D12).
|
|
18
|
+
*/
|
|
19
|
+
/** A parsed JSON object, exactly as `config.json` / `hf_quant_config.json` arrive on the wire. */
|
|
20
|
+
export type JsonObject = Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Every sibling that is a `.gguf` file. GGUF is not a case of the naming rule at all — this engine
|
|
23
|
+
* never loads a GGUF checkpoint's `config.json` — it is the separate, always-reject gate
|
|
24
|
+
* `compatibility.ts` applies before the naming rule runs at all (spec `tensorrt-llm-models`: "for
|
|
25
|
+
* GGUF there is llama.cpp"). A listing with even one `.gguf` file counts, which also covers a
|
|
26
|
+
* GGUF-only listing.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isGgufCheckpoint(files: readonly {
|
|
29
|
+
path: string;
|
|
30
|
+
}[]): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The naming rule, applied in order. `null` means unrecognised: the caller must reject it as an
|
|
33
|
+
* unsupported format rather than reporting `bf16`/`fp16` by default.
|
|
34
|
+
*
|
|
35
|
+
* `hf_quant_config.json` being present at all stops the rule at step 1 — its own `null` (an
|
|
36
|
+
* unrecognised or missing `quant_algo`) is final and never falls through to `config.json`/`dtype`,
|
|
37
|
+
* matching the README: "if hf_quant_config.json is present ... the format is its quant_algo".
|
|
38
|
+
*/
|
|
39
|
+
export declare function quantizationFormat(configJson: JsonObject, hfQuantConfigJson: JsonObject | null): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* The KV-cache's own quantization algorithm — NVIDIA ModelOpt's `kv_cache_quant_algo`, a sibling
|
|
42
|
+
* field to `quant_algo` above, read the same way and from the same object: `hf_quant_config.json`'s
|
|
43
|
+
* `quantization` when the repository carries the file, else `config.json`'s `quantization_config`.
|
|
44
|
+
* Used by the KV-cache memory formula (`compatibility.ts`, task 2.16w round 1) to size a KV cache
|
|
45
|
+
* entry at 1 byte (`FP8`) instead of the engine's own default 2 bytes (`bf16`/`fp16`). `undefined`
|
|
46
|
+
* when neither object carries it — an unquantized KV cache, or a checkpoint this naming rule does
|
|
47
|
+
* not otherwise recognise.
|
|
48
|
+
*/
|
|
49
|
+
export declare function kvCacheQuantAlgo(configJson: JsonObject, hfQuantConfigJson: JsonObject | null): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* What the naming rule actually saw when it did not recognise a format, for error messages only —
|
|
52
|
+
* never consulted to decide the format itself (that is `quantizationFormat`'s job alone). Mirrors
|
|
53
|
+
* `quantizationFormat`'s own step order, so the two must be kept in sync.
|
|
54
|
+
*/
|
|
55
|
+
export declare function describeUnrecognizedQuantization(configJson: JsonObject, hfQuantConfigJson: JsonObject | null): string;
|
|
56
|
+
/** What NVIDIA ModelOpt calls a checkpoint whose layers carry different formats. */
|
|
57
|
+
export declare const MIXED_PRECISION = "mixed_precision";
|
|
58
|
+
/**
|
|
59
|
+
* The formats a checkpoint actually needs the engine to load. One format for every checkpoint but a
|
|
60
|
+
* ModelOpt `MIXED_PRECISION` one, whose `hf_quant_config.json` lists a `quant_algo` per layer in
|
|
61
|
+
* `quantization.quantized_layers` (e.g. `NVFP4` + `FP8`, or `W4A16_NVFP4` + `FP8`): every distinct
|
|
62
|
+
* per-layer format, named by the same rule as a whole-checkpoint `quant_algo`. An empty list means
|
|
63
|
+
* the mixed checkpoint names no per-layer format at all, which the caller must reject.
|
|
64
|
+
*/
|
|
65
|
+
export declare function quantizationComponents(format: string, hfQuantConfigJson: JsonObject | null): string[];
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quantization-format naming rule: a fixed checkpoint-metadata → format-name mapping that
|
|
3
|
+
* `RuntimeDescriptor.quantization[].format` (`src/contracts/environment.ts`) is keyed by. Ported
|
|
4
|
+
* verbatim from `atomic-chat-conf`'s README, "Runtime descriptors (`runtimes/`)" →
|
|
5
|
+
* `quantization[].format` names — this file *is* the core side of that conf↔core contract, so any
|
|
6
|
+
* change to the rule has to change both places together.
|
|
7
|
+
*
|
|
8
|
+
* The inputs are a checkpoint's `config.json` and, when the repository carries the file, its
|
|
9
|
+
* `hf_quant_config.json`. A checkpoint can be quantized with no `quantization_config` in
|
|
10
|
+
* `config.json` at all (e.g. `nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-FP8`,
|
|
11
|
+
* `nvidia/Llama-3.3-70B-Instruct-NVFP4`), so `hf_quant_config.json` must always be checked before
|
|
12
|
+
* `dtype`/`torch_dtype` is trusted. Steps apply in order, stopping at the first that matches; `null`
|
|
13
|
+
* means the naming rule does not recognise the checkpoint (an unsupported `quant_method` such as
|
|
14
|
+
* `awq`/`gptq`, an `fp8` checkpoint without the block size this engine loads, or a `dtype` other
|
|
15
|
+
* than `bfloat16`/`float16`) — the caller rejects that as an unsupported format, never guesses.
|
|
16
|
+
*
|
|
17
|
+
* Pure and network-free, like every file in this module (design D12).
|
|
18
|
+
*/
|
|
19
|
+
const FP8_PB_WO = 'fp8_pb_wo';
|
|
20
|
+
const FP8_BLOCK_SCALES = 'fp8_block_scales';
|
|
21
|
+
function asObject(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
23
|
+
? value
|
|
24
|
+
: undefined;
|
|
25
|
+
}
|
|
26
|
+
function asNonEmptyString(value) {
|
|
27
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
28
|
+
}
|
|
29
|
+
/** `fp8_pb_wo` is what NVIDIA ModelOpt calls it; the engine itself calls the same format `fp8_block_scales`. */
|
|
30
|
+
function renameFp8PbWo(quantAlgo) {
|
|
31
|
+
const lower = quantAlgo.toLowerCase();
|
|
32
|
+
return lower === FP8_PB_WO ? FP8_BLOCK_SCALES : lower;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Every sibling that is a `.gguf` file. GGUF is not a case of the naming rule at all — this engine
|
|
36
|
+
* never loads a GGUF checkpoint's `config.json` — it is the separate, always-reject gate
|
|
37
|
+
* `compatibility.ts` applies before the naming rule runs at all (spec `tensorrt-llm-models`: "for
|
|
38
|
+
* GGUF there is llama.cpp"). A listing with even one `.gguf` file counts, which also covers a
|
|
39
|
+
* GGUF-only listing.
|
|
40
|
+
*/
|
|
41
|
+
export function isGgufCheckpoint(files) {
|
|
42
|
+
return files.some((file) => file.path.toLowerCase().endsWith('.gguf'));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Step 1: `hf_quant_config.json` present (NVIDIA ModelOpt) → its `quantization.quant_algo`,
|
|
46
|
+
* lower-cased and renamed. The README rule stops here whenever the file is present at all — a
|
|
47
|
+
* present-but-empty/missing `quant_algo` (e.g. a ModelOpt KV-cache-only export) is `null`
|
|
48
|
+
* (unrecognised), never a reason to fall through to `config.json`/`dtype`.
|
|
49
|
+
*/
|
|
50
|
+
function fromHfQuantConfig(hfQuantConfigJson) {
|
|
51
|
+
const quantization = asObject(hfQuantConfigJson.quantization);
|
|
52
|
+
const quantAlgo = quantization === undefined ? undefined : asNonEmptyString(quantization.quant_algo);
|
|
53
|
+
return quantAlgo === undefined ? null : renameFp8PbWo(quantAlgo);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Step 2: `config.json`'s `quantization_config.quant_method`. Returns `null` (recognised as *not
|
|
57
|
+
* loadable*, distinct from `undefined` meaning "step 2 does not apply") for every `quant_method`
|
|
58
|
+
* this engine release cannot load, so the caller never falls through to reading `dtype` for a
|
|
59
|
+
* checkpoint that step 2 already identified as quantized.
|
|
60
|
+
*/
|
|
61
|
+
function fromConfigQuantizationConfig(configJson) {
|
|
62
|
+
const quantizationConfig = asObject(configJson.quantization_config);
|
|
63
|
+
if (quantizationConfig === undefined)
|
|
64
|
+
return undefined;
|
|
65
|
+
const quantMethod = quantizationConfig.quant_method;
|
|
66
|
+
if (quantMethod === 'modelopt') {
|
|
67
|
+
const quantAlgo = asNonEmptyString(quantizationConfig.quant_algo);
|
|
68
|
+
return quantAlgo === undefined ? null : renameFp8PbWo(quantAlgo);
|
|
69
|
+
}
|
|
70
|
+
if (quantMethod === 'fp8') {
|
|
71
|
+
const blockSize = quantizationConfig.weight_block_size;
|
|
72
|
+
const isFp8BlockScales = Array.isArray(blockSize) && blockSize.length === 2 && blockSize[0] === 128 && blockSize[1] === 128;
|
|
73
|
+
// `fp8` without that exact block size is not loadable by this engine release
|
|
74
|
+
// (`model_config.py:323`) and must not fall through to step 3 as `bf16`/`fp16`.
|
|
75
|
+
return isFp8BlockScales ? FP8_BLOCK_SCALES : null;
|
|
76
|
+
}
|
|
77
|
+
if (quantMethod === 'mxfp4')
|
|
78
|
+
return 'mxfp4';
|
|
79
|
+
// awq, gptq, anything else: not loadable by this engine release.
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The checkpoint's weight dtype: `dtype` is what TensorRT-LLM itself reads, `torch_dtype` is the
|
|
84
|
+
* legacy fallback. A VLM-style config (Qwen3.5 and later) declares it only on `text_config`, which
|
|
85
|
+
* TensorRT-LLM 1.3 falls back to as well (`model_config.py`), so this does too.
|
|
86
|
+
*/
|
|
87
|
+
function configDtype(configJson) {
|
|
88
|
+
const textConfig = asObject(configJson.text_config);
|
|
89
|
+
return (asNonEmptyString(configJson.dtype) ??
|
|
90
|
+
asNonEmptyString(configJson.torch_dtype) ??
|
|
91
|
+
(textConfig === undefined
|
|
92
|
+
? undefined
|
|
93
|
+
: (asNonEmptyString(textConfig.dtype) ?? asNonEmptyString(textConfig.torch_dtype))));
|
|
94
|
+
}
|
|
95
|
+
/** Step 3: unquantized, named by `configDtype`. */
|
|
96
|
+
function fromDtype(configJson) {
|
|
97
|
+
const dtype = configDtype(configJson);
|
|
98
|
+
if (dtype === 'bfloat16')
|
|
99
|
+
return 'bf16';
|
|
100
|
+
if (dtype === 'float16')
|
|
101
|
+
return 'fp16';
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The naming rule, applied in order. `null` means unrecognised: the caller must reject it as an
|
|
106
|
+
* unsupported format rather than reporting `bf16`/`fp16` by default.
|
|
107
|
+
*
|
|
108
|
+
* `hf_quant_config.json` being present at all stops the rule at step 1 — its own `null` (an
|
|
109
|
+
* unrecognised or missing `quant_algo`) is final and never falls through to `config.json`/`dtype`,
|
|
110
|
+
* matching the README: "if hf_quant_config.json is present ... the format is its quant_algo".
|
|
111
|
+
*/
|
|
112
|
+
export function quantizationFormat(configJson, hfQuantConfigJson) {
|
|
113
|
+
if (hfQuantConfigJson !== null) {
|
|
114
|
+
return fromHfQuantConfig(hfQuantConfigJson);
|
|
115
|
+
}
|
|
116
|
+
const fromConfig = fromConfigQuantizationConfig(configJson);
|
|
117
|
+
if (fromConfig !== undefined)
|
|
118
|
+
return fromConfig;
|
|
119
|
+
return fromDtype(configJson);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The KV-cache's own quantization algorithm — NVIDIA ModelOpt's `kv_cache_quant_algo`, a sibling
|
|
123
|
+
* field to `quant_algo` above, read the same way and from the same object: `hf_quant_config.json`'s
|
|
124
|
+
* `quantization` when the repository carries the file, else `config.json`'s `quantization_config`.
|
|
125
|
+
* Used by the KV-cache memory formula (`compatibility.ts`, task 2.16w round 1) to size a KV cache
|
|
126
|
+
* entry at 1 byte (`FP8`) instead of the engine's own default 2 bytes (`bf16`/`fp16`). `undefined`
|
|
127
|
+
* when neither object carries it — an unquantized KV cache, or a checkpoint this naming rule does
|
|
128
|
+
* not otherwise recognise.
|
|
129
|
+
*/
|
|
130
|
+
export function kvCacheQuantAlgo(configJson, hfQuantConfigJson) {
|
|
131
|
+
if (hfQuantConfigJson !== null) {
|
|
132
|
+
const quantization = asObject(hfQuantConfigJson.quantization);
|
|
133
|
+
return quantization === undefined ? undefined : asNonEmptyString(quantization.kv_cache_quant_algo);
|
|
134
|
+
}
|
|
135
|
+
const quantizationConfig = asObject(configJson.quantization_config);
|
|
136
|
+
return quantizationConfig === undefined
|
|
137
|
+
? undefined
|
|
138
|
+
: asNonEmptyString(quantizationConfig.kv_cache_quant_algo);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* What the naming rule actually saw when it did not recognise a format, for error messages only —
|
|
142
|
+
* never consulted to decide the format itself (that is `quantizationFormat`'s job alone). Mirrors
|
|
143
|
+
* `quantizationFormat`'s own step order, so the two must be kept in sync.
|
|
144
|
+
*/
|
|
145
|
+
export function describeUnrecognizedQuantization(configJson, hfQuantConfigJson) {
|
|
146
|
+
if (hfQuantConfigJson !== null) {
|
|
147
|
+
const quantization = asObject(hfQuantConfigJson.quantization);
|
|
148
|
+
const quantAlgo = quantization?.quant_algo;
|
|
149
|
+
return `hf_quant_config.json quantization.quant_algo=${JSON.stringify(quantAlgo ?? null)}`;
|
|
150
|
+
}
|
|
151
|
+
const quantizationConfig = asObject(configJson.quantization_config);
|
|
152
|
+
if (quantizationConfig !== undefined) {
|
|
153
|
+
const quantMethod = quantizationConfig.quant_method;
|
|
154
|
+
if (quantMethod === 'modelopt') {
|
|
155
|
+
return `config.json quantization_config.quant_method="modelopt" quant_algo=${JSON.stringify(quantizationConfig.quant_algo ?? null)}`;
|
|
156
|
+
}
|
|
157
|
+
if (quantMethod === 'fp8') {
|
|
158
|
+
return `config.json quantization_config.quant_method="fp8" weight_block_size=${JSON.stringify(quantizationConfig.weight_block_size ?? null)}`;
|
|
159
|
+
}
|
|
160
|
+
return `config.json quantization_config.quant_method=${JSON.stringify(quantMethod ?? null)}`;
|
|
161
|
+
}
|
|
162
|
+
return `config.json dtype=${JSON.stringify(configDtype(configJson) ?? null)}`;
|
|
163
|
+
}
|
|
164
|
+
/** What NVIDIA ModelOpt calls a checkpoint whose layers carry different formats. */
|
|
165
|
+
export const MIXED_PRECISION = 'mixed_precision';
|
|
166
|
+
/**
|
|
167
|
+
* The formats a checkpoint actually needs the engine to load. One format for every checkpoint but a
|
|
168
|
+
* ModelOpt `MIXED_PRECISION` one, whose `hf_quant_config.json` lists a `quant_algo` per layer in
|
|
169
|
+
* `quantization.quantized_layers` (e.g. `NVFP4` + `FP8`, or `W4A16_NVFP4` + `FP8`): every distinct
|
|
170
|
+
* per-layer format, named by the same rule as a whole-checkpoint `quant_algo`. An empty list means
|
|
171
|
+
* the mixed checkpoint names no per-layer format at all, which the caller must reject.
|
|
172
|
+
*/
|
|
173
|
+
export function quantizationComponents(format, hfQuantConfigJson) {
|
|
174
|
+
if (format !== MIXED_PRECISION)
|
|
175
|
+
return [format];
|
|
176
|
+
const quantization = hfQuantConfigJson === null ? undefined : asObject(hfQuantConfigJson.quantization);
|
|
177
|
+
const layers = quantization === undefined ? undefined : asObject(quantization.quantized_layers);
|
|
178
|
+
if (layers === undefined)
|
|
179
|
+
return [];
|
|
180
|
+
const formats = new Set();
|
|
181
|
+
for (const layer of Object.values(layers)) {
|
|
182
|
+
const quantAlgo = asNonEmptyString(asObject(layer)?.quant_algo);
|
|
183
|
+
if (quantAlgo === undefined)
|
|
184
|
+
return [];
|
|
185
|
+
formats.add(renameFp8PbWo(quantAlgo));
|
|
186
|
+
}
|
|
187
|
+
return [...formats].sort();
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=quant-format.js.map
|