Skip to content
8 changes: 8 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ const nextConfig: NextConfig = {
];
return config;
},
images: {
remotePatterns: [
{
protocol: "https",
hostname: "avatars.githubusercontent.com",
},
],
},
};

export default nextConfig;
6 changes: 6 additions & 0 deletions public/amd/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Place amd-rocgfx-logo.png here.

Download from: https://rocm.docs.amd.com/en/latest/_static/amd-rocgfx-logo.png
Alternative: https://raw.githubusercontent.com/RadeonOpenCompute/ROCm/<version>/docs/_static/amd-rocgfx-logo.png

The component falls back to a styled text badge if this image is absent.
8 changes: 7 additions & 1 deletion src/app/api/gateway-metrics/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ export interface SystemMetrics {
uptime: number;
hostname: string;
platform: string;
// ROCm system-level info (available when rocmGpus.length > 0)
rocmDetected: boolean;
rocmRuntimeVersion: string;
}

interface GatewayPresence {
Expand Down Expand Up @@ -134,8 +137,9 @@ async function getLocalMetrics(): Promise<SystemMetrics> {

// Detect ROCm GPU data if available
let rocmGpus: ROCmGPUInfo[] = [];
let rocmInfo: { detected: boolean; gpus: ROCmGPUInfo[]; runtimeVersion?: string } = { detected: false, gpus: [] };
try {
const rocmInfo = await detectROCm();
rocmInfo = await detectROCm();
if (rocmInfo.detected && rocmInfo.gpus.length > 0) {
rocmGpus = rocmInfo.gpus;
}
Expand Down Expand Up @@ -255,6 +259,8 @@ async function getLocalMetrics(): Promise<SystemMetrics> {
uptime: Math.round(process.uptime()),
hostname: osInfo.hostname,
platform: `${osInfo.platform} ${osInfo.arch}`,
rocmDetected: rocmGpus.length > 0,
rocmRuntimeVersion: rocmInfo.runtimeVersion ?? "",
};
}

Expand Down
9 changes: 8 additions & 1 deletion src/app/api/system/metrics/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export interface SystemMetrics {
uptime: number;
hostname: string;
platform: string;
// ROCm system-level info (available when rocmGpus.length > 0)
rocmDetected: boolean;
rocmRuntimeVersion: string;
// GPU fallback source info (indicates which detection method was used)
gpuDetectionMethod?: "rocm" | "systeminfo" | "basic-sysfs" | "none";
}
Expand Down Expand Up @@ -135,9 +138,11 @@ export async function GET(
let rocmGpus: ROCmGPUInfo[] = [];
let basicGpus: BasicGPUInfo[] = [];
let gpuDetectionMethod: SystemMetrics["gpuDetectionMethod"] = "none";
// Keep rocmInfo in scope so we can read runtimeVersion for the response
let rocmInfo: { detected: boolean; gpus: ROCmGPUInfo[]; runtimeVersion?: string } = { detected: false, gpus: [] };

try {
const rocmInfo = await detectROCm();
rocmInfo = await detectROCm();
if (rocmInfo.detected && rocmInfo.gpus.length > 0) {
rocmGpus = rocmInfo.gpus;
}
Expand Down Expand Up @@ -263,6 +268,8 @@ export async function GET(
uptime: Math.round(process.uptime()),
hostname: osInfo.hostname,
platform: `${osInfo.platform} ${osInfo.arch}`,
rocmDetected: rocmGpus.length > 0,
rocmRuntimeVersion: rocmInfo.runtimeVersion ?? "",
gpuDetectionMethod,
};

Expand Down
20 changes: 20 additions & 0 deletions src/components/SystemMetricsDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import Image from "next/image";
import { useEffect, useState, useCallback } from "react";
import {
Cpu,
Expand Down Expand Up @@ -84,6 +85,9 @@ interface SystemMetrics {
uptime: number;
hostname: string;
platform: string;
// ROCm system-level info (only populated when rocmGpus.length > 0)
rocmDetected: boolean;
rocmRuntimeVersion: string;
}

function MetricRow({
Expand Down Expand Up @@ -537,6 +541,22 @@ export function SystemMetricsDashboard() {
)}
</div>

{/* ROCm Powered By — shown when ROCm is detected */}
{metrics.rocmDetected && (
<div className="flex items-center gap-2 pt-2 border-t border-border/30">
<Image
src="https://avatars.githubusercontent.com/u/16900649?s=280&v=4"
alt="AMD ROCm"
width={80}
height={32}
className="h-8 w-auto object-contain"
/>
<span className="text-xs text-muted-foreground">
Powered by ROCm {metrics.rocmRuntimeVersion}
</span>
</div>
)}

{/* GPU Hardware Details — collapsible */}
{(primaryGpu.deviceId || primaryGpu.driverVersion || primaryGpu.vbiosVersion) && (
<div className="pt-2 border-t border-border/30">
Expand Down
27 changes: 27 additions & 0 deletions src/lib/system/rocm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,35 @@ async function getComprehensiveGpuInfo(rocmSmiPath: string): Promise<{

/**
* Get ROCm runtime version
* Prefers /opt/rocm/.info/version-rocm (e.g. "7.2.1-81") which reflects the actual
* ROCm stack version. Falls back to rocminfo parsing which reports the GFX
* runtime version (e.g. "1.18" for gfx1151) rather than the ROCm release version.
*/
async function getRocmVersion(rocmInfoPath: string): Promise<string> {
// Try the ROCm version file first — this is the actual ROCm release version
const versionFilePaths = [
"/opt/rocm/.info/version-rocm",
"/opt/rocm/.info/version",
];

for (const versionFile of versionFilePaths) {
try {
const { stdout } = await execAsync(`cat ${versionFile} 2>/dev/null`);
const trimmed = stdout.trim();
// version files are in format "7.2.1-81" — strip the package suffix
const match = trimmed.match(/^(\d+\.\d+\.\d+)/);
if (match) {
return match[1];
}
if (trimmed) {
return trimmed;
}
} catch {
// Continue to next file
}
}

// Fallback: parse rocminfo output (less reliable — gives GFX runtime version)
try {
const { stdout } = await execAsync(`${rocmInfoPath} 2>&1`);
const versionMatch = stdout.match(/Runtime Version:\s*([\d.]+)/);
Expand Down
Loading