mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-04-05 16:26:15 +02:00
fix(benchmark): UI improvements and GPU detection fix
- Fix GPU detection to properly identify AMD discrete GPUs - Fix gauge colors (high scores now green, low scores red) - Fix gauge centering (SVG size matches container) - Add info tooltips for Tokens/sec and Time to First Token Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a9b8a906a6
commit
5e514440c5
|
|
@ -223,14 +223,29 @@ export class BenchmarkService {
|
|||
}
|
||||
}
|
||||
|
||||
// Get GPU model (prefer discrete GPU)
|
||||
// Get GPU model (prefer discrete GPU with dedicated VRAM)
|
||||
let gpuModel: string | null = null
|
||||
if (graphics.controllers && graphics.controllers.length > 0) {
|
||||
const discreteGpu = graphics.controllers.find(
|
||||
(g) => !g.vendor?.toLowerCase().includes('intel') &&
|
||||
!g.vendor?.toLowerCase().includes('amd') ||
|
||||
(g.vram && g.vram > 0)
|
||||
)
|
||||
// First, look for discrete GPUs (NVIDIA, AMD discrete, or any with significant VRAM)
|
||||
const discreteGpu = graphics.controllers.find((g) => {
|
||||
const vendor = g.vendor?.toLowerCase() || ''
|
||||
const model = g.model?.toLowerCase() || ''
|
||||
// NVIDIA GPUs are always discrete
|
||||
if (vendor.includes('nvidia') || model.includes('geforce') || model.includes('rtx') || model.includes('quadro')) {
|
||||
return true
|
||||
}
|
||||
// AMD discrete GPUs (Radeon, not integrated APU graphics)
|
||||
if ((vendor.includes('amd') || vendor.includes('ati')) &&
|
||||
(model.includes('radeon') || model.includes('rx ') || model.includes('vega')) &&
|
||||
!model.includes('graphics')) {
|
||||
return true
|
||||
}
|
||||
// Any GPU with dedicated VRAM > 512MB is likely discrete
|
||||
if (g.vram && g.vram > 512) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
gpuModel = discreteGpu?.model || graphics.controllers[0]?.model || null
|
||||
}
|
||||
|
||||
|
|
|
|||
35
admin/inertia/components/InfoTooltip.tsx
Normal file
35
admin/inertia/components/InfoTooltip.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { InformationCircleIcon } from '@heroicons/react/24/outline'
|
||||
import { useState } from 'react'
|
||||
|
||||
interface InfoTooltipProps {
|
||||
text: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function InfoTooltip({ text, className = '' }: InfoTooltipProps) {
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
|
||||
return (
|
||||
<span className={`relative inline-flex items-center ${className}`}>
|
||||
<button
|
||||
type="button"
|
||||
className="text-desert-stone-dark hover:text-desert-green transition-colors p-0.5"
|
||||
onMouseEnter={() => setIsVisible(true)}
|
||||
onMouseLeave={() => setIsVisible(false)}
|
||||
onFocus={() => setIsVisible(true)}
|
||||
onBlur={() => setIsVisible(false)}
|
||||
aria-label="More information"
|
||||
>
|
||||
<InformationCircleIcon className="w-4 h-4" />
|
||||
</button>
|
||||
{isVisible && (
|
||||
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 z-50">
|
||||
<div className="bg-desert-stone-dark text-white text-xs rounded-lg px-3 py-2 max-w-xs whitespace-normal shadow-lg">
|
||||
{text}
|
||||
<div className="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-desert-stone-dark" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
@ -31,23 +31,24 @@ export default function CircularGauge({
|
|||
|
||||
const displayValue = animated ? animatedValue : value
|
||||
|
||||
// Size configs: container size must match SVG size (2 * (radius + strokeWidth))
|
||||
const sizes = {
|
||||
sm: {
|
||||
container: 'w-32 h-32',
|
||||
container: 'w-28 h-28', // 112px = 2 * (48 + 8)
|
||||
strokeWidth: 8,
|
||||
radius: 48,
|
||||
fontSize: 'text-xl',
|
||||
labelSize: 'text-xs',
|
||||
},
|
||||
md: {
|
||||
container: 'w-40 h-40',
|
||||
container: 'w-[140px] h-[140px]', // 140px = 2 * (60 + 10)
|
||||
strokeWidth: 10,
|
||||
radius: 60,
|
||||
fontSize: 'text-2xl',
|
||||
labelSize: 'text-sm',
|
||||
},
|
||||
lg: {
|
||||
container: 'w-60 h-60',
|
||||
container: 'w-[244px] h-[244px]', // 244px = 2 * (110 + 12)
|
||||
strokeWidth: 12,
|
||||
radius: 110,
|
||||
fontSize: 'text-4xl',
|
||||
|
|
@ -60,10 +61,11 @@ export default function CircularGauge({
|
|||
const offset = circumference - (displayValue / 100) * circumference
|
||||
|
||||
const getColor = () => {
|
||||
if (value >= 90) return 'desert-red'
|
||||
if (value >= 75) return 'desert-orange'
|
||||
if (value >= 50) return 'desert-tan'
|
||||
return 'desert-olive'
|
||||
// For benchmarks: higher scores = better = green
|
||||
if (value >= 75) return 'desert-green'
|
||||
if (value >= 50) return 'desert-olive'
|
||||
if (value >= 25) return 'desert-orange'
|
||||
return 'desert-red'
|
||||
}
|
||||
|
||||
const color = getColor()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import CircularGauge from '~/components/systeminfo/CircularGauge'
|
|||
import InfoCard from '~/components/systeminfo/InfoCard'
|
||||
import Alert from '~/components/Alert'
|
||||
import StyledButton from '~/components/StyledButton'
|
||||
import InfoTooltip from '~/components/InfoTooltip'
|
||||
import {
|
||||
ChartBarIcon,
|
||||
CpuChipIcon,
|
||||
|
|
@ -521,7 +522,10 @@ export default function BenchmarkPage(props: {
|
|||
<div className="text-3xl font-bold text-desert-green">
|
||||
{latestResult.ai_tokens_per_second.toFixed(1)}
|
||||
</div>
|
||||
<div className="text-sm text-desert-stone-dark">Tokens per Second</div>
|
||||
<div className="text-sm text-desert-stone-dark flex items-center gap-1">
|
||||
Tokens per Second
|
||||
<InfoTooltip text="How fast the AI generates text. Higher is better. 30+ tokens/sec feels responsive, 60+ feels instant." />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -532,7 +536,10 @@ export default function BenchmarkPage(props: {
|
|||
<div className="text-3xl font-bold text-desert-green">
|
||||
{latestResult.ai_time_to_first_token?.toFixed(0) || 'N/A'} ms
|
||||
</div>
|
||||
<div className="text-sm text-desert-stone-dark">Time to First Token</div>
|
||||
<div className="text-sm text-desert-stone-dark flex items-center gap-1">
|
||||
Time to First Token
|
||||
<InfoTooltip text="How quickly the AI starts responding after you send a message. Lower is better. Under 500ms feels instant." />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user