@infersec/conduit 1.112.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
@@ -137936,12 +137936,20 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
137936
137936
  const deviceDir = path$1.join(DRM_PATH, entry, "device");
137937
137937
  const totalStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_vram_total"));
137938
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"));
137939
137941
  const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
137940
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;
137941
137945
  const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
137942
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;
137943
137949
  if (validTotal) {
137944
137950
  return {
137951
+ gttTotalBytes: validGttTotal ? gttTotalBytes : null,
137952
+ gttUsedBytes: validGttUsed ? gttUsedBytes : null,
137945
137953
  memoryTotalBytes: totalBytes,
137946
137954
  memoryUsedBytes: validUsed ? usedBytes : null
137947
137955
  };
@@ -137951,36 +137959,74 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
137951
137959
  catch {
137952
137960
  // sysfs not available
137953
137961
  }
137954
- return { memoryTotalBytes: null, memoryUsedBytes: null };
137962
+ return {
137963
+ gttTotalBytes: null,
137964
+ gttUsedBytes: null,
137965
+ memoryTotalBytes: null,
137966
+ memoryUsedBytes: null
137967
+ };
137955
137968
  }
137956
- 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 }) {
137957
137995
  try {
137958
- const { stdout } = await execa("rocm-smi", [
137959
- "--showbus",
137960
- "--showmeminfo",
137961
- "vram",
137962
- "--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
+ })
137963
138003
  ]);
137964
- const parsed = JSON.parse(stdout);
137965
- const results = [];
137966
- const cards = Object.entries(parsed).filter(([key]) => key.startsWith("card"));
137967
- for (const [, data] of cards) {
137968
- const bus = data["PCI Bus"] ?? null;
137969
- const totalStr = data["VRAM Total Memory (B)"] ?? null;
137970
- const usedStr = data["VRAM Total Used Memory (B)"] ?? null;
137971
- const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
137972
- const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
137973
- const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
137974
- const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
137975
- if (bus) {
137976
- results.push({
137977
- bus,
137978
- memoryTotalBytes: validTotal ? totalBytes : null,
137979
- 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
137980
138012
  });
137981
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
+ }
137982
138018
  }
137983
- 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
+ });
137984
138030
  }
137985
138031
  catch {
137986
138032
  return [];
@@ -138045,8 +138091,9 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
138045
138091
  vendor: "NVIDIA"
138046
138092
  });
138047
138093
  }
138048
- // Unified-memory devices (e.g. NVIDIA GB10 on DGX Spark) report "[N/A]" for
138049
- // 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.)
138050
138097
  if (gpus.some(gpu => gpu.memoryTotalBytes === null)) {
138051
138098
  const systemMemory = await readSystemMemoryBytes({ logger });
138052
138099
  const total = systemMemory.totalBytes;
@@ -138070,7 +138117,7 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
138070
138117
  }
138071
138118
  }
138072
138119
  function buildMergedGPUs(options) {
138073
- const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, sysfsVRAMMap } = options;
138120
+ const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, systemTotalBytes, sysfsVRAMMap } = options;
138074
138121
  const rocmByBus = new Map();
138075
138122
  for (const entry of rocmVRAM) {
138076
138123
  const key = normalizeBusAddress(entry.bus);
@@ -138111,6 +138158,7 @@ function buildMergedGPUs(options) {
138111
138158
  gpu: existing,
138112
138159
  key,
138113
138160
  rocmByBus,
138161
+ systemTotalBytes,
138114
138162
  sysfsVRAMMap
138115
138163
  });
138116
138164
  }
@@ -138130,30 +138178,73 @@ function buildMergedGPUs(options) {
138130
138178
  gpu,
138131
138179
  key,
138132
138180
  rocmByBus,
138181
+ systemTotalBytes,
138133
138182
  sysfsVRAMMap
138134
138183
  });
138135
138184
  byBus.set(key, gpu);
138136
138185
  }
138137
138186
  return [...byBus.values()];
138138
138187
  }
138139
- 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 }) {
138140
138217
  const sysfs = sysfsVRAMMap.get(key);
138141
- let totalBytes = sysfs?.memoryTotalBytes ?? null;
138142
- let usedBytes = sysfs?.memoryUsedBytes ?? null;
138143
- 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) {
138144
138223
  const rocm = rocmByBus.get(key);
138145
138224
  if (rocm) {
138146
- totalBytes = rocm.memoryTotalBytes;
138147
- usedBytes = rocm.memoryUsedBytes;
138148
- }
138149
- }
138150
- 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))
138151
138239
  return;
138152
- gpu.memoryTotalBytes = totalBytes;
138153
- 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;
138154
138245
  gpu.memoryFreeBytes =
138155
- usedBytes !== null && Number.isFinite(usedBytes)
138156
- ? Math.max(totalBytes - usedBytes, 0)
138246
+ gpu.memoryUsedBytes !== null
138247
+ ? Math.max(gpu.memoryTotalBytes - gpu.memoryUsedBytes, 0)
138157
138248
  : null;
138158
138249
  }
138159
138250
  async function collectMachineMetadata({ logger }) {
@@ -138164,7 +138255,7 @@ async function collectMachineMetadata({ logger }) {
138164
138255
  si.graphics(),
138165
138256
  detectGPUsViaLspci(),
138166
138257
  detectGPUsViaNvidiaSmi({ logger }),
138167
- detectVRAMViaRocmSmi()
138258
+ detectVRAMViaRocmSmi({ logger })
138168
138259
  ]);
138169
138260
  const cpuInfo = cpuResult.status === "fulfilled" ? cpuResult.value : null;
138170
138261
  const memInfo = memResult.status === "fulfilled" ? memResult.value : null;
@@ -138219,6 +138310,7 @@ async function collectMachineMetadata({ logger }) {
138219
138310
  nvidiaGPUs: resolvedNvidiaGPUs,
138220
138311
  rocmVRAM: resolvedRocmVRAM,
138221
138312
  siGPUs,
138313
+ systemTotalBytes: memInfo?.total ?? null,
138222
138314
  sysfsVRAMMap
138223
138315
  });
138224
138316
  const machineMetadata = {
@@ -342892,12 +342984,13 @@ async function runModelFit(options) {
342892
342984
  console.log();
342893
342985
  const feasible = filterFeasibleModels({ detection, models: recommendedModels });
342894
342986
  if (feasible.length === 0) {
342987
+ const smallestTierGB = Math.min(...recommendedModels.map(model => model.vramTierGB));
342895
342988
  console.error("No recommended models fit this hardware. " +
342896
342989
  `Budget: ${formatBytes$1(detection.gpus.some(gpu => gpu.memoryTotalBytes)
342897
342990
  ? Math.max(...detection.gpus
342898
342991
  .map(gpu => gpu.memoryTotalBytes ?? 0)
342899
342992
  .filter(bytes => bytes > 0))
342900
- : detection.memory.totalBytes)}. Smallest tier starts at 2 GB.`);
342993
+ : detection.memory.totalBytes)}. Smallest tier starts at ${smallestTierGB} GB.`);
342901
342994
  process.exitCode = 1;
342902
342995
  return;
342903
342996
  }
package/dist/cli.sea.cjs CHANGED
@@ -158165,12 +158165,20 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
158165
158165
  const deviceDir = path$1.join(DRM_PATH, entry, "device");
158166
158166
  const totalStr = await readSysfsFile(path$1.join(deviceDir, "mem_info_vram_total"));
158167
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"));
158168
158170
  const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
158169
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;
158170
158174
  const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
158171
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;
158172
158178
  if (validTotal) {
158173
158179
  return {
158180
+ gttTotalBytes: validGttTotal ? gttTotalBytes : null,
158181
+ gttUsedBytes: validGttUsed ? gttUsedBytes : null,
158174
158182
  memoryTotalBytes: totalBytes,
158175
158183
  memoryUsedBytes: validUsed ? usedBytes : null
158176
158184
  };
@@ -158180,36 +158188,74 @@ async function detectVRAMViaSysfs(pciBusSuffix) {
158180
158188
  catch {
158181
158189
  // sysfs not available
158182
158190
  }
158183
- return { memoryTotalBytes: null, memoryUsedBytes: null };
158191
+ return {
158192
+ gttTotalBytes: null,
158193
+ gttUsedBytes: null,
158194
+ memoryTotalBytes: null,
158195
+ memoryUsedBytes: null
158196
+ };
158184
158197
  }
158185
- 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 }) {
158186
158224
  try {
158187
- const { stdout } = await execa("rocm-smi", [
158188
- "--showbus",
158189
- "--showmeminfo",
158190
- "vram",
158191
- "--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
+ })
158192
158232
  ]);
158193
- const parsed = JSON.parse(stdout);
158194
- const results = [];
158195
- const cards = Object.entries(parsed).filter(([key]) => key.startsWith("card"));
158196
- for (const [, data] of cards) {
158197
- const bus = data["PCI Bus"] ?? null;
158198
- const totalStr = data["VRAM Total Memory (B)"] ?? null;
158199
- const usedStr = data["VRAM Total Used Memory (B)"] ?? null;
158200
- const totalBytes = totalStr !== null ? parseInt(totalStr, 10) : null;
158201
- const usedBytes = usedStr !== null ? parseInt(usedStr, 10) : null;
158202
- const validTotal = totalBytes !== null && Number.isFinite(totalBytes) && totalBytes >= 0;
158203
- const validUsed = usedBytes !== null && Number.isFinite(usedBytes) && usedBytes >= 0;
158204
- if (bus) {
158205
- results.push({
158206
- bus,
158207
- memoryTotalBytes: validTotal ? totalBytes : null,
158208
- 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
158209
158241
  });
158210
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
+ }
158211
158247
  }
158212
- 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
+ });
158213
158259
  }
158214
158260
  catch {
158215
158261
  return [];
@@ -158274,8 +158320,9 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
158274
158320
  vendor: "NVIDIA"
158275
158321
  });
158276
158322
  }
158277
- // Unified-memory devices (e.g. NVIDIA GB10 on DGX Spark) report "[N/A]" for
158278
- // 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.)
158279
158326
  if (gpus.some(gpu => gpu.memoryTotalBytes === null)) {
158280
158327
  const systemMemory = await readSystemMemoryBytes({ logger });
158281
158328
  const total = systemMemory.totalBytes;
@@ -158299,7 +158346,7 @@ async function detectGPUsViaNvidiaSmi({ logger }) {
158299
158346
  }
158300
158347
  }
158301
158348
  function buildMergedGPUs(options) {
158302
- const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, sysfsVRAMMap } = options;
158349
+ const { lspciGPUs, nvidiaGPUs, rocmVRAM, siGPUs, systemTotalBytes, sysfsVRAMMap } = options;
158303
158350
  const rocmByBus = new Map();
158304
158351
  for (const entry of rocmVRAM) {
158305
158352
  const key = normalizeBusAddress(entry.bus);
@@ -158340,6 +158387,7 @@ function buildMergedGPUs(options) {
158340
158387
  gpu: existing,
158341
158388
  key,
158342
158389
  rocmByBus,
158390
+ systemTotalBytes,
158343
158391
  sysfsVRAMMap
158344
158392
  });
158345
158393
  }
@@ -158359,30 +158407,73 @@ function buildMergedGPUs(options) {
158359
158407
  gpu,
158360
158408
  key,
158361
158409
  rocmByBus,
158410
+ systemTotalBytes,
158362
158411
  sysfsVRAMMap
158363
158412
  });
158364
158413
  byBus.set(key, gpu);
158365
158414
  }
158366
158415
  return [...byBus.values()];
158367
158416
  }
158368
- 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 }) {
158369
158446
  const sysfs = sysfsVRAMMap.get(key);
158370
- let totalBytes = sysfs?.memoryTotalBytes ?? null;
158371
- let usedBytes = sysfs?.memoryUsedBytes ?? null;
158372
- 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) {
158373
158452
  const rocm = rocmByBus.get(key);
158374
158453
  if (rocm) {
158375
- totalBytes = rocm.memoryTotalBytes;
158376
- usedBytes = rocm.memoryUsedBytes;
158377
- }
158378
- }
158379
- 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))
158380
158468
  return;
158381
- gpu.memoryTotalBytes = totalBytes;
158382
- 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;
158383
158474
  gpu.memoryFreeBytes =
158384
- usedBytes !== null && Number.isFinite(usedBytes)
158385
- ? Math.max(totalBytes - usedBytes, 0)
158475
+ gpu.memoryUsedBytes !== null
158476
+ ? Math.max(gpu.memoryTotalBytes - gpu.memoryUsedBytes, 0)
158386
158477
  : null;
158387
158478
  }
158388
158479
  async function collectMachineMetadata({ logger }) {
@@ -158393,7 +158484,7 @@ async function collectMachineMetadata({ logger }) {
158393
158484
  si.graphics(),
158394
158485
  detectGPUsViaLspci(),
158395
158486
  detectGPUsViaNvidiaSmi({ logger }),
158396
- detectVRAMViaRocmSmi()
158487
+ detectVRAMViaRocmSmi({ logger })
158397
158488
  ]);
158398
158489
  const cpuInfo = cpuResult.status === "fulfilled" ? cpuResult.value : null;
158399
158490
  const memInfo = memResult.status === "fulfilled" ? memResult.value : null;
@@ -158448,6 +158539,7 @@ async function collectMachineMetadata({ logger }) {
158448
158539
  nvidiaGPUs: resolvedNvidiaGPUs,
158449
158540
  rocmVRAM: resolvedRocmVRAM,
158450
158541
  siGPUs,
158542
+ systemTotalBytes: memInfo?.total ?? null,
158451
158543
  sysfsVRAMMap
158452
158544
  });
158453
158545
  const machineMetadata = {
@@ -363121,12 +363213,13 @@ async function runModelFit(options) {
363121
363213
  console.log();
363122
363214
  const feasible = filterFeasibleModels({ detection, models: recommendedModels });
363123
363215
  if (feasible.length === 0) {
363216
+ const smallestTierGB = Math.min(...recommendedModels.map(model => model.vramTierGB));
363124
363217
  console.error("No recommended models fit this hardware. " +
363125
363218
  `Budget: ${formatBytes$1(detection.gpus.some(gpu => gpu.memoryTotalBytes)
363126
363219
  ? Math.max(...detection.gpus
363127
363220
  .map(gpu => gpu.memoryTotalBytes ?? 0)
363128
363221
  .filter(bytes => bytes > 0))
363129
- : detection.memory.totalBytes)}. Smallest tier starts at 2 GB.`);
363222
+ : detection.memory.totalBytes)}. Smallest tier starts at ${smallestTierGB} GB.`);
363130
363223
  process.exitCode = 1;
363131
363224
  return;
363132
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.112.0",
4
+ "version": "1.112.1",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },