@@ -89,21 +89,39 @@ func GPUs() ([]*gpu.GraphicsCard, error) {
8989}
9090
9191func TotalAvailableVRAM () (uint64 , error ) {
92+ // First, try ghw library detection
9293 gpus , err := GPUs ()
93- if err != nil {
94- return 0 , err
94+ if err == nil {
95+ var totalVRAM uint64
96+ for _ , gpu := range gpus {
97+ if gpu != nil && gpu .Node != nil && gpu .Node .Memory != nil {
98+ if gpu .Node .Memory .TotalUsableBytes > 0 {
99+ totalVRAM += uint64 (gpu .Node .Memory .TotalUsableBytes )
100+ }
101+ }
102+ }
103+ // If we got valid VRAM from ghw, return it
104+ if totalVRAM > 0 {
105+ return totalVRAM , nil
106+ }
95107 }
96108
97- var totalVRAM uint64
98- for _ , gpu := range gpus {
99- if gpu != nil && gpu .Node != nil && gpu .Node .Memory != nil {
100- if gpu .Node .Memory .TotalUsableBytes > 0 {
101- totalVRAM += uint64 (gpu .Node .Memory .TotalUsableBytes )
102- }
109+ // Fallback to binary-based detection via GetGPUMemoryUsage()
110+ // This works even when ghw dependencies are missing from the base image
111+ gpuMemoryInfo := GetGPUMemoryUsage ()
112+ if len (gpuMemoryInfo ) > 0 {
113+ var totalVRAM uint64
114+ for _ , gpu := range gpuMemoryInfo {
115+ totalVRAM += gpu .TotalVRAM
116+ }
117+ if totalVRAM > 0 {
118+ xlog .Debug ("VRAM detected via binary tools" , "total_vram" , totalVRAM )
119+ return totalVRAM , nil
103120 }
104121 }
105122
106- return totalVRAM , nil
123+ // No VRAM detected
124+ return 0 , nil
107125}
108126
109127func HasGPU (vendor string ) bool {
@@ -122,6 +140,66 @@ func HasGPU(vendor string) bool {
122140 return false
123141}
124142
143+ // DetectGPUVendor detects the GPU vendor using multiple methods with fallbacks.
144+ // First tries ghw library, then falls back to binary detection.
145+ // Returns vendor string (VendorNVIDIA, VendorAMD, VendorIntel, VendorVulkan) or empty string if not detected.
146+ // Priority order: NVIDIA > AMD > Intel > Vulkan
147+ func DetectGPUVendor () (string , error ) {
148+ // First, try ghw library detection
149+ gpus , err := GPUs ()
150+ if err == nil && len (gpus ) > 0 {
151+ for _ , gpu := range gpus {
152+ if gpu .DeviceInfo != nil && gpu .DeviceInfo .Vendor != nil {
153+ vendorName := strings .ToUpper (gpu .DeviceInfo .Vendor .Name )
154+ if strings .Contains (vendorName , strings .ToUpper (VendorNVIDIA )) {
155+ xlog .Debug ("GPU vendor detected via ghw" , "vendor" , VendorNVIDIA )
156+ return VendorNVIDIA , nil
157+ }
158+ if strings .Contains (vendorName , strings .ToUpper (VendorAMD )) {
159+ xlog .Debug ("GPU vendor detected via ghw" , "vendor" , VendorAMD )
160+ return VendorAMD , nil
161+ }
162+ if strings .Contains (vendorName , strings .ToUpper (VendorIntel )) {
163+ xlog .Debug ("GPU vendor detected via ghw" , "vendor" , VendorIntel )
164+ return VendorIntel , nil
165+ }
166+ }
167+ }
168+ }
169+
170+ // Fallback to binary detection (priority: NVIDIA > AMD > Intel > Vulkan)
171+ // Check for nvidia-smi
172+ if _ , err := exec .LookPath ("nvidia-smi" ); err == nil {
173+ xlog .Debug ("GPU vendor detected via binary" , "vendor" , VendorNVIDIA , "binary" , "nvidia-smi" )
174+ return VendorNVIDIA , nil
175+ }
176+
177+ // Check for rocm-smi (AMD)
178+ if _ , err := exec .LookPath ("rocm-smi" ); err == nil {
179+ xlog .Debug ("GPU vendor detected via binary" , "vendor" , VendorAMD , "binary" , "rocm-smi" )
180+ return VendorAMD , nil
181+ }
182+
183+ // Check for xpu-smi or intel_gpu_top (Intel)
184+ if _ , err := exec .LookPath ("xpu-smi" ); err == nil {
185+ xlog .Debug ("GPU vendor detected via binary" , "vendor" , VendorIntel , "binary" , "xpu-smi" )
186+ return VendorIntel , nil
187+ }
188+ if _ , err := exec .LookPath ("intel_gpu_top" ); err == nil {
189+ xlog .Debug ("GPU vendor detected via binary" , "vendor" , VendorIntel , "binary" , "intel_gpu_top" )
190+ return VendorIntel , nil
191+ }
192+
193+ // Check for vulkaninfo (Vulkan - lowest priority as it can detect any GPU)
194+ if _ , err := exec .LookPath ("vulkaninfo" ); err == nil {
195+ xlog .Debug ("GPU vendor detected via binary" , "vendor" , VendorVulkan , "binary" , "vulkaninfo" )
196+ return VendorVulkan , nil
197+ }
198+
199+ // No vendor detected
200+ return "" , nil
201+ }
202+
125203// isUnifiedMemoryDevice checks if the given GPU name matches any known unified memory device
126204func isUnifiedMemoryDevice (gpuName string ) bool {
127205 gpuNameUpper := strings .ToUpper (gpuName )
0 commit comments