lemonade-sdk 8.1.8__py3-none-any.whl → 8.1.10__py3-none-any.whl

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.

Potentially problematic release.


This version of lemonade-sdk might be problematic. Click here for more details.

@@ -1,9 +1,11 @@
1
1
  from abc import ABC, abstractmethod
2
2
  import importlib.metadata
3
+ import logging
3
4
  import platform
4
5
  import re
5
6
  import subprocess
6
7
  import ctypes
8
+ import glob
7
9
  from .inference_engines import detect_inference_engines
8
10
 
9
11
  # AMD GPU classification keywords - shared across all OS implementations
@@ -19,6 +21,28 @@ AMD_DISCRETE_GPU_KEYWORDS = [
19
21
  "fury",
20
22
  ]
21
23
 
24
+ # NVIDIA GPU classification keywords - shared across all OS implementations
25
+ # NVIDIA GPUs are typically discrete by default, but we include keywords for clarity
26
+ NVIDIA_DISCRETE_GPU_KEYWORDS = [
27
+ "geforce",
28
+ "rtx",
29
+ "gtx",
30
+ "quadro",
31
+ "tesla",
32
+ "titan",
33
+ "a100",
34
+ "a40",
35
+ "a30",
36
+ "a10",
37
+ "a6000",
38
+ "a5000",
39
+ "a4000",
40
+ "a2000",
41
+ "t1000",
42
+ "t600",
43
+ "t400",
44
+ ]
45
+
22
46
 
23
47
  class SystemInfo(ABC):
24
48
  """
@@ -51,6 +75,7 @@ class SystemInfo(ABC):
51
75
  "cpu": self.get_cpu_device(),
52
76
  "amd_igpu": self.get_amd_igpu_device(include_inference_engines=True),
53
77
  "amd_dgpu": self.get_amd_dgpu_devices(include_inference_engines=True),
78
+ "nvidia_dgpu": self.get_nvidia_dgpu_devices(include_inference_engines=True),
54
79
  "npu": self.get_npu_device(),
55
80
  }
56
81
  return device_dict
@@ -82,6 +107,15 @@ class SystemInfo(ABC):
82
107
  list: List of AMD dGPU device information.
83
108
  """
84
109
 
110
+ @abstractmethod
111
+ def get_nvidia_dgpu_devices(self, include_inference_engines: bool = False) -> list:
112
+ """
113
+ Retrieves NVIDIA discrete GPU device information.
114
+
115
+ Returns:
116
+ list: List of NVIDIA dGPU device information.
117
+ """
118
+
85
119
  @abstractmethod
86
120
  def get_npu_device(self) -> dict:
87
121
  """
@@ -173,34 +207,56 @@ class WindowsSystemInfo(SystemInfo):
173
207
  Returns:
174
208
  list: List of detected GPU info dictionaries
175
209
  """
210
+ logging.debug(f"Starting AMD GPU detection for type: {gpu_type}")
176
211
  gpu_devices = []
177
212
  try:
178
213
  video_controllers = self.connection.Win32_VideoController()
179
- for controller in video_controllers:
214
+ logging.debug(f"Found {len(video_controllers)} video controllers")
215
+
216
+ for i, controller in enumerate(video_controllers):
217
+ logging.debug(
218
+ f"Controller {i}: Name='{controller.Name}', "
219
+ f"PNPDeviceID='{getattr(controller, 'PNPDeviceID', 'N/A')}'"
220
+ )
221
+
180
222
  if (
181
223
  controller.Name
182
224
  and "AMD" in controller.Name
183
225
  and "Radeon" in controller.Name
184
226
  ):
227
+ logging.debug(f"Found AMD Radeon GPU: {controller.Name}")
185
228
 
186
229
  name_lower = controller.Name.lower()
230
+ logging.debug(f"GPU name (lowercase): {name_lower}")
187
231
 
188
232
  # Keyword-based classification - simple and reliable
233
+ matching_keywords = [
234
+ kw for kw in AMD_DISCRETE_GPU_KEYWORDS if kw in name_lower
235
+ ]
189
236
  is_discrete_by_name = any(
190
237
  kw in name_lower for kw in AMD_DISCRETE_GPU_KEYWORDS
191
238
  )
192
239
  is_integrated = not is_discrete_by_name
193
240
 
241
+ logging.debug(f"Matching discrete keywords: {matching_keywords}")
242
+ logging.debug(
243
+ f"Classified as discrete: {not is_integrated}, integrated: {is_integrated}"
244
+ )
245
+
194
246
  # Filter based on requested type
195
247
  if (gpu_type == "integrated" and is_integrated) or (
196
248
  gpu_type == "discrete" and not is_integrated
197
249
  ):
250
+ logging.debug(
251
+ f"GPU matches requested type '{gpu_type}', processing..."
252
+ )
198
253
 
199
254
  device_type = "amd_igpu" if is_integrated else "amd_dgpu"
200
255
  gpu_info = {
201
256
  "name": controller.Name,
202
257
  "available": True,
203
258
  }
259
+ logging.debug(f"Created GPU info for {device_type}: {gpu_info}")
204
260
 
205
261
  driver_version = self.get_driver_version(
206
262
  "AMD-OpenCL User Mode Driver"
@@ -208,6 +264,21 @@ class WindowsSystemInfo(SystemInfo):
208
264
  gpu_info["driver_version"] = (
209
265
  driver_version if driver_version else "Unknown"
210
266
  )
267
+ logging.debug(f"Driver version: {gpu_info['driver_version']}")
268
+
269
+ # Get VRAM information for discrete GPUs
270
+ if not is_integrated: # Only add VRAM for discrete GPUs
271
+ # Try dxdiag first (most reliable for dedicated memory)
272
+ vram_gb = self._get_gpu_vram_dxdiag_simple(controller.Name)
273
+
274
+ # Fallback to WMI if dxdiag fails
275
+ if vram_gb == 0.0:
276
+ vram_gb = self._get_gpu_vram_wmi(controller)
277
+
278
+ if vram_gb > 0.0:
279
+ gpu_info["vram_gb"] = vram_gb
280
+ else:
281
+ gpu_info["vram_gb"] = "Unknown"
211
282
 
212
283
  if include_inference_engines:
213
284
  gpu_info["inference_engines"] = (
@@ -216,11 +287,26 @@ class WindowsSystemInfo(SystemInfo):
216
287
  )
217
288
  )
218
289
  gpu_devices.append(gpu_info)
290
+ logging.debug(f"Added GPU to devices list: {gpu_info}")
291
+ else:
292
+ logging.debug(
293
+ f"GPU does not match requested type '{gpu_type}', skipping"
294
+ )
295
+ continue
296
+ else:
297
+ logging.debug(
298
+ f"Skipping non-AMD/non-Radeon controller: {controller.Name}"
299
+ )
219
300
 
220
301
  except Exception as e: # pylint: disable=broad-except
221
302
  error_msg = f"AMD {gpu_type} GPU detection failed: {e}"
303
+ logging.debug(f"Exception in AMD GPU detection: {e}")
222
304
  return [{"available": False, "error": error_msg}]
223
305
 
306
+ logging.debug(
307
+ f"AMD GPU detection completed. Found {len(gpu_devices)} {gpu_type} GPUs: "
308
+ f"{[gpu.get('name', 'Unknown') for gpu in gpu_devices]}"
309
+ )
224
310
  return gpu_devices
225
311
 
226
312
  def get_amd_igpu_device(self, include_inference_engines: bool = False) -> dict:
@@ -255,6 +341,67 @@ class WindowsSystemInfo(SystemInfo):
255
341
  else [{"available": False, "error": "No AMD discrete GPU found"}]
256
342
  )
257
343
 
344
+ def get_nvidia_dgpu_devices(self, include_inference_engines: bool = False) -> list:
345
+ """
346
+ Retrieves NVIDIA discrete GPU device information using WMI.
347
+
348
+ Returns:
349
+ list: List of NVIDIA dGPU device information.
350
+ """
351
+ gpu_devices = []
352
+ try:
353
+ video_controllers = self.connection.Win32_VideoController()
354
+ for controller in video_controllers:
355
+ if controller.Name and "NVIDIA" in controller.Name.upper():
356
+ name_lower = controller.Name.lower()
357
+
358
+ # Most NVIDIA GPUs are discrete, but we can check keywords for confirmation
359
+ is_discrete = (
360
+ any(kw in name_lower for kw in NVIDIA_DISCRETE_GPU_KEYWORDS)
361
+ or "nvidia" in name_lower
362
+ ) # Default to discrete for NVIDIA
363
+
364
+ if is_discrete:
365
+ gpu_info = {
366
+ "name": controller.Name,
367
+ "available": True,
368
+ }
369
+
370
+ # Try to get NVIDIA driver version using multiple methods
371
+ driver_version = self._get_nvidia_driver_version_windows()
372
+ gpu_info["driver_version"] = (
373
+ driver_version if driver_version else "Unknown"
374
+ )
375
+
376
+ # Get VRAM information
377
+ vram_gb = self._get_gpu_vram_wmi(controller)
378
+ if vram_gb == 0.0:
379
+ # Fallback to nvidia-smi
380
+ vram_gb = self._get_nvidia_vram_smi()
381
+
382
+ if vram_gb > 0.0:
383
+ gpu_info["vram_gb"] = vram_gb
384
+ else:
385
+ gpu_info["vram_gb"] = "Unknown"
386
+
387
+ if include_inference_engines:
388
+ gpu_info["inference_engines"] = (
389
+ self._detect_inference_engines(
390
+ "nvidia_dgpu", controller.Name
391
+ )
392
+ )
393
+ gpu_devices.append(gpu_info)
394
+
395
+ except Exception as e: # pylint: disable=broad-except
396
+ error_msg = f"NVIDIA discrete GPU detection failed: {e}"
397
+ return [{"available": False, "error": error_msg}]
398
+
399
+ return (
400
+ gpu_devices
401
+ if gpu_devices
402
+ else [{"available": False, "error": "No NVIDIA discrete GPU found"}]
403
+ )
404
+
258
405
  def get_npu_device(self) -> dict:
259
406
  """
260
407
  Retrieves NPU device information using existing methods.
@@ -374,6 +521,169 @@ class WindowsSystemInfo(SystemInfo):
374
521
  return drivers[0].DriverVersion
375
522
  return ""
376
523
 
524
+ def _get_gpu_vram_wmi(self, controller) -> float:
525
+ """
526
+ Get GPU VRAM from WMI VideoController.
527
+
528
+ Args:
529
+ controller: WMI Win32_VideoController object
530
+
531
+ Returns:
532
+ float: VRAM in GB, or 0.0 if detection fails
533
+ """
534
+ try:
535
+ if hasattr(controller, "AdapterRAM"):
536
+ adapter_ram = controller.AdapterRAM
537
+ if adapter_ram and adapter_ram > 0:
538
+ # AdapterRAM is in bytes, convert to GB
539
+ vram_bytes = int(adapter_ram)
540
+ vram_gb = round(vram_bytes / (1024**3), 1)
541
+ return vram_gb
542
+ except (ValueError, AttributeError):
543
+ pass
544
+ return 0.0
545
+
546
+ def _get_gpu_vram_dxdiag_simple(self, gpu_name: str) -> float:
547
+ """
548
+ Get GPU VRAM using dxdiag, looking specifically for dedicated memory.
549
+
550
+ Args:
551
+ gpu_name: Name of the GPU to look for
552
+
553
+ Returns:
554
+ float: VRAM in GB, or 0.0 if detection fails
555
+ """
556
+ try:
557
+ import tempfile
558
+ import os
559
+
560
+ with tempfile.NamedTemporaryFile(
561
+ mode="w+", suffix=".txt", delete=False
562
+ ) as temp_file:
563
+ temp_path = temp_file.name
564
+
565
+ try:
566
+ subprocess.run(
567
+ ["dxdiag", "/t", temp_path],
568
+ check=True,
569
+ timeout=30,
570
+ capture_output=True,
571
+ )
572
+
573
+ with open(temp_path, "r", encoding="utf-8", errors="ignore") as f:
574
+ dxdiag_output = f.read()
575
+
576
+ lines = dxdiag_output.split("\n")
577
+ found_gpu = False
578
+
579
+ for line in lines:
580
+ line = line.strip()
581
+
582
+ # Check if this is our GPU
583
+ if "Card name:" in line and gpu_name.lower() in line.lower():
584
+ found_gpu = True
585
+ continue
586
+
587
+ # Look for dedicated memory line
588
+ if found_gpu and "Dedicated Memory:" in line:
589
+ memory_match = re.search(
590
+ r"(\d+(?:\.\d+)?)\s*MB", line, re.IGNORECASE
591
+ )
592
+ if memory_match:
593
+ vram_mb = float(memory_match.group(1))
594
+ vram_gb = round(vram_mb / 1024, 1)
595
+ return vram_gb
596
+
597
+ # Reset if we hit another display device
598
+ if "Card name:" in line and gpu_name.lower() not in line.lower():
599
+ found_gpu = False
600
+
601
+ finally:
602
+ try:
603
+ os.unlink(temp_path)
604
+ except Exception: # pylint: disable=broad-except
605
+ pass
606
+
607
+ except Exception: # pylint: disable=broad-except
608
+ pass
609
+
610
+ return 0.0
611
+
612
+ def _get_nvidia_driver_version_windows(self) -> str:
613
+ """
614
+ Get NVIDIA driver version on Windows using nvidia-smi and WMI fallback.
615
+
616
+ Returns:
617
+ str: Driver version, or empty string if detection fails
618
+ """
619
+ # Primary: Try nvidia-smi command
620
+ try:
621
+ output = (
622
+ subprocess.check_output(
623
+ [
624
+ "nvidia-smi",
625
+ "--query-gpu=driver_version",
626
+ "--format=csv,noheader,nounits",
627
+ ],
628
+ stderr=subprocess.DEVNULL,
629
+ )
630
+ .decode()
631
+ .strip()
632
+ )
633
+ if output and output != "N/A":
634
+ return output.split("\n")[0]
635
+ except (subprocess.CalledProcessError, FileNotFoundError):
636
+ pass
637
+
638
+ # Fallback: Try WMI Win32_PnPSignedDriver with NVIDIA patterns
639
+ try:
640
+ nvidia_patterns = [
641
+ "NVIDIA GeForce",
642
+ "NVIDIA RTX",
643
+ "NVIDIA GTX",
644
+ "NVIDIA Quadro",
645
+ ]
646
+ all_drivers = self.connection.Win32_PnPSignedDriver()
647
+ for driver in all_drivers:
648
+ if driver.DeviceName and any(
649
+ pattern in driver.DeviceName for pattern in nvidia_patterns
650
+ ):
651
+ if driver.DriverVersion:
652
+ return driver.DriverVersion
653
+ except Exception: # pylint: disable=broad-except
654
+ pass
655
+
656
+ return ""
657
+
658
+ def _get_nvidia_vram_smi(self) -> float:
659
+ """
660
+ Get NVIDIA GPU VRAM using nvidia-smi command.
661
+
662
+ Returns:
663
+ float: VRAM in GB, or 0.0 if detection fails
664
+ """
665
+ try:
666
+ output = (
667
+ subprocess.check_output(
668
+ [
669
+ "nvidia-smi",
670
+ "--query-gpu=memory.total",
671
+ "--format=csv,noheader,nounits",
672
+ ],
673
+ stderr=subprocess.DEVNULL,
674
+ )
675
+ .decode()
676
+ .strip()
677
+ )
678
+
679
+ # nvidia-smi returns memory in MB
680
+ vram_mb = int(output.split("\n")[0])
681
+ vram_gb = round(vram_mb / 1024, 1)
682
+ return vram_gb
683
+ except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
684
+ pass
685
+ return 0.0
686
+
377
687
  @staticmethod
378
688
  def get_npu_power_mode() -> str:
379
689
  """
@@ -490,6 +800,14 @@ class WSLSystemInfo(SystemInfo):
490
800
  """
491
801
  return []
492
802
 
803
+ def get_nvidia_dgpu_devices(self, include_inference_engines: bool = False) -> list:
804
+ """
805
+ Retrieves NVIDIA discrete GPU device information in WSL environment.
806
+ """
807
+ return [
808
+ {"available": False, "error": "NVIDIA GPU detection not supported in WSL"}
809
+ ]
810
+
493
811
  def get_npu_device(self) -> dict:
494
812
  """
495
813
  Retrieves NPU device information in WSL environment.
@@ -625,6 +943,20 @@ class LinuxSystemInfo(SystemInfo):
625
943
  "name": device_name,
626
944
  "available": True,
627
945
  }
946
+
947
+ # Get VRAM information for discrete GPUs
948
+ if not is_integrated: # Only add VRAM for discrete GPUs
949
+ vram_gb = self._get_amd_vram_rocm_smi_linux()
950
+ if vram_gb == 0.0:
951
+ # Fallback to sysfs - extract PCI ID from lspci line
952
+ pci_id = line.split()[0] if line else ""
953
+ vram_gb = self._get_amd_vram_sysfs(pci_id)
954
+
955
+ if vram_gb > 0.0:
956
+ gpu_info["vram_gb"] = vram_gb
957
+ else:
958
+ gpu_info["vram_gb"] = "Unknown"
959
+
628
960
  if include_inference_engines:
629
961
  gpu_info["inference_engines"] = (
630
962
  self._detect_inference_engines(device_type, device_name)
@@ -669,6 +1001,66 @@ class LinuxSystemInfo(SystemInfo):
669
1001
  else [{"available": False, "error": "No AMD discrete GPU found"}]
670
1002
  )
671
1003
 
1004
+ def get_nvidia_dgpu_devices(self, include_inference_engines: bool = False) -> list:
1005
+ """
1006
+ Retrieves NVIDIA discrete GPU device information using lspci.
1007
+
1008
+ Returns:
1009
+ list: List of NVIDIA dGPU device information.
1010
+ """
1011
+ gpu_devices = []
1012
+ try:
1013
+ lspci_output = subprocess.check_output(
1014
+ "lspci | grep -i 'vga\\|3d\\|display'", shell=True
1015
+ ).decode()
1016
+
1017
+ for line in lspci_output.split("\n"):
1018
+ if line.strip() and "NVIDIA" in line.upper():
1019
+ name_lower = line.lower()
1020
+
1021
+ # Most NVIDIA GPUs are discrete, check keywords for confirmation
1022
+ is_discrete = (
1023
+ any(kw in name_lower for kw in NVIDIA_DISCRETE_GPU_KEYWORDS)
1024
+ or "nvidia" in name_lower
1025
+ ) # Default to discrete for NVIDIA
1026
+
1027
+ if is_discrete:
1028
+ device_name = line.split(": ")[1] if ": " in line else line
1029
+
1030
+ gpu_info = {
1031
+ "name": device_name,
1032
+ "available": True,
1033
+ }
1034
+
1035
+ # Try to get NVIDIA driver version using multiple methods
1036
+ driver_version = self._get_nvidia_driver_version_linux()
1037
+ gpu_info["driver_version"] = (
1038
+ driver_version if driver_version else "Unknown"
1039
+ )
1040
+
1041
+ # Get VRAM information
1042
+ vram_gb = self._get_nvidia_vram_smi_linux()
1043
+ if vram_gb > 0.0:
1044
+ gpu_info["vram_gb"] = vram_gb
1045
+
1046
+ if include_inference_engines:
1047
+ gpu_info["inference_engines"] = (
1048
+ self._detect_inference_engines(
1049
+ "nvidia_dgpu", device_name
1050
+ )
1051
+ )
1052
+ gpu_devices.append(gpu_info)
1053
+
1054
+ except Exception as e: # pylint: disable=broad-except
1055
+ error_msg = f"NVIDIA discrete GPU detection failed: {e}"
1056
+ return [{"available": False, "error": error_msg}]
1057
+
1058
+ return (
1059
+ gpu_devices
1060
+ if gpu_devices
1061
+ else [{"available": False, "error": "No NVIDIA discrete GPU found"}]
1062
+ )
1063
+
672
1064
  def get_npu_device(self) -> dict:
673
1065
  """
674
1066
  Retrieves NPU device information (limited support on Linux).
@@ -681,6 +1073,69 @@ class LinuxSystemInfo(SystemInfo):
681
1073
  "error": "NPU detection not yet implemented for Linux",
682
1074
  }
683
1075
 
1076
+ def _get_nvidia_driver_version_linux(self) -> str:
1077
+ """
1078
+ Get NVIDIA driver version on Linux using nvidia-smi and proc fallback.
1079
+
1080
+ Returns:
1081
+ str: Driver version, or empty string if detection fails
1082
+ """
1083
+ # Primary: Try nvidia-smi command
1084
+ try:
1085
+ output = (
1086
+ subprocess.check_output(
1087
+ "nvidia-smi --query-gpu=driver_version --format=csv,noheader,nounits",
1088
+ shell=True,
1089
+ stderr=subprocess.DEVNULL,
1090
+ )
1091
+ .decode()
1092
+ .strip()
1093
+ )
1094
+ if output and output != "N/A":
1095
+ return output.split("\n")[0]
1096
+ except (subprocess.CalledProcessError, FileNotFoundError):
1097
+ pass
1098
+
1099
+ # Fallback: Try /proc/driver/nvidia/version
1100
+ try:
1101
+ with open("/proc/driver/nvidia/version", "r", encoding="utf-8") as f:
1102
+ content = f.read()
1103
+ # Look for version pattern like "NVRM version:
1104
+ # NVIDIA UNIX x86_64 Kernel Module 470.82.00"
1105
+ match = re.search(r"Kernel Module\s+(\d+\.\d+(?:\.\d+)?)", content)
1106
+ if match:
1107
+ return match.group(1)
1108
+ except (FileNotFoundError, IOError):
1109
+ pass
1110
+
1111
+ return ""
1112
+
1113
+ def _get_nvidia_vram_smi_linux(self) -> float:
1114
+ """
1115
+ Get NVIDIA GPU VRAM on Linux using nvidia-smi command.
1116
+
1117
+ Returns:
1118
+ float: VRAM in GB, or 0.0 if detection fails
1119
+ """
1120
+ try:
1121
+ output = (
1122
+ subprocess.check_output(
1123
+ "nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits",
1124
+ shell=True,
1125
+ stderr=subprocess.DEVNULL,
1126
+ )
1127
+ .decode()
1128
+ .strip()
1129
+ )
1130
+
1131
+ # nvidia-smi returns memory in MB
1132
+ vram_mb = int(output.split("\n")[0])
1133
+ vram_gb = round(vram_mb / 1024, 1)
1134
+ return vram_gb
1135
+ except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
1136
+ pass
1137
+ return 0.0
1138
+
684
1139
  @staticmethod
685
1140
  def get_processor_name() -> str:
686
1141
  """
@@ -758,6 +1213,109 @@ class LinuxSystemInfo(SystemInfo):
758
1213
  info_dict["Physical Memory"] = self.get_physical_memory()
759
1214
  return info_dict
760
1215
 
1216
+ def _get_nvidia_vram_smi_linux(self) -> float:
1217
+ """
1218
+ Get NVIDIA GPU VRAM using nvidia-smi command on Linux.
1219
+
1220
+ Returns:
1221
+ float: VRAM in GB, or 0.0 if detection fails
1222
+ """
1223
+ try:
1224
+ output = (
1225
+ subprocess.check_output(
1226
+ [
1227
+ "nvidia-smi",
1228
+ "--query-gpu=memory.total",
1229
+ "--format=csv,noheader,nounits",
1230
+ ],
1231
+ stderr=subprocess.DEVNULL,
1232
+ )
1233
+ .decode()
1234
+ .strip()
1235
+ )
1236
+
1237
+ # nvidia-smi returns memory in MB
1238
+ vram_mb = int(output.split("\n")[0])
1239
+ vram_gb = round(vram_mb / 1024, 1)
1240
+ return vram_gb
1241
+ except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
1242
+ pass
1243
+ return 0.0
1244
+
1245
+ def _get_amd_vram_rocm_smi_linux(self) -> float:
1246
+ """
1247
+ Get AMD GPU VRAM using rocm-smi command on Linux.
1248
+
1249
+ Returns:
1250
+ float: VRAM in GB, or 0.0 if detection fails
1251
+ """
1252
+ try:
1253
+ output = (
1254
+ subprocess.check_output(
1255
+ ["rocm-smi", "--showmeminfo", "vram", "--csv"],
1256
+ stderr=subprocess.DEVNULL,
1257
+ )
1258
+ .decode()
1259
+ .strip()
1260
+ )
1261
+
1262
+ # Parse CSV output to extract VRAM
1263
+ lines = output.split("\n")
1264
+ for line in lines:
1265
+ if "Total VRAM" in line or "vram" in line.lower():
1266
+ # Extract numeric value (assuming it's in MB or GB)
1267
+ numbers = re.findall(r"\d+", line)
1268
+ if numbers:
1269
+ vram_value = int(numbers[0])
1270
+ # Assume MB if value is large, GB if small
1271
+ if vram_value > 100: # Likely MB
1272
+ vram_gb = round(vram_value / 1024, 1)
1273
+ else: # Likely GB
1274
+ vram_gb = float(vram_value)
1275
+ return vram_gb
1276
+ except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
1277
+ pass
1278
+ return 0.0
1279
+
1280
+ def _get_amd_vram_sysfs(self, pci_id: str) -> float:
1281
+ """
1282
+ Get AMD GPU VRAM using sysfs on Linux.
1283
+
1284
+ Args:
1285
+ pci_id: PCI ID of the GPU (e.g., "0000:01:00.0")
1286
+
1287
+ Returns:
1288
+ float: VRAM in GB, or 0.0 if detection fails
1289
+ """
1290
+ try:
1291
+ # Try different sysfs paths for VRAM information
1292
+ sysfs_paths = [
1293
+ f"/sys/bus/pci/devices/{pci_id}/mem_info_vram_total",
1294
+ "/sys/class/drm/card*/device/mem_info_vram_total",
1295
+ ]
1296
+
1297
+ for path in sysfs_paths:
1298
+ try:
1299
+ if "*" in path:
1300
+ # Handle wildcard paths
1301
+ matching_paths = glob.glob(path)
1302
+ for match_path in matching_paths:
1303
+ with open(match_path, "r", encoding="utf-8") as f:
1304
+ vram_bytes = int(f.read().strip())
1305
+ vram_gb = round(vram_bytes / (1024**3), 1)
1306
+ if vram_gb > 0:
1307
+ return vram_gb
1308
+ else:
1309
+ with open(path, "r", encoding="utf-8") as f:
1310
+ vram_bytes = int(f.read().strip())
1311
+ vram_gb = round(vram_bytes / (1024**3), 1)
1312
+ return vram_gb
1313
+ except (FileNotFoundError, ValueError, PermissionError):
1314
+ continue
1315
+ except Exception: # pylint: disable=broad-except
1316
+ pass
1317
+ return 0.0
1318
+
761
1319
  def _detect_inference_engines(self, device_type: str, device_name: str) -> dict:
762
1320
  """
763
1321
  Detect available inference engines for a specific device type.
@@ -803,6 +1361,17 @@ class UnsupportedOSSystemInfo(SystemInfo):
803
1361
  """
804
1362
  return []
805
1363
 
1364
+ def get_nvidia_dgpu_devices(self, include_inference_engines: bool = False) -> list:
1365
+ """
1366
+ Retrieves NVIDIA discrete GPU device information for unsupported OS.
1367
+ """
1368
+ return [
1369
+ {
1370
+ "available": False,
1371
+ "error": "Device detection not supported on this operating system",
1372
+ }
1373
+ ]
1374
+
806
1375
  def get_npu_device(self) -> dict:
807
1376
  """
808
1377
  Retrieves NPU device information for unsupported OS.