lemonade-sdk 9.1.1__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.
- lemonade/__init__.py +5 -0
- lemonade/api.py +180 -0
- lemonade/cache.py +92 -0
- lemonade/cli.py +173 -0
- lemonade/common/__init__.py +0 -0
- lemonade/common/build.py +176 -0
- lemonade/common/cli_helpers.py +139 -0
- lemonade/common/exceptions.py +98 -0
- lemonade/common/filesystem.py +368 -0
- lemonade/common/inference_engines.py +408 -0
- lemonade/common/network.py +93 -0
- lemonade/common/printing.py +110 -0
- lemonade/common/status.py +471 -0
- lemonade/common/system_info.py +1411 -0
- lemonade/common/test_helpers.py +28 -0
- lemonade/profilers/__init__.py +1 -0
- lemonade/profilers/agt_power.py +437 -0
- lemonade/profilers/hwinfo_power.py +429 -0
- lemonade/profilers/memory_tracker.py +259 -0
- lemonade/profilers/profiler.py +58 -0
- lemonade/sequence.py +363 -0
- lemonade/state.py +159 -0
- lemonade/tools/__init__.py +1 -0
- lemonade/tools/accuracy.py +432 -0
- lemonade/tools/adapter.py +114 -0
- lemonade/tools/bench.py +302 -0
- lemonade/tools/flm/__init__.py +1 -0
- lemonade/tools/flm/utils.py +305 -0
- lemonade/tools/huggingface/bench.py +187 -0
- lemonade/tools/huggingface/load.py +235 -0
- lemonade/tools/huggingface/utils.py +359 -0
- lemonade/tools/humaneval.py +264 -0
- lemonade/tools/llamacpp/bench.py +255 -0
- lemonade/tools/llamacpp/load.py +222 -0
- lemonade/tools/llamacpp/utils.py +1260 -0
- lemonade/tools/management_tools.py +319 -0
- lemonade/tools/mmlu.py +319 -0
- lemonade/tools/oga/__init__.py +0 -0
- lemonade/tools/oga/bench.py +120 -0
- lemonade/tools/oga/load.py +804 -0
- lemonade/tools/oga/migration.py +403 -0
- lemonade/tools/oga/utils.py +462 -0
- lemonade/tools/perplexity.py +147 -0
- lemonade/tools/prompt.py +263 -0
- lemonade/tools/report/__init__.py +0 -0
- lemonade/tools/report/llm_report.py +203 -0
- lemonade/tools/report/table.py +899 -0
- lemonade/tools/server/__init__.py +0 -0
- lemonade/tools/server/flm.py +133 -0
- lemonade/tools/server/llamacpp.py +320 -0
- lemonade/tools/server/serve.py +2123 -0
- lemonade/tools/server/static/favicon.ico +0 -0
- lemonade/tools/server/static/index.html +279 -0
- lemonade/tools/server/static/js/chat.js +1059 -0
- lemonade/tools/server/static/js/model-settings.js +183 -0
- lemonade/tools/server/static/js/models.js +1395 -0
- lemonade/tools/server/static/js/shared.js +556 -0
- lemonade/tools/server/static/logs.html +191 -0
- lemonade/tools/server/static/styles.css +2654 -0
- lemonade/tools/server/static/webapp.html +321 -0
- lemonade/tools/server/tool_calls.py +153 -0
- lemonade/tools/server/tray.py +664 -0
- lemonade/tools/server/utils/macos_tray.py +226 -0
- lemonade/tools/server/utils/port.py +77 -0
- lemonade/tools/server/utils/thread.py +85 -0
- lemonade/tools/server/utils/windows_tray.py +408 -0
- lemonade/tools/server/webapp.py +34 -0
- lemonade/tools/server/wrapped_server.py +559 -0
- lemonade/tools/tool.py +374 -0
- lemonade/version.py +1 -0
- lemonade_install/__init__.py +1 -0
- lemonade_install/install.py +239 -0
- lemonade_sdk-9.1.1.dist-info/METADATA +276 -0
- lemonade_sdk-9.1.1.dist-info/RECORD +84 -0
- lemonade_sdk-9.1.1.dist-info/WHEEL +5 -0
- lemonade_sdk-9.1.1.dist-info/entry_points.txt +5 -0
- lemonade_sdk-9.1.1.dist-info/licenses/LICENSE +201 -0
- lemonade_sdk-9.1.1.dist-info/licenses/NOTICE.md +47 -0
- lemonade_sdk-9.1.1.dist-info/top_level.txt +3 -0
- lemonade_server/cli.py +805 -0
- lemonade_server/model_manager.py +758 -0
- lemonade_server/pydantic_models.py +159 -0
- lemonade_server/server_models.json +643 -0
- lemonade_server/settings.py +39 -0
|
@@ -0,0 +1,1411 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import importlib.metadata
|
|
3
|
+
import logging
|
|
4
|
+
import platform
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
import ctypes
|
|
8
|
+
import glob
|
|
9
|
+
from .inference_engines import detect_inference_engines
|
|
10
|
+
|
|
11
|
+
# AMD GPU classification keywords - shared across all OS implementations
|
|
12
|
+
# If a GPU name contains any of these keywords, it's considered discrete
|
|
13
|
+
# Everything else is assumed to be integrated
|
|
14
|
+
AMD_DISCRETE_GPU_KEYWORDS = [
|
|
15
|
+
"rx ",
|
|
16
|
+
"xt",
|
|
17
|
+
"pro w",
|
|
18
|
+
"pro v",
|
|
19
|
+
"radeon pro",
|
|
20
|
+
"firepro",
|
|
21
|
+
"fury",
|
|
22
|
+
]
|
|
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
|
+
|
|
46
|
+
|
|
47
|
+
class SystemInfo(ABC):
|
|
48
|
+
"""
|
|
49
|
+
Abstract base class for OS-dependent system information classes.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
def get_dict(self):
|
|
56
|
+
"""
|
|
57
|
+
Retrieves all the system information into a dictionary.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
dict: System information.
|
|
61
|
+
"""
|
|
62
|
+
info_dict = {
|
|
63
|
+
"OS Version": self.get_os_version(),
|
|
64
|
+
}
|
|
65
|
+
return info_dict
|
|
66
|
+
|
|
67
|
+
def get_device_dict(self):
|
|
68
|
+
"""
|
|
69
|
+
Retrieves device information into a dictionary.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
dict: Device information.
|
|
73
|
+
"""
|
|
74
|
+
device_dict = {
|
|
75
|
+
"cpu": self.get_cpu_device(),
|
|
76
|
+
"amd_igpu": self.get_amd_igpu_device(include_inference_engines=True),
|
|
77
|
+
"amd_dgpu": self.get_amd_dgpu_devices(include_inference_engines=True),
|
|
78
|
+
"nvidia_dgpu": self.get_nvidia_dgpu_devices(include_inference_engines=True),
|
|
79
|
+
"npu": self.get_npu_device(),
|
|
80
|
+
}
|
|
81
|
+
return device_dict
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def get_cpu_device(self) -> dict:
|
|
85
|
+
"""
|
|
86
|
+
Retrieves CPU device information.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
dict: CPU device information.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
def get_amd_igpu_device(self, include_inference_engines: bool = False) -> dict:
|
|
94
|
+
"""
|
|
95
|
+
Retrieves AMD integrated GPU device information.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
dict: AMD iGPU device information.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def get_amd_dgpu_devices(self, include_inference_engines: bool = False) -> list:
|
|
103
|
+
"""
|
|
104
|
+
Retrieves AMD discrete GPU device information.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
list: List of AMD dGPU device information.
|
|
108
|
+
"""
|
|
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
|
+
|
|
119
|
+
@abstractmethod
|
|
120
|
+
def get_npu_device(self) -> dict:
|
|
121
|
+
"""
|
|
122
|
+
Retrieves NPU device information.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
dict: NPU device information.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def get_os_version() -> str:
|
|
130
|
+
"""
|
|
131
|
+
Retrieves the OS version.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
str: OS Version.
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
return platform.platform()
|
|
138
|
+
except Exception as e: # pylint: disable=broad-except
|
|
139
|
+
return f"ERROR - {e}"
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def get_python_packages() -> list:
|
|
143
|
+
"""
|
|
144
|
+
Retrieves the Python package versions.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
list: List of Python package versions in the form ["package-name==package-version", ...]
|
|
148
|
+
"""
|
|
149
|
+
# Get Python Packages
|
|
150
|
+
distributions = importlib.metadata.distributions()
|
|
151
|
+
return [
|
|
152
|
+
f"{dist.metadata['name']}=={dist.metadata['version']}"
|
|
153
|
+
for dist in distributions
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class WindowsSystemInfo(SystemInfo):
|
|
158
|
+
"""
|
|
159
|
+
Class used to access system information in Windows.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(self):
|
|
163
|
+
super().__init__()
|
|
164
|
+
import wmi
|
|
165
|
+
|
|
166
|
+
self.connection = wmi.WMI()
|
|
167
|
+
|
|
168
|
+
def get_cpu_device(self) -> dict:
|
|
169
|
+
"""
|
|
170
|
+
Retrieves CPU device information using WMI.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
dict: CPU device information.
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
processors = self.connection.Win32_Processor()
|
|
177
|
+
if processors:
|
|
178
|
+
processor = processors[0]
|
|
179
|
+
cpu_name = processor.Name.strip()
|
|
180
|
+
cpu_info = {
|
|
181
|
+
"name": cpu_name,
|
|
182
|
+
"cores": processor.NumberOfCores,
|
|
183
|
+
"threads": processor.NumberOfLogicalProcessors,
|
|
184
|
+
"max_clock_speed_mhz": processor.MaxClockSpeed,
|
|
185
|
+
"available": True,
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# Add inference engine detection
|
|
189
|
+
cpu_info["inference_engines"] = self._detect_inference_engines(
|
|
190
|
+
"cpu", cpu_name
|
|
191
|
+
)
|
|
192
|
+
return cpu_info
|
|
193
|
+
|
|
194
|
+
except Exception as e: # pylint: disable=broad-except
|
|
195
|
+
return {"available": False, "error": f"CPU detection failed: {e}"}
|
|
196
|
+
|
|
197
|
+
return {"available": False, "error": "No CPU information found"}
|
|
198
|
+
|
|
199
|
+
def _detect_amd_gpus(self, gpu_type: str, include_inference_engines: bool = False):
|
|
200
|
+
"""
|
|
201
|
+
Shared AMD GPU detection logic for both integrated and discrete GPUs.
|
|
202
|
+
Uses keyword-based classification for simplicity and reliability.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
gpu_type: Either "integrated" or "discrete"
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
list: List of detected GPU info dictionaries
|
|
209
|
+
"""
|
|
210
|
+
logging.debug(f"Starting AMD GPU detection for type: {gpu_type}")
|
|
211
|
+
gpu_devices = []
|
|
212
|
+
try:
|
|
213
|
+
video_controllers = self.connection.Win32_VideoController()
|
|
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
|
+
|
|
222
|
+
if (
|
|
223
|
+
controller.Name
|
|
224
|
+
and "AMD" in controller.Name
|
|
225
|
+
and "Radeon" in controller.Name
|
|
226
|
+
):
|
|
227
|
+
logging.debug(f"Found AMD Radeon GPU: {controller.Name}")
|
|
228
|
+
|
|
229
|
+
name_lower = controller.Name.lower()
|
|
230
|
+
logging.debug(f"GPU name (lowercase): {name_lower}")
|
|
231
|
+
|
|
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
|
+
]
|
|
236
|
+
is_discrete_by_name = any(
|
|
237
|
+
kw in name_lower for kw in AMD_DISCRETE_GPU_KEYWORDS
|
|
238
|
+
)
|
|
239
|
+
is_integrated = not is_discrete_by_name
|
|
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
|
+
|
|
246
|
+
# Filter based on requested type
|
|
247
|
+
if (gpu_type == "integrated" and is_integrated) or (
|
|
248
|
+
gpu_type == "discrete" and not is_integrated
|
|
249
|
+
):
|
|
250
|
+
logging.debug(
|
|
251
|
+
f"GPU matches requested type '{gpu_type}', processing..."
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
device_type = "amd_igpu" if is_integrated else "amd_dgpu"
|
|
255
|
+
gpu_info = {
|
|
256
|
+
"name": controller.Name,
|
|
257
|
+
"available": True,
|
|
258
|
+
}
|
|
259
|
+
logging.debug(f"Created GPU info for {device_type}: {gpu_info}")
|
|
260
|
+
|
|
261
|
+
driver_version = self.get_driver_version(
|
|
262
|
+
"AMD-OpenCL User Mode Driver"
|
|
263
|
+
)
|
|
264
|
+
gpu_info["driver_version"] = (
|
|
265
|
+
driver_version if driver_version else "Unknown"
|
|
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"
|
|
282
|
+
|
|
283
|
+
if include_inference_engines:
|
|
284
|
+
gpu_info["inference_engines"] = (
|
|
285
|
+
self._detect_inference_engines(
|
|
286
|
+
device_type, controller.Name
|
|
287
|
+
)
|
|
288
|
+
)
|
|
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
|
+
)
|
|
300
|
+
|
|
301
|
+
except Exception as e: # pylint: disable=broad-except
|
|
302
|
+
error_msg = f"AMD {gpu_type} GPU detection failed: {e}"
|
|
303
|
+
logging.debug(f"Exception in AMD GPU detection: {e}")
|
|
304
|
+
return [{"available": False, "error": error_msg}]
|
|
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
|
+
)
|
|
310
|
+
return gpu_devices
|
|
311
|
+
|
|
312
|
+
def get_amd_igpu_device(self, include_inference_engines: bool = False) -> dict:
|
|
313
|
+
"""
|
|
314
|
+
Retrieves AMD integrated GPU device information using keyword-based classification.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
dict: AMD iGPU device information.
|
|
318
|
+
"""
|
|
319
|
+
igpu_devices = self._detect_amd_gpus(
|
|
320
|
+
"integrated", include_inference_engines=include_inference_engines
|
|
321
|
+
)
|
|
322
|
+
return (
|
|
323
|
+
igpu_devices[0]
|
|
324
|
+
if igpu_devices
|
|
325
|
+
else {"available": False, "error": "No AMD integrated GPU found"}
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
def get_amd_dgpu_devices(self, include_inference_engines: bool = False):
|
|
329
|
+
"""
|
|
330
|
+
Retrieves AMD discrete GPU device information using keyword-based classification.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
list: List of AMD dGPU device information.
|
|
334
|
+
"""
|
|
335
|
+
dgpu_devices = self._detect_amd_gpus(
|
|
336
|
+
"discrete", include_inference_engines=include_inference_engines
|
|
337
|
+
)
|
|
338
|
+
return (
|
|
339
|
+
dgpu_devices
|
|
340
|
+
if dgpu_devices
|
|
341
|
+
else [{"available": False, "error": "No AMD discrete GPU found"}]
|
|
342
|
+
)
|
|
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
|
+
|
|
405
|
+
def get_npu_device(self) -> dict:
|
|
406
|
+
"""
|
|
407
|
+
Retrieves NPU device information using existing methods.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
dict: NPU device information.
|
|
411
|
+
"""
|
|
412
|
+
try:
|
|
413
|
+
# Check if NPU driver is present
|
|
414
|
+
driver_version = self.get_driver_version("NPU Compute Accelerator Device")
|
|
415
|
+
if driver_version:
|
|
416
|
+
power_mode = self.get_npu_power_mode()
|
|
417
|
+
npu_info = {
|
|
418
|
+
"name": "AMD NPU",
|
|
419
|
+
"driver_version": driver_version,
|
|
420
|
+
"power_mode": power_mode,
|
|
421
|
+
"available": True,
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
# Add inference engine detection
|
|
425
|
+
npu_info["inference_engines"] = self._detect_inference_engines(
|
|
426
|
+
"npu", "AMD NPU"
|
|
427
|
+
)
|
|
428
|
+
return npu_info
|
|
429
|
+
except Exception as e: # pylint: disable=broad-except
|
|
430
|
+
return {"available": False, "error": f"NPU detection failed: {e}"}
|
|
431
|
+
|
|
432
|
+
return {"available": False, "error": "No NPU device found"}
|
|
433
|
+
|
|
434
|
+
def get_processor_name(self) -> str:
|
|
435
|
+
"""
|
|
436
|
+
Retrieves the name of the processor.
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
str: Name of the processor.
|
|
440
|
+
"""
|
|
441
|
+
processors = self.connection.Win32_Processor()
|
|
442
|
+
if processors:
|
|
443
|
+
return (
|
|
444
|
+
f"{processors[0].Name.strip()} "
|
|
445
|
+
f"({processors[0].NumberOfCores} cores, "
|
|
446
|
+
f"{processors[0].NumberOfLogicalProcessors} logical processors)"
|
|
447
|
+
)
|
|
448
|
+
return "Processor information not found."
|
|
449
|
+
|
|
450
|
+
def get_basic_processor_name(self) -> str:
|
|
451
|
+
"""
|
|
452
|
+
Retrieves the basic name of the processor without core/thread details.
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
str: Basic name of the processor.
|
|
456
|
+
"""
|
|
457
|
+
processors = self.connection.Win32_Processor()
|
|
458
|
+
if processors:
|
|
459
|
+
return processors[0].Name.strip()
|
|
460
|
+
return "Processor information not found."
|
|
461
|
+
|
|
462
|
+
def get_system_model(self) -> str:
|
|
463
|
+
"""
|
|
464
|
+
Retrieves the model of the computer system.
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
str: Model of the computer system.
|
|
468
|
+
"""
|
|
469
|
+
systems = self.connection.Win32_ComputerSystem()
|
|
470
|
+
if systems:
|
|
471
|
+
return systems[0].Model
|
|
472
|
+
return "System model information not found."
|
|
473
|
+
|
|
474
|
+
def get_physical_memory(self) -> str:
|
|
475
|
+
"""
|
|
476
|
+
Retrieves the physical memory of the computer system.
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
str: Physical memory.
|
|
480
|
+
"""
|
|
481
|
+
memory = self.connection.Win32_PhysicalMemory()
|
|
482
|
+
if memory:
|
|
483
|
+
total_capacity = sum([int(m.Capacity) for m in memory])
|
|
484
|
+
total_capacity_str = f"{total_capacity/(1024**3)} GB"
|
|
485
|
+
return total_capacity_str
|
|
486
|
+
return "Physical memory information not found."
|
|
487
|
+
|
|
488
|
+
def get_bios_version(self) -> str:
|
|
489
|
+
"""
|
|
490
|
+
Retrieves the BIOS Version of the computer system.
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
str: BIOS Version.
|
|
494
|
+
"""
|
|
495
|
+
bios = self.connection.Win32_BIOS()
|
|
496
|
+
if bios:
|
|
497
|
+
return bios[0].Name
|
|
498
|
+
return "BIOS Version not found."
|
|
499
|
+
|
|
500
|
+
def get_max_clock_speed(self) -> str:
|
|
501
|
+
"""
|
|
502
|
+
Retrieves the max clock speed of the CPU of the system.
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
str: Max CPU clock speed.
|
|
506
|
+
"""
|
|
507
|
+
processor = self.connection.Win32_Processor()
|
|
508
|
+
if processor:
|
|
509
|
+
return f"{processor[0].MaxClockSpeed} MHz"
|
|
510
|
+
return "Max CPU clock speed not found."
|
|
511
|
+
|
|
512
|
+
def get_driver_version(self, device_name) -> str:
|
|
513
|
+
"""
|
|
514
|
+
Retrieves the driver version for the specified device name.
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
str: Driver version, or None if device driver not found.
|
|
518
|
+
"""
|
|
519
|
+
drivers = self.connection.Win32_PnPSignedDriver(DeviceName=device_name)
|
|
520
|
+
if drivers:
|
|
521
|
+
return drivers[0].DriverVersion
|
|
522
|
+
return ""
|
|
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
|
+
|
|
687
|
+
@staticmethod
|
|
688
|
+
def get_npu_power_mode() -> str:
|
|
689
|
+
"""
|
|
690
|
+
Retrieves the NPU power mode.
|
|
691
|
+
|
|
692
|
+
Returns:
|
|
693
|
+
str: NPU power mode.
|
|
694
|
+
"""
|
|
695
|
+
try:
|
|
696
|
+
out = subprocess.check_output(
|
|
697
|
+
[
|
|
698
|
+
r"C:\Windows\System32\AMD\xrt-smi.exe",
|
|
699
|
+
"examine",
|
|
700
|
+
"-r",
|
|
701
|
+
"platform",
|
|
702
|
+
],
|
|
703
|
+
stderr=subprocess.STDOUT,
|
|
704
|
+
).decode()
|
|
705
|
+
lines = out.splitlines()
|
|
706
|
+
modes = [line.split()[-1] for line in lines if "Mode" in line]
|
|
707
|
+
if len(modes) > 0:
|
|
708
|
+
return modes[0]
|
|
709
|
+
except FileNotFoundError:
|
|
710
|
+
# xrt-smi not present
|
|
711
|
+
pass
|
|
712
|
+
except subprocess.CalledProcessError:
|
|
713
|
+
pass
|
|
714
|
+
return "NPU power mode not found."
|
|
715
|
+
|
|
716
|
+
@staticmethod
|
|
717
|
+
def get_windows_power_setting() -> str:
|
|
718
|
+
"""
|
|
719
|
+
Retrieves the Windows power setting.
|
|
720
|
+
|
|
721
|
+
Returns:
|
|
722
|
+
str: Windows power setting.
|
|
723
|
+
"""
|
|
724
|
+
try:
|
|
725
|
+
# Capture output as bytes
|
|
726
|
+
out_bytes = subprocess.check_output(["powercfg", "/getactivescheme"])
|
|
727
|
+
|
|
728
|
+
# Get system's OEM code page (e.g., cp437, cp850)
|
|
729
|
+
oem_cp = "cp" + str(ctypes.windll.kernel32.GetOEMCP())
|
|
730
|
+
|
|
731
|
+
# Decode using detected OEM code page
|
|
732
|
+
out = out_bytes.decode(oem_cp)
|
|
733
|
+
|
|
734
|
+
# Extract power scheme name from parentheses
|
|
735
|
+
match = re.search(r"\((.*?)\)", out)
|
|
736
|
+
if match:
|
|
737
|
+
return match.group(1)
|
|
738
|
+
return "Power scheme name not found in output"
|
|
739
|
+
|
|
740
|
+
except subprocess.CalledProcessError:
|
|
741
|
+
return "Windows power setting not found (command failed)"
|
|
742
|
+
except Exception as e: # pylint: disable=broad-except
|
|
743
|
+
return f"Error retrieving power setting: {str(e)}"
|
|
744
|
+
|
|
745
|
+
def get_dict(self) -> dict:
|
|
746
|
+
"""
|
|
747
|
+
Retrieves all the system information into a dictionary.
|
|
748
|
+
|
|
749
|
+
Returns:
|
|
750
|
+
dict: System information.
|
|
751
|
+
"""
|
|
752
|
+
info_dict = super().get_dict()
|
|
753
|
+
info_dict["Processor"] = self.get_basic_processor_name()
|
|
754
|
+
info_dict["OEM System"] = self.get_system_model()
|
|
755
|
+
info_dict["Physical Memory"] = self.get_physical_memory()
|
|
756
|
+
info_dict["BIOS Version"] = self.get_bios_version()
|
|
757
|
+
info_dict["CPU Max Clock"] = self.get_max_clock_speed()
|
|
758
|
+
info_dict["Windows Power Setting"] = self.get_windows_power_setting()
|
|
759
|
+
return info_dict
|
|
760
|
+
|
|
761
|
+
def _detect_inference_engines(self, device_type: str, device_name: str) -> dict:
|
|
762
|
+
"""
|
|
763
|
+
Detect available inference engines for a specific device type.
|
|
764
|
+
|
|
765
|
+
Args:
|
|
766
|
+
device_type: Device type ("cpu", "amd_igpu", "amd_dgpu", "npu")
|
|
767
|
+
device_name: Device name
|
|
768
|
+
|
|
769
|
+
Returns:
|
|
770
|
+
dict: Available inference engines and their information.
|
|
771
|
+
"""
|
|
772
|
+
try:
|
|
773
|
+
from .inference_engines import detect_inference_engines
|
|
774
|
+
|
|
775
|
+
return detect_inference_engines(device_type, device_name)
|
|
776
|
+
except Exception as e: # pylint: disable=broad-except
|
|
777
|
+
return {"error": f"Inference engine detection failed: {str(e)}"}
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
class WSLSystemInfo(SystemInfo):
|
|
781
|
+
"""
|
|
782
|
+
Class used to access system information in WSL.
|
|
783
|
+
"""
|
|
784
|
+
|
|
785
|
+
def get_cpu_device(self) -> dict:
|
|
786
|
+
"""
|
|
787
|
+
Retrieves CPU device information in WSL environment.
|
|
788
|
+
"""
|
|
789
|
+
return {"available": False, "error": "Device detection not supported in WSL"}
|
|
790
|
+
|
|
791
|
+
def get_amd_igpu_device(self, include_inference_engines: bool = False) -> dict:
|
|
792
|
+
"""
|
|
793
|
+
Retrieves AMD integrated GPU device information in WSL environment.
|
|
794
|
+
"""
|
|
795
|
+
return {"available": False, "error": "GPU detection not supported in WSL"}
|
|
796
|
+
|
|
797
|
+
def get_amd_dgpu_devices(self, include_inference_engines: bool = False) -> list:
|
|
798
|
+
"""
|
|
799
|
+
Retrieves AMD discrete GPU device information in WSL environment.
|
|
800
|
+
"""
|
|
801
|
+
return []
|
|
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
|
+
|
|
811
|
+
def get_npu_device(self) -> dict:
|
|
812
|
+
"""
|
|
813
|
+
Retrieves NPU device information in WSL environment.
|
|
814
|
+
"""
|
|
815
|
+
return {"available": False, "error": "NPU detection not supported in WSL"}
|
|
816
|
+
|
|
817
|
+
@staticmethod
|
|
818
|
+
def get_system_model() -> str:
|
|
819
|
+
"""
|
|
820
|
+
Retrieves the model of the computer system.
|
|
821
|
+
|
|
822
|
+
Returns:
|
|
823
|
+
str: Model of the computer system.
|
|
824
|
+
"""
|
|
825
|
+
try:
|
|
826
|
+
oem_info = (
|
|
827
|
+
subprocess.check_output(
|
|
828
|
+
'powershell.exe -Command "wmic computersystem get model"',
|
|
829
|
+
shell=True,
|
|
830
|
+
)
|
|
831
|
+
.decode()
|
|
832
|
+
.strip()
|
|
833
|
+
)
|
|
834
|
+
oem_info = (
|
|
835
|
+
oem_info.replace("\r", "").replace("\n", "").split("Model")[-1].strip()
|
|
836
|
+
)
|
|
837
|
+
return oem_info
|
|
838
|
+
except Exception as e: # pylint: disable=broad-except
|
|
839
|
+
return f"ERROR - {e}"
|
|
840
|
+
|
|
841
|
+
def get_dict(self) -> dict:
|
|
842
|
+
"""
|
|
843
|
+
Retrieves all the system information into a dictionary.
|
|
844
|
+
|
|
845
|
+
Returns:
|
|
846
|
+
dict: System information.
|
|
847
|
+
"""
|
|
848
|
+
info_dict = super().get_dict()
|
|
849
|
+
info_dict["OEM System"] = self.get_system_model()
|
|
850
|
+
return info_dict
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
class LinuxSystemInfo(SystemInfo):
|
|
854
|
+
"""
|
|
855
|
+
Class used to access system information in Linux.
|
|
856
|
+
"""
|
|
857
|
+
|
|
858
|
+
def get_cpu_device(self) -> dict:
|
|
859
|
+
"""
|
|
860
|
+
Retrieves CPU device information using /proc/cpuinfo and lscpu.
|
|
861
|
+
|
|
862
|
+
Returns:
|
|
863
|
+
dict: CPU device information.
|
|
864
|
+
"""
|
|
865
|
+
try:
|
|
866
|
+
cpu_info = subprocess.check_output("lscpu", shell=True).decode()
|
|
867
|
+
cpu_data = {}
|
|
868
|
+
|
|
869
|
+
for line in cpu_info.split("\n"):
|
|
870
|
+
if "Model name:" in line:
|
|
871
|
+
cpu_data["name"] = line.split(":")[1].strip()
|
|
872
|
+
elif "CPU(s):" in line and "NUMA" not in line:
|
|
873
|
+
cpu_data["threads"] = int(line.split(":")[1].strip())
|
|
874
|
+
elif "Core(s) per socket:" in line:
|
|
875
|
+
cores_per_socket = int(line.split(":")[1].strip())
|
|
876
|
+
sockets = (
|
|
877
|
+
1 # Default to 1 socket as most laptops have a single socket
|
|
878
|
+
)
|
|
879
|
+
for l in cpu_info.split("\n"):
|
|
880
|
+
if "Socket(s):" in l:
|
|
881
|
+
sockets = int(l.split(":")[1].strip())
|
|
882
|
+
break
|
|
883
|
+
cpu_data["cores"] = cores_per_socket * sockets
|
|
884
|
+
elif "Architecture:" in line:
|
|
885
|
+
cpu_data["architecture"] = line.split(":")[1].strip()
|
|
886
|
+
|
|
887
|
+
if "name" in cpu_data:
|
|
888
|
+
cpu_name = cpu_data.get("name", "Unknown")
|
|
889
|
+
cpu_info = {
|
|
890
|
+
"name": cpu_data.get("name", "Unknown"),
|
|
891
|
+
"cores": cpu_data.get("cores", "Unknown"),
|
|
892
|
+
"threads": cpu_data.get("threads", "Unknown"),
|
|
893
|
+
"architecture": cpu_data.get("architecture", "Unknown"),
|
|
894
|
+
"available": True,
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
# Add inference engine detection
|
|
898
|
+
cpu_info["inference_engines"] = self._detect_inference_engines(
|
|
899
|
+
"cpu", cpu_name
|
|
900
|
+
)
|
|
901
|
+
return cpu_info
|
|
902
|
+
except Exception as e: # pylint: disable=broad-except
|
|
903
|
+
return {"available": False, "error": f"CPU detection failed: {e}"}
|
|
904
|
+
|
|
905
|
+
return {"available": False, "error": "No CPU information found"}
|
|
906
|
+
|
|
907
|
+
def _detect_amd_gpus(self, gpu_type: str, include_inference_engines: bool = False):
|
|
908
|
+
"""
|
|
909
|
+
Shared AMD GPU detection logic for both integrated and discrete GPUs.
|
|
910
|
+
Uses keyword-based classification for simplicity and reliability.
|
|
911
|
+
|
|
912
|
+
Args:
|
|
913
|
+
gpu_type: Either "integrated" or "discrete".
|
|
914
|
+
|
|
915
|
+
Returns:
|
|
916
|
+
list: List of detected GPU info dictionaries.
|
|
917
|
+
"""
|
|
918
|
+
gpu_devices = []
|
|
919
|
+
try:
|
|
920
|
+
lspci_output = subprocess.check_output(
|
|
921
|
+
"lspci | grep -i 'vga\\|3d\\|display'", shell=True
|
|
922
|
+
).decode()
|
|
923
|
+
|
|
924
|
+
for line in lspci_output.split("\n"):
|
|
925
|
+
if line.strip() and "AMD" in line:
|
|
926
|
+
name_lower = line.lower()
|
|
927
|
+
|
|
928
|
+
# Keyword-based classification - simple and reliable
|
|
929
|
+
is_discrete_by_name = any(
|
|
930
|
+
kw in name_lower for kw in AMD_DISCRETE_GPU_KEYWORDS
|
|
931
|
+
)
|
|
932
|
+
is_integrated = not is_discrete_by_name
|
|
933
|
+
|
|
934
|
+
# Filter based on requested type
|
|
935
|
+
if (gpu_type == "integrated" and is_integrated) or (
|
|
936
|
+
gpu_type == "discrete" and not is_integrated
|
|
937
|
+
):
|
|
938
|
+
|
|
939
|
+
device_type = "amd_igpu" if is_integrated else "amd_dgpu"
|
|
940
|
+
device_name = line.split(": ")[1] if ": " in line else line
|
|
941
|
+
|
|
942
|
+
gpu_info = {
|
|
943
|
+
"name": device_name,
|
|
944
|
+
"available": True,
|
|
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
|
+
|
|
960
|
+
if include_inference_engines:
|
|
961
|
+
gpu_info["inference_engines"] = (
|
|
962
|
+
self._detect_inference_engines(device_type, device_name)
|
|
963
|
+
)
|
|
964
|
+
gpu_devices.append(gpu_info)
|
|
965
|
+
|
|
966
|
+
except Exception as e: # pylint: disable=broad-except
|
|
967
|
+
error_msg = f"AMD {gpu_type} GPU detection failed: {e}"
|
|
968
|
+
return [{"available": False, "error": error_msg}]
|
|
969
|
+
|
|
970
|
+
return gpu_devices
|
|
971
|
+
|
|
972
|
+
def get_amd_igpu_device(self, include_inference_engines: bool = False) -> dict:
|
|
973
|
+
"""
|
|
974
|
+
Retrieves AMD integrated GPU device information using keyword-based classification.
|
|
975
|
+
|
|
976
|
+
Returns:
|
|
977
|
+
dict: AMD iGPU device information.
|
|
978
|
+
"""
|
|
979
|
+
igpu_devices = self._detect_amd_gpus(
|
|
980
|
+
"integrated", include_inference_engines=include_inference_engines
|
|
981
|
+
)
|
|
982
|
+
return (
|
|
983
|
+
igpu_devices[0]
|
|
984
|
+
if igpu_devices
|
|
985
|
+
else {"available": False, "error": "No AMD integrated GPU found"}
|
|
986
|
+
)
|
|
987
|
+
|
|
988
|
+
def get_amd_dgpu_devices(self, include_inference_engines: bool = False) -> list:
|
|
989
|
+
"""
|
|
990
|
+
Retrieves AMD discrete GPU device information using keyword-based classification.
|
|
991
|
+
|
|
992
|
+
Returns:
|
|
993
|
+
list: List of AMD dGPU device information.
|
|
994
|
+
"""
|
|
995
|
+
dgpu_devices = self._detect_amd_gpus(
|
|
996
|
+
"discrete", include_inference_engines=include_inference_engines
|
|
997
|
+
)
|
|
998
|
+
return (
|
|
999
|
+
dgpu_devices
|
|
1000
|
+
if dgpu_devices
|
|
1001
|
+
else [{"available": False, "error": "No AMD discrete GPU found"}]
|
|
1002
|
+
)
|
|
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
|
+
|
|
1064
|
+
def get_npu_device(self) -> dict:
|
|
1065
|
+
"""
|
|
1066
|
+
Retrieves NPU device information (limited support on Linux).
|
|
1067
|
+
|
|
1068
|
+
Returns:
|
|
1069
|
+
dict: NPU device information.
|
|
1070
|
+
"""
|
|
1071
|
+
return {
|
|
1072
|
+
"available": False,
|
|
1073
|
+
"error": "NPU detection not yet implemented for Linux",
|
|
1074
|
+
}
|
|
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
|
+
@staticmethod
|
|
1114
|
+
def get_processor_name() -> str:
|
|
1115
|
+
"""
|
|
1116
|
+
Retrieves the name of the processor.
|
|
1117
|
+
|
|
1118
|
+
Returns:
|
|
1119
|
+
str: Name of the processor.
|
|
1120
|
+
"""
|
|
1121
|
+
# Get CPU Information
|
|
1122
|
+
try:
|
|
1123
|
+
cpu_info = subprocess.check_output("lscpu", shell=True).decode()
|
|
1124
|
+
for line in cpu_info.split("\n"):
|
|
1125
|
+
if "Model name:" in line:
|
|
1126
|
+
return line.split(":")[1].strip()
|
|
1127
|
+
except Exception as e: # pylint: disable=broad-except
|
|
1128
|
+
return f"ERROR - {e}"
|
|
1129
|
+
|
|
1130
|
+
@staticmethod
|
|
1131
|
+
def get_system_model() -> str:
|
|
1132
|
+
"""
|
|
1133
|
+
Retrieves the model of the computer system.
|
|
1134
|
+
|
|
1135
|
+
Returns:
|
|
1136
|
+
str: Model of the computer system.
|
|
1137
|
+
"""
|
|
1138
|
+
# Get OEM System Information
|
|
1139
|
+
try:
|
|
1140
|
+
oem_info = (
|
|
1141
|
+
subprocess.check_output(
|
|
1142
|
+
"sudo -n dmidecode -s system-product-name",
|
|
1143
|
+
shell=True,
|
|
1144
|
+
stderr=subprocess.DEVNULL,
|
|
1145
|
+
)
|
|
1146
|
+
.decode()
|
|
1147
|
+
.strip()
|
|
1148
|
+
.replace("\n", " ")
|
|
1149
|
+
)
|
|
1150
|
+
return oem_info
|
|
1151
|
+
except subprocess.CalledProcessError:
|
|
1152
|
+
# This catches the case where sudo requires a password
|
|
1153
|
+
return "Unable to get oem info - password required"
|
|
1154
|
+
except Exception as e: # pylint: disable=broad-except
|
|
1155
|
+
return f"ERROR - {e}"
|
|
1156
|
+
|
|
1157
|
+
@staticmethod
|
|
1158
|
+
def get_physical_memory() -> str:
|
|
1159
|
+
"""
|
|
1160
|
+
Retrieves the physical memory of the computer system.
|
|
1161
|
+
|
|
1162
|
+
Returns:
|
|
1163
|
+
str: Physical memory.
|
|
1164
|
+
"""
|
|
1165
|
+
try:
|
|
1166
|
+
mem_info = (
|
|
1167
|
+
subprocess.check_output("free -m", shell=True)
|
|
1168
|
+
.decode()
|
|
1169
|
+
.split("\n")[1]
|
|
1170
|
+
.split()[1]
|
|
1171
|
+
)
|
|
1172
|
+
mem_info_gb = round(int(mem_info) / 1024, 2)
|
|
1173
|
+
return f"{mem_info_gb} GB"
|
|
1174
|
+
except Exception as e: # pylint: disable=broad-except
|
|
1175
|
+
return f"ERROR - {e}"
|
|
1176
|
+
|
|
1177
|
+
def get_dict(self) -> dict:
|
|
1178
|
+
"""
|
|
1179
|
+
Retrieves all the system information into a dictionary.
|
|
1180
|
+
|
|
1181
|
+
Returns:
|
|
1182
|
+
dict: System information.
|
|
1183
|
+
"""
|
|
1184
|
+
info_dict = super().get_dict()
|
|
1185
|
+
info_dict["Processor"] = self.get_processor_name()
|
|
1186
|
+
info_dict["OEM System"] = self.get_system_model()
|
|
1187
|
+
info_dict["Physical Memory"] = self.get_physical_memory()
|
|
1188
|
+
return info_dict
|
|
1189
|
+
|
|
1190
|
+
def _get_nvidia_vram_smi_linux(self) -> float:
|
|
1191
|
+
"""
|
|
1192
|
+
Get NVIDIA GPU VRAM using nvidia-smi command on Linux.
|
|
1193
|
+
|
|
1194
|
+
Returns:
|
|
1195
|
+
float: VRAM in GB, or 0.0 if detection fails
|
|
1196
|
+
"""
|
|
1197
|
+
try:
|
|
1198
|
+
output = (
|
|
1199
|
+
subprocess.check_output(
|
|
1200
|
+
[
|
|
1201
|
+
"nvidia-smi",
|
|
1202
|
+
"--query-gpu=memory.total",
|
|
1203
|
+
"--format=csv,noheader,nounits",
|
|
1204
|
+
],
|
|
1205
|
+
stderr=subprocess.DEVNULL,
|
|
1206
|
+
)
|
|
1207
|
+
.decode()
|
|
1208
|
+
.strip()
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1211
|
+
# nvidia-smi returns memory in MB
|
|
1212
|
+
vram_mb = int(output.split("\n")[0])
|
|
1213
|
+
vram_gb = round(vram_mb / 1024, 1)
|
|
1214
|
+
return vram_gb
|
|
1215
|
+
except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
|
|
1216
|
+
pass
|
|
1217
|
+
return 0.0
|
|
1218
|
+
|
|
1219
|
+
def _get_amd_vram_rocm_smi_linux(self) -> float:
|
|
1220
|
+
"""
|
|
1221
|
+
Get AMD GPU VRAM using rocm-smi command on Linux.
|
|
1222
|
+
|
|
1223
|
+
Returns:
|
|
1224
|
+
float: VRAM in GB, or 0.0 if detection fails
|
|
1225
|
+
"""
|
|
1226
|
+
try:
|
|
1227
|
+
output = (
|
|
1228
|
+
subprocess.check_output(
|
|
1229
|
+
["rocm-smi", "--showmeminfo", "vram", "--csv"],
|
|
1230
|
+
stderr=subprocess.DEVNULL,
|
|
1231
|
+
)
|
|
1232
|
+
.decode()
|
|
1233
|
+
.strip()
|
|
1234
|
+
)
|
|
1235
|
+
|
|
1236
|
+
# Parse CSV output to extract VRAM
|
|
1237
|
+
lines = output.split("\n")
|
|
1238
|
+
for line in lines:
|
|
1239
|
+
if "Total VRAM" in line or "vram" in line.lower():
|
|
1240
|
+
# Extract numeric value (assuming it's in MB or GB)
|
|
1241
|
+
numbers = re.findall(r"\d+", line)
|
|
1242
|
+
if numbers:
|
|
1243
|
+
vram_value = int(numbers[0])
|
|
1244
|
+
# Assume MB if value is large, GB if small
|
|
1245
|
+
if vram_value > 100: # Likely MB
|
|
1246
|
+
vram_gb = round(vram_value / 1024, 1)
|
|
1247
|
+
else: # Likely GB
|
|
1248
|
+
vram_gb = float(vram_value)
|
|
1249
|
+
return vram_gb
|
|
1250
|
+
except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
|
|
1251
|
+
pass
|
|
1252
|
+
return 0.0
|
|
1253
|
+
|
|
1254
|
+
def _get_amd_vram_sysfs(self, pci_id: str) -> float:
|
|
1255
|
+
"""
|
|
1256
|
+
Get AMD GPU VRAM using sysfs on Linux.
|
|
1257
|
+
|
|
1258
|
+
Args:
|
|
1259
|
+
pci_id: PCI ID of the GPU (e.g., "0000:01:00.0")
|
|
1260
|
+
|
|
1261
|
+
Returns:
|
|
1262
|
+
float: VRAM in GB, or 0.0 if detection fails
|
|
1263
|
+
"""
|
|
1264
|
+
try:
|
|
1265
|
+
# Try different sysfs paths for VRAM information
|
|
1266
|
+
sysfs_paths = [
|
|
1267
|
+
f"/sys/bus/pci/devices/{pci_id}/mem_info_vram_total",
|
|
1268
|
+
"/sys/class/drm/card*/device/mem_info_vram_total",
|
|
1269
|
+
]
|
|
1270
|
+
|
|
1271
|
+
for path in sysfs_paths:
|
|
1272
|
+
try:
|
|
1273
|
+
if "*" in path:
|
|
1274
|
+
# Handle wildcard paths
|
|
1275
|
+
matching_paths = glob.glob(path)
|
|
1276
|
+
for match_path in matching_paths:
|
|
1277
|
+
with open(match_path, "r", encoding="utf-8") as f:
|
|
1278
|
+
vram_bytes = int(f.read().strip())
|
|
1279
|
+
vram_gb = round(vram_bytes / (1024**3), 1)
|
|
1280
|
+
if vram_gb > 0:
|
|
1281
|
+
return vram_gb
|
|
1282
|
+
else:
|
|
1283
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
1284
|
+
vram_bytes = int(f.read().strip())
|
|
1285
|
+
vram_gb = round(vram_bytes / (1024**3), 1)
|
|
1286
|
+
return vram_gb
|
|
1287
|
+
except (FileNotFoundError, ValueError, PermissionError):
|
|
1288
|
+
continue
|
|
1289
|
+
except Exception: # pylint: disable=broad-except
|
|
1290
|
+
pass
|
|
1291
|
+
return 0.0
|
|
1292
|
+
|
|
1293
|
+
def _detect_inference_engines(self, device_type: str, device_name: str) -> dict:
|
|
1294
|
+
"""
|
|
1295
|
+
Detect available inference engines for a specific device type.
|
|
1296
|
+
|
|
1297
|
+
Args:
|
|
1298
|
+
device_type: Device type ("cpu", "amd_igpu", "amd_dgpu", "npu")
|
|
1299
|
+
|
|
1300
|
+
Returns:
|
|
1301
|
+
dict: Available inference engines and their information.
|
|
1302
|
+
"""
|
|
1303
|
+
try:
|
|
1304
|
+
return detect_inference_engines(device_type, device_name)
|
|
1305
|
+
except Exception as e: # pylint: disable=broad-except
|
|
1306
|
+
return {"error": f"Inference engine detection failed: {str(e)}"}
|
|
1307
|
+
|
|
1308
|
+
|
|
1309
|
+
class UnsupportedOSSystemInfo(SystemInfo):
|
|
1310
|
+
"""
|
|
1311
|
+
Class used to access system information in unsupported operating systems.
|
|
1312
|
+
"""
|
|
1313
|
+
|
|
1314
|
+
def get_cpu_device(self) -> dict:
|
|
1315
|
+
"""
|
|
1316
|
+
Retrieves CPU device information for unsupported OS.
|
|
1317
|
+
"""
|
|
1318
|
+
return {
|
|
1319
|
+
"available": False,
|
|
1320
|
+
"error": "Device detection not supported on this operating system",
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
def get_amd_igpu_device(self, include_inference_engines: bool = False) -> dict:
|
|
1324
|
+
"""
|
|
1325
|
+
Retrieves AMD integrated GPU device information for unsupported OS.
|
|
1326
|
+
"""
|
|
1327
|
+
return {
|
|
1328
|
+
"available": False,
|
|
1329
|
+
"error": "Device detection not supported on this operating system",
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
def get_amd_dgpu_devices(self, include_inference_engines: bool = False) -> list:
|
|
1333
|
+
"""
|
|
1334
|
+
Retrieves AMD discrete GPU device information for unsupported OS.
|
|
1335
|
+
"""
|
|
1336
|
+
return []
|
|
1337
|
+
|
|
1338
|
+
def get_nvidia_dgpu_devices(self, include_inference_engines: bool = False) -> list:
|
|
1339
|
+
"""
|
|
1340
|
+
Retrieves NVIDIA discrete GPU device information for unsupported OS.
|
|
1341
|
+
"""
|
|
1342
|
+
return [
|
|
1343
|
+
{
|
|
1344
|
+
"available": False,
|
|
1345
|
+
"error": "Device detection not supported on this operating system",
|
|
1346
|
+
}
|
|
1347
|
+
]
|
|
1348
|
+
|
|
1349
|
+
def get_npu_device(self) -> dict:
|
|
1350
|
+
"""
|
|
1351
|
+
Retrieves NPU device information for unsupported OS.
|
|
1352
|
+
"""
|
|
1353
|
+
return {
|
|
1354
|
+
"available": False,
|
|
1355
|
+
"error": "Device detection not supported on this operating system",
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
def get_dict(self):
|
|
1359
|
+
"""
|
|
1360
|
+
Retrieves all the system information into a dictionary.
|
|
1361
|
+
|
|
1362
|
+
Returns:
|
|
1363
|
+
dict: System information.
|
|
1364
|
+
"""
|
|
1365
|
+
info_dict = super().get_dict()
|
|
1366
|
+
info_dict["Error"] = "UNSUPPORTED OS"
|
|
1367
|
+
return info_dict
|
|
1368
|
+
|
|
1369
|
+
|
|
1370
|
+
def get_system_info() -> SystemInfo:
|
|
1371
|
+
"""
|
|
1372
|
+
Creates the appropriate SystemInfo object based on the operating system.
|
|
1373
|
+
|
|
1374
|
+
Returns:
|
|
1375
|
+
A subclass of SystemInfo for the current operating system.
|
|
1376
|
+
"""
|
|
1377
|
+
os_type = platform.system()
|
|
1378
|
+
if os_type == "Windows":
|
|
1379
|
+
return WindowsSystemInfo()
|
|
1380
|
+
elif os_type == "Linux":
|
|
1381
|
+
# WSL has to be handled differently compared to native Linux.
|
|
1382
|
+
if "microsoft" in str(platform.release()):
|
|
1383
|
+
return WSLSystemInfo()
|
|
1384
|
+
else:
|
|
1385
|
+
return LinuxSystemInfo()
|
|
1386
|
+
else:
|
|
1387
|
+
return UnsupportedOSSystemInfo()
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
def get_system_info_dict() -> dict:
|
|
1391
|
+
"""
|
|
1392
|
+
Puts the system information into a dictionary.
|
|
1393
|
+
|
|
1394
|
+
Returns:
|
|
1395
|
+
dict: Dictionary containing the system information.
|
|
1396
|
+
"""
|
|
1397
|
+
return get_system_info().get_dict()
|
|
1398
|
+
|
|
1399
|
+
|
|
1400
|
+
def get_device_info_dict() -> dict:
|
|
1401
|
+
"""
|
|
1402
|
+
Puts the device information into a dictionary.
|
|
1403
|
+
|
|
1404
|
+
Returns:
|
|
1405
|
+
dict: Dictionary containing the device information.
|
|
1406
|
+
"""
|
|
1407
|
+
return get_system_info().get_device_dict()
|
|
1408
|
+
|
|
1409
|
+
|
|
1410
|
+
# This file was originally licensed under Apache 2.0. It has been modified.
|
|
1411
|
+
# Modifications Copyright (c) 2025 AMD
|