@infersec/conduit 1.111.0 → 1.112.1

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.js CHANGED
@@ -21567,6 +21567,90 @@ object$5({
21567
21567
  // Source of truth for curated recommended models. Consumed by @infersec/definitions
21568
21568
  // and the website model page generator (applications/website/scripts/generate-model-pages.ts).
21569
21569
  const modelsData = [
21570
+ {
21571
+ author: "LiquidAI",
21572
+ baseModel: "LiquidAI/LFM2.5-2.6B",
21573
+ blurb: "Liquid's LFM2.5 2.6B hybrid at high fidelity - agentic post-training, 128K context, and 220 tok/s decode on an M5 Max. Fits a 4 GB card with room to spare.",
21574
+ contextLength: 131072,
21575
+ fileSizeBytes: 2874779648,
21576
+ format: "gguf",
21577
+ license: "lfm1.0",
21578
+ parameterCount: "2.6B",
21579
+ quantization: "Q8_0",
21580
+ slug: "LiquidAI/LFM2.5-2.6B-GGUF",
21581
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-2.6B-GGUF",
21582
+ vramTierGB: 4
21583
+ },
21584
+ {
21585
+ author: "LiquidAI",
21586
+ baseModel: "LiquidAI/LFM2.5-2.6B",
21587
+ blurb: "Liquid's LFM2.5 2.6B hybrid at 4-bit - competitive with models 4x its size on tool use and multi-step agentic tasks. A 1.7 GB download that sips memory.",
21588
+ contextLength: 131072,
21589
+ fileSizeBytes: 1674455040,
21590
+ format: "gguf",
21591
+ license: "lfm1.0",
21592
+ parameterCount: "2.6B",
21593
+ quantization: "Q4_K_M",
21594
+ slug: "LiquidAI/LFM2.5-2.6B-GGUF",
21595
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-2.6B-GGUF",
21596
+ vramTierGB: 4
21597
+ },
21598
+ {
21599
+ author: "LiquidAI",
21600
+ baseModel: "LiquidAI/LFM2.5-2.6B",
21601
+ blurb: "The native BF16 checkpoint of Liquid's LFM2.5 2.6B for vLLM and SGLang - hybrid conv + GQA architecture, 128K context window, and agentic reinforcement-learning post-training.",
21602
+ contextLength: 131072,
21603
+ fileSizeBytes: 5394427456,
21604
+ format: "safetensors",
21605
+ license: "lfm1.0",
21606
+ parameterCount: "2.6B",
21607
+ quantization: "BF16",
21608
+ slug: "LiquidAI/LFM2.5-2.6B",
21609
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-2.6B",
21610
+ vramTierGB: 8
21611
+ },
21612
+ {
21613
+ author: "LiquidAI",
21614
+ baseModel: "LiquidAI/LFM2.5-230M",
21615
+ blurb: "Liquid's most compact LFM2.5 at high fidelity - a 230M edge model distilled from the 350M, tuned for tool use and data extraction. 213 tok/s decode on a Galaxy S25 Ultra.",
21616
+ contextLength: 32768,
21617
+ fileSizeBytes: 246598496,
21618
+ format: "gguf",
21619
+ license: "lfm1.0",
21620
+ parameterCount: "230M",
21621
+ quantization: "Q8_0",
21622
+ slug: "LiquidAI/LFM2.5-230M-GGUF",
21623
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-230M-GGUF",
21624
+ vramTierGB: 2
21625
+ },
21626
+ {
21627
+ author: "LiquidAI",
21628
+ baseModel: "LiquidAI/LFM2.5-230M",
21629
+ blurb: "Liquid's most compact LFM2.5 at 4-bit - a 153 MB download that brings real tool-use capability to the tightest memory budgets, from Raspberry Pi to phones.",
21630
+ contextLength: 32768,
21631
+ fileSizeBytes: 153406304,
21632
+ format: "gguf",
21633
+ license: "lfm1.0",
21634
+ parameterCount: "230M",
21635
+ quantization: "Q4_K_M",
21636
+ slug: "LiquidAI/LFM2.5-230M-GGUF",
21637
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-230M-GGUF",
21638
+ vramTierGB: 2
21639
+ },
21640
+ {
21641
+ author: "LiquidAI",
21642
+ baseModel: "LiquidAI/LFM2.5-230M",
21643
+ blurb: "The native BF16 checkpoint of Liquid's LFM2.5 230M for vLLM - a 230M edge model with a 32K context window, suited to data extraction and lightweight on-device agent pipelines.",
21644
+ contextLength: 32768,
21645
+ fileSizeBytes: 459401112,
21646
+ format: "safetensors",
21647
+ license: "lfm1.0",
21648
+ parameterCount: "230M",
21649
+ quantization: "BF16",
21650
+ slug: "LiquidAI/LFM2.5-230M",
21651
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-230M",
21652
+ vramTierGB: 4
21653
+ },
21570
21654
  {
21571
21655
  author: "Qwen",
21572
21656
  baseModel: "Qwen/Qwen3-8B",
@@ -21623,6 +21707,48 @@ const modelsData = [
21623
21707
  sourceURL: "https://huggingface.co/bartowski/deepreinforce-ai_Ornith-1.0-9B-GGUF",
21624
21708
  vramTierGB: 16
21625
21709
  },
21710
+ {
21711
+ author: "unsloth",
21712
+ baseModel: "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16",
21713
+ blurb: "NVIDIA's Nemotron 3.5 Lightning MoE squeezed into 2-bit - 30B total with only 3B active in a Mamba-2 + MoE + attention hybrid. Configurable thinking and a 256K context on a single 24 GB card.",
21714
+ contextLength: 262144,
21715
+ fileSizeBytes: 19432070208,
21716
+ format: "gguf",
21717
+ license: "openmdw-1.1",
21718
+ parameterCount: "30B",
21719
+ quantization: "UD-IQ2_M",
21720
+ slug: "unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21721
+ sourceURL: "https://huggingface.co/unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21722
+ vramTierGB: 24
21723
+ },
21724
+ {
21725
+ author: "unsloth",
21726
+ baseModel: "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16",
21727
+ blurb: "NVIDIA's Nemotron 3.5 Lightning at Unsloth's dynamic 4-bit - a Mamba-2 hybrid MoE with 3B active parameters, toggleable reasoning, and strong instruction following (IFBench 71.9). Needs a 32 GB GPU.",
21728
+ contextLength: 262144,
21729
+ fileSizeBytes: 25266255936,
21730
+ format: "gguf",
21731
+ license: "openmdw-1.1",
21732
+ parameterCount: "30B",
21733
+ quantization: "UD-Q4_K_M",
21734
+ slug: "unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21735
+ sourceURL: "https://huggingface.co/unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21736
+ vramTierGB: 32
21737
+ },
21738
+ {
21739
+ author: "unsloth",
21740
+ baseModel: "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16",
21741
+ blurb: "Nemotron 3.5 Lightning at 8-bit fidelity - near-reference quality from the 30B-A3B Mamba-2 hybrid, with configurable thinking and DSpark speculative decoding support. Needs a 48 GB card.",
21742
+ contextLength: 262144,
21743
+ fileSizeBytes: 35004643392,
21744
+ format: "gguf",
21745
+ license: "openmdw-1.1",
21746
+ parameterCount: "30B",
21747
+ quantization: "Q8_0",
21748
+ slug: "unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21749
+ sourceURL: "https://huggingface.co/unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21750
+ vramTierGB: 48
21751
+ },
21626
21752
  {
21627
21753
  author: "unsloth",
21628
21754
  baseModel: "openai/gpt-oss-20b",
@@ -21681,6 +21807,76 @@ const modelsData = [
21681
21807
  sourceURL: "https://huggingface.co/unsloth/Qwen3.8-Flash-Next-GGUF",
21682
21808
  vramTierGB: 128
21683
21809
  },
21810
+ {
21811
+ author: "Qwen",
21812
+ baseModel: "Qwen/Qwen3.8-Flash-Next",
21813
+ blurb: "The official BF16 release of Qwen's experimental Qwen4-preview architecture - 180B total with only 6B activated, plus 51B n-gram embeddings, a vision encoder, and a 262K native context. For vLLM/SGLang on multi-GPU server hardware.",
21814
+ contextLength: 262144,
21815
+ fileSizeBytes: 360000192888,
21816
+ format: "safetensors",
21817
+ license: "qwen-community-1.0",
21818
+ parameterCount: "180B",
21819
+ quantization: "BF16",
21820
+ slug: "Qwen/Qwen3.8-Flash-Next",
21821
+ sourceURL: "https://huggingface.co/Qwen/Qwen3.8-Flash-Next",
21822
+ vramTierGB: 640
21823
+ },
21824
+ {
21825
+ author: "RadixArk",
21826
+ baseModel: "Qwen/Qwen3.8-Flash-Next",
21827
+ blurb: "Qwen3.8-Flash-Next with its routed experts quantized to NVIDIA's NVFP4 W4A4 via Model Optimizer - 135 GB instead of 360 GB BF16, keeping GSM8K/AIME in-band with the reference. Serve with SGLang on Blackwell hardware.",
21828
+ contextLength: 262144,
21829
+ fileSizeBytes: 135195303851,
21830
+ format: "safetensors",
21831
+ license: "qwen-community-1.0",
21832
+ parameterCount: "180B",
21833
+ quantization: "NVFP4",
21834
+ slug: "RadixArk/Qwen3.8-Flash-Next-NVFP4",
21835
+ sourceURL: "https://huggingface.co/RadixArk/Qwen3.8-Flash-Next-NVFP4",
21836
+ vramTierGB: 256
21837
+ },
21838
+ {
21839
+ author: "deepseek-ai",
21840
+ baseModel: "deepseek-ai/DeepSeek-V4-Flash",
21841
+ blurb: "DeepSeek's V4-Flash - a 284B MoE with only 13B activated and a native 1M context window, shipped in mixed FP4/FP8 precision. Hybrid CSA/HCA attention keeps long-context inference cheap, with three reasoning-effort modes including Think Max.",
21842
+ contextLength: 1048576,
21843
+ fileSizeBytes: 159617149040,
21844
+ format: "safetensors",
21845
+ license: "mit",
21846
+ parameterCount: "284B",
21847
+ quantization: "FP4-FP8",
21848
+ slug: "deepseek-ai/DeepSeek-V4-Flash",
21849
+ sourceURL: "https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash",
21850
+ vramTierGB: 256
21851
+ },
21852
+ {
21853
+ author: "unsloth",
21854
+ baseModel: "deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
21855
+ blurb: "Unsloth's 2-bit cut of DeepSeek's experimental vision variant of V4-Flash - the same 284B MoE with 13B activated, plus image understanding via a separate mmproj projector. Fits 128 GB of unified memory or 2x 96 GB.",
21856
+ contextLength: 1048576,
21857
+ fileSizeBytes: 90692965818,
21858
+ format: "gguf",
21859
+ license: "mit",
21860
+ parameterCount: "284B",
21861
+ quantization: "UD-IQ2_XXS",
21862
+ slug: "unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21863
+ sourceURL: "https://huggingface.co/unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21864
+ vramTierGB: 128
21865
+ },
21866
+ {
21867
+ author: "unsloth",
21868
+ baseModel: "deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
21869
+ blurb: "The 4-bit cut of DeepSeek's experimental vision variant of V4-Flash - 284B MoE with 13B activated at higher fidelity, with multimodal agent scores well clear of the text-only release. Image input currently needs a preview llama.cpp build; needs 256 GB.",
21870
+ contextLength: 1048576,
21871
+ fileSizeBytes: 136662447706,
21872
+ format: "gguf",
21873
+ license: "mit",
21874
+ parameterCount: "284B",
21875
+ quantization: "UD-IQ4_XS",
21876
+ slug: "unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21877
+ sourceURL: "https://huggingface.co/unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21878
+ vramTierGB: 256
21879
+ },
21684
21880
  {
21685
21881
  author: "unsloth",
21686
21882
  baseModel: "Qwen/Qwen3.6-27B",
@@ -137740,12 +137936,20 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
137740
137936
  const deviceDir = path$1.join(DRM_PATH, entry, "device");
137741
137937
  const totalStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_vram_total"));
137742
137938
  const usedStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_vram_used"));
137939
+ const gttTotalStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_gtt_total"));
137940
+ const gttUsedStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_gtt_used"));
137743
137941
  const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
137744
137942
  const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
137943
+ const gttTotalBytes = gttTotalStr !== null ? parseInt(gttTotalStr, 10) : null;
137944
+ const gttUsedBytes = gttUsedStr !== null ? parseInt(gttUsedStr, 10) : null;
137745
137945
  const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
137746
137946
  const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
137947
+ const validGttTotal = gttTotalBytes !== null && Number.isFinite(gttTotalBytes) && gttTotalBytes >= 0;
137948
+ const validGttUsed = gttUsedBytes !== null && Number.isFinite(gttUsedBytes) && gttUsedBytes >= 0;
137747
137949
  if (validTotal) {
137748
137950
  return {
137951
+ gttTotalBytes: validGttTotal ? gttTotalBytes : null,
137952
+ gttUsedBytes: validGttUsed ? gttUsedBytes : null,
137749
137953
  memoryTotalBytes: totalBytes,
137750
137954
  memoryUsedBytes: validUsed ? usedBytes : null
137751
137955
  };
@@ -137755,36 +137959,74 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
137755
137959
  catch {
137756
137960
  // sysfs not available
137757
137961
  }
137758
- return { memoryTotalBytes: null, memoryUsedBytes: null };
137962
+ return {
137963
+ gttTotalBytes: null,
137964
+ gttUsedBytes: null,
137965
+ memoryTotalBytes: null,
137966
+ memoryUsedBytes: null
137967
+ };
137759
137968
  }
137760
- async function detectVRAMViaRocmSmi() {
137969
+ // rocm-smi accepts a single --showmeminfo type per invocation, so VRAM and GTT
137970
+ // arrive from separate calls; both share this per-card parser.
137971
+ function parseRocmSmiMemory({ keyPrefix, stdout }) {
137972
+ const parsed = JSON.parse(stdout);
137973
+ const results = [];
137974
+ const cards = Object.entries(parsed).filter(([key]) => key.startsWith("card"));
137975
+ for (const [, data] of cards) {
137976
+ const bus = data["PCI Bus"] ?? null;
137977
+ const totalStr = data[`${keyPrefix} Total Memory (B)`] ?? null;
137978
+ const usedStr = data[`${keyPrefix} Total Used Memory (B)`] ?? null;
137979
+ const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
137980
+ const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
137981
+ const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
137982
+ const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
137983
+ if (bus) {
137984
+ results.push({
137985
+ bus,
137986
+ memoryTotalBytes: validTotal ? totalBytes : null,
137987
+ memoryUsedBytes: validUsed ? usedBytes : null
137988
+ });
137989
+ }
137990
+ }
137991
+ return results;
137992
+ }
137993
+ const ROCM_SMI_TIMEOUT_MS = 10_000;
137994
+ async function detectVRAMViaRocmSmi({ logger }) {
137761
137995
  try {
137762
- const { stdout } = await execa("rocm-smi", [
137763
- "--showbus",
137764
- "--showmeminfo",
137765
- "vram",
137766
- "--json"
137996
+ const [vramResult, gttResult] = await Promise.allSettled([
137997
+ execa("rocm-smi", ["--showbus", "--showmeminfo", "vram", "--json"], {
137998
+ timeout: ROCM_SMI_TIMEOUT_MS
137999
+ }),
138000
+ execa("rocm-smi", ["--showbus", "--showmeminfo", "gtt", "--json"], {
138001
+ timeout: ROCM_SMI_TIMEOUT_MS
138002
+ })
137767
138003
  ]);
137768
- const parsed = JSON.parse(stdout);
137769
- const results = [];
137770
- const cards = Object.entries(parsed).filter(([key]) => key.startsWith("card"));
137771
- for (const [, data] of cards) {
137772
- const bus = data["PCI Bus"] ?? null;
137773
- const totalStr = data["VRAM Total Memory (B)"] ?? null;
137774
- const usedStr = data["VRAM Total Used Memory (B)"] ?? null;
137775
- const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
137776
- const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
137777
- const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
137778
- const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
137779
- if (bus) {
137780
- results.push({
137781
- bus,
137782
- memoryTotalBytes: validTotal ? totalBytes : null,
137783
- memoryUsedBytes: validUsed ? usedBytes : null
138004
+ if (vramResult.status !== "fulfilled")
138005
+ return [];
138006
+ let gttEntries = [];
138007
+ if (gttResult.status === "fulfilled") {
138008
+ try {
138009
+ gttEntries = parseRocmSmiMemory({
138010
+ keyPrefix: "GTT",
138011
+ stdout: gttResult.value.stdout
137784
138012
  });
137785
138013
  }
138014
+ catch (error) {
138015
+ // Unusable GTT output (e.g. older rocm-smi) degrades to VRAM-only
138016
+ logger.warn("rocm-smi GTT output parse failed", { error: asError(error) });
138017
+ }
137786
138018
  }
137787
- return results;
138019
+ const gttByBus = new Map(gttEntries.map(entry => [entry.bus, entry]));
138020
+ return parseRocmSmiMemory({ keyPrefix: "VRAM", stdout: vramResult.value.stdout }).map(vram => {
138021
+ const gtt = gttByBus.get(vram.bus);
138022
+ return {
138023
+ bus: vram.bus,
138024
+ gttTotalBytes: gtt?.memoryTotalBytes ?? null,
138025
+ gttUsedBytes: gtt?.memoryUsedBytes ?? null,
138026
+ memoryTotalBytes: vram.memoryTotalBytes,
138027
+ memoryUsedBytes: vram.memoryUsedBytes
138028
+ };
138029
+ });
137788
138030
  }
137789
138031
  catch {
137790
138032
  return [];
@@ -137849,8 +138091,9 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
137849
138091
  vendor: "NVIDIA"
137850
138092
  });
137851
138093
  }
137852
- // Unified-memory devices (e.g. NVIDIA GB10 on DGX Spark) report "[N/A]" for
137853
- // memory. Their compute pool is system RAM, so fall back to /proc/meminfo.
138094
+ // Shared/unified-memory devices (e.g. NVIDIA GB10 on DGX Spark) report
138095
+ // "[N/A]" for memory. Their compute pool is system RAM, so fall back to
138096
+ // /proc/meminfo. (AMD iGPUs get the same treatment via GTT merging.)
137854
138097
  if (gpus.some(gpu => gpu.memoryTotalBytes === null)) {
137855
138098
  const systemMemory = await readSystemMemoryBytes({ logger });
137856
138099
  const total = systemMemory.totalBytes;
@@ -137874,7 +138117,7 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
137874
138117
  }
137875
138118
  }
137876
138119
  function buildMergedGPUs(options) {
137877
- const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, sysfsVRAMMap } = options;
138120
+ const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, systemTotalBytes, sysfsVRAMMap } = options;
137878
138121
  const rocmByBus = new Map();
137879
138122
  for (const entry of rocmVRAM) {
137880
138123
  const key = normalizeBusAddress(entry.bus);
@@ -137915,6 +138158,7 @@ function buildMergedGPUs(options) {
137915
138158
  gpu: existing,
137916
138159
  key,
137917
138160
  rocmByBus,
138161
+ systemTotalBytes,
137918
138162
  sysfsVRAMMap
137919
138163
  });
137920
138164
  }
@@ -137934,30 +138178,73 @@ function buildMergedGPUs(options) {
137934
138178
  gpu,
137935
138179
  key,
137936
138180
  rocmByBus,
138181
+ systemTotalBytes,
137937
138182
  sysfsVRAMMap
137938
138183
  });
137939
138184
  byBus.set(key, gpu);
137940
138185
  }
137941
138186
  return [...byBus.values()];
137942
138187
  }
137943
- function applySysfsOrRocmVRAM({ gpu, key, rocmByBus, sysfsVRAMMap }) {
138188
+ // Shared-memory GPUs (AMD iGPUs) expose a small dedicated VRAM carve-out via
138189
+ // mem_info_vram_total while the real usable pool - GTT - is carved dynamically
138190
+ // from system RAM. When GTT exceeds VRAM the device is treated as integrated
138191
+ // and the two pools combine (capped by installed RAM). Note that GTT defaults
138192
+ // to half of system RAM even on discrete GPUs, so a dGPU with less VRAM than
138193
+ // that also merges - acceptable, since GTT remains real addressable memory
138194
+ // for amdgpu compute (albeit slower over PCIe).
138195
+ function mergeGTTMemory({ gttTotalBytes, gttUsedBytes, systemTotalBytes, vramTotalBytes, vramUsedBytes }) {
138196
+ if (vramTotalBytes === null || !Number.isFinite(vramTotalBytes)) {
138197
+ return { memoryTotalBytes: null, memoryUsedBytes: null };
138198
+ }
138199
+ const isIntegrated = gttTotalBytes !== null && gttTotalBytes > vramTotalBytes;
138200
+ if (!isIntegrated) {
138201
+ return { memoryTotalBytes: vramTotalBytes, memoryUsedBytes: vramUsedBytes };
138202
+ }
138203
+ const combined = vramTotalBytes + gttTotalBytes;
138204
+ const capped = systemTotalBytes !== null && systemTotalBytes > 0
138205
+ ? Math.min(combined, systemTotalBytes)
138206
+ : combined;
138207
+ const hasCompleteUsage = vramUsedBytes !== null &&
138208
+ Number.isFinite(vramUsedBytes) &&
138209
+ gttUsedBytes !== null &&
138210
+ Number.isFinite(gttUsedBytes);
138211
+ const memoryUsedBytes = hasCompleteUsage
138212
+ ? Math.min(vramUsedBytes + gttUsedBytes, capped)
138213
+ : null;
138214
+ return { memoryTotalBytes: capped, memoryUsedBytes };
138215
+ }
138216
+ function applySysfsOrRocmVRAM({ gpu, key, rocmByBus, systemTotalBytes, sysfsVRAMMap }) {
137944
138217
  const sysfs = sysfsVRAMMap.get(key);
137945
- let totalBytes = sysfs?.memoryTotalBytes ?? null;
137946
- let usedBytes = sysfs?.memoryUsedBytes ?? null;
137947
- if (totalBytes === null) {
138218
+ let vramTotalBytes = sysfs?.memoryTotalBytes ?? null;
138219
+ let vramUsedBytes = sysfs?.memoryUsedBytes ?? null;
138220
+ let gttTotalBytes = sysfs?.gttTotalBytes ?? null;
138221
+ let gttUsedBytes = sysfs?.gttUsedBytes ?? null;
138222
+ if (vramTotalBytes === null) {
137948
138223
  const rocm = rocmByBus.get(key);
137949
138224
  if (rocm) {
137950
- totalBytes = rocm.memoryTotalBytes;
137951
- usedBytes = rocm.memoryUsedBytes;
137952
- }
137953
- }
137954
- if (totalBytes === null || !Number.isFinite(totalBytes))
138225
+ vramTotalBytes = rocm.memoryTotalBytes;
138226
+ vramUsedBytes = rocm.memoryUsedBytes;
138227
+ gttTotalBytes = rocm.gttTotalBytes;
138228
+ gttUsedBytes = rocm.gttUsedBytes;
138229
+ }
138230
+ }
138231
+ const merged = mergeGTTMemory({
138232
+ gttTotalBytes,
138233
+ gttUsedBytes,
138234
+ systemTotalBytes,
138235
+ vramTotalBytes,
138236
+ vramUsedBytes
138237
+ });
138238
+ if (merged.memoryTotalBytes === null || !Number.isFinite(merged.memoryTotalBytes))
137955
138239
  return;
137956
- gpu.memoryTotalBytes = totalBytes;
137957
- gpu.memoryUsedBytes = usedBytes !== null && Number.isFinite(usedBytes) ? usedBytes : null;
138240
+ gpu.memoryTotalBytes = merged.memoryTotalBytes;
138241
+ gpu.memoryUsedBytes =
138242
+ merged.memoryUsedBytes !== null && Number.isFinite(merged.memoryUsedBytes)
138243
+ ? merged.memoryUsedBytes
138244
+ : null;
137958
138245
  gpu.memoryFreeBytes =
137959
- usedBytes !== null && Number.isFinite(usedBytes)
137960
- ? Math.max(totalBytes - usedBytes, 0)
138246
+ gpu.memoryUsedBytes !== null
138247
+ ? Math.max(gpu.memoryTotalBytes - gpu.memoryUsedBytes, 0)
137961
138248
  : null;
137962
138249
  }
137963
138250
  async function collectMachineMetadata({ logger }) {
@@ -137968,7 +138255,7 @@ async function collectMachineMetadata({ logger }) {
137968
138255
  si.graphics(),
137969
138256
  detectGPUsViaLspci(),
137970
138257
  detectGPUsViaNvidiaSmi({ logger }),
137971
- detectVRAMViaRocmSmi()
138258
+ detectVRAMViaRocmSmi({ logger })
137972
138259
  ]);
137973
138260
  const cpuInfo = cpuResult.status === "fulfilled" ? cpuResult.value : null;
137974
138261
  const memInfo = memResult.status === "fulfilled" ? memResult.value : null;
@@ -138023,6 +138310,7 @@ async function collectMachineMetadata({ logger }) {
138023
138310
  nvidiaGPUs: resolvedNvidiaGPUs,
138024
138311
  rocmVRAM: resolvedRocmVRAM,
138025
138312
  siGPUs,
138313
+ systemTotalBytes: memInfo?.total ?? null,
138026
138314
  sysfsVRAMMap
138027
138315
  });
138028
138316
  const machineMetadata = {
@@ -342696,12 +342984,13 @@ async function runModelFit(options) {
342696
342984
  console.log();
342697
342985
  const feasible = filterFeasibleModels({ detection, models: recommendedModels });
342698
342986
  if (feasible.length === 0) {
342987
+ const smallestTierGB = Math.min(...recommendedModels.map(model => model.vramTierGB));
342699
342988
  console.error("No recommended models fit this hardware. " +
342700
342989
  `Budget: ${formatBytes$1(detection.gpus.some(gpu => gpu.memoryTotalBytes)
342701
342990
  ? Math.max(...detection.gpus
342702
342991
  .map(gpu => gpu.memoryTotalBytes ?? 0)
342703
342992
  .filter(bytes => bytes > 0))
342704
- : detection.memory.totalBytes)}. Smallest tier starts at 16 GB.`);
342993
+ : detection.memory.totalBytes)}. Smallest tier starts at ${smallestTierGB} GB.`);
342705
342994
  process.exitCode = 1;
342706
342995
  return;
342707
342996
  }
package/dist/cli.sea.cjs CHANGED
@@ -21581,6 +21581,90 @@ object$5({
21581
21581
  // Source of truth for curated recommended models. Consumed by @infersec/definitions
21582
21582
  // and the website model page generator (applications/website/scripts/generate-model-pages.ts).
21583
21583
  const modelsData = [
21584
+ {
21585
+ author: "LiquidAI",
21586
+ baseModel: "LiquidAI/LFM2.5-2.6B",
21587
+ blurb: "Liquid's LFM2.5 2.6B hybrid at high fidelity - agentic post-training, 128K context, and 220 tok/s decode on an M5 Max. Fits a 4 GB card with room to spare.",
21588
+ contextLength: 131072,
21589
+ fileSizeBytes: 2874779648,
21590
+ format: "gguf",
21591
+ license: "lfm1.0",
21592
+ parameterCount: "2.6B",
21593
+ quantization: "Q8_0",
21594
+ slug: "LiquidAI/LFM2.5-2.6B-GGUF",
21595
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-2.6B-GGUF",
21596
+ vramTierGB: 4
21597
+ },
21598
+ {
21599
+ author: "LiquidAI",
21600
+ baseModel: "LiquidAI/LFM2.5-2.6B",
21601
+ blurb: "Liquid's LFM2.5 2.6B hybrid at 4-bit - competitive with models 4x its size on tool use and multi-step agentic tasks. A 1.7 GB download that sips memory.",
21602
+ contextLength: 131072,
21603
+ fileSizeBytes: 1674455040,
21604
+ format: "gguf",
21605
+ license: "lfm1.0",
21606
+ parameterCount: "2.6B",
21607
+ quantization: "Q4_K_M",
21608
+ slug: "LiquidAI/LFM2.5-2.6B-GGUF",
21609
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-2.6B-GGUF",
21610
+ vramTierGB: 4
21611
+ },
21612
+ {
21613
+ author: "LiquidAI",
21614
+ baseModel: "LiquidAI/LFM2.5-2.6B",
21615
+ blurb: "The native BF16 checkpoint of Liquid's LFM2.5 2.6B for vLLM and SGLang - hybrid conv + GQA architecture, 128K context window, and agentic reinforcement-learning post-training.",
21616
+ contextLength: 131072,
21617
+ fileSizeBytes: 5394427456,
21618
+ format: "safetensors",
21619
+ license: "lfm1.0",
21620
+ parameterCount: "2.6B",
21621
+ quantization: "BF16",
21622
+ slug: "LiquidAI/LFM2.5-2.6B",
21623
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-2.6B",
21624
+ vramTierGB: 8
21625
+ },
21626
+ {
21627
+ author: "LiquidAI",
21628
+ baseModel: "LiquidAI/LFM2.5-230M",
21629
+ blurb: "Liquid's most compact LFM2.5 at high fidelity - a 230M edge model distilled from the 350M, tuned for tool use and data extraction. 213 tok/s decode on a Galaxy S25 Ultra.",
21630
+ contextLength: 32768,
21631
+ fileSizeBytes: 246598496,
21632
+ format: "gguf",
21633
+ license: "lfm1.0",
21634
+ parameterCount: "230M",
21635
+ quantization: "Q8_0",
21636
+ slug: "LiquidAI/LFM2.5-230M-GGUF",
21637
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-230M-GGUF",
21638
+ vramTierGB: 2
21639
+ },
21640
+ {
21641
+ author: "LiquidAI",
21642
+ baseModel: "LiquidAI/LFM2.5-230M",
21643
+ blurb: "Liquid's most compact LFM2.5 at 4-bit - a 153 MB download that brings real tool-use capability to the tightest memory budgets, from Raspberry Pi to phones.",
21644
+ contextLength: 32768,
21645
+ fileSizeBytes: 153406304,
21646
+ format: "gguf",
21647
+ license: "lfm1.0",
21648
+ parameterCount: "230M",
21649
+ quantization: "Q4_K_M",
21650
+ slug: "LiquidAI/LFM2.5-230M-GGUF",
21651
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-230M-GGUF",
21652
+ vramTierGB: 2
21653
+ },
21654
+ {
21655
+ author: "LiquidAI",
21656
+ baseModel: "LiquidAI/LFM2.5-230M",
21657
+ blurb: "The native BF16 checkpoint of Liquid's LFM2.5 230M for vLLM - a 230M edge model with a 32K context window, suited to data extraction and lightweight on-device agent pipelines.",
21658
+ contextLength: 32768,
21659
+ fileSizeBytes: 459401112,
21660
+ format: "safetensors",
21661
+ license: "lfm1.0",
21662
+ parameterCount: "230M",
21663
+ quantization: "BF16",
21664
+ slug: "LiquidAI/LFM2.5-230M",
21665
+ sourceURL: "https://huggingface.co/LiquidAI/LFM2.5-230M",
21666
+ vramTierGB: 4
21667
+ },
21584
21668
  {
21585
21669
  author: "Qwen",
21586
21670
  baseModel: "Qwen/Qwen3-8B",
@@ -21637,6 +21721,48 @@ const modelsData = [
21637
21721
  sourceURL: "https://huggingface.co/bartowski/deepreinforce-ai_Ornith-1.0-9B-GGUF",
21638
21722
  vramTierGB: 16
21639
21723
  },
21724
+ {
21725
+ author: "unsloth",
21726
+ baseModel: "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16",
21727
+ blurb: "NVIDIA's Nemotron 3.5 Lightning MoE squeezed into 2-bit - 30B total with only 3B active in a Mamba-2 + MoE + attention hybrid. Configurable thinking and a 256K context on a single 24 GB card.",
21728
+ contextLength: 262144,
21729
+ fileSizeBytes: 19432070208,
21730
+ format: "gguf",
21731
+ license: "openmdw-1.1",
21732
+ parameterCount: "30B",
21733
+ quantization: "UD-IQ2_M",
21734
+ slug: "unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21735
+ sourceURL: "https://huggingface.co/unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21736
+ vramTierGB: 24
21737
+ },
21738
+ {
21739
+ author: "unsloth",
21740
+ baseModel: "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16",
21741
+ blurb: "NVIDIA's Nemotron 3.5 Lightning at Unsloth's dynamic 4-bit - a Mamba-2 hybrid MoE with 3B active parameters, toggleable reasoning, and strong instruction following (IFBench 71.9). Needs a 32 GB GPU.",
21742
+ contextLength: 262144,
21743
+ fileSizeBytes: 25266255936,
21744
+ format: "gguf",
21745
+ license: "openmdw-1.1",
21746
+ parameterCount: "30B",
21747
+ quantization: "UD-Q4_K_M",
21748
+ slug: "unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21749
+ sourceURL: "https://huggingface.co/unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21750
+ vramTierGB: 32
21751
+ },
21752
+ {
21753
+ author: "unsloth",
21754
+ baseModel: "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16",
21755
+ blurb: "Nemotron 3.5 Lightning at 8-bit fidelity - near-reference quality from the 30B-A3B Mamba-2 hybrid, with configurable thinking and DSpark speculative decoding support. Needs a 48 GB card.",
21756
+ contextLength: 262144,
21757
+ fileSizeBytes: 35004643392,
21758
+ format: "gguf",
21759
+ license: "openmdw-1.1",
21760
+ parameterCount: "30B",
21761
+ quantization: "Q8_0",
21762
+ slug: "unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21763
+ sourceURL: "https://huggingface.co/unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF",
21764
+ vramTierGB: 48
21765
+ },
21640
21766
  {
21641
21767
  author: "unsloth",
21642
21768
  baseModel: "openai/gpt-oss-20b",
@@ -21695,6 +21821,76 @@ const modelsData = [
21695
21821
  sourceURL: "https://huggingface.co/unsloth/Qwen3.8-Flash-Next-GGUF",
21696
21822
  vramTierGB: 128
21697
21823
  },
21824
+ {
21825
+ author: "Qwen",
21826
+ baseModel: "Qwen/Qwen3.8-Flash-Next",
21827
+ blurb: "The official BF16 release of Qwen's experimental Qwen4-preview architecture - 180B total with only 6B activated, plus 51B n-gram embeddings, a vision encoder, and a 262K native context. For vLLM/SGLang on multi-GPU server hardware.",
21828
+ contextLength: 262144,
21829
+ fileSizeBytes: 360000192888,
21830
+ format: "safetensors",
21831
+ license: "qwen-community-1.0",
21832
+ parameterCount: "180B",
21833
+ quantization: "BF16",
21834
+ slug: "Qwen/Qwen3.8-Flash-Next",
21835
+ sourceURL: "https://huggingface.co/Qwen/Qwen3.8-Flash-Next",
21836
+ vramTierGB: 640
21837
+ },
21838
+ {
21839
+ author: "RadixArk",
21840
+ baseModel: "Qwen/Qwen3.8-Flash-Next",
21841
+ blurb: "Qwen3.8-Flash-Next with its routed experts quantized to NVIDIA's NVFP4 W4A4 via Model Optimizer - 135 GB instead of 360 GB BF16, keeping GSM8K/AIME in-band with the reference. Serve with SGLang on Blackwell hardware.",
21842
+ contextLength: 262144,
21843
+ fileSizeBytes: 135195303851,
21844
+ format: "safetensors",
21845
+ license: "qwen-community-1.0",
21846
+ parameterCount: "180B",
21847
+ quantization: "NVFP4",
21848
+ slug: "RadixArk/Qwen3.8-Flash-Next-NVFP4",
21849
+ sourceURL: "https://huggingface.co/RadixArk/Qwen3.8-Flash-Next-NVFP4",
21850
+ vramTierGB: 256
21851
+ },
21852
+ {
21853
+ author: "deepseek-ai",
21854
+ baseModel: "deepseek-ai/DeepSeek-V4-Flash",
21855
+ blurb: "DeepSeek's V4-Flash - a 284B MoE with only 13B activated and a native 1M context window, shipped in mixed FP4/FP8 precision. Hybrid CSA/HCA attention keeps long-context inference cheap, with three reasoning-effort modes including Think Max.",
21856
+ contextLength: 1048576,
21857
+ fileSizeBytes: 159617149040,
21858
+ format: "safetensors",
21859
+ license: "mit",
21860
+ parameterCount: "284B",
21861
+ quantization: "FP4-FP8",
21862
+ slug: "deepseek-ai/DeepSeek-V4-Flash",
21863
+ sourceURL: "https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash",
21864
+ vramTierGB: 256
21865
+ },
21866
+ {
21867
+ author: "unsloth",
21868
+ baseModel: "deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
21869
+ blurb: "Unsloth's 2-bit cut of DeepSeek's experimental vision variant of V4-Flash - the same 284B MoE with 13B activated, plus image understanding via a separate mmproj projector. Fits 128 GB of unified memory or 2x 96 GB.",
21870
+ contextLength: 1048576,
21871
+ fileSizeBytes: 90692965818,
21872
+ format: "gguf",
21873
+ license: "mit",
21874
+ parameterCount: "284B",
21875
+ quantization: "UD-IQ2_XXS",
21876
+ slug: "unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21877
+ sourceURL: "https://huggingface.co/unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21878
+ vramTierGB: 128
21879
+ },
21880
+ {
21881
+ author: "unsloth",
21882
+ baseModel: "deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
21883
+ blurb: "The 4-bit cut of DeepSeek's experimental vision variant of V4-Flash - 284B MoE with 13B activated at higher fidelity, with multimodal agent scores well clear of the text-only release. Image input currently needs a preview llama.cpp build; needs 256 GB.",
21884
+ contextLength: 1048576,
21885
+ fileSizeBytes: 136662447706,
21886
+ format: "gguf",
21887
+ license: "mit",
21888
+ parameterCount: "284B",
21889
+ quantization: "UD-IQ4_XS",
21890
+ slug: "unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21891
+ sourceURL: "https://huggingface.co/unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF",
21892
+ vramTierGB: 256
21893
+ },
21698
21894
  {
21699
21895
  author: "unsloth",
21700
21896
  baseModel: "Qwen/Qwen3.6-27B",
@@ -157969,12 +158165,20 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
157969
158165
  const deviceDir = path$1.join(DRM_PATH, entry, "device");
157970
158166
  const totalStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_vram_total"));
157971
158167
  const usedStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_vram_used"));
158168
+ const gttTotalStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_gtt_total"));
158169
+ const gttUsedStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_gtt_used"));
157972
158170
  const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
157973
158171
  const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
158172
+ const gttTotalBytes = gttTotalStr !== null ? parseInt(gttTotalStr, 10) : null;
158173
+ const gttUsedBytes = gttUsedStr !== null ? parseInt(gttUsedStr, 10) : null;
157974
158174
  const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
157975
158175
  const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
158176
+ const validGttTotal = gttTotalBytes !== null && Number.isFinite(gttTotalBytes) && gttTotalBytes >= 0;
158177
+ const validGttUsed = gttUsedBytes !== null && Number.isFinite(gttUsedBytes) && gttUsedBytes >= 0;
157976
158178
  if (validTotal) {
157977
158179
  return {
158180
+ gttTotalBytes: validGttTotal ? gttTotalBytes : null,
158181
+ gttUsedBytes: validGttUsed ? gttUsedBytes : null,
157978
158182
  memoryTotalBytes: totalBytes,
157979
158183
  memoryUsedBytes: validUsed ? usedBytes : null
157980
158184
  };
@@ -157984,36 +158188,74 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
157984
158188
  catch {
157985
158189
  // sysfs not available
157986
158190
  }
157987
- return { memoryTotalBytes: null, memoryUsedBytes: null };
158191
+ return {
158192
+ gttTotalBytes: null,
158193
+ gttUsedBytes: null,
158194
+ memoryTotalBytes: null,
158195
+ memoryUsedBytes: null
158196
+ };
157988
158197
  }
157989
- async function detectVRAMViaRocmSmi() {
158198
+ // rocm-smi accepts a single --showmeminfo type per invocation, so VRAM and GTT
158199
+ // arrive from separate calls; both share this per-card parser.
158200
+ function parseRocmSmiMemory({ keyPrefix, stdout }) {
158201
+ const parsed = JSON.parse(stdout);
158202
+ const results = [];
158203
+ const cards = Object.entries(parsed).filter(([key]) => key.startsWith("card"));
158204
+ for (const [, data] of cards) {
158205
+ const bus = data["PCI Bus"] ?? null;
158206
+ const totalStr = data[`${keyPrefix} Total Memory (B)`] ?? null;
158207
+ const usedStr = data[`${keyPrefix} Total Used Memory (B)`] ?? null;
158208
+ const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
158209
+ const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
158210
+ const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
158211
+ const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
158212
+ if (bus) {
158213
+ results.push({
158214
+ bus,
158215
+ memoryTotalBytes: validTotal ? totalBytes : null,
158216
+ memoryUsedBytes: validUsed ? usedBytes : null
158217
+ });
158218
+ }
158219
+ }
158220
+ return results;
158221
+ }
158222
+ const ROCM_SMI_TIMEOUT_MS = 10_000;
158223
+ async function detectVRAMViaRocmSmi({ logger }) {
157990
158224
  try {
157991
- const { stdout } = await execa("rocm-smi", [
157992
- "--showbus",
157993
- "--showmeminfo",
157994
- "vram",
157995
- "--json"
158225
+ const [vramResult, gttResult] = await Promise.allSettled([
158226
+ execa("rocm-smi", ["--showbus", "--showmeminfo", "vram", "--json"], {
158227
+ timeout: ROCM_SMI_TIMEOUT_MS
158228
+ }),
158229
+ execa("rocm-smi", ["--showbus", "--showmeminfo", "gtt", "--json"], {
158230
+ timeout: ROCM_SMI_TIMEOUT_MS
158231
+ })
157996
158232
  ]);
157997
- const parsed = JSON.parse(stdout);
157998
- const results = [];
157999
- const cards = Object.entries(parsed).filter(([key]) => key.startsWith("card"));
158000
- for (const [, data] of cards) {
158001
- const bus = data["PCI Bus"] ?? null;
158002
- const totalStr = data["VRAM Total Memory (B)"] ?? null;
158003
- const usedStr = data["VRAM Total Used Memory (B)"] ?? null;
158004
- const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
158005
- const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
158006
- const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
158007
- const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
158008
- if (bus) {
158009
- results.push({
158010
- bus,
158011
- memoryTotalBytes: validTotal ? totalBytes : null,
158012
- memoryUsedBytes: validUsed ? usedBytes : null
158233
+ if (vramResult.status !== "fulfilled")
158234
+ return [];
158235
+ let gttEntries = [];
158236
+ if (gttResult.status === "fulfilled") {
158237
+ try {
158238
+ gttEntries = parseRocmSmiMemory({
158239
+ keyPrefix: "GTT",
158240
+ stdout: gttResult.value.stdout
158013
158241
  });
158014
158242
  }
158243
+ catch (error) {
158244
+ // Unusable GTT output (e.g. older rocm-smi) degrades to VRAM-only
158245
+ logger.warn("rocm-smi GTT output parse failed", { error: asError(error) });
158246
+ }
158015
158247
  }
158016
- return results;
158248
+ const gttByBus = new Map(gttEntries.map(entry => [entry.bus, entry]));
158249
+ return parseRocmSmiMemory({ keyPrefix: "VRAM", stdout: vramResult.value.stdout }).map(vram => {
158250
+ const gtt = gttByBus.get(vram.bus);
158251
+ return {
158252
+ bus: vram.bus,
158253
+ gttTotalBytes: gtt?.memoryTotalBytes ?? null,
158254
+ gttUsedBytes: gtt?.memoryUsedBytes ?? null,
158255
+ memoryTotalBytes: vram.memoryTotalBytes,
158256
+ memoryUsedBytes: vram.memoryUsedBytes
158257
+ };
158258
+ });
158017
158259
  }
158018
158260
  catch {
158019
158261
  return [];
@@ -158078,8 +158320,9 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
158078
158320
  vendor: "NVIDIA"
158079
158321
  });
158080
158322
  }
158081
- // Unified-memory devices (e.g. NVIDIA GB10 on DGX Spark) report "[N/A]" for
158082
- // memory. Their compute pool is system RAM, so fall back to /proc/meminfo.
158323
+ // Shared/unified-memory devices (e.g. NVIDIA GB10 on DGX Spark) report
158324
+ // "[N/A]" for memory. Their compute pool is system RAM, so fall back to
158325
+ // /proc/meminfo. (AMD iGPUs get the same treatment via GTT merging.)
158083
158326
  if (gpus.some(gpu => gpu.memoryTotalBytes === null)) {
158084
158327
  const systemMemory = await readSystemMemoryBytes({ logger });
158085
158328
  const total = systemMemory.totalBytes;
@@ -158103,7 +158346,7 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
158103
158346
  }
158104
158347
  }
158105
158348
  function buildMergedGPUs(options) {
158106
- const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, sysfsVRAMMap } = options;
158349
+ const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, systemTotalBytes, sysfsVRAMMap } = options;
158107
158350
  const rocmByBus = new Map();
158108
158351
  for (const entry of rocmVRAM) {
158109
158352
  const key = normalizeBusAddress(entry.bus);
@@ -158144,6 +158387,7 @@ function buildMergedGPUs(options) {
158144
158387
  gpu: existing,
158145
158388
  key,
158146
158389
  rocmByBus,
158390
+ systemTotalBytes,
158147
158391
  sysfsVRAMMap
158148
158392
  });
158149
158393
  }
@@ -158163,30 +158407,73 @@ function buildMergedGPUs(options) {
158163
158407
  gpu,
158164
158408
  key,
158165
158409
  rocmByBus,
158410
+ systemTotalBytes,
158166
158411
  sysfsVRAMMap
158167
158412
  });
158168
158413
  byBus.set(key, gpu);
158169
158414
  }
158170
158415
  return [...byBus.values()];
158171
158416
  }
158172
- function applySysfsOrRocmVRAM({ gpu, key, rocmByBus, sysfsVRAMMap }) {
158417
+ // Shared-memory GPUs (AMD iGPUs) expose a small dedicated VRAM carve-out via
158418
+ // mem_info_vram_total while the real usable pool - GTT - is carved dynamically
158419
+ // from system RAM. When GTT exceeds VRAM the device is treated as integrated
158420
+ // and the two pools combine (capped by installed RAM). Note that GTT defaults
158421
+ // to half of system RAM even on discrete GPUs, so a dGPU with less VRAM than
158422
+ // that also merges - acceptable, since GTT remains real addressable memory
158423
+ // for amdgpu compute (albeit slower over PCIe).
158424
+ function mergeGTTMemory({ gttTotalBytes, gttUsedBytes, systemTotalBytes, vramTotalBytes, vramUsedBytes }) {
158425
+ if (vramTotalBytes === null || !Number.isFinite(vramTotalBytes)) {
158426
+ return { memoryTotalBytes: null, memoryUsedBytes: null };
158427
+ }
158428
+ const isIntegrated = gttTotalBytes !== null && gttTotalBytes > vramTotalBytes;
158429
+ if (!isIntegrated) {
158430
+ return { memoryTotalBytes: vramTotalBytes, memoryUsedBytes: vramUsedBytes };
158431
+ }
158432
+ const combined = vramTotalBytes + gttTotalBytes;
158433
+ const capped = systemTotalBytes !== null && systemTotalBytes > 0
158434
+ ? Math.min(combined, systemTotalBytes)
158435
+ : combined;
158436
+ const hasCompleteUsage = vramUsedBytes !== null &&
158437
+ Number.isFinite(vramUsedBytes) &&
158438
+ gttUsedBytes !== null &&
158439
+ Number.isFinite(gttUsedBytes);
158440
+ const memoryUsedBytes = hasCompleteUsage
158441
+ ? Math.min(vramUsedBytes + gttUsedBytes, capped)
158442
+ : null;
158443
+ return { memoryTotalBytes: capped, memoryUsedBytes };
158444
+ }
158445
+ function applySysfsOrRocmVRAM({ gpu, key, rocmByBus, systemTotalBytes, sysfsVRAMMap }) {
158173
158446
  const sysfs = sysfsVRAMMap.get(key);
158174
- let totalBytes = sysfs?.memoryTotalBytes ?? null;
158175
- let usedBytes = sysfs?.memoryUsedBytes ?? null;
158176
- if (totalBytes === null) {
158447
+ let vramTotalBytes = sysfs?.memoryTotalBytes ?? null;
158448
+ let vramUsedBytes = sysfs?.memoryUsedBytes ?? null;
158449
+ let gttTotalBytes = sysfs?.gttTotalBytes ?? null;
158450
+ let gttUsedBytes = sysfs?.gttUsedBytes ?? null;
158451
+ if (vramTotalBytes === null) {
158177
158452
  const rocm = rocmByBus.get(key);
158178
158453
  if (rocm) {
158179
- totalBytes = rocm.memoryTotalBytes;
158180
- usedBytes = rocm.memoryUsedBytes;
158181
- }
158182
- }
158183
- if (totalBytes === null || !Number.isFinite(totalBytes))
158454
+ vramTotalBytes = rocm.memoryTotalBytes;
158455
+ vramUsedBytes = rocm.memoryUsedBytes;
158456
+ gttTotalBytes = rocm.gttTotalBytes;
158457
+ gttUsedBytes = rocm.gttUsedBytes;
158458
+ }
158459
+ }
158460
+ const merged = mergeGTTMemory({
158461
+ gttTotalBytes,
158462
+ gttUsedBytes,
158463
+ systemTotalBytes,
158464
+ vramTotalBytes,
158465
+ vramUsedBytes
158466
+ });
158467
+ if (merged.memoryTotalBytes === null || !Number.isFinite(merged.memoryTotalBytes))
158184
158468
  return;
158185
- gpu.memoryTotalBytes = totalBytes;
158186
- gpu.memoryUsedBytes = usedBytes !== null && Number.isFinite(usedBytes) ? usedBytes : null;
158469
+ gpu.memoryTotalBytes = merged.memoryTotalBytes;
158470
+ gpu.memoryUsedBytes =
158471
+ merged.memoryUsedBytes !== null && Number.isFinite(merged.memoryUsedBytes)
158472
+ ? merged.memoryUsedBytes
158473
+ : null;
158187
158474
  gpu.memoryFreeBytes =
158188
- usedBytes !== null && Number.isFinite(usedBytes)
158189
- ? Math.max(totalBytes - usedBytes, 0)
158475
+ gpu.memoryUsedBytes !== null
158476
+ ? Math.max(gpu.memoryTotalBytes - gpu.memoryUsedBytes, 0)
158190
158477
  : null;
158191
158478
  }
158192
158479
  async function collectMachineMetadata({ logger }) {
@@ -158197,7 +158484,7 @@ async function collectMachineMetadata({ logger }) {
158197
158484
  si.graphics(),
158198
158485
  detectGPUsViaLspci(),
158199
158486
  detectGPUsViaNvidiaSmi({ logger }),
158200
- detectVRAMViaRocmSmi()
158487
+ detectVRAMViaRocmSmi({ logger })
158201
158488
  ]);
158202
158489
  const cpuInfo = cpuResult.status === "fulfilled" ? cpuResult.value : null;
158203
158490
  const memInfo = memResult.status === "fulfilled" ? memResult.value : null;
@@ -158252,6 +158539,7 @@ async function collectMachineMetadata({ logger }) {
158252
158539
  nvidiaGPUs: resolvedNvidiaGPUs,
158253
158540
  rocmVRAM: resolvedRocmVRAM,
158254
158541
  siGPUs,
158542
+ systemTotalBytes: memInfo?.total ?? null,
158255
158543
  sysfsVRAMMap
158256
158544
  });
158257
158545
  const machineMetadata = {
@@ -362925,12 +363213,13 @@ async function runModelFit(options) {
362925
363213
  console.log();
362926
363214
  const feasible = filterFeasibleModels({ detection, models: recommendedModels });
362927
363215
  if (feasible.length === 0) {
363216
+ const smallestTierGB = Math.min(...recommendedModels.map(model => model.vramTierGB));
362928
363217
  console.error("No recommended models fit this hardware. " +
362929
363218
  `Budget: ${formatBytes$1(detection.gpus.some(gpu => gpu.memoryTotalBytes)
362930
363219
  ? Math.max(...detection.gpus
362931
363220
  .map(gpu => gpu.memoryTotalBytes ?? 0)
362932
363221
  .filter(bytes => bytes > 0))
362933
- : detection.memory.totalBytes)}. Smallest tier starts at 16 GB.`);
363222
+ : detection.memory.totalBytes)}. Smallest tier starts at ${smallestTierGB} GB.`);
362934
363223
  process.exitCode = 1;
362935
363224
  return;
362936
363225
  }
@@ -1,8 +1,26 @@
1
1
  import type { InferenceAgentMachineMetadata } from "@infersec/definitions";
2
2
  import type { Logger } from "@infersec/logger";
3
3
  export declare function normalizeBusAddress(bus: string | null | undefined): string | null;
4
+ export declare function parseRocmSmiMemory({ keyPrefix, stdout }: {
5
+ keyPrefix: "GTT" | "VRAM";
6
+ stdout: string;
7
+ }): Array<{
8
+ bus: string;
9
+ memoryTotalBytes: number | null;
10
+ memoryUsedBytes: number | null;
11
+ }>;
4
12
  export declare function parseNvidiaSmiNumber(value: string | undefined): number | null;
5
13
  export declare function parseNvidiaSmiMemoryBytes(value: string | undefined): number | null;
14
+ export declare function mergeGTTMemory({ gttTotalBytes, gttUsedBytes, systemTotalBytes, vramTotalBytes, vramUsedBytes }: {
15
+ gttTotalBytes: number | null;
16
+ gttUsedBytes: number | null;
17
+ systemTotalBytes: number | null;
18
+ vramTotalBytes: number | null;
19
+ vramUsedBytes: number | null;
20
+ }): {
21
+ memoryTotalBytes: number | null;
22
+ memoryUsedBytes: number | null;
23
+ };
6
24
  export declare function collectMachineMetadata({ logger }: {
7
25
  logger: Logger;
8
26
  }): Promise<InferenceAgentMachineMetadata>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@infersec/conduit",
3
3
  "description": "End user conduit agent for connecting local LLMs to the cloud.",
4
- "version": "1.111.0",
4
+ "version": "1.112.1",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },