Compare commits

...
Author SHA1 Message Date
ssongliu 726fd94e07 fix(dashboard): improve vLLM monitoring display and preserve samples (#14326) 2026-11-06 23:44:30 +08:00
王贺andssongliu 95f12c16c3 feat(ai): add GPU environment diagnostics and device summaries (#14322)
* feat(ai): add GPU environment diagnostics and device summaries

* chore(ai): remove GPU diagnostics comments and test files

* feat: separate host and container GPU environment status

* fix(ai): correct GPU environment diagnostics and timeout handling

---------

Co-authored-by: ssongliu <sloooop1x@gmail.com>
2026-11-06 23:16:10 +08:00
Yuki Guo 7a40ef9725 refactor : modify the text for the quick-access menu button (#14321) 2026-11-06 22:33:01 +08:00
ssongliu ea31289da3 feat(dashboard): add vLLM token throughput monitoring (#14325) 2026-11-06 21:47:02 +08:00
31 changed files with 2200 additions and 137 deletions
+4 -14
View File
@@ -4,8 +4,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/service" "github.com/1Panel-dev/1Panel/agent/app/service"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/accelerator"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -21,20 +19,12 @@ func (b *BaseApi) LoadModelGPUDevices(c *gin.Context) {
// @Security Timestamp // @Security Timestamp
// @Router /ai/gpu/load [get] // @Router /ai/gpu/load [get]
func (b *BaseApi) LoadGpuInfo(c *gin.Context) { func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
ok, client := accelerator.New() info, err := service.LoadGPUInfo(c.Request.Context(), c.Query("refresh") == "true")
if ok { if err != nil {
snapshot, err := client.Collect(c.Request.Context()) helper.BadRequest(c, err)
if err != nil {
helper.BadRequest(c, err)
return
}
if warning := snapshot.Warning(); warning != nil {
global.LOG.Warnf("load realtime accelerator data partially failed, err: %v", warning)
}
helper.SuccessWithData(c, &snapshot.Info)
return return
} }
helper.SuccessWithData(c, &accelerator.Info{}) helper.SuccessWithData(c, info)
} }
// @Tags AI // @Tags AI
+38
View File
@@ -0,0 +1,38 @@
package dto
import (
"time"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/accelerator"
)
type GPURealtimeInfo struct {
accelerator.Info
Environment GPUEnvironment `json:"environment"`
Summaries []GPUSummary `json:"summaries"`
}
type GPUEnvironment struct {
CheckedAt time.Time `json:"checkedAt"`
HardwareStatus string `json:"hardwareStatus"`
Checks []GPUEnvironmentCheck `json:"checks"`
}
type GPUEnvironmentCheck struct {
Vendor string `json:"vendor"`
Item string `json:"item"`
Status string `json:"status"`
Code string `json:"code,omitempty"`
Version string `json:"version,omitempty"`
Reason string `json:"reason,omitempty"`
}
type GPUSummary struct {
Vendor string `json:"vendor"`
DriverVersion string `json:"driverVersion"`
CudaVersion string `json:"cudaVersion"`
CardCount *int `json:"cardCount"`
MemoryTotal *float64 `json:"memoryTotal"`
SharedMemoryTotal *float64 `json:"sharedMemoryTotal"`
MemoryPartial bool `json:"memoryPartial"`
}
+13 -8
View File
@@ -192,6 +192,18 @@ func (u *DockerService) LoadDockerStatus() *dto.DockerStatus {
return &data return &data
} }
func readDockerDaemonConfig() (map[string]interface{}, error) {
file, err := os.ReadFile(constant.DaemonJsonPath)
if err != nil {
return nil, err
}
daemonMap := make(map[string]interface{})
if err := json.Unmarshal(file, &daemonMap); err != nil {
return nil, err
}
return daemonMap, nil
}
func (u *DockerService) LoadDockerConf() (*dto.DaemonJsonConf, error) { func (u *DockerService) LoadDockerConf() (*dto.DaemonJsonConf, error) {
ctx := context.Background() ctx := context.Background()
var data dto.DaemonJsonConf var data dto.DaemonJsonConf
@@ -210,18 +222,11 @@ func (u *DockerService) LoadDockerConf() (*dto.DaemonJsonConf, error) {
if strings.Contains(stdout2, "Swarm: active") { if strings.Contains(stdout2, "Swarm: active") {
data.IsSwarm = true data.IsSwarm = true
} }
if _, err := os.Stat(constant.DaemonJsonPath); err != nil { daemonMap, err := readDockerDaemonConfig()
return &data, nil
}
file, err := os.ReadFile(constant.DaemonJsonPath)
if err != nil { if err != nil {
return &data, nil return &data, nil
} }
var conf daemonJsonItem var conf daemonJsonItem
daemonMap := make(map[string]interface{})
if err := json.Unmarshal(file, &daemonMap); err != nil {
return &data, nil
}
arr, err := json.Marshal(daemonMap) arr, err := json.Marshal(daemonMap)
if err != nil { if err != nil {
return &data, err return &data, err
+440
View File
@@ -0,0 +1,440 @@
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/accelerator"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/npu"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types/system"
)
const gpuEnvironmentCacheKey = "gpu:environment"
var gpuEnvironmentLock = make(chan struct{}, 1)
var gpuVendors = []string{"nvidia", "intel", "ascend", "amd"}
type gpuEnvironmentProbe struct {
CheckedAt time.Time `json:"checkedAt"`
Hardware map[string]int `json:"hardware"`
HardwareError string `json:"hardwareError,omitempty"`
Commands map[string]bool `json:"commands"`
NVIDIAChecked bool `json:"nvidiaChecked"`
Checks []dto.GPUEnvironmentCheck `json:"checks"`
}
func LoadGPUInfo(ctx context.Context, refresh bool) (*dto.GPURealtimeInfo, error) {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := ctx.Err(); err != nil {
return nil, err
}
snapshot := &accelerator.Snapshot{}
var collectErr error
if available, client := accelerator.New(); available {
collected, err := client.Collect(ctx)
collectErr = err
if collected != nil {
snapshot = collected
}
}
if err := ctx.Err(); err != nil {
return nil, err
}
if collectErr != nil {
snapshot.Info.Warnings = append(snapshot.Info.Warnings, collectErr.Error())
}
if snapshot.Info.CollectedAt.IsZero() {
snapshot.Info.CollectedAt = time.Now()
}
if global.LOG != nil {
for _, warning := range snapshot.Info.Warnings {
global.LOG.Warnf("load realtime accelerator data: %s", warning)
}
}
summaries := summarizeGPUs(snapshot)
nvidiaPresent := slices.ContainsFunc(summaries, func(item dto.GPUSummary) bool { return item.Vendor == "nvidia" })
probe := loadGPUEnvironmentProbe(ctx, refresh, nvidiaPresent)
if err := ctx.Err(); err != nil {
return nil, err
}
environment := assembleGPUEnvironment(probe, summaries, collectErr, snapshot.Info)
for _, check := range environment.Checks {
if check.Item == "driver" && check.Vendor != "" && !slices.ContainsFunc(summaries, func(item dto.GPUSummary) bool { return item.Vendor == check.Vendor }) {
summaries = append(summaries, dto.GPUSummary{Vendor: check.Vendor, MemoryPartial: true})
}
}
return &dto.GPURealtimeInfo{Info: snapshot.Info, Environment: environment, Summaries: summaries}, nil
}
func loadGPUEnvironmentProbe(ctx context.Context, refresh, nvidiaPresent bool) gpuEnvironmentProbe {
select {
case gpuEnvironmentLock <- struct{}{}:
defer func() { <-gpuEnvironmentLock }()
case <-ctx.Done():
return gpuEnvironmentProbe{CheckedAt: time.Now(), HardwareError: ctx.Err().Error()}
}
if !refresh && global.CACHE != nil {
var cached gpuEnvironmentProbe
if value := global.CACHE.Get(gpuEnvironmentCacheKey); value != "" && json.Unmarshal([]byte(value), &cached) == nil && (!nvidiaPresent || cached.NVIDIAChecked) {
return cached
}
}
probe := gpuEnvironmentProbe{CheckedAt: time.Now(), Commands: map[string]bool{}}
var err error
probe.Hardware, err = detectGPUHardware("/sys/bus/pci/devices")
if err != nil {
probe.HardwareError = err.Error()
}
for vendor := range gpu.ManagementCommands() {
probe.Commands[vendor] = true
}
probe.Commands["intel"], _ = xpu.New()
probe.Commands["ascend"], _ = npu.New()
if nvidiaPresent || probe.Hardware["nvidia"] > 0 {
probe.NVIDIAChecked = true
probe.Checks = checkNVIDIAContainerEnvironment(ctx)
}
if ctx.Err() == nil && global.CACHE != nil {
if data, err := json.Marshal(probe); err == nil {
global.CACHE.SetWithTTL(gpuEnvironmentCacheKey, string(data), time.Minute)
}
}
return probe
}
func detectGPUHardware(directory string) (map[string]int, error) {
devices := map[string]int{}
entries, err := os.ReadDir(directory)
if err != nil {
return devices, err
}
var readErrors []string
for _, entry := range entries {
path := filepath.Join(directory, entry.Name())
vendorData, err := os.ReadFile(filepath.Join(path, "vendor"))
if err != nil {
readErrors = append(readErrors, err.Error())
continue
}
vendor := map[string]string{"0x10de": "nvidia", "0x8086": "intel", "0x19e5": "ascend", "0x1002": "amd"}[strings.ToLower(strings.TrimSpace(string(vendorData)))]
if vendor == "" {
continue
}
classData, err := os.ReadFile(filepath.Join(path, "class"))
if err != nil {
readErrors = append(readErrors, err.Error())
continue
}
class, err := strconv.ParseUint(strings.TrimSpace(string(classData)), 0, 32)
if err != nil {
readErrors = append(readErrors, fmt.Sprintf("read PCI class for %s: %v", entry.Name(), err))
continue
}
baseClass := class >> 16
if (vendor == "ascend" && baseClass == 0x12) || (vendor != "ascend" && (baseClass == 0x03 || (vendor == "nvidia" && baseClass == 0x12))) {
devices[vendor]++
}
}
if len(readErrors) > 0 {
return devices, fmt.Errorf("PCI inventory incomplete: %s", strings.Join(readErrors, "; "))
}
return devices, nil
}
func assembleGPUEnvironment(probe gpuEnvironmentProbe, summaries []dto.GPUSummary, collectErr error, info accelerator.Info) dto.GPUEnvironment {
result := dto.GPUEnvironment{CheckedAt: probe.CheckedAt, HardwareStatus: "not_detected", Checks: []dto.GPUEnvironmentCheck{}}
if len(probe.Hardware) > 0 || len(summaries) > 0 {
result.HardwareStatus = "detected"
} else if probe.HardwareError != "" {
result.HardwareStatus = "unknown"
}
for _, vendor := range gpuVendors {
index := slices.IndexFunc(summaries, func(item dto.GPUSummary) bool { return item.Vendor == vendor })
if index < 0 && probe.Hardware[vendor] == 0 && !(probe.HardwareError != "" && probe.Commands[vendor] && collectErr != nil) {
continue
}
check := dto.GPUEnvironmentCheck{Vendor: vendor, Item: "driver", Status: "unknown"}
switch {
case index >= 0:
check.Status = "passed"
check.Version = summaries[index].DriverVersion
if vendor == "intel" {
var readErrors []string
for _, device := range info.XPUs {
if device.ReadError != "" {
readErrors = append(readErrors, device.ReadError)
}
}
if len(readErrors) > 0 {
check.Status = "unknown"
if len(readErrors) == len(info.XPUs) {
check.Status = "failed"
}
check.Reason = strings.Join(readErrors, "; ")
}
}
case !probe.Commands[vendor]:
check.Status = "missing"
check.Reason = "The device management utility was not found; check the driver and management tool installation."
case collectErr != nil:
check.Status = "failed"
check.Reason = collectErr.Error()
default:
check.Reason = "The management utility returned no readable devices."
if len(info.Warnings) > 0 {
check.Status = "failed"
check.Reason = strings.Join(info.Warnings, "; ")
}
}
result.Checks = append(result.Checks, check)
}
result.Checks = append(result.Checks, probe.Checks...)
if probe.HardwareError != "" {
result.Checks = append(result.Checks, dto.GPUEnvironmentCheck{Item: "driver", Status: "unknown", Reason: probe.HardwareError})
}
return result
}
func findGPUContainerCommand(name string) string {
for _, path := range []string{name, filepath.Join("/usr/bin", name), filepath.Join("/usr/sbin", name), filepath.Join("/usr/local/bin", name), filepath.Join("/usr/local/sbin", name), filepath.Join("/usr/local/nvidia/toolkit", name)} {
if cmd.Which(path) {
return path
}
if filepath.IsAbs(path) {
if info, err := os.Stat(path); err == nil && !info.IsDir() {
return path
}
}
}
return ""
}
func checkNVIDIAContainerEnvironment(ctx context.Context) []dto.GPUEnvironmentCheck {
toolkitCtx, cancelToolkit := context.WithTimeout(ctx, 5*time.Second)
toolkit := checkNVIDIAToolkit(toolkitCtx, findGPUContainerCommand, func(ctx context.Context, path string) (string, error) {
return cmd.NewCommandMgr(cmd.WithContext(ctx), cmd.WithTimeout(3*time.Second)).RunWithStdout(path, "--version")
})
cancelToolkit()
dockerCtx, cancelDocker := context.WithTimeout(ctx, 5*time.Second)
defer cancelDocker()
dockerCheck := dto.GPUEnvironmentCheck{Vendor: "nvidia", Item: "docker", Status: "failed"}
cli, err := docker.NewDockerClient()
if err == nil {
defer cli.Close()
var info system.Info
info, err = cli.Info(dockerCtx)
if err == nil {
dockerCheck = checkNVIDIADockerInfo(info, false)
if cli.DaemonHost() == "unix:///var/run/docker.sock" {
dockerCheck = checkNVIDIADockerConfig(info, readDockerDaemonConfig, findGPUContainerCommand)
} else {
dockerCheck.Status = "unknown"
dockerCheck.Reason = "The selected Docker daemon's NVIDIA runtime cannot be verified using local executables or the default daemon.json."
}
}
}
if err != nil {
dockerCheck.Reason = err.Error()
}
return []dto.GPUEnvironmentCheck{toolkit, dockerCheck}
}
func checkNVIDIAToolkit(ctx context.Context, findCommand func(string) string, runVersion func(context.Context, string) (string, error)) dto.GPUEnvironmentCheck {
toolkit := dto.GPUEnvironmentCheck{Vendor: "nvidia", Item: "toolkit", Status: "missing"}
cli := findCommand("nvidia-container-cli")
if cli == "" {
toolkit.Reason = "The required nvidia-container-cli component was not found."
return toolkit
}
runtime := findCommand("nvidia-container-runtime")
if runtime == "" {
runtime = findCommand("nvidia-container-runtime-hook")
}
if runtime == "" {
toolkit.Reason = "The NVIDIA container runtime and runtime hook were not found."
return toolkit
}
data, err := runVersion(ctx, cli)
if err != nil || strings.TrimSpace(data) == "" {
toolkit.Status = "failed"
toolkit.Reason = "nvidia-container-cli returned an empty version."
if err != nil {
toolkit.Reason = err.Error()
}
return toolkit
}
toolkit.Status = "passed"
toolkit.Version = strings.SplitN(strings.TrimSpace(data), "\n", 2)[0]
if data, err := runVersion(ctx, runtime); err != nil || strings.TrimSpace(data) == "" {
toolkit.Status = "failed"
toolkit.Reason = "The NVIDIA container runtime returned an empty version."
if err != nil {
toolkit.Reason = err.Error()
}
return toolkit
}
return toolkit
}
func checkNVIDIADockerInfo(info system.Info, hookInstalled bool) dto.GPUEnvironmentCheck {
result := dto.GPUEnvironmentCheck{Vendor: "nvidia", Item: "docker", Status: "unconfigured", Version: info.ServerVersion}
if _, ok := info.Runtimes["nvidia"]; ok {
result.Status = "passed"
return result
}
if hookInstalled {
result.Status = "unknown"
result.Reason = "NVIDIA runtime hook is installed, but Docker does not expose its GPU device-driver registration."
}
return result
}
func checkNVIDIADockerConfig(info system.Info, readConfig func() (map[string]interface{}, error), findCommand func(string) string) dto.GPUEnvironmentCheck {
result := checkNVIDIADockerInfo(info, false)
if result.Status == "passed" {
runtimePath := info.Runtimes["nvidia"].Path
if runtimePath == "" {
result.Status = "unknown"
result.Reason = "Docker registered an NVIDIA runtime without an executable path; its availability cannot be verified."
return result
}
resolved := runtimePath
if !filepath.IsAbs(runtimePath) {
resolved = findCommand(runtimePath)
}
if resolved == "" || !cmd.Which(resolved) {
result.Status = "failed"
result.Code = "dockerConfigInvalid"
result.Reason = fmt.Sprintf("The registered NVIDIA runtime %q cannot be found or executed.", runtimePath)
}
return result
}
if hook := findCommand("nvidia-container-runtime-hook"); hook != "" && cmd.Which(hook) {
result = checkNVIDIADockerInfo(info, true)
}
config, err := readConfig()
if err != nil {
var syntaxError *json.SyntaxError
var typeError *json.UnmarshalTypeError
switch {
case os.IsNotExist(err):
if result.Status == "unknown" {
return result
}
result.Code = "dockerConfigMissing"
result.Reason = fmt.Sprintf("Docker configuration %s is missing; configure runtimes.nvidia.path and restart Docker.", constant.DaemonJsonPath)
case errors.As(err, &syntaxError), errors.As(err, &typeError):
result.Status = "failed"
result.Code = "dockerConfigInvalid"
result.Reason = fmt.Sprintf("Cannot parse Docker configuration %s: %v", constant.DaemonJsonPath, err)
default:
result.Status = "failed"
result.Code = "dockerConfigUnreadable"
result.Reason = fmt.Sprintf("Cannot read Docker configuration %s: %v", constant.DaemonJsonPath, err)
}
return result
}
if config == nil {
result.Status = "failed"
result.Code = "dockerConfigInvalid"
result.Reason = "Docker configuration must be a JSON object."
return result
}
runtimes, ok := config["runtimes"].(map[string]interface{})
if config["runtimes"] == nil || (ok && runtimes["nvidia"] == nil) {
if result.Status == "unknown" {
return result
}
result.Code = "dockerConfigMissing"
result.Reason = fmt.Sprintf("Docker configuration %s has no runtimes.nvidia entry.", constant.DaemonJsonPath)
return result
}
nvidia, ok := runtimes["nvidia"].(map[string]interface{})
runtimePath, pathOK := nvidia["path"].(string)
if !ok || !pathOK || strings.TrimSpace(runtimePath) == "" || (!filepath.IsAbs(runtimePath) && filepath.Base(runtimePath) != runtimePath) {
result.Status = "failed"
result.Code = "dockerConfigInvalid"
result.Reason = "runtimes.nvidia.path must contain a runtime executable name or absolute path."
return result
}
resolved := findCommand(runtimePath)
if resolved == "" || !cmd.Which(resolved) {
result.Status = "failed"
result.Code = "dockerConfigInvalid"
result.Reason = fmt.Sprintf("The configured NVIDIA runtime %q cannot be found or executed.", runtimePath)
return result
}
result.Status = "unconfigured"
result.Code = "dockerConfigPending"
result.Reason = "The default daemon.json contains runtimes.nvidia.path, but the running Docker daemon has not registered the NVIDIA runtime; confirm the daemon uses this configuration file, then restart Docker and check again."
return result
}
func summarizeGPUs(snapshot *accelerator.Snapshot) []dto.GPUSummary {
result := []dto.GPUSummary{}
for _, vendor := range gpuVendors {
summary := dto.GPUSummary{Vendor: vendor}
cards, chips := map[string]bool{}, map[string]bool{}
var versions []string
for _, device := range snapshot.Devices {
deviceVendor := device.Vendor
if device.XPU != nil {
deviceVendor = "intel"
}
if deviceVendor != vendor || device.ParentID != "" || chips[device.ID] {
continue
}
chips[device.ID] = true
cardID := device.ID
if device.NPU != nil {
cardID = strconv.Itoa(device.NPUIndex)
}
cards[cardID] = true
if device.DriverVersion != "" && !slices.Contains(versions, device.DriverVersion) {
versions = append(versions, device.DriverVersion)
}
if !device.Metrics.MemoryTotal.Available() {
summary.MemoryPartial = true
continue
}
bytes := device.Metrics.MemoryTotal.ValueOrZero() * 1024 * 1024
if device.GPU != nil && device.GPU.SharedMemory {
if summary.SharedMemoryTotal == nil || bytes > *summary.SharedMemoryTotal {
summary.SharedMemoryTotal = &bytes
}
} else {
if summary.MemoryTotal == nil {
summary.MemoryTotal = new(float64)
}
*summary.MemoryTotal += bytes
}
}
if len(cards) > 0 {
count := len(cards)
summary.CardCount = &count
summary.DriverVersion = strings.Join(versions, " / ")
if vendor == "nvidia" {
summary.CudaVersion = snapshot.Info.CudaVersion
}
result = append(result, summary)
}
}
return result
}
@@ -39,6 +39,7 @@ func normalizeGPU(item *gpu.Device) Device {
Vendor: item.Type, Vendor: item.Type,
Index: int(item.Index), Index: int(item.Index),
Name: item.ProductName, Name: item.ProductName,
DriverVersion: item.DriverVersion,
Label: fmt.Sprintf("%d - %s", item.Index, item.ProductName), Label: fmt.Sprintf("%d - %s", item.Index, item.ProductName),
BusID: item.BusID, BusID: item.BusID,
Metrics: metrics, Metrics: metrics,
@@ -121,6 +122,7 @@ func normalizeXPU(item *xpu.Device) Device {
Vendor: item.Basic.VendorName, Vendor: item.Basic.VendorName,
Index: item.Basic.DeviceID, Index: item.Basic.DeviceID,
Name: item.Basic.DeviceName, Name: item.Basic.DeviceName,
DriverVersion: item.Basic.DriverVersion,
Label: fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName), Label: fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName),
BusID: item.Basic.PciBdfAddress, BusID: item.Basic.PciBdfAddress,
Metrics: metrics, Metrics: metrics,
+3 -1
View File
@@ -56,7 +56,9 @@ func (p npuProvider) Collect(ctx context.Context) (*ProviderSnapshot, error) {
NPUs: info.Devices, NPUs: info.Devices,
} }
for index := range result.NPUs { for index := range result.NPUs {
result.Devices = append(result.Devices, normalizeNPU(&result.NPUs[index])) device := normalizeNPU(&result.NPUs[index])
device.DriverVersion = info.DriverVersion
result.Devices = append(result.Devices, device)
} }
return result, nil return result, nil
} }
@@ -102,6 +102,7 @@ type Device struct {
NPUIndex int NPUIndex int
ChipIndex int ChipIndex int
Name string Name string
DriverVersion string
Label string Label string
BusID string BusID string
Metrics Metrics Metrics Metrics
+15 -3
View File
@@ -23,12 +23,24 @@ type providerResult struct {
err error err error
} }
func New() (bool, Client) { func ManagementCommands() map[string]string {
client := Client{} commands := make(map[string]string)
if cmd.Which(nvidiaSMICommand) { if cmd.Which(nvidiaSMICommand) {
client.providers = append(client.providers, nvidiaSMI{}) commands["nvidia"] = nvidiaSMICommand
} }
if command, ok := findAMDSMI(); ok { if command, ok := findAMDSMI(); ok {
commands["amd"] = command
}
return commands
}
func New() (bool, Client) {
client := Client{}
commands := ManagementCommands()
if commands["nvidia"] != "" {
client.providers = append(client.providers, nvidiaSMI{})
}
if command := commands["amd"]; command != "" {
client.providers = append(client.providers, amdSMI{command: command}) client.providers = append(client.providers, amdSMI{command: command})
} }
return len(client.providers) > 0, client return len(client.providers) > 0, client
+1
View File
@@ -63,6 +63,7 @@ type Info struct {
} }
type Device struct { type Device struct {
ReadError string `json:"-"`
Tiles []TileStats `json:"tiles"` Tiles []TileStats `json:"tiles"`
ProcessStatus string `json:"processStatus"` ProcessStatus string `json:"processStatus"`
+4 -2
View File
@@ -122,8 +122,9 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
wgCmd.Wait() wgCmd.Wait()
if xpuErr != nil { if xpuErr != nil {
xpu.ReadError = fmt.Sprintf("xpu-smi device %d: %v", device.DeviceID, xpuErr)
mu.Lock() mu.Lock()
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi device %d: %v", device.DeviceID, xpuErr)) res.Warnings = append(res.Warnings, xpu.ReadError)
res.Devices = append(res.Devices, xpu) res.Devices = append(res.Devices, xpu)
mu.Unlock() mu.Unlock()
return return
@@ -131,8 +132,9 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
var info discoveryDevice var info discoveryDevice
if err := json.Unmarshal([]byte(xpuData), &info); err != nil { if err := json.Unmarshal([]byte(xpuData), &info); err != nil {
xpu.ReadError = fmt.Sprintf("xpu-smi device %d output: %v", device.DeviceID, err)
mu.Lock() mu.Lock()
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi device %d output: %v", device.DeviceID, err)) res.Warnings = append(res.Warnings, xpu.ReadError)
res.Devices = append(res.Devices, xpu) res.Devices = append(res.Devices, xpu)
mu.Unlock() mu.Unlock()
return return
+24
View File
@@ -37,6 +37,28 @@ export namespace AI {
info?: string; info?: string;
} }
export interface GPUEnvironmentCheck {
vendor: string;
item: 'driver' | 'toolkit' | 'docker';
status: 'passed' | 'missing' | 'unconfigured' | 'failed' | 'unknown';
code?: string;
version?: string;
reason?: string;
}
export interface GPUEnvironment {
checkedAt: string;
hardwareStatus: 'detected' | 'not_detected' | 'unknown';
checks: GPUEnvironmentCheck[];
}
export interface GPUSummary {
vendor: string;
driverVersion: string;
cudaVersion: string;
cardCount: number | null;
memoryTotal: number | null;
sharedMemoryTotal: number | null;
memoryPartial: boolean;
}
export interface Info { export interface Info {
collectedAt?: string; collectedAt?: string;
warnings?: string[]; warnings?: string[];
@@ -47,6 +69,8 @@ export namespace AI {
npu: NPU[]; npu: NPU[];
xpuDriverVersion: string; xpuDriverVersion: string;
xpu: XpuInfo['xpu']; xpu: XpuInfo['xpu'];
environment?: GPUEnvironment;
summaries?: GPUSummary[];
} }
export interface GPU { export interface GPU {
sharedMemory?: boolean; sharedMemory?: boolean;
+5 -2
View File
@@ -47,8 +47,11 @@ export const loadModelGPUDevices = () => {
return http.get<{ devices: AI.ModelGPUDevice[]; warnings: string[] }>('/ai/model/gpu/devices'); return http.get<{ devices: AI.ModelGPUDevice[]; warnings: string[] }>('/ai/model/gpu/devices');
}; };
export const loadGPUInfo = () => { export const loadGPUInfo = (refresh = false, currentNode?: string) => {
return http.get<AI.Info>(`/ai/gpu/load`); return http.get<AI.Info>(`/ai/gpu/load`, refresh ? { refresh: true } : {}, {
timeout: TimeoutEnum.T_40S,
headers: currentNode ? { CurrentNode: currentNode } : undefined,
});
}; };
export const getGPUOptions = (currentNode?: string) => { export const getGPUOptions = (currentNode?: string) => {
return http.get<AI.MonitorGPUOptions>( return http.get<AI.MonitorGPUOptions>(
@@ -5,8 +5,10 @@
<el-radio-group v-model="activeName" @change="handleChange"> <el-radio-group v-model="activeName" @change="handleChange">
<el-radio-button <el-radio-button
class="router_card_button" class="router_card_button"
:class="{ 'opacity-50': button.disabled }"
:label="button.label" :label="button.label"
:value="button.label" :value="button.label"
:disabled="button.disabled"
v-for="(button, index) in buttonArray" v-for="(button, index) in buttonArray"
size="large" size="large"
:key="index" :key="index"
@@ -94,7 +96,10 @@ const expiresAlertKey = computed(() => (isEnterprise.value ? 'xpack.expiresEnter
const handleChange = async (label: string) => { const handleChange = async (label: string) => {
const btn = buttonArray.value.find((btn) => btn.label === label); const btn = buttonArray.value.find((btn) => btn.label === label);
if (!btn) return; if (!btn || btn.disabled) {
syncActiveName();
return;
}
try { try {
if (btn.path) await routerToPathWithQuery(btn.path, { uncached: 'true' }); if (btn.path) await routerToPathWithQuery(btn.path, { uncached: 'true' });
else if (btn.name) await routerToNameWithQuery(btn.name, { uncached: 'true' }); else if (btn.name) await routerToNameWithQuery(btn.name, { uncached: 'true' });
+67 -2
View File
@@ -1743,8 +1743,8 @@ const message = {
running: 'Running requests', running: 'Running requests',
waiting: 'Waiting requests', waiting: 'Waiting requests',
cacheUsage: 'KV cache usage', cacheUsage: 'KV cache usage',
promptThroughput: 'Input token throughput', promptThroughput: 'Input throughput',
generationThroughput: 'Output token throughput', generationThroughput: 'Output throughput',
requestThroughput: 'Completed requests', requestThroughput: 'Completed requests',
timeToFirstToken: 'Mean time to first token', timeToFirstToken: 'Mean time to first token',
timePerOutputToken: 'Mean inter-token latency', timePerOutputToken: 'Mean inter-token latency',
@@ -1764,6 +1764,69 @@ const message = {
timePerOutputTokenChart: 'Inter-token latency', timePerOutputTokenChart: 'Inter-token latency',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'Host GPU',
containerGPU: 'Container GPU support',
monitorAvailable: 'Monitoring available',
monitorPartial: 'Partial monitoring',
notReady: 'Not ready',
ready: 'Ready',
checkedAt: 'Checked at',
checkDetailsHelp: 'Review the check details, verify the environment, and check again.',
driverActionTitle: 'Check the host GPU driver first',
driverActionHelp:
'Install or repair the affected device driver before continuing with the container environment checks.',
driverGuide: 'View driver installation guide',
toolkitActionTitle: 'Container GPU support is not ready',
toolkitActionHelp: 'Install or repair NVIDIA Container Toolkit, then check the Docker configuration.',
toolkitGuide: 'View Toolkit installation guide',
dockerActionTitle: 'Docker NVIDIA GPU support is not ready',
dockerActionHelp: 'Check the NVIDIA runtime configuration in Docker, then check again.',
monitorUnaffected: 'Host GPU monitoring is unaffected by this issue.',
title: 'Environment checks',
passed: 'Environment checks passed',
issues: 'Detected {count} environment issues. Follow the guidance to check them.',
recheck: 'Check again',
installGuide: 'View installation guide',
goContainerSettings: 'Open Container Settings',
details: 'Check details',
vendor: 'Vendor',
gpuCount: 'GPU count',
cardCount: 'Card count',
notDetected: 'No supported GPU/NPU/XPU devices were detected on the current node.',
unknown: 'Could not confirm device status. Review the check details and try again.',
loadFailed: 'Could not read device information. Please try again.',
notApplicable: 'Not applicable',
cudaHelper:
'This is the CUDA version supported by the driver, not the CUDA Toolkit version installed on the host.',
partialMemory: 'Some memory data is unavailable',
sharedMemory: 'Shared system memory: {memory}',
driver: 'Driver and device management tools',
nvidiaDriver: 'NVIDIA Driver',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Docker NVIDIA GPU configuration',
statusPassed: 'Normal',
statusMissing: 'Not detected',
statusUnconfigured: 'Configuration required',
statusFailed: 'Check failed',
statusUnknown: 'Unknown',
driverMissing:
'The {vendor} driver was not detected. Install or check the driver (detection tool: {tool}).',
driverFailed: 'Could not read device information using {tool}. Check the driver status.',
driverUnknown: 'Could not confirm the device driver status. Check {tool}.',
toolkitMissing:
'NVIDIA Container Toolkit was not detected. Install and configure it before using NVIDIA GPUs in containers.',
toolkitFailed: 'Could not check NVIDIA Container Toolkit. Review the failure details.',
dockerConfigMissing:
'No runtimes.nvidia entry was detected in the default Docker configuration file. Complete the configuration using the documentation.',
dockerConfigInvalid:
'The NVIDIA runtime configuration in Docker is invalid. Check runtimes.nvidia.path in daemon.json.',
dockerConfigPending:
'The default configuration file contains NVIDIA runtime configuration, but the current Docker daemon has not loaded it. Confirm the configuration file, restart Docker, and check again.',
dockerConfigUnreadable: 'Could not read the Docker configuration file. Review the check details.',
dockerUnconfigured: 'Docker NVIDIA GPU configuration is incomplete.',
dockerFailed: 'Could not check the Docker NVIDIA GPU configuration. Review the failure details.',
},
systemMemory: 'Shared system memory', systemMemory: 'Shared system memory',
systemMemoryUsed: 'System memory used', systemMemoryUsed: 'System memory used',
systemMemoryTotal: 'Total system memory', systemMemoryTotal: 'Total system memory',
@@ -1823,6 +1886,8 @@ const message = {
pcieWidth: 'PCIe width (current / maximum)', pcieWidth: 'PCIe width (current / maximum)',
gpu: 'GPU Management', gpu: 'GPU Management',
monitoringTitle: 'GPU Monitoring', monitoringTitle: 'GPU Monitoring',
currentHelper:
'View real-time status and resource usage of GPU/NPU/XPU devices on the current node. The overview summarizes all devices; detailed metrics are shown per device.',
gpuHelper: 'No supported GPU/XPU/NPU management command was detected. Please check and try again!', gpuHelper: 'No supported GPU/XPU/NPU management command was detected. Please check and try again!',
process: 'Process Information', process: 'Process Information',
processCount: 'Process Count', processCount: 'Process Count',
+72 -2
View File
@@ -1754,8 +1754,8 @@ const message = {
running: 'Solicitudes en ejecución', running: 'Solicitudes en ejecución',
waiting: 'Solicitudes en espera', waiting: 'Solicitudes en espera',
cacheUsage: 'Uso de caché KV', cacheUsage: 'Uso de caché KV',
promptThroughput: 'Rendimiento de tokens de entrada', promptThroughput: 'Rendimiento de entrada',
generationThroughput: 'Rendimiento de tokens de salida', generationThroughput: 'Rendimiento de salida',
requestThroughput: 'Solicitudes completadas', requestThroughput: 'Solicitudes completadas',
timeToFirstToken: 'Latencia media del primer token', timeToFirstToken: 'Latencia media del primer token',
timePerOutputToken: 'Latencia media entre tokens', timePerOutputToken: 'Latencia media entre tokens',
@@ -1776,6 +1776,74 @@ const message = {
timePerOutputTokenChart: 'Latencia entre tokens', timePerOutputTokenChart: 'Latencia entre tokens',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'GPU del host',
containerGPU: 'Compatibilidad GPU en contenedores',
monitorAvailable: 'Supervisión disponible',
monitorPartial: 'Supervisión parcial',
notReady: 'No está listo',
ready: 'Listo',
checkedAt: 'Última comprobación',
checkDetailsHelp: 'Revisa los detalles, verifica el entorno y vuelve a comprobarlo.',
driverActionTitle: 'Comprueba primero el controlador GPU del host',
driverActionHelp:
'Instala o repara el controlador del dispositivo afectado antes de continuar con las comprobaciones del entorno de contenedores.',
driverGuide: 'Ver guía de instalación del controlador',
toolkitActionTitle: 'La compatibilidad GPU en contenedores no está lista',
toolkitActionHelp:
'Instala o repara NVIDIA Container Toolkit y después comprueba la configuración de Docker.',
toolkitGuide: 'Ver guía de instalación de Toolkit',
dockerActionTitle: 'La compatibilidad NVIDIA GPU de Docker no está lista',
dockerActionHelp:
'Comprueba la configuración del runtime NVIDIA en Docker y vuelve a realizar la comprobación.',
monitorUnaffected: 'Este problema no afecta a la supervisión de la GPU del host.',
title: 'Comprobaciones del entorno',
passed: 'Comprobaciones del entorno superadas',
issues: 'Problemas del entorno detectados: {count}. Siga las indicaciones para revisarlos.',
recheck: 'Comprobar de nuevo',
installGuide: 'Ver guía de instalación',
goContainerSettings: 'Abrir configuración de contenedores',
details: 'Detalles de la comprobación',
vendor: 'Fabricante',
gpuCount: 'Cantidad de GPU',
cardCount: 'Número de tarjetas',
notDetected: 'No se detectaron dispositivos GPU/NPU/XPU compatibles en el nodo actual.',
unknown: 'No se pudo confirmar el estado del dispositivo. Revise los detalles y vuelva a comprobarlo.',
loadFailed: 'No se pudo leer la información del dispositivo. Inténtelo de nuevo.',
notApplicable: 'No aplicable',
cudaHelper:
'Esta es la versión de CUDA compatible con el controlador, no la versión de CUDA Toolkit instalada en el host.',
partialMemory: 'Algunos datos de memoria no están disponibles',
sharedMemory: 'Memoria del sistema compartida: {memory}',
driver: 'Controlador y herramientas de gestión de dispositivos',
nvidiaDriver: 'Controlador NVIDIA',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Configuración de GPU NVIDIA en Docker',
statusPassed: 'Normal',
statusMissing: 'No detectado',
statusUnconfigured: 'Configuración pendiente',
statusFailed: 'Error de comprobación',
statusUnknown: 'Desconocido',
driverMissing:
'No se detectó el controlador de {vendor}. Instale o compruebe el controlador (herramienta de detección: {tool}).',
driverFailed:
'No se pudo leer la información del dispositivo mediante {tool}. Compruebe el estado del controlador.',
driverUnknown: 'No se pudo confirmar el estado del controlador del dispositivo. Compruebe {tool}.',
toolkitMissing:
'No se detectó NVIDIA Container Toolkit. Instálelo y configúrelo antes de usar GPU NVIDIA en contenedores.',
toolkitFailed: 'No se pudo comprobar NVIDIA Container Toolkit. Revise los detalles del error.',
dockerConfigMissing:
'No se detectó la entrada runtimes.nvidia en el archivo de configuración predeterminado de Docker. Complete la configuración siguiendo la documentación.',
dockerConfigInvalid:
'La configuración del entorno de ejecución de NVIDIA en Docker no es válida. Compruebe runtimes.nvidia.path en daemon.json.',
dockerConfigPending:
'El archivo de configuración predeterminado contiene la configuración del entorno de ejecución de NVIDIA, pero el servicio Docker actual aún no la ha cargado. Confirme el archivo de configuración, reinicie Docker y vuelva a comprobarlo.',
dockerConfigUnreadable:
'No se pudo leer el archivo de configuración de Docker. Revise los detalles de la comprobación.',
dockerUnconfigured: 'La configuración de GPU NVIDIA en Docker está incompleta.',
dockerFailed:
'No se pudo comprobar la configuración de GPU NVIDIA en Docker. Revise los detalles del error.',
},
systemMemory: 'Memoria compartida del sistema', systemMemory: 'Memoria compartida del sistema',
systemMemoryUsed: 'Memoria del sistema utilizada', systemMemoryUsed: 'Memoria del sistema utilizada',
systemMemoryTotal: 'Memoria total del sistema', systemMemoryTotal: 'Memoria total del sistema',
@@ -1836,6 +1904,8 @@ const message = {
pcieWidth: 'Ancho PCIe (actual / máximo)', pcieWidth: 'Ancho PCIe (actual / máximo)',
gpu: 'Gestión de GPU', gpu: 'Gestión de GPU',
monitoringTitle: 'Monitoreo de GPU', monitoringTitle: 'Monitoreo de GPU',
currentHelper:
'Consulte el estado en tiempo real y el uso de recursos de los dispositivos GPU/NPU/XPU del nodo actual. El resumen superior incluye todos los dispositivos; las métricas detalladas se muestran por dispositivo.',
gpuHelper: gpuHelper:
'No se detectó ningún comando de administración de GPU/XPU/NPU compatible. ¡Compruebe e inténtelo de nuevo!', 'No se detectó ningún comando de administración de GPU/XPU/NPU compatible. ¡Compruebe e inténtelo de nuevo!',
process: 'Información del Proceso', process: 'Información del Proceso',
+65 -2
View File
@@ -1719,8 +1719,8 @@ const message = {
running: 'درخواست‌های در حال اجرا', running: 'درخواست‌های در حال اجرا',
waiting: 'درخواست‌های در انتظار', waiting: 'درخواست‌های در انتظار',
cacheUsage: 'مصرف حافظه نهان KV', cacheUsage: 'مصرف حافظه نهان KV',
promptThroughput: 'گذردهی توکن ورودی', promptThroughput: 'گذردهی ورودی',
generationThroughput: 'گذردهی توکن خروجی', generationThroughput: 'گذردهی خروجی',
requestThroughput: 'نرخ درخواست‌های تکمیل‌شده', requestThroughput: 'نرخ درخواست‌های تکمیل‌شده',
timeToFirstToken: 'میانگین تأخیر توکن اول', timeToFirstToken: 'میانگین تأخیر توکن اول',
timePerOutputToken: 'میانگین تأخیر بین توکن‌ها', timePerOutputToken: 'میانگین تأخیر بین توکن‌ها',
@@ -1740,6 +1740,67 @@ const message = {
timePerOutputTokenChart: 'تأخیر بین توکن‌ها', timePerOutputTokenChart: 'تأخیر بین توکن‌ها',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'GPU میزبان',
containerGPU: 'پشتیبانی GPU در کانتینر',
monitorAvailable: 'پایش در دسترس است',
monitorPartial: 'پایش جزئی',
notReady: 'آماده نیست',
ready: 'آماده',
checkedAt: 'زمان بررسی',
checkDetailsHelp: 'جزئیات بررسی را ببینید، وضعیت محیط را تأیید کنید و دوباره بررسی کنید.',
driverActionTitle: 'ابتدا درایور GPU میزبان را بررسی کنید',
driverActionHelp: 'پیش از ادامه بررسی محیط کانتینر، درایور دستگاه دارای مشکل را نصب یا تعمیر کنید.',
driverGuide: 'مشاهده راهنمای نصب درایور',
toolkitActionTitle: 'پشتیبانی GPU در کانتینر آماده نیست',
toolkitActionHelp:
'ابتدا NVIDIA Container Toolkit را نصب یا تعمیر کنید، سپس پیکربندی Docker را بررسی کنید.',
toolkitGuide: 'مشاهده راهنمای نصب Toolkit',
dockerActionTitle: 'پشتیبانی NVIDIA GPU در Docker آماده نیست',
dockerActionHelp: 'پیکربندی محیط اجرای NVIDIA در Docker را بررسی کنید و سپس دوباره بررسی کنید.',
monitorUnaffected: 'این مشکل بر پایش GPU میزبان تأثیری ندارد.',
title: 'بررسی محیط',
passed: 'بررسی‌های محیط با موفقیت انجام شد',
issues: '{count} مشکل در محیط شناسایی شد. برای بررسی آن‌ها راهنما را دنبال کنید.',
recheck: 'بررسی دوباره',
installGuide: 'مشاهده راهنمای نصب',
goContainerSettings: 'باز کردن تنظیمات کانتینر',
details: 'جزئیات بررسی',
vendor: 'سازنده',
gpuCount: 'تعداد GPU',
cardCount: 'تعداد کارت‌ها',
notDetected: 'هیچ دستگاه GPU/NPU/XPU پشتیبانی‌شده‌ای در گره فعلی شناسایی نشد.',
unknown: 'وضعیت دستگاه قابل تأیید نیست. جزئیات بررسی را ببینید و دوباره تلاش کنید.',
loadFailed: 'خواندن اطلاعات دستگاه ممکن نیست. دوباره تلاش کنید.',
notApplicable: 'قابل اعمال نیست',
cudaHelper: 'این نسخه CUDA پشتیبانی‌شده توسط درایور است، نه نسخه CUDA Toolkit نصب‌شده روی میزبان.',
partialMemory: 'بخشی از داده‌های حافظه در دسترس نیست',
sharedMemory: 'حافظه مشترک سیستم: {memory}',
driver: 'درایور و ابزارهای مدیریت دستگاه',
nvidiaDriver: 'درایور NVIDIA',
toolkit: 'NVIDIA Container Toolkit',
docker: 'پیکربندی GPU انویدیا در Docker',
statusPassed: 'عادی',
statusMissing: 'شناسایی نشد',
statusUnconfigured: 'نیازمند پیکربندی',
statusFailed: 'بررسی ناموفق',
statusUnknown: 'نامشخص',
driverMissing: 'درایور {vendor} شناسایی نشد. درایور را نصب یا بررسی کنید (ابزار شناسایی: {tool}).',
driverFailed: 'خواندن اطلاعات دستگاه با {tool} ممکن نیست. وضعیت درایور را بررسی کنید.',
driverUnknown: 'وضعیت درایور دستگاه قابل تأیید نیست. {tool} را بررسی کنید.',
toolkitMissing:
'NVIDIA Container Toolkit شناسایی نشد. پیش از استفاده از GPU انویدیا در کانتینرها، نصب و پیکربندی آن را کامل کنید.',
toolkitFailed: 'بررسی NVIDIA Container Toolkit ممکن نیست. جزئیات خطا را ببینید.',
dockerConfigMissing:
'گزینه runtimes.nvidia در فایل پیکربندی پیش‌فرض Docker شناسایی نشد. پیکربندی را طبق مستندات کامل کنید.',
dockerConfigInvalid:
'پیکربندی محیط اجرای NVIDIA در Docker نامعتبر است. مقدار runtimes.nvidia.path را در daemon.json بررسی کنید.',
dockerConfigPending:
'فایل پیکربندی پیش‌فرض شامل پیکربندی محیط اجرای NVIDIA است، اما سرویس فعلی Docker هنوز آن را بارگذاری نکرده است. فایل پیکربندی را تأیید کنید، Docker را دوباره راه‌اندازی کنید و سپس دوباره بررسی کنید.',
dockerConfigUnreadable: 'خواندن فایل پیکربندی Docker ممکن نیست. جزئیات بررسی را ببینید.',
dockerUnconfigured: 'پیکربندی GPU انویدیا در Docker کامل نشده است.',
dockerFailed: 'بررسی پیکربندی GPU انویدیا در Docker ممکن نیست. جزئیات خطا را ببینید.',
},
systemMemory: 'حافظهٔ مشترک سیستم', systemMemory: 'حافظهٔ مشترک سیستم',
systemMemoryUsed: 'حافظهٔ مصرف‌شدهٔ سیستم', systemMemoryUsed: 'حافظهٔ مصرف‌شدهٔ سیستم',
systemMemoryTotal: 'کل حافظهٔ سیستم', systemMemoryTotal: 'کل حافظهٔ سیستم',
@@ -1798,6 +1859,8 @@ const message = {
pcieWidth: 'عرض PCIe (فعلی / حداکثر)', pcieWidth: 'عرض PCIe (فعلی / حداکثر)',
gpu: 'مدیریت GPU', gpu: 'مدیریت GPU',
monitoringTitle: 'نظارت بر GPU', monitoringTitle: 'نظارت بر GPU',
currentHelper:
'وضعیت لحظه‌ای و میزان استفاده از منابع دستگاه‌های GPU/NPU/XPU در گره فعلی را مشاهده کنید. نمای کلی در بالا، خلاصه همه دستگاه‌ها را نشان می‌دهد؛ شاخص‌های دقیق برای هر دستگاه جداگانه نمایش داده می‌شوند.',
gpuHelper: 'هیچ دستور مدیریت GPU/XPU/NPU پشتیبانی‌شده‌ای شناسایی نشد. لطفاً بررسی و دوباره تلاش کنید!', gpuHelper: 'هیچ دستور مدیریت GPU/XPU/NPU پشتیبانی‌شده‌ای شناسایی نشد. لطفاً بررسی و دوباره تلاش کنید!',
process: 'اطلاعات فرآیند', process: 'اطلاعات فرآیند',
processCount: 'تعداد فرآیندها', processCount: 'تعداد فرآیندها',
+68 -2
View File
@@ -1726,8 +1726,8 @@ const message = {
running: '実行中のリクエスト', running: '実行中のリクエスト',
waiting: '待機中のリクエスト', waiting: '待機中のリクエスト',
cacheUsage: 'KVキャッシュ使用率', cacheUsage: 'KVキャッシュ使用率',
promptThroughput: '入力トークンスループット', promptThroughput: '入力スループット',
generationThroughput: '出力トークンスループット', generationThroughput: '出力スループット',
requestThroughput: '完了リクエスト数/秒', requestThroughput: '完了リクエスト数/秒',
timeToFirstToken: '平均初回トークン遅延', timeToFirstToken: '平均初回トークン遅延',
timePerOutputToken: '平均トークン間遅延', timePerOutputToken: '平均トークン間遅延',
@@ -1747,6 +1747,70 @@ const message = {
timePerOutputTokenChart: 'トークン間遅延', timePerOutputTokenChart: 'トークン間遅延',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'ホスト GPU',
containerGPU: 'コンテナーの GPU 対応',
monitorAvailable: '監視可能',
monitorPartial: '一部監視可能',
notReady: '未準備',
ready: '準備完了',
checkedAt: '確認日時',
checkDetailsHelp: '確認の詳細を参照し、環境の状態を確認してから再チェックしてください。',
driverActionTitle: 'まずホスト GPU ドライバーを確認してください',
driverActionHelp:
'問題のあるデバイスのドライバーをインストールまたは修復してから、コンテナー環境の確認を続けてください。',
driverGuide: 'ドライバーのインストール手順',
toolkitActionTitle: 'コンテナーの GPU 環境が未準備です',
toolkitActionHelp:
'NVIDIA Container Toolkit をインストールまたは修復してから、Docker の設定を確認してください。',
toolkitGuide: 'Toolkit のインストール手順',
dockerActionTitle: 'Docker の NVIDIA GPU 環境が未準備です',
dockerActionHelp: 'Docker の NVIDIA ランタイム設定を確認してから、再チェックしてください。',
monitorUnaffected: 'この問題はホスト GPU の監視に影響しません。',
title: '環境チェック',
passed: '環境チェックに合格しました',
issues: '環境の問題が {count} 件見つかりました。案内に従って確認してください。',
recheck: '再チェック',
installGuide: 'インストールガイドを見る',
goContainerSettings: 'コンテナー設定を開く',
details: 'チェックの詳細',
vendor: 'メーカー',
gpuCount: 'GPU 数',
cardCount: 'カード数',
notDetected: '現在のノードで対応する GPU/NPU/XPU デバイスが検出されませんでした。',
unknown: 'デバイスの状態を確認できません。チェックの詳細を確認してから再実行してください。',
loadFailed: 'デバイス情報を読み取れません。再試行してください。',
notApplicable: '対象外',
cudaHelper:
'これはドライバーが対応する CUDA バージョンであり、ホストにインストールされた CUDA Toolkit のバージョンではありません。',
partialMemory: '一部のメモリデータを取得できません',
sharedMemory: '共有システムメモリ:{memory}',
driver: 'ドライバーとデバイス管理ツール',
nvidiaDriver: 'NVIDIA ドライバー',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Docker の NVIDIA GPU 設定',
statusPassed: '正常',
statusMissing: '未検出',
statusUnconfigured: '設定が必要',
statusFailed: 'チェック失敗',
statusUnknown: '不明',
driverMissing:
'{vendor} ドライバーが検出されませんでした。ドライバーをインストールするか、状態を確認してください(検出ツール:{tool})。',
driverFailed: '{tool} でデバイス情報を読み取れません。ドライバーの状態を確認してください。',
driverUnknown: 'デバイスのドライバーの状態を確認できません。{tool} を確認してください。',
toolkitMissing:
'NVIDIA Container Toolkit が検出されませんでした。コンテナーで NVIDIA GPU を使用する前にインストールと設定を完了してください。',
toolkitFailed: 'NVIDIA Container Toolkit をチェックできません。失敗の詳細を確認してください。',
dockerConfigMissing:
'Docker の既定の設定ファイルに runtimes.nvidia が検出されませんでした。ドキュメントに従って設定を完了してください。',
dockerConfigInvalid:
'Docker の NVIDIA ランタイム設定が正しくありません。daemon.json の runtimes.nvidia.path を確認してください。',
dockerConfigPending:
'既定の設定ファイルに NVIDIA ランタイム設定がありますが、現在の Docker にはまだ読み込まれていません。設定ファイルを確認し、Docker を再起動してから再チェックしてください。',
dockerConfigUnreadable: 'Docker の設定ファイルを読み取れません。チェックの詳細を確認してください。',
dockerUnconfigured: 'Docker の NVIDIA GPU 設定が完了していません。',
dockerFailed: 'Docker の NVIDIA GPU 設定をチェックできません。失敗の詳細を確認してください。',
},
systemMemory: '共有システムメモリ', systemMemory: '共有システムメモリ',
systemMemoryUsed: 'システムメモリ使用量', systemMemoryUsed: 'システムメモリ使用量',
systemMemoryTotal: 'システムメモリ合計', systemMemoryTotal: 'システムメモリ合計',
@@ -1804,6 +1868,8 @@ const message = {
pcieWidth: 'PCIe 幅(現在 / 最大)', pcieWidth: 'PCIe 幅(現在 / 最大)',
gpu: 'GPU 管理', gpu: 'GPU 管理',
monitoringTitle: 'GPU 監視', monitoringTitle: 'GPU 監視',
currentHelper:
'現在のノードにある GPU/NPU/XPU デバイスのリアルタイムの稼働状態とリソース使用状況を確認できます。上部には全デバイスの概要を表示し、詳細な指標はデバイスごとに表示します。',
gpuHelper: '対応する GPU/XPU/NPU 管理コマンドが検出されませんでした。確認して再試行してください!', gpuHelper: '対応する GPU/XPU/NPU 管理コマンドが検出されませんでした。確認して再試行してください!',
process: 'プロセス情報', process: 'プロセス情報',
type: 'タイプ', type: 'タイプ',
+65 -2
View File
@@ -1707,8 +1707,8 @@ const message = {
running: '실행 중인 요청', running: '실행 중인 요청',
waiting: '대기 중인 요청', waiting: '대기 중인 요청',
cacheUsage: 'KV 캐시 사용률', cacheUsage: 'KV 캐시 사용률',
promptThroughput: '입력 토큰 처리량', promptThroughput: '입력 처리량',
generationThroughput: '출력 토큰 처리량', generationThroughput: '출력 처리량',
requestThroughput: '완료 요청 속도', requestThroughput: '완료 요청 속도',
timeToFirstToken: '평균 첫 토큰 지연', timeToFirstToken: '평균 첫 토큰 지연',
timePerOutputToken: '평균 토큰 간 지연', timePerOutputToken: '평균 토큰 간 지연',
@@ -1728,6 +1728,67 @@ const message = {
timePerOutputTokenChart: '토큰 간 지연', timePerOutputTokenChart: '토큰 간 지연',
}, },
gpu: { gpu: {
environment: {
hostGPU: '호스트 GPU',
containerGPU: '컨테이너 GPU 지원',
monitorAvailable: '모니터링 가능',
monitorPartial: '일부 모니터링 가능',
notReady: '준비되지 않음',
ready: '준비됨',
checkedAt: '확인 시간',
checkDetailsHelp: '확인 세부 정보를 보고 환경 상태를 확인한 후 다시 검사하세요.',
driverActionTitle: '먼저 호스트 GPU 드라이버를 확인하세요',
driverActionHelp: '문제가 있는 장치의 드라이버를 설치하거나 복구한 후 컨테이너 환경 검사를 계속하세요.',
driverGuide: '드라이버 설치 안내 보기',
toolkitActionTitle: '컨테이너 GPU 지원이 준비되지 않았습니다',
toolkitActionHelp: 'NVIDIA Container Toolkit을 설치하거나 복구한 후 Docker 구성을 확인하세요.',
toolkitGuide: 'Toolkit 설치 안내 보기',
dockerActionTitle: 'Docker의 NVIDIA GPU 지원이 준비되지 않았습니다',
dockerActionHelp: 'Docker의 NVIDIA 런타임 구성을 확인한 후 다시 검사하세요.',
monitorUnaffected: '이 문제는 호스트 GPU 모니터링에 영향을 주지 않습니다.',
title: '환경 점검',
passed: '환경 점검을 통과했습니다',
issues: '환경 문제 {count}개가 발견되었습니다. 안내에 따라 확인하세요.',
recheck: '다시 점검',
installGuide: '설치 가이드 보기',
goContainerSettings: '컨테이너 설정 열기',
details: '점검 세부 정보',
vendor: '제조사',
gpuCount: 'GPU 수',
cardCount: '카드 수',
notDetected: '현재 노드에서 지원되는 GPU/NPU/XPU 장치가 감지되지 않았습니다.',
unknown: '장치 상태를 확인할 수 없습니다. 점검 세부 정보를 확인한 후 다시 시도하세요.',
loadFailed: '장치 정보를 읽을 수 없습니다. 다시 시도하세요.',
notApplicable: '해당 없음',
cudaHelper: '이 버전은 드라이버가 지원하는 CUDA 버전이며 호스트에 설치된 CUDA Toolkit 버전이 아닙니다.',
partialMemory: '일부 메모리 데이터를 가져오지 못했습니다',
sharedMemory: '공유 시스템 메모리: {memory}',
driver: '드라이버 및 장치 관리 도구',
nvidiaDriver: 'NVIDIA 드라이버',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Docker NVIDIA GPU 구성',
statusPassed: '정상',
statusMissing: '감지되지 않음',
statusUnconfigured: '구성 필요',
statusFailed: '점검 실패',
statusUnknown: '알 수 없음',
driverMissing:
'{vendor} 드라이버가 감지되지 않았습니다. 드라이버를 설치하거나 확인하세요(감지 도구: {tool}).',
driverFailed: '{tool}를 통해 장치 정보를 읽을 수 없습니다. 드라이버 상태를 확인하세요.',
driverUnknown: '장치 드라이버 상태를 확인할 수 없습니다. {tool}를 확인하세요.',
toolkitMissing:
'NVIDIA Container Toolkit이 감지되지 않았습니다. 컨테이너에서 NVIDIA GPU를 사용하기 전에 설치와 구성을 완료하세요.',
toolkitFailed: 'NVIDIA Container Toolkit을 점검할 수 없습니다. 실패 세부 정보를 확인하세요.',
dockerConfigMissing:
'Docker 기본 구성 파일에서 runtimes.nvidia 항목이 감지되지 않았습니다. 문서에 따라 구성을 완료하세요.',
dockerConfigInvalid:
'Docker의 NVIDIA 런타임 구성이 올바르지 않습니다. daemon.json의 runtimes.nvidia.path를 확인하세요.',
dockerConfigPending:
'기본 구성 파일에 NVIDIA 런타임 구성이 있지만 현재 Docker에서 아직 로드하지 않았습니다. 구성 파일을 확인하고 Docker를 다시 시작한 후 다시 점검하세요.',
dockerConfigUnreadable: 'Docker 구성 파일을 읽을 수 없습니다. 점검 세부 정보를 확인하세요.',
dockerUnconfigured: 'Docker NVIDIA GPU 구성이 완료되지 않았습니다.',
dockerFailed: 'Docker NVIDIA GPU 구성을 점검할 수 없습니다. 실패 세부 정보를 확인하세요.',
},
systemMemory: '공유 시스템 메모리', systemMemory: '공유 시스템 메모리',
systemMemoryUsed: '사용 중인 시스템 메모리', systemMemoryUsed: '사용 중인 시스템 메모리',
systemMemoryTotal: '전체 시스템 메모리', systemMemoryTotal: '전체 시스템 메모리',
@@ -1785,6 +1846,8 @@ const message = {
pcieWidth: 'PCIe 너비 (현재 / 최대)', pcieWidth: 'PCIe 너비 (현재 / 최대)',
gpu: 'GPU 관리', gpu: 'GPU 관리',
monitoringTitle: 'GPU 모니터링', monitoringTitle: 'GPU 모니터링',
currentHelper:
'현재 노드의 GPU/NPU/XPU 장치에 대한 실시간 상태와 리소스 사용량을 확인합니다. 상단에는 모든 장치의 요약이 표시되며, 상세 지표는 장치별로 표시됩니다.',
gpuHelper: '지원되는 GPU/XPU/NPU 관리 명령을 감지하지 못했습니다. 확인하고 다시 시도하세요!', gpuHelper: '지원되는 GPU/XPU/NPU 관리 명령을 감지하지 못했습니다. 확인하고 다시 시도하세요!',
process: '프로세스 정보', process: '프로세스 정보',
type: '유형', type: '유형',
+64 -2
View File
@@ -1696,8 +1696,8 @@ const message = {
running: 'ຄຳຮ້ອງຂໍທີ່ກຳລັງເຮັດວຽກ', running: 'ຄຳຮ້ອງຂໍທີ່ກຳລັງເຮັດວຽກ',
waiting: 'ຄຳຮ້ອງຂໍທີ່ກຳລັງລໍຖ້າ', waiting: 'ຄຳຮ້ອງຂໍທີ່ກຳລັງລໍຖ້າ',
cacheUsage: 'ການໃຊ້ງານແຄດ KV', cacheUsage: 'ການໃຊ້ງານແຄດ KV',
promptThroughput: 'ອັດຕາ Token ຂາເຂົ້າ', promptThroughput: 'ອັດຕາຂາເຂົ້າ',
generationThroughput: 'ອັດຕາ Token ຂາອອກ', generationThroughput: 'ອັດຕາຂາອອກ',
requestThroughput: 'ອັດຕາຄຳຮ້ອງຂໍສຳເລັດ', requestThroughput: 'ອັດຕາຄຳຮ້ອງຂໍສຳເລັດ',
timeToFirstToken: 'ເວລາລໍຖ້າສະເລ່ຍຂອງ Token ທຳອິດ', timeToFirstToken: 'ເວລາລໍຖ້າສະເລ່ຍຂອງ Token ທຳອິດ',
timePerOutputToken: 'ເວລາລໍຖ້າສະເລ່ຍລະຫວ່າງ Token', timePerOutputToken: 'ເວລາລໍຖ້າສະເລ່ຍລະຫວ່າງ Token',
@@ -1717,6 +1717,66 @@ const message = {
timePerOutputTokenChart: 'ເວລາລໍຖ້າລະຫວ່າງ Token', timePerOutputTokenChart: 'ເວລາລໍຖ້າລະຫວ່າງ Token',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'GPU ຂອງໂຮສ',
containerGPU: 'ການຮອງຮັບ GPU ໃນຄອນເທນເນີ',
monitorAvailable: 'ສາມາດຕິດຕາມໄດ້',
monitorPartial: 'ຕິດຕາມໄດ້ບາງສ່ວນ',
notReady: 'ຍັງບໍ່ພ້ອມ',
ready: 'ພ້ອມແລ້ວ',
checkedAt: 'ເວລາກວດສອບ',
checkDetailsHelp: 'ເບິ່ງລາຍລະອຽດການກວດສອບ, ຢືນຢັນສະຖານະສະພາບແວດລ້ອມ ແລ້ວກວດສອບອີກຄັ້ງ.',
driverActionTitle: 'ກວດສອບໄດຣເວີ GPU ຂອງໂຮສກ່ອນ',
driverActionHelp: 'ຕິດຕັ້ງ ຫຼື ສ້ອມແປງໄດຣເວີຂອງອຸປະກອນທີ່ມີບັນຫາ ກ່ອນກວດສອບສະພາບແວດລ້ອມຄອນເທນເນີຕໍ່.',
driverGuide: 'ເບິ່ງຄູ່ມືຕິດຕັ້ງໄດຣເວີ',
toolkitActionTitle: 'ການຮອງຮັບ GPU ໃນຄອນເທນເນີຍັງບໍ່ພ້ອມ',
toolkitActionHelp: 'ຕິດຕັ້ງ ຫຼື ສ້ອມແປງ NVIDIA Container Toolkit ກ່ອນ ແລ້ວກວດສອບການຕັ້ງຄ່າ Docker.',
toolkitGuide: 'ເບິ່ງຄູ່ມືຕິດຕັ້ງ Toolkit',
dockerActionTitle: 'ການຮອງຮັບ NVIDIA GPU ຂອງ Docker ຍັງບໍ່ພ້ອມ',
dockerActionHelp: 'ກວດສອບການຕັ້ງຄ່າ NVIDIA runtime ໃນ Docker ແລ້ວກວດສອບອີກຄັ້ງ.',
monitorUnaffected: 'ບັນຫານີ້ບໍ່ກະທົບຕໍ່ການຕິດຕາມ GPU ຂອງໂຮສ.',
title: 'ກວດສອບສະພາບແວດລ້ອມ',
passed: 'ຜ່ານການກວດສອບສະພາບແວດລ້ອມແລ້ວ',
issues: 'ພົບບັນຫາສະພາບແວດລ້ອມ {count} ລາຍການ. ກະລຸນາກວດສອບຕາມຄຳແນະນຳ.',
recheck: 'ກວດສອບອີກຄັ້ງ',
installGuide: 'ເບິ່ງຄູ່ມືການຕິດຕັ້ງ',
goContainerSettings: 'ເປີດການຕັ້ງຄ່າຄອນເທນເນີ',
details: 'ລາຍລະອຽດການກວດສອບ',
vendor: 'ຜູ້ຜະລິດ',
gpuCount: 'ຈຳນວນ GPU',
cardCount: 'ຈຳນວນບັດ',
notDetected: 'ບໍ່ພົບອຸປະກອນ GPU/NPU/XPU ທີ່ຮອງຮັບໃນໂນດປັດຈຸບັນ.',
unknown: 'ບໍ່ສາມາດຢືນຢັນສະຖານະອຸປະກອນໄດ້. ກະລຸນາເບິ່ງລາຍລະອຽດແລ້ວກວດສອບອີກຄັ້ງ.',
loadFailed: 'ບໍ່ສາມາດອ່ານຂໍ້ມູນອຸປະກອນໄດ້. ກະລຸນາລອງໃໝ່.',
notApplicable: 'ບໍ່ກ່ຽວຂ້ອງ',
cudaHelper: 'ນີ້ແມ່ນເວີຊັນ CUDA ທີ່ໄດຣເວີຮອງຮັບ ບໍ່ແມ່ນເວີຊັນ CUDA Toolkit ທີ່ຕິດຕັ້ງໃນໂຮສ.',
partialMemory: 'ບໍ່ສາມາດດຶງຂໍ້ມູນຄວາມຈຳບາງສ່ວນໄດ້',
sharedMemory: 'ຄວາມຈຳລະບົບຮ່ວມກັນ: {memory}',
driver: 'ໄດຣເວີ ແລະ ເຄື່ອງມືຈັດການອຸປະກອນ',
nvidiaDriver: 'ໄດຣເວີ NVIDIA',
toolkit: 'NVIDIA Container Toolkit',
docker: 'ການຕັ້ງຄ່າ NVIDIA GPU ໃນ Docker',
statusPassed: 'ປົກກະຕິ',
statusMissing: 'ບໍ່ພົບ',
statusUnconfigured: 'ຕ້ອງຕັ້ງຄ່າ',
statusFailed: 'ການກວດສອບລົ້ມເຫຼວ',
statusUnknown: 'ບໍ່ຮູ້',
driverMissing: 'ບໍ່ພົບໄດຣເວີ {vendor}. ກະລຸນາຕິດຕັ້ງ ຫຼື ກວດສອບໄດຣເວີ (ເຄື່ອງມືກວດສອບ: {tool}).',
driverFailed: 'ບໍ່ສາມາດອ່ານຂໍ້ມູນອຸປະກອນຜ່ານ {tool} ໄດ້. ກະລຸນາກວດສອບສະຖານະໄດຣເວີ.',
driverUnknown: 'ບໍ່ສາມາດຢືນຢັນສະຖານະໄດຣເວີອຸປະກອນໄດ້. ກະລຸນາກວດສອບ {tool}.',
toolkitMissing:
'ບໍ່ພົບ NVIDIA Container Toolkit. ກະລຸນາຕິດຕັ້ງ ແລະ ຕັ້ງຄ່າກ່ອນໃຊ້ NVIDIA GPU ໃນຄອນເທນເນີ.',
toolkitFailed: 'ບໍ່ສາມາດກວດສອບ NVIDIA Container Toolkit ໄດ້. ກະລຸນາເບິ່ງລາຍລະອຽດຂໍ້ຜິດພາດ.',
dockerConfigMissing:
'ບໍ່ພົບ runtimes.nvidia ໃນໄຟລ໌ການຕັ້ງຄ່າເລີ່ມຕົ້ນຂອງ Docker. ກະລຸນາຕັ້ງຄ່າໃຫ້ສຳເລັດຕາມເອກະສານ.',
dockerConfigInvalid:
'ການຕັ້ງຄ່າ NVIDIA runtime ໃນ Docker ບໍ່ຖືກຕ້ອງ. ກະລຸນາກວດສອບ runtimes.nvidia.path ໃນ daemon.json.',
dockerConfigPending:
'ໄຟລ໌ການຕັ້ງຄ່າເລີ່ມຕົ້ນມີການຕັ້ງຄ່າ NVIDIA runtime ແລ້ວ ແຕ່ບໍລິການ Docker ປັດຈຸບັນຍັງບໍ່ໄດ້ໂຫຼດ. ກະລຸນາຢືນຢັນໄຟລ໌ການຕັ້ງຄ່າ, ເລີ່ມ Docker ໃໝ່ ແລ້ວກວດສອບອີກຄັ້ງ.',
dockerConfigUnreadable: 'ບໍ່ສາມາດອ່ານໄຟລ໌ການຕັ້ງຄ່າ Docker ໄດ້. ກະລຸນາເບິ່ງລາຍລະອຽດການກວດສອບ.',
dockerUnconfigured: 'ການຕັ້ງຄ່າ NVIDIA GPU ໃນ Docker ຍັງບໍ່ສຳເລັດ.',
dockerFailed: 'ບໍ່ສາມາດກວດສອບການຕັ້ງຄ່າ NVIDIA GPU ໃນ Docker ໄດ້. ກະລຸນາເບິ່ງລາຍລະອຽດຂໍ້ຜິດພາດ.',
},
systemMemory: 'ໜ່ວຍຄວາມຈຳລະບົບທີ່ໃຊ້ຮ່ວມກັນ', systemMemory: 'ໜ່ວຍຄວາມຈຳລະບົບທີ່ໃຊ້ຮ່ວມກັນ',
systemMemoryUsed: 'ໜ່ວຍຄວາມຈຳລະບົບທີ່ໃຊ້ແລ້ວ', systemMemoryUsed: 'ໜ່ວຍຄວາມຈຳລະບົບທີ່ໃຊ້ແລ້ວ',
systemMemoryTotal: 'ໜ່ວຍຄວາມຈຳລະບົບທັງໝົດ', systemMemoryTotal: 'ໜ່ວຍຄວາມຈຳລະບົບທັງໝົດ',
@@ -1774,6 +1834,8 @@ const message = {
pcieWidth: 'ຄວາມກວ້າງ PCIe (ປັດຈຸບັນ / ສູງສຸດ)', pcieWidth: 'ຄວາມກວ້າງ PCIe (ປັດຈຸບັນ / ສູງສຸດ)',
gpu: 'ການຈັດການ GPU', gpu: 'ການຈັດການ GPU',
monitoringTitle: 'ຕິດຕາມ GPU', monitoringTitle: 'ຕິດຕາມ GPU',
currentHelper:
'ເບິ່ງສະຖານະການເຮັດວຽກ ແລະ ການໃຊ້ຊັບພະຍາກອນແບບເວລາຈິງຂອງອຸປະກອນ GPU/NPU/XPU ໃນໂນດປັດຈຸບັນ. ພາບລວມດ້ານເທິງສະຫຼຸບຂໍ້ມູນຂອງທຸກອຸປະກອນ ແລະ ສະແດງຕົວຊີ້ວັດລະອຽດແຍກຕາມອຸປະກອນ.',
gpuHelper: 'ກວດບໍ່ພົບຄຳສັ່ງຈັດການ GPU/XPU/NPU ທີ່ຮອງຮັບ. ກະລຸນາກວດສອບ ແລະ ລອງໃໝ່!', gpuHelper: 'ກວດບໍ່ພົບຄຳສັ່ງຈັດການ GPU/XPU/NPU ທີ່ຮອງຮັບ. ກະລຸນາກວດສອບ ແລະ ລອງໃໝ່!',
process: 'ຂໍ້ມູນໂປຣເຊສ', process: 'ຂໍ້ມູນໂປຣເຊສ',
processCount: 'ຈຳນວນໂປຣເຊສ', processCount: 'ຈຳນວນໂປຣເຊສ',
+66 -2
View File
@@ -1757,8 +1757,8 @@ const message = {
running: 'Permintaan berjalan', running: 'Permintaan berjalan',
waiting: 'Permintaan menunggu', waiting: 'Permintaan menunggu',
cacheUsage: 'Penggunaan cache KV', cacheUsage: 'Penggunaan cache KV',
promptThroughput: 'Daya pemprosesan token input', promptThroughput: 'Daya pemprosesan input',
generationThroughput: 'Daya pemprosesan token output', generationThroughput: 'Daya pemprosesan output',
requestThroughput: 'Kadar permintaan selesai', requestThroughput: 'Kadar permintaan selesai',
timeToFirstToken: 'Purata kependaman token pertama', timeToFirstToken: 'Purata kependaman token pertama',
timePerOutputToken: 'Purata kependaman antara token', timePerOutputToken: 'Purata kependaman antara token',
@@ -1778,6 +1778,68 @@ const message = {
timePerOutputTokenChart: 'Kependaman antara token', timePerOutputTokenChart: 'Kependaman antara token',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'GPU hos',
containerGPU: 'Sokongan GPU kontena',
monitorAvailable: 'Pemantauan tersedia',
monitorPartial: 'Pemantauan separa',
notReady: 'Belum sedia',
ready: 'Sedia',
checkedAt: 'Masa pemeriksaan',
checkDetailsHelp: 'Semak butiran pemeriksaan, sahkan keadaan persekitaran dan periksa semula.',
driverActionTitle: 'Periksa pemacu GPU hos terlebih dahulu',
driverActionHelp:
'Pasang atau baiki pemacu peranti yang bermasalah sebelum meneruskan pemeriksaan persekitaran kontena.',
driverGuide: 'Lihat panduan pemasangan pemacu',
toolkitActionTitle: 'Sokongan GPU kontena belum sedia',
toolkitActionHelp: 'Pasang atau baiki NVIDIA Container Toolkit, kemudian periksa konfigurasi Docker.',
toolkitGuide: 'Lihat panduan pemasangan Toolkit',
dockerActionTitle: 'Sokongan NVIDIA GPU Docker belum sedia',
dockerActionHelp: 'Periksa konfigurasi runtime NVIDIA dalam Docker, kemudian periksa semula.',
monitorUnaffected: 'Isu ini tidak menjejaskan pemantauan GPU hos.',
title: 'Semakan persekitaran',
passed: 'Semakan persekitaran berjaya',
issues: 'Masalah persekitaran dikesan: {count}. Ikuti panduan untuk menyemaknya.',
recheck: 'Semak semula',
installGuide: 'Lihat panduan pemasangan',
goContainerSettings: 'Buka tetapan bekas',
details: 'Butiran semakan',
vendor: 'Pengeluar',
gpuCount: 'Bilangan GPU',
cardCount: 'Bilangan kad',
notDetected: 'Tiada peranti GPU/NPU/XPU yang disokong dikesan pada nod semasa.',
unknown: 'Status peranti tidak dapat dipastikan. Lihat butiran semakan dan cuba lagi.',
loadFailed: 'Maklumat peranti tidak dapat dibaca. Sila cuba lagi.',
notApplicable: 'Tidak berkenaan',
cudaHelper:
'Ini ialah versi CUDA yang disokong oleh pemacu, bukan versi CUDA Toolkit yang dipasang pada hos.',
partialMemory: 'Sebahagian data memori tidak tersedia',
sharedMemory: 'Memori sistem dikongsi: {memory}',
driver: 'Pemacu dan alat pengurusan peranti',
nvidiaDriver: 'Pemacu NVIDIA',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Konfigurasi NVIDIA GPU Docker',
statusPassed: 'Normal',
statusMissing: 'Tidak dikesan',
statusUnconfigured: 'Konfigurasi diperlukan',
statusFailed: 'Semakan gagal',
statusUnknown: 'Tidak diketahui',
driverMissing: 'Pemacu {vendor} tidak dikesan. Pasang atau semak pemacu (alat pengesanan: {tool}).',
driverFailed: 'Maklumat peranti tidak dapat dibaca menggunakan {tool}. Semak status pemacu.',
driverUnknown: 'Status pemacu peranti tidak dapat dipastikan. Semak {tool}.',
toolkitMissing:
'NVIDIA Container Toolkit tidak dikesan. Pasang dan konfigurasikannya sebelum menggunakan NVIDIA GPU dalam bekas.',
toolkitFailed: 'NVIDIA Container Toolkit tidak dapat disemak. Lihat butiran kegagalan.',
dockerConfigMissing:
'Entri runtimes.nvidia tidak dikesan dalam fail konfigurasi lalai Docker. Lengkapkan konfigurasi mengikut dokumentasi.',
dockerConfigInvalid:
'Konfigurasi masa jalan NVIDIA dalam Docker tidak sah. Semak runtimes.nvidia.path dalam daemon.json.',
dockerConfigPending:
'Fail konfigurasi lalai mengandungi konfigurasi masa jalan NVIDIA, tetapi perkhidmatan Docker semasa belum memuatkannya. Sahkan fail konfigurasi, mulakan semula Docker dan semak sekali lagi.',
dockerConfigUnreadable: 'Fail konfigurasi Docker tidak dapat dibaca. Lihat butiran semakan.',
dockerUnconfigured: 'Konfigurasi NVIDIA GPU Docker belum lengkap.',
dockerFailed: 'Konfigurasi NVIDIA GPU Docker tidak dapat disemak. Lihat butiran kegagalan.',
},
systemMemory: 'Memori sistem dikongsi', systemMemory: 'Memori sistem dikongsi',
systemMemoryUsed: 'Memori sistem digunakan', systemMemoryUsed: 'Memori sistem digunakan',
systemMemoryTotal: 'Jumlah memori sistem', systemMemoryTotal: 'Jumlah memori sistem',
@@ -1838,6 +1900,8 @@ const message = {
pcieWidth: 'Lebar PCIe (semasa / maksimum)', pcieWidth: 'Lebar PCIe (semasa / maksimum)',
gpu: 'Pengurusan GPU', gpu: 'Pengurusan GPU',
monitoringTitle: 'Pemantauan GPU', monitoringTitle: 'Pemantauan GPU',
currentHelper:
'Lihat status masa nyata dan penggunaan sumber peranti GPU/NPU/XPU pada nod semasa. Ringkasan di bahagian atas merangkumi semua peranti; metrik terperinci dipaparkan mengikut peranti.',
gpuHelper: 'Tiada arahan pengurusan GPU/XPU/NPU yang disokong dikesan. Sila periksa dan cuba lagi!', gpuHelper: 'Tiada arahan pengurusan GPU/XPU/NPU yang disokong dikesan. Sila periksa dan cuba lagi!',
process: 'Maklumat Proses', process: 'Maklumat Proses',
type: 'Jenis', type: 'Jenis',
+73 -2
View File
@@ -1754,8 +1754,8 @@ const message = {
running: 'Solicitações em execução', running: 'Solicitações em execução',
waiting: 'Solicitações em espera', waiting: 'Solicitações em espera',
cacheUsage: 'Uso do cache KV', cacheUsage: 'Uso do cache KV',
promptThroughput: 'Taxa de tokens de entrada', promptThroughput: 'Taxa de entrada',
generationThroughput: 'Taxa de tokens de saída', generationThroughput: 'Taxa de saída',
requestThroughput: 'Solicitações concluídas', requestThroughput: 'Solicitações concluídas',
timeToFirstToken: 'Latência média do primeiro token', timeToFirstToken: 'Latência média do primeiro token',
timePerOutputToken: 'Latência média entre tokens', timePerOutputToken: 'Latência média entre tokens',
@@ -1776,6 +1776,75 @@ const message = {
timePerOutputTokenChart: 'Latência entre tokens', timePerOutputTokenChart: 'Latência entre tokens',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'GPU do host',
containerGPU: 'Suporte a GPU em contêineres',
monitorAvailable: 'Monitoramento disponível',
monitorPartial: 'Monitoramento parcial',
notReady: 'Não está pronto',
ready: 'Pronto',
checkedAt: 'Verificado em',
checkDetailsHelp: 'Confira os detalhes, verifique o ambiente e execute a verificação novamente.',
driverActionTitle: 'Verifique primeiro o driver da GPU do host',
driverActionHelp:
'Instale ou repare o driver do dispositivo afetado antes de continuar a verificação do ambiente de contêineres.',
driverGuide: 'Ver guia de instalação do driver',
toolkitActionTitle: 'O suporte a GPU em contêineres não está pronto',
toolkitActionHelp:
'Instale ou repare o NVIDIA Container Toolkit e depois verifique a configuração do Docker.',
toolkitGuide: 'Ver guia de instalação do Toolkit',
dockerActionTitle: 'O suporte a NVIDIA GPU no Docker não está pronto',
dockerActionHelp:
'Verifique a configuração do runtime NVIDIA no Docker e execute a verificação novamente.',
monitorUnaffected: 'Este problema não afeta o monitoramento da GPU do host.',
title: 'Verificações do ambiente',
passed: 'Verificações do ambiente concluídas com sucesso',
issues: 'Problemas de ambiente detectados: {count}. Siga as orientações para verificá-los.',
recheck: 'Verificar novamente',
installGuide: 'Ver guia de instalação',
goContainerSettings: 'Abrir configurações de contêineres',
details: 'Detalhes da verificação',
vendor: 'Fabricante',
gpuCount: 'Quantidade de GPUs',
cardCount: 'Quantidade de placas',
notDetected: 'Nenhum dispositivo GPU/NPU/XPU compatível foi detectado no nó atual.',
unknown:
'Não foi possível confirmar o estado do dispositivo. Consulte os detalhes e verifique novamente.',
loadFailed: 'Não foi possível ler as informações do dispositivo. Tente novamente.',
notApplicable: 'Não se aplica',
cudaHelper:
'Esta é a versão do CUDA compatível com o driver, não a versão do CUDA Toolkit instalada no host.',
partialMemory: 'Alguns dados de memória estão indisponíveis',
sharedMemory: 'Memória compartilhada do sistema: {memory}',
driver: 'Driver e ferramentas de gerenciamento de dispositivos',
nvidiaDriver: 'Driver NVIDIA',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Configuração de GPU NVIDIA no Docker',
statusPassed: 'Normal',
statusMissing: 'Não detectado',
statusUnconfigured: 'Configuração pendente',
statusFailed: 'Falha na verificação',
statusUnknown: 'Desconhecido',
driverMissing:
'O driver {vendor} não foi detectado. Instale ou verifique o driver (ferramenta de detecção: {tool}).',
driverFailed:
'Não foi possível ler as informações do dispositivo usando {tool}. Verifique o estado do driver.',
driverUnknown: 'Não foi possível confirmar o estado do driver do dispositivo. Verifique {tool}.',
toolkitMissing:
'NVIDIA Container Toolkit não foi detectado. Instale e configure a ferramenta antes de usar GPUs NVIDIA em contêineres.',
toolkitFailed: 'Não foi possível verificar o NVIDIA Container Toolkit. Consulte os detalhes da falha.',
dockerConfigMissing:
'Nenhuma entrada runtimes.nvidia foi detectada no arquivo de configuração padrão do Docker. Conclua a configuração seguindo a documentação.',
dockerConfigInvalid:
'A configuração do ambiente de execução NVIDIA no Docker é inválida. Verifique runtimes.nvidia.path no daemon.json.',
dockerConfigPending:
'O arquivo de configuração padrão contém a configuração do ambiente de execução NVIDIA, mas o serviço Docker atual ainda não a carregou. Confirme o arquivo de configuração, reinicie o Docker e verifique novamente.',
dockerConfigUnreadable:
'Não foi possível ler o arquivo de configuração do Docker. Consulte os detalhes da verificação.',
dockerUnconfigured: 'A configuração de GPU NVIDIA no Docker está incompleta.',
dockerFailed:
'Não foi possível verificar a configuração de GPU NVIDIA no Docker. Consulte os detalhes da falha.',
},
systemMemory: 'Memória compartilhada do sistema', systemMemory: 'Memória compartilhada do sistema',
systemMemoryUsed: 'Memória do sistema em uso', systemMemoryUsed: 'Memória do sistema em uso',
systemMemoryTotal: 'Memória total do sistema', systemMemoryTotal: 'Memória total do sistema',
@@ -1836,6 +1905,8 @@ const message = {
pcieWidth: 'Largura PCIe (atual / máxima)', pcieWidth: 'Largura PCIe (atual / máxima)',
gpu: 'Gerenciamento de GPU', gpu: 'Gerenciamento de GPU',
monitoringTitle: 'Monitoramento de GPU', monitoringTitle: 'Monitoramento de GPU',
currentHelper:
'Veja o estado em tempo real e o uso de recursos dos dispositivos GPU/NPU/XPU no nó atual. O resumo superior inclui todos os dispositivos; as métricas detalhadas são exibidas por dispositivo.',
gpuHelper: gpuHelper:
'Nenhum comando de gerenciamento de GPU/XPU/NPU compatível foi detectado. Verifique e tente novamente!', 'Nenhum comando de gerenciamento de GPU/XPU/NPU compatível foi detectado. Verifique e tente novamente!',
process: 'Informações do Processo', process: 'Informações do Processo',
+71 -2
View File
@@ -1747,8 +1747,8 @@ const message = {
running: 'Выполняемые запросы', running: 'Выполняемые запросы',
waiting: 'Ожидающие запросы', waiting: 'Ожидающие запросы',
cacheUsage: 'Использование KV-кэша', cacheUsage: 'Использование KV-кэша',
promptThroughput: 'Скорость входных токенов', promptThroughput: 'Скорость ввода',
generationThroughput: 'Скорость выходных токенов', generationThroughput: 'Скорость вывода',
requestThroughput: 'Завершённые запросы', requestThroughput: 'Завершённые запросы',
timeToFirstToken: 'Среднее время до первого токена', timeToFirstToken: 'Среднее время до первого токена',
timePerOutputToken: 'Средняя задержка между токенами', timePerOutputToken: 'Средняя задержка между токенами',
@@ -1768,6 +1768,73 @@ const message = {
timePerOutputTokenChart: 'Задержка между токенами', timePerOutputTokenChart: 'Задержка между токенами',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'GPU хоста',
containerGPU: 'Поддержка GPU в контейнерах',
monitorAvailable: 'Мониторинг доступен',
monitorPartial: 'Частичный мониторинг',
notReady: 'Не готово',
ready: 'Готово',
checkedAt: 'Время проверки',
checkDetailsHelp: 'Изучите результаты проверки, проверьте состояние среды и повторите проверку.',
driverActionTitle: 'Сначала проверьте драйвер GPU хоста',
driverActionHelp:
'Установите или восстановите драйвер проблемного устройства, затем продолжите проверку среды контейнеров.',
driverGuide: 'Инструкция по установке драйвера',
toolkitActionTitle: 'Поддержка GPU в контейнерах не готова',
toolkitActionHelp:
'Установите или восстановите NVIDIA Container Toolkit, затем проверьте настройки Docker.',
toolkitGuide: 'Инструкция по установке Toolkit',
dockerActionTitle: 'Поддержка NVIDIA GPU в Docker не готова',
dockerActionHelp: 'Проверьте настройки среды выполнения NVIDIA в Docker и повторите проверку.',
monitorUnaffected: 'Эта проблема не влияет на мониторинг GPU хоста.',
title: 'Проверка окружения',
passed: 'Проверки окружения пройдены',
issues: 'Обнаружено проблем окружения: {count}. Проверьте их согласно подсказкам.',
recheck: 'Проверить снова',
installGuide: 'Руководство по установке',
goContainerSettings: 'Открыть настройки контейнеров',
details: 'Подробности проверки',
vendor: 'Производитель',
gpuCount: 'Количество GPU',
cardCount: 'Количество карт',
notDetected: 'На текущем узле не обнаружены поддерживаемые устройства GPU/NPU/XPU.',
unknown:
'Не удалось определить состояние устройства. Просмотрите подробности проверки и повторите попытку.',
loadFailed: 'Не удалось прочитать сведения об устройстве. Повторите попытку.',
notApplicable: 'Не применимо',
cudaHelper:
'Это версия CUDA, поддерживаемая драйвером, а не версия CUDA Toolkit, установленная на хосте.',
partialMemory: 'Часть данных о памяти недоступна',
sharedMemory: 'Общая системная память: {memory}',
driver: 'Драйвер и средства управления устройствами',
nvidiaDriver: 'Драйвер NVIDIA',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Настройка NVIDIA GPU в Docker',
statusPassed: 'Норма',
statusMissing: 'Не обнаружено',
statusUnconfigured: 'Требуется настройка',
statusFailed: 'Ошибка проверки',
statusUnknown: 'Неизвестно',
driverMissing:
'Драйвер {vendor} не обнаружен. Установите или проверьте драйвер (средство обнаружения: {tool}).',
driverFailed:
'Не удалось прочитать сведения об устройстве с помощью {tool}. Проверьте состояние драйвера.',
driverUnknown: 'Не удалось определить состояние драйвера устройства. Проверьте {tool}.',
toolkitMissing:
'NVIDIA Container Toolkit не обнаружен. Установите и настройте его перед использованием NVIDIA GPU в контейнерах.',
toolkitFailed: 'Не удалось проверить NVIDIA Container Toolkit. Просмотрите сведения об ошибке.',
dockerConfigMissing:
'Запись runtimes.nvidia не обнаружена в файле конфигурации Docker по умолчанию. Завершите настройку согласно документации.',
dockerConfigInvalid:
'Конфигурация среды выполнения NVIDIA в Docker некорректна. Проверьте runtimes.nvidia.path в daemon.json.',
dockerConfigPending:
'Файл конфигурации по умолчанию содержит настройки среды выполнения NVIDIA, но текущая служба Docker ещё не загрузила их. Проверьте файл конфигурации, перезапустите Docker и повторите проверку.',
dockerConfigUnreadable:
'Не удалось прочитать файл конфигурации Docker. Просмотрите подробности проверки.',
dockerUnconfigured: 'Настройка NVIDIA GPU в Docker не завершена.',
dockerFailed: 'Не удалось проверить настройку NVIDIA GPU в Docker. Просмотрите сведения об ошибке.',
},
systemMemory: 'Общая системная память', systemMemory: 'Общая системная память',
systemMemoryUsed: 'Использовано системной памяти', systemMemoryUsed: 'Использовано системной памяти',
systemMemoryTotal: 'Всего системной памяти', systemMemoryTotal: 'Всего системной памяти',
@@ -1828,6 +1895,8 @@ const message = {
pcieWidth: 'Ширина PCIe (текущая / максимальная)', pcieWidth: 'Ширина PCIe (текущая / максимальная)',
gpu: 'Управление GPU', gpu: 'Управление GPU',
monitoringTitle: 'Мониторинг GPU', monitoringTitle: 'Мониторинг GPU',
currentHelper:
'Просматривайте состояние и использование ресурсов устройств GPU/NPU/XPU текущего узла в реальном времени. Вверху показана сводка по всем устройствам, а подробные показатели отображаются для каждого устройства отдельно.',
gpuHelper: 'Поддерживаемая команда управления GPU/XPU/NPU не обнаружена. Проверьте и повторите попытку!', gpuHelper: 'Поддерживаемая команда управления GPU/XPU/NPU не обнаружена. Проверьте и повторите попытку!',
process: 'Информация о Процессе', process: 'Информация о Процессе',
type: 'Тип', type: 'Тип',
+69 -2
View File
@@ -1750,8 +1750,8 @@ const message = {
running: 'Çalışan istekler', running: 'Çalışan istekler',
waiting: 'Bekleyen istekler', waiting: 'Bekleyen istekler',
cacheUsage: 'KV önbellek kullanımı', cacheUsage: 'KV önbellek kullanımı',
promptThroughput: 'Girdi token hızı', promptThroughput: 'Girdi hızı',
generationThroughput: 'Çıktı token hızı', generationThroughput: 'Çıktı hızı',
requestThroughput: 'Tamamlanan istek hızı', requestThroughput: 'Tamamlanan istek hızı',
timeToFirstToken: 'Ortalama ilk token gecikmesi', timeToFirstToken: 'Ortalama ilk token gecikmesi',
timePerOutputToken: 'Ortalama token arası gecikme', timePerOutputToken: 'Ortalama token arası gecikme',
@@ -1772,6 +1772,71 @@ const message = {
timePerOutputTokenChart: 'Token arası gecikme', timePerOutputTokenChart: 'Token arası gecikme',
}, },
gpu: { gpu: {
environment: {
hostGPU: 'Ana makine GPU’su',
containerGPU: 'Konteyner GPU desteği',
monitorAvailable: 'İzleme kullanılabilir',
monitorPartial: 'Kısmi izleme',
notReady: 'Hazır değil',
ready: 'Hazır',
checkedAt: 'Kontrol zamanı',
checkDetailsHelp: 'Kontrol ayrıntılarını inceleyin, ortam durumunu doğrulayın ve yeniden kontrol edin.',
driverActionTitle: 'Önce ana makine GPU sürücüsünü kontrol edin',
driverActionHelp:
'Konteyner ortamını kontrol etmeye devam etmeden önce sorunlu aygıtın sürücüsünü kurun veya onarın.',
driverGuide: 'Sürücü kurulum kılavuzunu görüntüle',
toolkitActionTitle: 'Konteyner GPU desteği hazır değil',
toolkitActionHelp:
'Önce NVIDIA Container Toolkit’i kurun veya onarın, ardından Docker yapılandırmasını kontrol edin.',
toolkitGuide: 'Toolkit kurulum kılavuzunu görüntüle',
dockerActionTitle: 'Docker NVIDIA GPU desteği hazır değil',
dockerActionHelp:
'Docker içindeki NVIDIA çalışma zamanı yapılandırmasını kontrol edip yeniden deneyin.',
monitorUnaffected: 'Bu sorun ana makine GPU izlemesini etkilemez.',
title: 'Ortam denetimleri',
passed: 'Ortam denetimleri başarılı',
issues: '{count} ortam sorunu algılandı. Denetlemek için yönergeleri izleyin.',
recheck: 'Yeniden denetle',
installGuide: 'Kurulum kılavuzunu görüntüle',
goContainerSettings: 'Kapsayıcı ayarlarını aç',
details: 'Denetim ayrıntıları',
vendor: 'Üretici',
gpuCount: 'GPU sayısı',
cardCount: 'Kart sayısı',
notDetected: 'Geçerli düğümde desteklenen GPU/NPU/XPU aygıtı algılanmadı.',
unknown: 'Aygıt durumu doğrulanamadı. Denetim ayrıntılarını inceleyip yeniden deneyin.',
loadFailed: 'Aygıt bilgileri okunamadı. Lütfen yeniden deneyin.',
notApplicable: 'Uygulanamaz',
cudaHelper:
'Bu, sürücünün desteklediği CUDA sürümüdür; ana makinede kurulu CUDA Toolkit sürümü değildir.',
partialMemory: 'Bazı bellek verileri alınamadı',
sharedMemory: 'Paylaşılan sistem belleği: {memory}',
driver: 'Sürücü ve aygıt yönetim araçları',
nvidiaDriver: 'NVIDIA sürücüsü',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Docker NVIDIA GPU yapılandırması',
statusPassed: 'Normal',
statusMissing: 'Algılanmadı',
statusUnconfigured: 'Yapılandırma gerekli',
statusFailed: 'Denetim başarısız',
statusUnknown: 'Bilinmiyor',
driverMissing:
'{vendor} sürücüsü algılanmadı. Sürücüyü kurun veya denetleyin (algılama aracı: {tool}).',
driverFailed: 'Aygıt bilgileri {tool} ile okunamadı. Sürücü durumunu denetleyin.',
driverUnknown: 'Aygıt sürücüsünün durumu doğrulanamadı. {tool} aracını denetleyin.',
toolkitMissing:
'NVIDIA Container Toolkit algılanmadı. Kapsayıcılarda NVIDIA GPU kullanmadan önce kurulum ve yapılandırmayı tamamlayın.',
toolkitFailed: 'NVIDIA Container Toolkit denetlenemedi. Hata ayrıntılarını inceleyin.',
dockerConfigMissing:
'Varsayılan Docker yapılandırma dosyasında runtimes.nvidia girdisi algılanmadı. Yapılandırmayı belgelere göre tamamlayın.',
dockerConfigInvalid:
'Docker NVIDIA çalışma zamanı yapılandırması geçersiz. daemon.json dosyasındaki runtimes.nvidia.path değerini denetleyin.',
dockerConfigPending:
'Varsayılan yapılandırma dosyasında NVIDIA çalışma zamanı yapılandırması var, ancak mevcut Docker hizmeti bunu henüz yüklemedi. Yapılandırma dosyasını doğrulayın, Docker hizmetini yeniden başlatıp tekrar denetleyin.',
dockerConfigUnreadable: 'Docker yapılandırma dosyası okunamadı. Denetim ayrıntılarını inceleyin.',
dockerUnconfigured: 'Docker NVIDIA GPU yapılandırması tamamlanmadı.',
dockerFailed: 'Docker NVIDIA GPU yapılandırması denetlenemedi. Hata ayrıntılarını inceleyin.',
},
systemMemory: 'Paylaşılan sistem belleği', systemMemory: 'Paylaşılan sistem belleği',
systemMemoryUsed: 'Kullanılan sistem belleği', systemMemoryUsed: 'Kullanılan sistem belleği',
systemMemoryTotal: 'Toplam sistem belleği', systemMemoryTotal: 'Toplam sistem belleği',
@@ -1831,6 +1896,8 @@ const message = {
pcieWidth: 'PCIe genişliği (geçerli / en yüksek)', pcieWidth: 'PCIe genişliği (geçerli / en yüksek)',
gpu: 'GPU Yönetimi', gpu: 'GPU Yönetimi',
monitoringTitle: 'GPU İzleme', monitoringTitle: 'GPU İzleme',
currentHelper:
'Geçerli düğümdeki GPU/NPU/XPU aygıtlarının anlık durumunu ve kaynak kullanımını görüntüleyin. Üstteki özet tüm aygıtları kapsar; ayrıntılı ölçümler aygıt bazında gösterilir.',
gpuHelper: 'Desteklenen bir GPU/XPU/NPU yönetim komutu algılanmadı. Lütfen kontrol edip tekrar deneyin!', gpuHelper: 'Desteklenen bir GPU/XPU/NPU yönetim komutu algılanmadı. Lütfen kontrol edip tekrar deneyin!',
process: 'İşlem Bilgisi', process: 'İşlem Bilgisi',
type: 'Tür', type: 'Tür',
+62 -2
View File
@@ -1636,8 +1636,8 @@ const message = {
running: '執行中請求', running: '執行中請求',
waiting: '等待中請求', waiting: '等待中請求',
cacheUsage: 'KV Cache 使用率', cacheUsage: 'KV Cache 使用率',
promptThroughput: '輸入 Token 吞吐', promptThroughput: '輸入吞吐',
generationThroughput: '輸出 Token 吞吐', generationThroughput: '輸出吞吐',
requestThroughput: '完成請求速率', requestThroughput: '完成請求速率',
timeToFirstToken: '平均首 Token 延遲', timeToFirstToken: '平均首 Token 延遲',
timePerOutputToken: '平均 Token 間延遲', timePerOutputToken: '平均 Token 間延遲',
@@ -1656,6 +1656,64 @@ const message = {
timePerOutputTokenChart: 'Token 間延遲', timePerOutputTokenChart: 'Token 間延遲',
}, },
gpu: { gpu: {
environment: {
hostGPU: '主機 GPU',
containerGPU: '容器 GPU 支援',
monitorAvailable: '可監控',
monitorPartial: '部分可監控',
notReady: '尚未就緒',
ready: '已就緒',
checkedAt: '檢測時間',
checkDetailsHelp: '請查看檢測詳情,確認環境狀態後重新檢測。',
driverActionTitle: '優先檢查主機 GPU 驅動程式',
driverActionHelp: '請先安裝或修復異常裝置的驅動程式,驅動程式就緒後再繼續檢查容器環境。',
driverGuide: '查看驅動程式安裝指引',
toolkitActionTitle: '容器 GPU 支援尚未就緒',
toolkitActionHelp: '請先安裝或修復 NVIDIA Container Toolkit,再檢查 Docker 設定。',
toolkitGuide: '查看 Toolkit 安裝指引',
dockerActionTitle: 'Docker 的 NVIDIA GPU 支援尚未就緒',
dockerActionHelp: '請檢查 Docker 的 NVIDIA 執行環境設定,完成後重新檢測。',
monitorUnaffected: '主機 GPU 監控不受此問題影響。',
title: '環境檢查',
passed: '環境檢查通過',
issues: '偵測到 {count} 項環境問題,請依照提示檢查。',
recheck: '重新偵測',
installGuide: '查看安裝文件',
goContainerSettings: '前往容器設定',
details: '偵測詳情',
vendor: '廠商',
gpuCount: 'GPU 數量',
cardCount: '卡數量',
notDetected: '目前節點未偵測到支援的 GPU/NPU/XPU 裝置。',
unknown: '無法確認裝置狀態,請查看偵測詳情後重新偵測。',
loadFailed: '無法讀取裝置資訊,請重試。',
notApplicable: '不適用',
cudaHelper: '此版本表示驅動程式支援的 CUDA 版本,不代表主機已安裝的 CUDA Toolkit。',
partialMemory: '部分顯示記憶體資料未取得',
sharedMemory: '共用系統記憶體:{memory}',
driver: '驅動程式與裝置管理工具',
nvidiaDriver: 'NVIDIA 驅動',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Docker NVIDIA GPU 設定',
statusPassed: '正常',
statusMissing: '未偵測到',
statusUnconfigured: '待設定',
statusFailed: '偵測失敗',
statusUnknown: '未知',
driverMissing: '未偵測到 {vendor} 驅動程式,請安裝或檢查驅動程式(偵測工具:{tool})。',
driverFailed: '無法透過 {tool} 讀取裝置資訊,請檢查驅動程式狀態。',
driverUnknown: '無法確認裝置驅動程式狀態,請檢查 {tool}。',
toolkitMissing: '未偵測到 NVIDIA Container Toolkit,容器使用 NVIDIA GPU 前需完成安裝與設定。',
toolkitFailed: '無法偵測 NVIDIA Container Toolkit,請查看失敗原因。',
dockerConfigMissing: 'Docker 預設設定檔中未偵測到 runtimes.nvidia,請依照文件完成設定。',
dockerConfigInvalid:
'Docker 的 NVIDIA 執行階段設定有誤,請檢查 daemon.json 中的 runtimes.nvidia.path。',
dockerConfigPending:
'預設設定檔中已存在 NVIDIA 執行階段設定,但目前 Docker 尚未載入,請確認設定檔並重新啟動 Docker 後重新偵測。',
dockerConfigUnreadable: '無法讀取 Docker 設定檔,請查看偵測詳情。',
dockerUnconfigured: '尚未完成 Docker 的 NVIDIA GPU 設定。',
dockerFailed: '無法偵測 Docker 的 NVIDIA GPU 設定,請查看失敗原因。',
},
systemMemory: '系統共享記憶體', systemMemory: '系統共享記憶體',
systemMemoryUsed: '系統已用記憶體', systemMemoryUsed: '系統已用記憶體',
systemMemoryTotal: '系統總記憶體', systemMemoryTotal: '系統總記憶體',
@@ -1713,6 +1771,8 @@ const message = {
pcieWidth: 'PCIe 寬度(目前 / 最大)', pcieWidth: 'PCIe 寬度(目前 / 最大)',
gpu: 'GPU 管理', gpu: 'GPU 管理',
monitoringTitle: 'GPU 監控', monitoringTitle: 'GPU 監控',
currentHelper:
'查看目前節點 GPU/NPU/XPU 裝置的即時運作狀態與資源使用情況。頂部彙總所有裝置,詳細指標依裝置顯示。',
gpuHelper: '目前系統未偵測到支援的 GPU/XPU/NPU 管理指令,請檢查後重試', gpuHelper: '目前系統未偵測到支援的 GPU/XPU/NPU 管理指令,請檢查後重試',
process: '行程資訊', process: '行程資訊',
type: '類型', type: '類型',
+61 -2
View File
@@ -1649,8 +1649,8 @@ const message = {
running: '运行中请求', running: '运行中请求',
waiting: '等待中请求', waiting: '等待中请求',
cacheUsage: 'KV Cache 使用率', cacheUsage: 'KV Cache 使用率',
promptThroughput: '输入 Token 吞吐', promptThroughput: '输入吞吐',
generationThroughput: '输出 Token 吞吐', generationThroughput: '输出吞吐',
requestThroughput: '完成请求速率', requestThroughput: '完成请求速率',
timeToFirstToken: '平均首 Token 延迟', timeToFirstToken: '平均首 Token 延迟',
timePerOutputToken: '平均 Token 间延迟', timePerOutputToken: '平均 Token 间延迟',
@@ -1669,6 +1669,63 @@ const message = {
timePerOutputTokenChart: 'Token 间延迟', timePerOutputTokenChart: 'Token 间延迟',
}, },
gpu: { gpu: {
environment: {
hostGPU: '宿主机 GPU',
containerGPU: '容器 GPU 支持',
monitorAvailable: '可监控',
monitorPartial: '部分可监控',
notReady: '未就绪',
ready: '已就绪',
checkedAt: '检测时间',
checkDetailsHelp: '请查看检测详情,确认环境状态后重新检测。',
driverActionTitle: '优先检查宿主机 GPU 驱动',
driverActionHelp: '请先安装或修复异常设备的驱动,驱动就绪后再继续检查容器环境。',
driverGuide: '查看驱动安装指引',
toolkitActionTitle: '容器 GPU 支持未就绪',
toolkitActionHelp: '请先安装或修复 NVIDIA Container Toolkit,再检查 Docker 配置。',
toolkitGuide: '查看 Toolkit 安装指引',
dockerActionTitle: 'Docker 的 NVIDIA GPU 支持未就绪',
dockerActionHelp: '请检查 Docker 的 NVIDIA 运行时配置,完成后重新检测。',
monitorUnaffected: '宿主机 GPU 监控不受此问题影响。',
title: '环境检查',
passed: '环境检查通过',
issues: '检测到 {count} 项环境问题,请根据提示检查。',
recheck: '重新检测',
installGuide: '查看安装文档',
goContainerSettings: '前往容器配置',
details: '检测详情',
vendor: '厂商',
gpuCount: 'GPU 数量',
cardCount: '卡数量',
notDetected: '当前节点未检测到支持的 GPU/NPU/XPU 设备。',
unknown: '无法确认设备状态,请查看检测详情后重新检测。',
loadFailed: '无法读取设备信息,请重试。',
notApplicable: '不适用',
cudaHelper: '此版本表示驱动支持的 CUDA 版本,不代表宿主机已安装的 CUDA Toolkit。',
partialMemory: '部分显存数据未获取',
sharedMemory: '共享系统内存:{memory}',
driver: '驱动与设备管理工具',
nvidiaDriver: 'NVIDIA 驱动',
toolkit: 'NVIDIA Container Toolkit',
docker: 'Docker NVIDIA GPU 配置',
statusPassed: '正常',
statusMissing: '未检测到',
statusUnconfigured: '待配置',
statusFailed: '检测失败',
statusUnknown: '未知',
driverMissing: '未检测到 {vendor} 驱动,请安装或检查驱动(检测工具:{tool})。',
driverFailed: '无法通过 {tool} 读取设备信息,请检查驱动状态。',
driverUnknown: '无法确认设备驱动状态,请检查 {tool}。',
toolkitMissing: '未检测到 NVIDIA Container Toolkit,容器使用 NVIDIA GPU 前需完成安装与配置。',
toolkitFailed: '无法检测 NVIDIA Container Toolkit,请查看失败原因。',
dockerConfigMissing: 'Docker 默认配置文件中未检测到 runtimes.nvidia,请根据文档完成配置。',
dockerConfigInvalid: 'Docker 的 NVIDIA 运行时配置有误,请检查 daemon.json 中的 runtimes.nvidia.path。',
dockerConfigPending:
'默认配置文件中已存在 NVIDIA 运行时配置,但当前 Docker 尚未加载,请确认配置文件并重启 Docker 后重新检测。',
dockerConfigUnreadable: '无法读取 Docker 配置文件,请查看检测详情。',
dockerUnconfigured: '尚未完成 Docker 的 NVIDIA GPU 配置。',
dockerFailed: '无法检测 Docker 的 NVIDIA GPU 配置,请查看失败原因。',
},
systemMemory: '系统共享内存', systemMemory: '系统共享内存',
systemMemoryUsed: '系统已用内存', systemMemoryUsed: '系统已用内存',
systemMemoryTotal: '系统总内存', systemMemoryTotal: '系统总内存',
@@ -1726,6 +1783,8 @@ const message = {
pcieWidth: 'PCIe 宽度(当前 / 最大)', pcieWidth: 'PCIe 宽度(当前 / 最大)',
gpu: 'GPU 管理', gpu: 'GPU 管理',
monitoringTitle: 'GPU 监控', monitoringTitle: 'GPU 监控',
currentHelper:
'查看当前节点 GPU/NPU/XPU 设备的实时运行状态与资源使用情况。顶部汇总全部设备,详细指标按设备展示。',
gpuHelper: '当前系统未检测到支持的 GPU/XPU/NPU 管理指令,请检查后重试!', gpuHelper: '当前系统未检测到支持的 GPU/XPU/NPU 管理指令,请检查后重试!',
process: '进程信息', process: '进程信息',
processCount: '进程数', processCount: '进程数',
+1
View File
@@ -28,5 +28,6 @@ declare interface RouterButton {
path?: string; path?: string;
name?: string; name?: string;
count?: number; count?: number;
disabled?: boolean;
permission?: string | string[]; permission?: string | string[];
} }
+573 -63
View File
@@ -1,32 +1,216 @@
<template> <template>
<div> <div>
<RouterMenu /> <RouterMenu :history-disabled="historyDisabled" />
<div> <div>
<LayoutContent v-loading="loading" :title="$t('aiTools.gpu.monitoringTitle')" v-if="hasAccelerators"> <LayoutContent v-loading="loading" :title="$t('aiTools.gpu.monitoringTitle')">
<template #leftToolBar>
<h3 class="environment-heading">
{{ $t(showEnvironment ? 'aiTools.gpu.environment.title' : 'aiTools.gpu.monitoringTitle') }}
</h3>
</template>
<template #rightToolBar> <template #rightToolBar>
<TableSetting title="gpu-refresh" @search="refresh()" /> <TableSetting title="gpu-refresh" @search="search()" />
<TableRefresh @search="refresh()" /> <el-tooltip :content="$t('aiTools.gpu.environment.recheck')">
<TableRefresh :disabled="loading" @search="refresh()" />
</el-tooltip>
</template> </template>
<template #main> <template #main>
<div class="device-overview"> <el-alert
v-if="loadFailed"
class="common-prompt"
:title="$t('aiTools.gpu.environment.loadFailed')"
type="error"
:closable="false"
show-icon
>
<el-button type="primary" link :disabled="loading" @click="refresh()">
{{ $t('aiTools.gpu.environment.recheck') }}
</el-button>
</el-alert>
<section v-else-if="showEnvironment" class="environment-status">
<div class="environment-row">
<div class="environment-identity">
<el-icon :size="22"><Monitor /></el-icon>
<div>
<strong>{{ $t('aiTools.gpu.environment.hostGPU') }}</strong>
<div class="environment-description">
<template v-if="driverChecks.length">
<span v-for="check in driverChecks" :key="check.vendor">
{{ checkTitle(check) }} · {{ checkStatus(check) }}
{{ check.version }}
</span>
</template>
<span v-else>{{ hasAccelerators ? deviceCountText : emptyDescription }}</span>
</div>
</div>
</div>
<el-tag :type="hostEnvironmentStatus.type">
{{ $t('aiTools.gpu.environment.' + hostEnvironmentStatus.label) }}
</el-tag>
</div>
<div v-if="containerChecks.length" class="environment-row">
<div class="environment-identity">
<el-icon :size="22"><Box /></el-icon>
<div>
<strong>{{ $t('aiTools.gpu.environment.containerGPU') }}</strong>
<div class="environment-description">
<span v-for="check in containerChecks" :key="check.item">
{{ checkTitle(check) }} · {{ checkStatus(check) }}
</span>
</div>
</div>
</div>
<el-tag :type="containerEnvironmentStatus.type">
{{ $t('aiTools.gpu.environment.' + containerEnvironmentStatus.label) }}
</el-tag>
</div>
<div v-if="environmentGuidance" class="environment-guidance">
<div>
<strong>{{ $t('aiTools.gpu.environment.' + environmentGuidance.title) }}</strong>
<p>
<template v-if="environmentGuidance.item !== 'driver' && hasAccelerators">
{{ $t('aiTools.gpu.environment.monitorUnaffected') }}
</template>
{{ $t('aiTools.gpu.environment.' + environmentGuidance.description) }}
</p>
</div>
<el-button
v-if="environmentGuidance.action === 'docker'"
type="primary"
@click="routerToName('ContainerSetting')"
>
{{ $t('aiTools.gpu.environment.goContainerSettings') }}
</el-button>
<el-button
v-else-if="environmentGuidance.action === 'details'"
type="primary"
@click="environmentDetails = ['environment']"
>
{{ $t('aiTools.gpu.environment.details') }}
</el-button>
<el-button
v-else
tag="a"
type="primary"
href="https://docs.fit2cloud.com/1panel/faq/gpu-driver"
target="_blank"
rel="noopener noreferrer"
>
{{ $t('aiTools.gpu.environment.' + environmentGuidance.action) }}
</el-button>
</div>
</section>
<el-collapse
v-if="!loadFailed && (environmentChecks.length || gpuInfo.warnings?.length)"
v-model="environmentDetails"
class="environment-details"
>
<el-collapse-item :title="$t('aiTools.gpu.environment.details')" name="environment">
<div v-for="check in environmentChecks" :key="`${check.vendor}-${check.item}`" class="mb-2">
<div class="flex flex-wrap items-center gap-2">
<span>{{ checkTitle(check) }}</span>
<el-tag :type="checkTagType(check)" size="small">
{{ checkStatus(check) }}
</el-tag>
<span v-if="check.version">{{ check.version }}</span>
</div>
<p v-if="check.status !== 'passed'" class="environment-check-description">
{{ checkDescription(check) }}
</p>
<pre v-if="check.reason" class="whitespace-pre-wrap break-words mt-1">{{
check.reason
}}</pre>
</div>
<div v-for="warning in gpuInfo.warnings || []" :key="warning" class="break-words">
{{ warning }}
</div>
</el-collapse-item>
</el-collapse>
<div
v-if="showEnvironment && !loadFailed && gpuInfo.environment?.checkedAt"
class="input-help mb-4"
>
{{ $t('aiTools.gpu.environment.checkedAt') }}:
{{ new Date(gpuInfo.environment.checkedAt).toLocaleString() }}
</div>
<div v-for="summary in overviewRows" :key="summary.vendor" class="device-overview mb-2">
<div class="overview-item">
<span>{{ $t('aiTools.gpu.environment.vendor') }}</span>
<strong>{{ vendorTitle(summary.vendor) }}</strong>
</div>
<div class="overview-item"> <div class="overview-item">
<span>{{ $t('aiTools.gpu.driverVersion') }}</span> <span>{{ $t('aiTools.gpu.driverVersion') }}</span>
<strong>{{ gpuInfo.driverVersion }}</strong> <strong>{{ displayValue(summary.driverVersion) }}</strong>
</div> </div>
<div v-if="gpuInfo.cudaVersion" class="overview-item"> <div class="overview-item">
<span>{{ $t('aiTools.gpu.cudaVersion') }}</span> <span>{{ $t('aiTools.gpu.cudaVersion') }}</span>
<strong>{{ gpuInfo.cudaVersion }}</strong> <el-tooltip
v-if="summary.vendor === 'nvidia' || summary.cudaVersion"
:content="$t('aiTools.gpu.environment.cudaHelper')"
>
<strong>{{ displayValue(summary.cudaVersion) }}</strong>
</el-tooltip>
<strong v-else>{{ $t('aiTools.gpu.environment.notApplicable') }}</strong>
</div> </div>
<div v-if="gpuInfo.collectedAt" class="overview-item"> <div class="overview-item">
<span>{{ $t('aiTools.gpu.collectedAt') }}</span> <span>
<strong>{{ new Date(gpuInfo.collectedAt).toLocaleString() }}</strong> {{
$t(
summary.vendor === 'intel' || summary.vendor === 'xpu'
? 'aiTools.gpu.environment.gpuCount'
: 'aiTools.gpu.environment.cardCount',
)
}}
</span>
<strong>{{ summary.cardCountText }}</strong>
</div>
<div class="overview-item">
<span>
{{
$t(
summary.sharedMemoryOnly
? 'aiTools.gpu.systemMemory'
: 'aiTools.gpu.memoryTotal',
)
}}
</span>
<el-tooltip
:disabled="!summary.sharedMemoryOnly && !summary.memoryPartial"
:content="summaryMemoryTooltip(summary)"
>
<strong>
{{
formatSummaryMemory(
summary.sharedMemoryOnly ? summary.sharedMemoryTotal : summary.memoryTotal,
)
}}
</strong>
</el-tooltip>
<el-tooltip
v-if="!summary.sharedMemoryOnly && summary.sharedMemoryTotal !== null"
:content="$t('aiTools.gpu.systemMemoryHelper')"
>
<span class="whitespace-normal">
{{
$t('aiTools.gpu.environment.sharedMemory', {
memory: formatSummaryMemory(summary.sharedMemoryTotal),
})
}}
</span>
</el-tooltip>
</div> </div>
<div class="overview-count">{{ deviceCountText }}</div>
</div> </div>
<div v-if="hasAccelerators && gpuInfo.collectedAt" class="input-help">
<div class="gpu-group-list"> {{ $t('aiTools.gpu.collectedAt') }}:
{{ new Date(gpuInfo.collectedAt).toLocaleString() }}
</div>
<el-empty
v-if="!hasAccelerators && !loading && !loadFailed && !showEnvironment"
:description="emptyDescription"
/>
<div v-if="hasAccelerators" class="gpu-group-list">
<section v-for="group in gpuGroups" :key="group.key" class="gpu-group"> <section v-for="group in gpuGroups" :key="group.key" class="gpu-group">
<div class="group-header"> <div v-if="group.title" class="group-header">
<div class="group-identity"> <div class="group-identity">
<strong>{{ group.title }}</strong> <strong>{{ group.title }}</strong>
</div> </div>
@@ -79,7 +263,12 @@
> >
{{ $t('commons.button.view') }} {{ $t('commons.button.view') }}
</el-button> </el-button>
<el-button type="primary" link @click="openHistory(item)"> <el-button
type="primary"
link
:disabled="historyDisabled"
@click="openHistory(item)"
>
{{ $t('aiTools.gpu.history') }} {{ $t('aiTools.gpu.history') }}
</el-button> </el-button>
</div> </div>
@@ -168,10 +357,6 @@
</div> </div>
</section> </section>
<section v-if="xpuInfo.xpu.length" class="gpu-group"> <section v-if="xpuInfo.xpu.length" class="gpu-group">
<div class="group-header">
<div class="group-identity"><strong>XPU</strong></div>
</div>
<div class="device-card-grid"> <div class="device-card-grid">
<el-card <el-card
v-for="item in xpuInfo.xpu" v-for="item in xpuInfo.xpu"
@@ -208,7 +393,12 @@
> >
{{ $t('commons.button.view') }} {{ $t('commons.button.view') }}
</el-button> </el-button>
<el-button type="primary" link @click="openHistory(item)"> <el-button
type="primary"
link
:disabled="historyDisabled"
@click="openHistory(item)"
>
{{ $t('aiTools.gpu.history') }} {{ $t('aiTools.gpu.history') }}
</el-button> </el-button>
</div> </div>
@@ -321,42 +511,34 @@
/> />
</template> </template>
</DialogPro> </DialogPro>
<LayoutContent :title="$t('aiTools.gpu.monitoringTitle')" :divider="true" v-if="!hasAccelerators && !loading">
<template #rightToolBar>
<TableRefresh @search="refresh()" />
</template>
<template #main>
<div class="app-warn">
<div class="flx-center">
<span>{{ $t('aiTools.gpu.gpuHelper') }}</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
</LayoutContent>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref } from 'vue'; import { computed, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { InfoFilled } from '@element-plus/icons-vue'; import { Box, InfoFilled, Monitor } from '@element-plus/icons-vue';
import { loadGPUInfo } from '@/api/modules/ai'; import { loadGPUInfo } from '@/api/modules/ai';
import RouterMenu from '@/views/ai/gpu/index.vue'; import RouterMenu from '@/views/ai/gpu/index.vue';
import { AI } from '@/api/interface/ai'; import { AI } from '@/api/interface/ai';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { routerToName } from '@/utils/router';
import i18n from '@/lang'; import i18n from '@/lang';
import { MsgWarning } from '@/utils/message'; import { computeSizeForDocker } from '@/utils/size';
import DeviceDetails, { type DetailSection } from './components/DeviceDetails.vue'; import DeviceDetails, { type DetailSection } from './components/DeviceDetails.vue';
const router = useRouter(); const router = useRouter();
const loading = ref(); const route = useRoute();
const pagePath = route.path;
const { currentNode } = useGlobalStore();
const loading = ref(false);
const loadFailed = ref(false);
const environmentDetails = ref<string[]>([]);
const processDrawerVisible = ref(false); const processDrawerVisible = ref(false);
const processDeviceKey = ref(''); const processDeviceKey = ref('');
const detailDeviceKey = ref(''); const detailDeviceKey = ref('');
const processXPUId = ref<number | null>(null); const processXPUId = ref<number | null>(null);
const gpuInfo = ref<AI.Info>({ const emptyGPUInfo = (): AI.Info => ({
cudaVersion: '', cudaVersion: '',
driverVersion: '', driverVersion: '',
type: 'nvidia', type: 'nvidia',
@@ -365,6 +547,7 @@ const gpuInfo = ref<AI.Info>({
xpuDriverVersion: '', xpuDriverVersion: '',
xpu: [], xpu: [],
}); });
const gpuInfo = ref<AI.Info>(emptyGPUInfo());
const xpuInfo = ref<AI.XpuInfo>({ const xpuInfo = ref<AI.XpuInfo>({
driverVersion: '', driverVersion: '',
type: 'xpu', type: 'xpu',
@@ -396,7 +579,7 @@ const gpuGroups = computed<GPUGroup[]>(() => {
if (!groups.has(key)) { if (!groups.has(key)) {
groups.set(key, { groups.set(key, {
key, key,
title: item.type === 'ascend' ? `NPU ${item.npuIndex}` : item.type.toUpperCase(), title: item.type === 'ascend' ? `NPU ${item.npuIndex}` : '',
devices: [], devices: [],
}); });
} }
@@ -464,6 +647,235 @@ const hasAccelerators = computed(() => {
return gpuGroups.value.length > 0 || xpuInfo.value.xpu.length > 0; return gpuGroups.value.length > 0 || xpuInfo.value.xpu.length > 0;
}); });
const vendorTitle = (vendor: string): string => {
const titles: Record<string, string> = {
nvidia: 'NVIDIA',
intel: 'Intel',
xpu: 'Intel',
ascend: 'Huawei Ascend',
amd: 'AMD',
};
if (vendor === 'mixed') {
const vendors = new Set(gpuInfo.value.gpu.map((device) => device.type as string));
if (gpuInfo.value.npu.length) vendors.add('ascend');
if (xpuInfo.value.xpu.length) vendors.add('intel');
return Array.from(vendors, vendorTitle).join(' / ') || '—';
}
return titles[vendor] || vendor || '—';
};
const displayValue = (value: string | undefined) => {
return isAvailable(value) ? value : '—';
};
const formatSummaryMemory = (value: number | null) => {
return value === null || !Number.isFinite(value) ? '—' : computeSizeForDocker(value);
};
const overviewRows = computed(() => {
if (gpuInfo.value.summaries?.length) {
return gpuInfo.value.summaries
.filter(
(summary) =>
summary.cardCount > 0 ||
isAvailable(summary.driverVersion) ||
summary.memoryTotal !== null ||
summary.sharedMemoryTotal !== null,
)
.map((summary) => {
const devices = gpuInfo.value.gpu.filter((device) => device.type === summary.vendor);
const sharedMemoryTotal =
devices.length && !devices.some((device) => device.sharedMemory) ? null : summary.sharedMemoryTotal;
return {
...summary,
sharedMemoryTotal,
cardCountText: summary.cardCount === null ? '—' : String(summary.cardCount),
sharedMemoryOnly:
summary.memoryTotal === null &&
sharedMemoryTotal !== null &&
devices.every((device) => device.sharedMemory),
};
});
}
if (!hasAccelerators.value) return [];
const memoryDevices = [
...[...gpuInfo.value.gpu, ...gpuInfo.value.npu].map((device) => ({
id: deviceKey(device),
total: device.memTotal,
shared: device.type !== 'ascend' && Boolean(device.sharedMemory),
})),
...xpuInfo.value.xpu.map((device) => ({
id: `xpu-${device.basic.deviceID}`,
total: device.basic.memory,
shared: false,
})),
];
let memoryTotal: number | null = null;
let sharedMemoryTotal: number | null = null;
let memoryPartial = false;
const seen = new Set<string>();
for (const device of memoryDevices) {
if (seen.has(device.id)) continue;
seen.add(device.id);
const total = memoryMiB(device.total || '');
if (total === null || !Number.isFinite(total) || total < 0) {
memoryPartial = true;
continue;
}
const bytes = total * 1024 * 1024;
if (device.shared) {
sharedMemoryTotal = Math.max(sharedMemoryTotal ?? 0, bytes);
} else {
memoryTotal = (memoryTotal ?? 0) + bytes;
}
}
return [
{
vendor: gpuInfo.value.type,
driverVersion: gpuInfo.value.driverVersion,
cudaVersion: gpuInfo.value.cudaVersion,
cardCountText: deviceCountText.value,
memoryTotal,
sharedMemoryTotal,
sharedMemoryOnly: sharedMemoryTotal !== null && memoryDevices.every((device) => device.shared),
memoryPartial,
},
];
});
const environmentChecks = computed(() => gpuInfo.value.environment?.checks || []);
const historyDisabled = computed(
() =>
(loading.value && !gpuInfo.value.environment && !hasAccelerators.value) ||
environmentChecks.value.some(
(check) => check.item === 'driver' && check.status !== 'passed' && (check.vendor || !hasAccelerators.value),
),
);
const driverChecks = computed(() => environmentChecks.value.filter((check) => check.item === 'driver'));
const containerChecks = computed(() => environmentChecks.value.filter((check) => check.item !== 'driver'));
const showEnvironment = computed(
() =>
gpuInfo.value.environment &&
(gpuInfo.value.environment.hardwareStatus !== 'not_detected' || environmentChecks.value.length > 0),
);
const hostEnvironmentStatus = computed<{ type: 'success' | 'warning' | 'info'; label: string }>(() => {
const hasDriverIssue = driverChecks.value.some((check) => check.status !== 'passed');
if (hasAccelerators.value && (!hasDriverIssue || driverChecks.value.some((check) => check.status === 'passed'))) {
return hasDriverIssue
? { type: 'warning', label: 'monitorPartial' }
: { type: 'success', label: 'monitorAvailable' };
}
if (driverChecks.value.some((check) => ['missing', 'unconfigured', 'failed'].includes(check.status))) {
return { type: 'warning', label: 'notReady' };
}
return { type: 'info', label: 'statusUnknown' };
});
const containerEnvironmentStatus = computed<{ type: 'success' | 'warning' | 'info'; label: string }>(() => {
const nvidiaDriver = driverChecks.value.find((check) => check.vendor === 'nvidia');
if (nvidiaDriver && nvidiaDriver.status !== 'passed') {
return nvidiaDriver.status === 'unknown'
? { type: 'info', label: 'statusUnknown' }
: { type: 'warning', label: 'notReady' };
}
if (containerChecks.value.every((check) => check.status === 'passed')) {
return { type: 'success', label: 'ready' };
}
if (containerChecks.value.some((check) => ['missing', 'unconfigured', 'failed'].includes(check.status))) {
return { type: 'warning', label: 'notReady' };
}
return { type: 'info', label: 'statusUnknown' };
});
const environmentGuidance = computed(() => {
const issue =
driverChecks.value.find((check) => check.status !== 'passed') ||
containerChecks.value.find((check) => check.item === 'toolkit' && check.status !== 'passed') ||
containerChecks.value.find((check) => check.status !== 'passed');
if (!issue) return null;
if (issue.status === 'unknown' || (issue.item === 'driver' && !issue.vendor)) {
return { item: issue.item, title: 'unknown', description: 'checkDetailsHelp', action: 'details' };
}
if (issue.item === 'driver') {
return { item: issue.item, title: 'driverActionTitle', description: 'driverActionHelp', action: 'driverGuide' };
}
if (issue.item === 'toolkit') {
return {
item: issue.item,
title: 'toolkitActionTitle',
description: 'toolkitActionHelp',
action: 'toolkitGuide',
};
}
return { item: issue.item, title: 'dockerActionTitle', description: 'dockerActionHelp', action: 'docker' };
});
const emptyDescription = computed(() => {
if (!gpuInfo.value.environment) return i18n.global.t('aiTools.gpu.gpuHelper');
return gpuInfo.value.environment.hardwareStatus === 'not_detected'
? i18n.global.t('aiTools.gpu.environment.notDetected')
: i18n.global.t('aiTools.gpu.environment.unknown');
});
const checkStatus = (check: AI.GPUEnvironmentCheck) => {
const status = check.status[0].toUpperCase() + check.status.slice(1);
return i18n.global.t(`aiTools.gpu.environment.status${status}`);
};
const checkTagType = (check: AI.GPUEnvironmentCheck) => {
if (check.status === 'passed') return 'success';
return check.status === 'failed' ? 'danger' : 'warning';
};
const checkTitle = (check: AI.GPUEnvironmentCheck) => {
if (check.vendor === 'nvidia' && check.item === 'driver') {
return i18n.global.t('aiTools.gpu.environment.nvidiaDriver');
}
const title = i18n.global.t(`aiTools.gpu.environment.${check.item}`);
return check.vendor && check.item === 'driver' ? `${vendorTitle(check.vendor)} · ${title}` : title;
};
const checkDescription = (check: AI.GPUEnvironmentCheck) => {
const managementTools: Record<string, string> = {
nvidia: 'nvidia-smi',
intel: 'xpu-smi',
ascend: 'npu-smi',
amd: 'amd-smi',
};
const tool = managementTools[check.vendor];
let key: string;
if (check.item === 'driver') {
if (!tool) return i18n.global.t('aiTools.gpu.environment.unknown');
key =
check.status === 'missing' || check.status === 'unconfigured'
? 'driverMissing'
: check.status === 'failed'
? 'driverFailed'
: 'driverUnknown';
} else if (check.item === 'toolkit') {
key = check.status === 'missing' || check.status === 'unconfigured' ? 'toolkitMissing' : 'toolkitFailed';
} else {
const configKeys = [
'dockerConfigMissing',
'dockerConfigInvalid',
'dockerConfigPending',
'dockerConfigUnreadable',
];
const code = check.code || '';
key = configKeys.includes(code)
? code
: check.status === 'missing' || check.status === 'unconfigured'
? 'dockerUnconfigured'
: 'dockerFailed';
}
const description = i18n.global.t(`aiTools.gpu.environment.${key}`, {
tool: tool || '',
vendor: vendorTitle(check.vendor),
});
return check.item === 'driver' && key !== 'driverMissing' ? `${checkTitle(check)}: ${description}` : description;
};
const summaryMemoryTooltip = (summary: { sharedMemoryOnly: boolean; memoryPartial: boolean }) =>
[
summary.sharedMemoryOnly && i18n.global.t('aiTools.gpu.systemMemoryHelper'),
summary.memoryPartial && i18n.global.t('aiTools.gpu.environment.partialMemory'),
]
.filter(Boolean)
.join(' ');
const normalizeAcceleratorInfo = (data: AI.Info): AI.Info => { const normalizeAcceleratorInfo = (data: AI.Info): AI.Info => {
const devices = (data.gpu || []) as AcceleratorDevice[]; const devices = (data.gpu || []) as AcceleratorDevice[];
const legacyNPUs = devices const legacyNPUs = devices
@@ -495,22 +907,37 @@ const applyAcceleratorInfo = (data: AI.Info) => {
}; };
}; };
const search = async () => { let requestID = 0;
const search = async (refreshEnvironment = false) => {
if (route.path !== pagePath) return;
if (loading.value) return; if (loading.value) return;
const id = ++requestID;
const node = currentNode.value;
loading.value = true; loading.value = true;
loadFailed.value = false;
try { try {
const res = await loadGPUInfo(); const res = await loadGPUInfo(refreshEnvironment, node);
if (id !== requestID || node !== currentNode.value) return;
applyAcceleratorInfo(res.data); applyAcceleratorInfo(res.data);
if (res.data.warnings?.length) {
MsgWarning(res.data.warnings.join('; '));
}
} catch { } catch {
if (id === requestID && node === currentNode.value) loadFailed.value = true;
} finally { } finally {
loading.value = false; if (id === requestID) loading.value = false;
} }
}; };
const refresh = search; const refresh = () => search(true);
watch(currentNode, () => {
environmentDetails.value = [];
requestID++;
loading.value = false;
loadFailed.value = false;
applyAcceleratorInfo(emptyGPUInfo());
detailDeviceKey.value = '';
processDrawerVisible.value = false;
search(true);
});
const visibleSections = (sections: DetailSection[]) => { const visibleSections = (sections: DetailSection[]) => {
return sections return sections
@@ -765,6 +1192,7 @@ const stableDeviceID = (vendor: string, identifier: string | undefined, fallback
}; };
const openHistory = (item: AcceleratorDevice | AI.XpuInfo['xpu'][number]) => { const openHistory = (item: AcceleratorDevice | AI.XpuInfo['xpu'][number]) => {
if (historyDisabled.value) return;
let deviceID: string; let deviceID: string;
if ('basic' in item) { if ('basic' in item) {
deviceID = stableDeviceID( deviceID = stableDeviceID(
@@ -919,9 +1347,104 @@ const loadEcc = (val: string) => {
onMounted(() => { onMounted(() => {
search(); search();
}); });
onActivated(() => {
search();
});
const invalidateRequest = () => {
requestID++;
loading.value = false;
};
onDeactivated(invalidateRequest);
onBeforeUnmount(invalidateRequest);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.environment-heading {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.environment-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
padding: 16px 0;
border-top: 1px solid var(--el-border-color-lighter);
> .el-tag {
flex-shrink: 0;
}
}
.environment-identity {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
> .el-icon {
flex-shrink: 0;
color: var(--el-text-color-secondary);
}
strong {
color: var(--el-text-color-primary);
font-weight: 600;
}
}
.environment-description {
display: flex;
flex-wrap: wrap;
gap: 4px 16px;
margin-top: 5px;
color: var(--el-text-color-regular);
font-size: 12px;
overflow-wrap: anywhere;
}
.environment-guidance {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px;
margin: 12px 0 16px;
border-radius: 6px;
background: var(--el-color-warning-light-9);
strong {
color: var(--el-color-warning-dark-2);
}
p {
margin: 6px 0 0;
color: var(--el-text-color-regular);
font-size: 12px;
line-height: 1.7;
}
> .el-button {
flex-shrink: 0;
max-width: 100%;
height: auto;
min-height: 32px;
white-space: normal;
text-align: center;
}
}
.environment-details {
margin-bottom: 12px;
}
.environment-check-description {
margin: 6px 0;
color: var(--el-text-color-regular);
line-height: 1.7;
}
@media (max-width: 767px) {
.environment-guidance {
align-items: flex-start;
flex-direction: column;
}
}
.device-overview { .device-overview {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -954,16 +1477,6 @@ onMounted(() => {
line-height: 18px; line-height: 18px;
} }
} }
.overview-count {
margin-left: 16px;
padding-left: 16px;
border-left: 1px solid var(--el-border-color-lighter);
color: var(--el-color-primary);
font-size: 12px;
font-weight: 600;
line-height: 18px;
white-space: nowrap;
}
.gpu-group-list { .gpu-group-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -1168,9 +1681,6 @@ onMounted(() => {
gap: 8px 0; gap: 8px 0;
padding: 10px 12px; padding: 10px 12px;
} }
.overview-count {
width: auto;
}
.device-metrics { .device-metrics {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
+13 -12
View File
@@ -1,7 +1,7 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<RouterMenu /> <RouterMenu />
<template v-if="supported"> <template v-if="supported && options.length">
<el-card class="history-toolbar"> <el-card class="history-toolbar">
<div class="flex flex-wrap gap-3 items-center"> <div class="flex flex-wrap gap-3 items-center">
<el-select style="max-width: 100%" class="p-w-400" v-model="selectedDevice" @change="search"> <el-select style="max-width: 100%" class="p-w-400" v-model="selectedDevice" @change="search">
@@ -26,7 +26,10 @@
:clearable="false" :clearable="false"
@change="changeTimeRange" @change="changeTimeRange"
/> />
<TableRefresh @search="refresh" /> <TableSetting title="gpu-refresh" @search="refresh" />
<el-tooltip :content="$t('aiTools.gpu.environment.recheck')">
<TableRefresh :disabled="loading" @search="refresh" />
</el-tooltip>
</div> </div>
</div> </div>
<div v-if="history?.sampleCount && history.bucketSeconds > 0" class="history-summary"> <div v-if="history?.sampleCount && history.bucketSeconds > 0" class="history-summary">
@@ -67,26 +70,23 @@
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
<LayoutContent v-else-if="!loading" :title="$t('aiTools.gpu.monitoringTitle')" :divider="true"> <LayoutContent v-else :title="$t('aiTools.gpu.monitoringTitle')">
<template #rightToolBar> <template #rightToolBar>
<TableRefresh @search="refresh" /> <TableSetting title="gpu-refresh" @search="refresh" />
<el-tooltip :content="$t('aiTools.gpu.environment.recheck')">
<TableRefresh :disabled="loading" @search="refresh" />
</el-tooltip>
</template> </template>
<template #main> <template #main>
<el-alert <el-alert
v-if="failed" v-if="failed"
class="common-prompt"
:title="$t('aiTools.gpu.historyLoadFailed')" :title="$t('aiTools.gpu.historyLoadFailed')"
type="error" type="error"
:closable="false" :closable="false"
show-icon show-icon
/> />
<div v-else class="app-warn"> <el-empty v-else-if="!loading" :description="$t('aiTools.gpu.environment.notDetected')" />
<div class="flx-center">
<span>{{ $t('aiTools.gpu.gpuHelper') }}</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template> </template>
</LayoutContent> </LayoutContent>
</div> </div>
@@ -371,6 +371,7 @@ const loadOptions = async () => {
} }
}; };
const refresh = () => { const refresh = () => {
if (loading.value) return;
if (followNow) timeRangeGlobal.value = [timeRangeGlobal.value[0], new Date()]; if (followNow) timeRangeGlobal.value = [timeRangeGlobal.value[0], new Date()];
void loadOptions(); void loadOptions();
}; };
+11 -2
View File
@@ -9,8 +9,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import i18n from '@/lang'; import i18n from '@/lang';
import { computed } from 'vue';
const buttons = [ const props = defineProps({
historyDisabled: {
type: Boolean,
default: false,
},
});
const buttons = computed(() => [
{ {
label: i18n.global.t('aiTools.gpu.current'), label: i18n.global.t('aiTools.gpu.current'),
path: '/ai/gpu/current', path: '/ai/gpu/current',
@@ -18,6 +26,7 @@ const buttons = [
{ {
label: i18n.global.t('aiTools.gpu.history'), label: i18n.global.t('aiTools.gpu.history'),
path: '/ai/gpu/history', path: '/ai/gpu/history',
disabled: props.historyDisabled,
}, },
]; ]);
</script> </script>
+242 -4
View File
@@ -99,6 +99,7 @@
> >
<el-radio-button value="network">{{ $t('home.network') }}</el-radio-button> <el-radio-button value="network">{{ $t('home.network') }}</el-radio-button>
<el-radio-button value="io">{{ $t('home.io') }}</el-radio-button> <el-radio-button value="io">{{ $t('home.io') }}</el-radio-button>
<el-radio-button v-if="hasVllm" value="vllm">vLLM</el-radio-button>
</el-radio-group> </el-radio-group>
<el-select <el-select
v-if="chartOption === 'network'" v-if="chartOption === 'network'"
@@ -128,6 +129,26 @@
:value="item" :value="item"
/> />
</el-select> </el-select>
<el-select
v-if="chartOption === 'vllm'"
v-model="vllmInstallID"
class="p-w-200 float-right"
:placeholder="$t('aiTools.model.deployments')"
@visible-change="(visible) => visible && loadVllmOptions()"
>
<el-option
v-for="item in vllmOptions"
:key="item.appInstallId"
:label="item.name + (item.servedModelName ? ' · ' + item.servedModelName : '')"
:value="item.appInstallId"
:disabled="item.status !== 'Running'"
>
<span>
{{ item.name }}{{ item.servedModelName ? ' · ' + item.servedModelName : '' }}
</span>
<Status class="ml-2" :status="item.status" />
</el-option>
</el-select>
</template> </template>
<template #body> <template #body>
<div class="monitor-chart-content"> <div class="monitor-chart-content">
@@ -169,6 +190,47 @@
:dataZoom="true" :dataZoom="true"
/> />
</div> </div>
<template v-if="chartOption === 'vllm'">
<div class="monitor-tags" :style="monitorTagsStyle">
<el-tag>
{{ $t('aiTools.vllmMonitor.promptThroughput') }}:
{{ formatVllmMetric(vllmCurrent?.promptThroughput) }} tokens/s
</el-tag>
<el-tag>
{{ $t('aiTools.vllmMonitor.generationThroughput') }}:
{{ formatVllmMetric(vllmCurrent?.generationThroughput) }} tokens/s
</el-tag>
<el-tag>
{{ $t('aiTools.vllmMonitor.processing') }}:
{{ formatVllmMetric(vllmCurrent?.running) }}
</el-tag>
<el-tag>
{{ $t('aiTools.vllmMonitor.queued') }}:
{{ formatVllmMetric(vllmCurrent?.waiting) }}
</el-tag>
</div>
<div class="mobile-monitor-chart">
<v-charts
height="383px"
id="vllmChart"
type="line"
:option="vllmChartOption"
:dataZoom="true"
/>
</div>
<div class="vllm-monitor-footer">
<span>{{ vllmMonitorMessage }}</span>
<el-button
v-if="selectedVllm"
link
type="primary"
@click="vllmMonitorRef?.acceptParams(selectedVllm)"
>
{{ $t('commons.button.view') }}
</el-button>
</div>
<VllmMonitor v-if="selectedVllm" ref="vllmMonitorRef" />
</template>
</div> </div>
</template> </template>
</CardWithHeader> </CardWithHeader>
@@ -443,7 +505,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onBeforeUnmount, ref, reactive, computed, nextTick } from 'vue'; import { onMounted, onBeforeUnmount, ref, reactive, computed, nextTick, watch, defineAsyncComponent } from 'vue';
import SystemStatus from '@/views/home/status/index.vue'; import SystemStatus from '@/views/home/status/index.vue';
import AppLauncher from '@/views/home/app/index.vue'; import AppLauncher from '@/views/home/app/index.vue';
import VCharts from '@/components/v-charts/index.vue'; import VCharts from '@/components/v-charts/index.vue';
@@ -478,6 +540,9 @@ import {
} from '@/utils/dashboardCache'; } from '@/utils/dashboardCache';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { useCan } from '@/composables/useMenuManagePermission'; import { useCan } from '@/composables/useMenuManagePermission';
import { readPages } from '@/utils/pagination';
import { loadExtensionComponent } from '@/extensions/optional';
import type { ReqPage, ResPage, ResultData } from '@/api/interface';
const router = useRouter(); const router = useRouter();
import { useGlobalStore } from '@/composables/useGlobalStore'; import { useGlobalStore } from '@/composables/useGlobalStore';
const { const {
@@ -489,8 +554,146 @@ const {
hasNewVersion, hasNewVersion,
isAdminOrNodeAdmin, isAdminOrNodeAdmin,
isXpackOrEE, isXpackOrEE,
isProductPro,
currentNode,
} = useGlobalStore(); } = useGlobalStore();
interface VllmDeployment {
appInstallId: number;
name: string;
servedModelName: string;
status: string;
}
interface VllmSnapshot {
appInstallID: number;
createdAt: string;
status: string;
intervalSeconds: number;
promptThroughput: number | null;
generationThroughput: number | null;
running: number | null;
waiting: number | null;
metrics?: Record<string, Record<string, number>>;
}
const vllmModules = import.meta.glob<{
pageVllm: (req: ReqPage & { info: string }) => Promise<ResultData<ResPage<VllmDeployment>>>;
loadVllmCurrent: (id: number, signal: AbortSignal) => Promise<ResultData<VllmSnapshot>>;
}>('@/xpack/api/modules/vllm.ts', { eager: true });
const vllmMetricsModules = import.meta.glob<{
calculateCurrentMetrics: (snapshot: VllmSnapshot, previous?: VllmSnapshot) => VllmSnapshot;
}>('@/xpack/views/vllm/monitor/current.ts', { eager: true });
const vllmAPI = Object.values(vllmModules)[0];
const vllmMetrics = Object.values(vllmMetricsModules)[0];
const VllmMonitor = defineAsyncComponent(() => loadExtensionComponent('/vllm/monitor/index.vue'));
const vllmMonitorRef = ref();
const canViewVllm = useCan('ai_model_view');
const vllmOptions = ref<VllmDeployment[]>([]);
const vllmInstallID = ref<number>();
const vllmCurrent = ref<VllmSnapshot>();
const vllmLoadFailed = ref(false);
const vllmPoints = ref<Array<{ time: string; prompt: number | null; generation: number | null }>>([]);
let vllmTimer: ReturnType<typeof setTimeout>;
let vllmController: AbortController;
let vllmOptionsVersion = 0;
const hasVllm = computed(() => isProductPro.value && canViewVllm.value && vllmOptions.value.length > 0);
const selectedVllm = computed(() => vllmOptions.value.find((item) => item.appInstallId === vllmInstallID.value));
const formatVllmMetric = (value?: number | null) =>
value == null || !Number.isFinite(value) ? '--' : Number(value.toFixed(2)).toString();
const vllmMonitorMessage = computed(() => {
if (!selectedVllm.value) return i18n.global.t('aiTools.model.readinessNotRunning');
if (vllmLoadFailed.value) return i18n.global.t('aiTools.vllmMonitor.loadFailed');
if (vllmCurrent.value?.status === 'unavailable') return i18n.global.t('aiTools.vllmMonitor.unavailable');
if (vllmCurrent.value?.status === 'no_data') return i18n.global.t('aiTools.vllmMonitor.noMetrics');
if (!vllmCurrent.value?.intervalSeconds) return i18n.global.t('commons.table.statusWaiting');
return `${i18n.global.t('aiTools.vllmMonitor.sampleWindow')}: ${formatVllmMetric(vllmCurrent.value.intervalSeconds)} s`;
});
const vllmChartOption = computed(() => ({
xData: loadMonitorChartTime(vllmPoints.value.map((point) => point.time)),
yData: [
{
name: i18n.global.t('aiTools.vllmMonitor.promptThroughput'),
data: loadMonitorChartData(vllmPoints.value.map((point) => point.prompt)),
},
{
name: i18n.global.t('aiTools.vllmMonitor.generationThroughput'),
data: loadMonitorChartData(vllmPoints.value.map((point) => point.generation)),
},
],
grid: monitorChartGrid,
legend: monitorChartLegend,
formatStr: 'tokens/s',
yAxis: [
{
type: 'value',
name: '( tokens/s )',
min: 0,
max: ({ max }: { max: number }) => (max > 0 ? undefined : 1),
},
],
tooltip: {
trigger: 'axis',
valueFormatter: (value: number | null) => `${formatVllmMetric(value)} tokens/s`,
},
}));
const loadVllmOptions = async () => {
if (!isProductPro.value || !canViewVllm.value || !vllmAPI || !vllmMetrics) return;
const version = ++vllmOptionsVersion;
const node = currentNode.value;
try {
const items = await readPages((page) => {
if (version !== vllmOptionsVersion || node !== currentNode.value) throw new Error('node changed');
return vllmAPI.pageVllm({ ...page, info: '' });
});
if (version !== vllmOptionsVersion || node !== currentNode.value) return;
vllmOptions.value = items;
if (!items.some((item) => item.appInstallId === vllmInstallID.value && item.status === 'Running')) {
vllmInstallID.value = items.find((item) => item.status === 'Running')?.appInstallId;
}
if (!items.length && chartOption.value === 'vllm') chartOption.value = 'network';
} catch {
if (version === vllmOptionsVersion) vllmLoadFailed.value = true;
}
};
const stopVllmMonitor = (resetData = true) => {
clearTimeout(vllmTimer);
vllmController?.abort();
if (resetData) {
vllmCurrent.value = undefined;
vllmPoints.value = [];
vllmLoadFailed.value = false;
}
};
const refreshVllmMonitor = async () => {
const controller = new AbortController();
vllmController = controller;
try {
const { data } = await vllmAPI.loadVllmCurrent(vllmInstallID.value, controller.signal);
if (controller.signal.aborted) return;
const wasUnavailable = vllmCurrent.value?.status === 'unavailable';
vllmCurrent.value = vllmMetrics.calculateCurrentMetrics(data, vllmCurrent.value);
vllmLoadFailed.value = false;
vllmPoints.value.push({
time: dateFormatForSecond(new Date(data.createdAt)),
prompt: vllmCurrent.value.promptThroughput,
generation: vllmCurrent.value.generationThroughput,
});
if (data.status === 'unavailable' && !wasUnavailable) void loadVllmOptions();
} catch {
if (controller.signal.aborted) return;
vllmCurrent.value = undefined;
vllmLoadFailed.value = true;
vllmPoints.value.push({ time: dateFormatForSecond(new Date()), prompt: null, generation: null });
} finally {
if (!controller.signal.aborted) {
vllmPoints.value = vllmPoints.value.slice(-20);
vllmTimer = setTimeout(refreshVllmMonitor, 3000);
}
}
};
const DASHBOARD_CACHE_TTL = { const DASHBOARD_CACHE_TTL = {
safeStatus: 10 * 60 * 1000, safeStatus: 10 * 60 * 1000,
netOptions: 60 * 60 * 1000, netOptions: 60 * 60 * 1000,
@@ -505,7 +708,7 @@ const monitorTagsStyle = {
const monitorChartEmptyLength = 20; const monitorChartEmptyLength = 20;
const loadMonitorEmptyData = () => Array.from({ length: monitorChartEmptyLength }, () => null); const loadMonitorEmptyData = () => Array.from({ length: monitorChartEmptyLength }, () => null);
const loadMonitorEmptyTime = () => Array.from({ length: monitorChartEmptyLength }, () => ''); const loadMonitorEmptyTime = () => Array.from({ length: monitorChartEmptyLength }, () => '');
const loadMonitorChartData = (data: Array<number>) => (data.length === 0 ? loadMonitorEmptyData() : data); const loadMonitorChartData = (data: Array<number | null>) => (data.length === 0 ? loadMonitorEmptyData() : data);
const loadMonitorChartTime = (data: Array<string>) => (data.length === 0 ? loadMonitorEmptyTime() : data); const loadMonitorChartTime = (data: Array<string>) => (data.length === 0 ? loadMonitorEmptyTime() : data);
const loadIOChartOption = () => ({ const loadIOChartOption = () => ({
xData: loadMonitorChartTime(timeIODatas.value), xData: loadMonitorChartTime(timeIODatas.value),
@@ -718,6 +921,7 @@ const updateCurrentInfo = (data: Dashboard.CurrentInfo) => {
const changeOption = async () => { const changeOption = async () => {
isInit.value = true; isInit.value = true;
if (chartOption.value === 'vllm') void loadVllmOptions();
loadData(); loadData();
}; };
@@ -851,7 +1055,7 @@ const refreshDashboard = async () => {
clearDashboardCache(); clearDashboardCache();
onLoadBaseInfo(false, ''); onLoadBaseInfo(false, '');
hasRefreshedOptionsOnHover.value = false; hasRefreshedOptionsOnHover.value = false;
await Promise.allSettled([onLoadNetworkOptions(true), onLoadIOOptions(true), loadSettingInfo()]); await Promise.allSettled([onLoadNetworkOptions(true), onLoadIOOptions(true), loadSettingInfo(), loadVllmOptions()]);
MsgSuccess(i18n.global.t('commons.msg.refreshSuccess')); MsgSuccess(i18n.global.t('commons.msg.refreshSuccess'));
}; };
@@ -1028,7 +1232,7 @@ const saveMemo = async () => {
const loadData = async () => { const loadData = async () => {
if (chartOption.value === 'io') { if (chartOption.value === 'io') {
chartsOption.value['ioChart'] = loadIOChartOption(); chartsOption.value['ioChart'] = loadIOChartOption();
} else { } else if (chartOption.value === 'network') {
chartsOption.value['networkChart'] = loadNetworkChartOption(); chartsOption.value['networkChart'] = loadNetworkChartOption();
} }
}; };
@@ -1097,12 +1301,14 @@ const loadSource = (row: any) => {
const onFocus = () => { const onFocus = () => {
isActive.value = true; isActive.value = true;
skipNextCurrentInfoDelta.value = true; skipNextCurrentInfoDelta.value = true;
void loadVllmOptions();
}; };
const onBlur = () => { const onBlur = () => {
isActive.value = false; isActive.value = false;
}; };
const refreshOptionsOnHover = async () => { const refreshOptionsOnHover = async () => {
void loadVllmOptions();
if (hasRefreshedOptionsOnHover.value) return; if (hasRefreshedOptionsOnHover.value) return;
if (!netOptionsFromCache.value && !ioOptionsFromCache.value) return; if (!netOptionsFromCache.value && !ioOptionsFromCache.value) return;
hasRefreshedOptionsOnHover.value = true; hasRefreshedOptionsOnHover.value = true;
@@ -1155,7 +1361,29 @@ onMounted(() => {
loadMemo(); loadMemo();
}); });
watch(
[currentNode, isProductPro, canViewVllm],
() => {
vllmOptionsVersion++;
stopVllmMonitor();
vllmOptions.value = [];
vllmInstallID.value = undefined;
if (chartOption.value === 'vllm') chartOption.value = 'network';
void loadVllmOptions();
},
{ immediate: true, flush: 'sync' },
);
watch([chartOption, vllmInstallID, isActive], ([, installID], [, previousInstallID]) => {
stopVllmMonitor(installID !== previousInstallID);
if (chartOption.value === 'vllm' && hasVllm.value && vllmInstallID.value && isActive.value) {
void refreshVllmMonitor();
}
});
onBeforeUnmount(() => { onBeforeUnmount(() => {
vllmOptionsVersion++;
stopVllmMonitor();
window.removeEventListener('focus', onFocus); window.removeEventListener('focus', onFocus);
window.removeEventListener('blur', onBlur); window.removeEventListener('blur', onBlur);
isCurrentActive.value = false; isCurrentActive.value = false;
@@ -1189,6 +1417,16 @@ onBeforeUnmount(() => {
margin-top: 40px; margin-top: 40px;
} }
.vllm-monitor-footer {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
padding: 0 20px;
color: var(--el-text-color-secondary);
font-size: 12px;
}
@media only screen and (max-width: 767px) { @media only screen and (max-width: 767px) {
.dashboard-entrance-alert { .dashboard-entrance-alert {
padding-right: 40px; padding-right: 40px;
@@ -24,7 +24,7 @@
@keyup.enter="search" @keyup.enter="search"
@clear="search" @clear="search"
/> />
<el-button :disabled="loading" @click="search">{{ $t('commons.button.confirm') }}</el-button> <el-button :disabled="loading" @click="search">{{ $t('commons.button.search') }}</el-button>
</div> </div>
<div class="directory-row directory-header"> <div class="directory-row directory-header">
<span>{{ $t('file.path') }}</span> <span>{{ $t('file.path') }}</span>