mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-30 16:05:30 +00:00
feat: support GB10 shared memory and refine AI model UI (#14254)
This commit is contained in:
@@ -177,6 +177,7 @@ type DiskInfo struct {
|
||||
}
|
||||
|
||||
type GPUInfo struct {
|
||||
SharedMemory bool `json:"sharedMemory"`
|
||||
Type string `json:"type"`
|
||||
Index uint `json:"index"`
|
||||
NPUIndex uint `json:"npuIndex"`
|
||||
|
||||
+12
-11
@@ -56,17 +56,18 @@ type MonitorGPUOptions struct {
|
||||
Options []string `json:"options"`
|
||||
}
|
||||
type GPUChartHide struct {
|
||||
DeviceID string `json:"deviceID"`
|
||||
Legacy bool `json:"legacy"`
|
||||
ProductName string `json:"productName"`
|
||||
Type string `json:"type"`
|
||||
Process bool `json:"process"`
|
||||
GPU bool `json:"gpu"`
|
||||
Memory bool `json:"memory"`
|
||||
Power bool `json:"power"`
|
||||
PowerLimit bool `json:"powerLimit"`
|
||||
Temperature bool `json:"temperature"`
|
||||
Speed bool `json:"speed"`
|
||||
SharedMemory bool `json:"sharedMemory"`
|
||||
DeviceID string `json:"deviceID"`
|
||||
Legacy bool `json:"legacy"`
|
||||
ProductName string `json:"productName"`
|
||||
Type string `json:"type"`
|
||||
Process bool `json:"process"`
|
||||
GPU bool `json:"gpu"`
|
||||
Memory bool `json:"memory"`
|
||||
Power bool `json:"power"`
|
||||
PowerLimit bool `json:"powerLimit"`
|
||||
Temperature bool `json:"temperature"`
|
||||
Speed bool `json:"speed"`
|
||||
}
|
||||
type MonitorGPUSearch struct {
|
||||
Aggregation string `json:"aggregation" validate:"omitempty,oneof=avg max"`
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"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/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
|
||||
"github.com/robfig/cron/v3"
|
||||
@@ -205,7 +207,14 @@ func (m *MonitorService) LoadGPUOptions() dto.MonitorGPUOptions {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
data.ChartHide = append(data.ChartHide, dto.GPUChartHide{DeviceID: device.DeviceID, ProductName: device.ProductName, Type: device.DeviceType, Legacy: device.DeviceID == ""})
|
||||
_, productName, _ := strings.Cut(device.ProductName, " - ")
|
||||
data.ChartHide = append(data.ChartHide, dto.GPUChartHide{
|
||||
DeviceID: device.DeviceID,
|
||||
ProductName: device.ProductName,
|
||||
Type: device.DeviceType,
|
||||
Legacy: device.DeviceID == "",
|
||||
SharedMemory: strings.HasPrefix(device.DeviceID, "nvidia:") && gpu.UsesSystemMemory(productName),
|
||||
})
|
||||
data.Options = append(data.Options, device.ProductName)
|
||||
}
|
||||
return data
|
||||
@@ -800,9 +809,10 @@ func loadGPUOptions(snapshot *accelerator.Snapshot) dto.MonitorGPUOptions {
|
||||
})
|
||||
for _, item := range snapshot.Devices {
|
||||
chartHide := dto.GPUChartHide{
|
||||
DeviceID: item.ID,
|
||||
ProductName: item.Label,
|
||||
Type: string(item.Kind),
|
||||
SharedMemory: item.GPU != nil && item.GPU.SharedMemory,
|
||||
DeviceID: item.ID,
|
||||
ProductName: item.Label,
|
||||
Type: string(item.Kind),
|
||||
}
|
||||
data.ChartHide = append(data.ChartHide, chartHide)
|
||||
data.Options = append(data.Options, chartHide.ProductName)
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
)
|
||||
|
||||
const nvidiaSMICommand = "nvidia-smi"
|
||||
@@ -20,7 +22,31 @@ func (n nvidiaSMI) LoadInfo(ctx context.Context) (*Info, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calling %s failed: %w", nvidiaSMICommand, err)
|
||||
}
|
||||
return parseNvidiaSMI([]byte(itemData))
|
||||
info, err := parseNvidiaSMI([]byte(itemData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if slices.ContainsFunc(info.Devices, func(device Device) bool { return device.SharedMemory }) {
|
||||
memory, err := mem.VirtualMemoryWithContext(ctx)
|
||||
if err != nil {
|
||||
info.Warnings = append(info.Warnings, fmt.Sprintf("read system shared memory: %v", err))
|
||||
} else {
|
||||
for index := range info.Devices {
|
||||
device := &info.Devices[index]
|
||||
if !device.SharedMemory {
|
||||
continue
|
||||
}
|
||||
device.MemUsed = fmt.Sprintf("%d B", memory.Used)
|
||||
device.MemTotal = fmt.Sprintf("%d B", memory.Total)
|
||||
device.MemoryFree = fmt.Sprintf("%.2f MiB", float64(memory.Available)/(1024*1024))
|
||||
}
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func UsesSystemMemory(productName string) bool {
|
||||
return strings.EqualFold(strings.TrimSpace(productName), "NVIDIA GB10")
|
||||
}
|
||||
|
||||
func parseNvidiaSMI(buf []byte) (*Info, error) {
|
||||
@@ -38,6 +64,7 @@ func parseNvidiaSMI(buf []byte) (*Info, error) {
|
||||
for i := range s.Gpu {
|
||||
gpuItem := Device{
|
||||
Type: "nvidia",
|
||||
SharedMemory: UsesSystemMemory(s.Gpu[i].ProductName),
|
||||
MemoryActivity: s.Gpu[i].Utilization.MemoryUtil,
|
||||
EncoderUtil: s.Gpu[i].Utilization.EncoderUtil,
|
||||
DecoderUtil: s.Gpu[i].Utilization.DecoderUtil,
|
||||
@@ -72,6 +99,12 @@ func parseNvidiaSMI(buf []byte) (*Info, error) {
|
||||
ComputeMode: s.Gpu[i].ComputeMode,
|
||||
MigMode: s.Gpu[i].MigMode.CurrentMig,
|
||||
}
|
||||
if gpuItem.SharedMemory {
|
||||
gpuItem.MemUsed = "N/A"
|
||||
gpuItem.MemTotal = "N/A"
|
||||
gpuItem.MemoryFree = "N/A"
|
||||
gpuItem.MemoryReserved = "N/A"
|
||||
}
|
||||
gpuItem.ECCPending = s.Gpu[i].EccMode.PendingEcc
|
||||
gpuItem.ECCErrors = []ECCError{
|
||||
{Scope: "Volatile Total", Correctable: s.Gpu[i].EccErrors.Volatile.SingleBit.Total, Uncorrectable: s.Gpu[i].EccErrors.Volatile.DoubleBit.Total},
|
||||
|
||||
@@ -17,6 +17,7 @@ type ECCError struct {
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
SharedMemory bool `json:"sharedMemory"`
|
||||
ECCPending string `json:"eccPending"`
|
||||
ECCErrors []ECCError `json:"eccErrors"`
|
||||
MemoryActivity string `json:"memoryActivity"`
|
||||
|
||||
@@ -49,6 +49,7 @@ export namespace AI {
|
||||
xpu: XpuInfo['xpu'];
|
||||
}
|
||||
export interface GPU {
|
||||
sharedMemory?: boolean;
|
||||
eccPending?: string;
|
||||
eccErrors?: { scope: string; correctable: string; uncorrectable: string }[];
|
||||
memoryActivity?: string;
|
||||
@@ -139,6 +140,7 @@ export namespace AI {
|
||||
chartHide: Array<ChartHide>;
|
||||
}
|
||||
export interface ChartHide {
|
||||
sharedMemory?: boolean;
|
||||
deviceID?: string;
|
||||
legacy?: boolean;
|
||||
productName: string;
|
||||
|
||||
@@ -151,6 +151,7 @@ export namespace Dashboard {
|
||||
inodesUsedPercent: number;
|
||||
}
|
||||
export interface GPUInfo {
|
||||
sharedMemory?: boolean;
|
||||
type: string;
|
||||
index: number;
|
||||
npuIndex: number;
|
||||
|
||||
@@ -1071,11 +1071,11 @@ const message = {
|
||||
onlineModel: 'Online models',
|
||||
instanceDeployment: 'Deploy by instance',
|
||||
nvidiaDeployment: 'NVIDIA-optimized deployment',
|
||||
engines: 'Inference engines',
|
||||
engines: 'Local inference engines',
|
||||
modelManagement: 'Model management',
|
||||
serviceDetails: 'Service details',
|
||||
deployments: 'Local deployments',
|
||||
files: 'Model files',
|
||||
files: 'Model hub',
|
||||
connectExternal: 'Connect external service',
|
||||
deploymentTemplates: 'vLLM startup command templates',
|
||||
engineProvider: 'Engine / Provider',
|
||||
@@ -1643,6 +1643,11 @@ const message = {
|
||||
timePerOutputTokenChart: 'Inter-token latency',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'Shared system memory',
|
||||
systemMemoryUsed: 'System memory used',
|
||||
systemMemoryTotal: 'Total system memory',
|
||||
systemMemoryAvailable: 'Available system memory',
|
||||
systemMemoryHelper: 'Shared by the CPU and GPU. Shows system-wide memory usage, not GPU-only usage.',
|
||||
memoryActivity: 'Memory read/write activity',
|
||||
hotspotTemperature: 'Hotspot temperature',
|
||||
fanRPM: 'Fan speed (RPM)',
|
||||
@@ -1678,7 +1683,6 @@ const message = {
|
||||
historyPeak: 'Peak',
|
||||
historyAggregated:
|
||||
'{count} samples aggregated into {seconds}-second intervals; processes show counts only.',
|
||||
historyRaw: '{count} original samples.',
|
||||
historyMetricEmpty: 'No data for this metric in the selected time range',
|
||||
historyLoadFailed: 'Failed to load history. Please retry.',
|
||||
collectedAt: 'Collected at',
|
||||
|
||||
@@ -1073,11 +1073,11 @@ const message = {
|
||||
onlineModel: 'Modelos en línea',
|
||||
instanceDeployment: 'Despliegue por instancia',
|
||||
nvidiaDeployment: 'Despliegue optimizado para NVIDIA',
|
||||
engines: 'Motores de inferencia',
|
||||
engines: 'Motores de inferencia locales',
|
||||
modelManagement: 'Gestión de modelos',
|
||||
serviceDetails: 'Detalles del servicio',
|
||||
deployments: 'Despliegues locales',
|
||||
files: 'Archivos de modelos',
|
||||
files: 'Catálogo de modelos',
|
||||
connectExternal: 'Conectar servicio externo',
|
||||
deploymentTemplates: 'Plantillas de comandos de inicio de vLLM',
|
||||
engineProvider: 'Motor / Proveedor',
|
||||
@@ -1653,6 +1653,12 @@ const message = {
|
||||
timePerOutputTokenChart: 'Latencia entre tokens',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'Memoria compartida del sistema',
|
||||
systemMemoryUsed: 'Memoria del sistema utilizada',
|
||||
systemMemoryTotal: 'Memoria total del sistema',
|
||||
systemMemoryAvailable: 'Memoria disponible del sistema',
|
||||
systemMemoryHelper:
|
||||
'Compartida por la CPU y la GPU. Muestra el uso de memoria de todo el sistema, no solo de la GPU.',
|
||||
memoryActivity: 'Actividad de lectura/escritura de memoria',
|
||||
hotspotTemperature: 'Temperatura del punto caliente',
|
||||
fanRPM: 'Velocidad del ventilador (RPM)',
|
||||
@@ -1688,7 +1694,6 @@ const message = {
|
||||
historyPeak: 'Máximo',
|
||||
historyAggregated:
|
||||
'{count} muestras agrupadas en intervalos de {seconds} segundos; los procesos solo muestran cantidades.',
|
||||
historyRaw: '{count} muestras originales.',
|
||||
historyMetricEmpty: 'No hay datos de esta métrica en el período seleccionado',
|
||||
historyLoadFailed: 'No se pudo cargar el historial. Inténtelo de nuevo.',
|
||||
collectedAt: 'Fecha de recopilación',
|
||||
|
||||
@@ -1055,11 +1055,11 @@ const message = {
|
||||
onlineModel: 'مدلهای آنلاین',
|
||||
instanceDeployment: 'استقرار بر اساس نمونه',
|
||||
nvidiaDeployment: 'استقرار بهینهشده برای NVIDIA',
|
||||
engines: 'موتورهای استنتاج',
|
||||
engines: 'موتورهای استنتاج محلی',
|
||||
modelManagement: 'مدیریت مدلها',
|
||||
serviceDetails: 'جزئیات سرویس',
|
||||
deployments: 'استقرارهای محلی',
|
||||
files: 'فایلهای مدل',
|
||||
files: 'مرکز مدلها',
|
||||
connectExternal: 'اتصال به سرویس خارجی',
|
||||
deploymentTemplates: 'الگوهای فرمان راهاندازی vLLM',
|
||||
engineProvider: 'موتور / ارائهدهنده',
|
||||
@@ -1625,6 +1625,11 @@ const message = {
|
||||
timePerOutputTokenChart: 'تأخیر بین توکنها',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'حافظهٔ مشترک سیستم',
|
||||
systemMemoryUsed: 'حافظهٔ مصرفشدهٔ سیستم',
|
||||
systemMemoryTotal: 'کل حافظهٔ سیستم',
|
||||
systemMemoryAvailable: 'حافظهٔ در دسترس سیستم',
|
||||
systemMemoryHelper: 'بین CPU و GPU مشترک است. مصرف حافظهٔ کل سیستم را نشان میدهد، نه فقط مصرف GPU.',
|
||||
memoryActivity: 'فعالیت خواندن/نوشتن حافظه',
|
||||
hotspotTemperature: 'دمای نقطه داغ',
|
||||
fanRPM: 'سرعت فن (RPM)',
|
||||
@@ -1660,7 +1665,6 @@ const message = {
|
||||
historyPeak: 'بیشینه',
|
||||
historyAggregated:
|
||||
'{count} نمونه در بازههای {seconds} ثانیهای تجمیع شدهاند؛ فقط تعداد فرایندها نمایش داده میشود.',
|
||||
historyRaw: '{count} نمونه اصلی.',
|
||||
historyMetricEmpty: 'در بازه زمانی انتخابشده دادهای برای این شاخص وجود ندارد',
|
||||
historyLoadFailed: 'بارگیری سابقه ناموفق بود. دوباره تلاش کنید.',
|
||||
collectedAt: 'زمان گردآوری',
|
||||
|
||||
@@ -1059,11 +1059,11 @@ const message = {
|
||||
onlineModel: 'オンラインモデル',
|
||||
instanceDeployment: 'インスタンス単位でデプロイ',
|
||||
nvidiaDeployment: 'NVIDIA 向け最適化デプロイ',
|
||||
engines: '推論エンジン',
|
||||
engines: 'ローカル推論エンジン',
|
||||
modelManagement: 'モデル管理',
|
||||
serviceDetails: 'サービス詳細',
|
||||
deployments: 'ローカルデプロイ',
|
||||
files: 'モデルファイル',
|
||||
files: 'モデル広場',
|
||||
connectExternal: '外部サービスに接続',
|
||||
deploymentTemplates: 'vLLM 起動コマンドテンプレート',
|
||||
engineProvider: 'エンジン / プロバイダー',
|
||||
@@ -1629,6 +1629,11 @@ const message = {
|
||||
timePerOutputTokenChart: 'トークン間遅延',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: '共有システムメモリ',
|
||||
systemMemoryUsed: 'システムメモリ使用量',
|
||||
systemMemoryTotal: 'システムメモリ合計',
|
||||
systemMemoryAvailable: '利用可能なシステムメモリ',
|
||||
systemMemoryHelper: 'CPU と GPU で共有します。GPU 単体ではなく、システム全体のメモリ使用量を表示します。',
|
||||
memoryActivity: 'メモリ読み書き稼働率',
|
||||
hotspotTemperature: 'ホットスポット温度',
|
||||
fanRPM: 'ファン回転数(RPM)',
|
||||
@@ -1663,7 +1668,6 @@ const message = {
|
||||
historyAverage: '平均値',
|
||||
historyPeak: '最大値',
|
||||
historyAggregated: '{count} 件のサンプルを {seconds} 秒間隔で集計。プロセスは件数のみ表示します。',
|
||||
historyRaw: '元のサンプル {count} 件。',
|
||||
historyMetricEmpty: '選択した期間にこの指標のデータはありません',
|
||||
historyLoadFailed: '履歴の読み込みに失敗しました。再試行してください。',
|
||||
collectedAt: '収集日時',
|
||||
|
||||
@@ -1049,11 +1049,11 @@ const message = {
|
||||
onlineModel: '온라인 모델',
|
||||
instanceDeployment: '인스턴스별 배포',
|
||||
nvidiaDeployment: 'NVIDIA 최적화 배포',
|
||||
engines: '추론 엔진',
|
||||
engines: '로컬 추론 엔진',
|
||||
modelManagement: '모델 관리',
|
||||
serviceDetails: '서비스 상세 정보',
|
||||
deployments: '로컬 배포',
|
||||
files: '모델 파일',
|
||||
files: '모델 허브',
|
||||
connectExternal: '외부 서비스 연결',
|
||||
deploymentTemplates: 'vLLM 시작 명령 템플릿',
|
||||
engineProvider: '엔진 / 공급자',
|
||||
@@ -1614,6 +1614,11 @@ const message = {
|
||||
timePerOutputTokenChart: '토큰 간 지연',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: '공유 시스템 메모리',
|
||||
systemMemoryUsed: '사용 중인 시스템 메모리',
|
||||
systemMemoryTotal: '전체 시스템 메모리',
|
||||
systemMemoryAvailable: '사용 가능한 시스템 메모리',
|
||||
systemMemoryHelper: 'CPU와 GPU가 공유합니다. GPU만의 사용량이 아닌 시스템 전체 메모리 사용량을 표시합니다.',
|
||||
memoryActivity: '메모리 읽기/쓰기 활성률',
|
||||
hotspotTemperature: '핫스폿 온도',
|
||||
fanRPM: '팬 회전 속도 (RPM)',
|
||||
@@ -1648,7 +1653,6 @@ const message = {
|
||||
historyAverage: '평균',
|
||||
historyPeak: '최댓값',
|
||||
historyAggregated: '샘플 {count}개를 {seconds}초 간격으로 집계합니다. 프로세스는 개수만 표시합니다.',
|
||||
historyRaw: '원본 샘플 {count}개.',
|
||||
historyMetricEmpty: '선택한 기간에 이 지표의 데이터가 없습니다',
|
||||
historyLoadFailed: '기록을 불러오지 못했습니다. 다시 시도하세요.',
|
||||
collectedAt: '수집 시간',
|
||||
|
||||
@@ -1046,11 +1046,11 @@ const message = {
|
||||
onlineModel: 'ໂມເດວອອນລາຍ',
|
||||
instanceDeployment: 'ນຳໃຊ້ຕາມອິນສະແຕນ',
|
||||
nvidiaDeployment: 'ການນຳໃຊ້ທີ່ປັບໃຫ້ເໝາະກັບ NVIDIA',
|
||||
engines: 'ເຄື່ອງຈັກອະນຸມານ',
|
||||
engines: 'ເຄື່ອງຈັກອະນຸມານພາຍໃນ',
|
||||
modelManagement: 'ຈັດການໂມເດວ',
|
||||
serviceDetails: 'ລາຍລະອຽດບໍລິການ',
|
||||
deployments: 'ການນຳໃຊ້ພາຍໃນເຄື່ອງ',
|
||||
files: 'ໄຟລ໌ໂມເດວ',
|
||||
files: 'ສູນລວມໂມເດວ',
|
||||
connectExternal: 'ເຊື່ອມຕໍ່ບໍລິການພາຍນອກ',
|
||||
deploymentTemplates: 'ແມ່ແບບຄຳສັ່ງເລີ່ມຕົ້ນ vLLM',
|
||||
engineProvider: 'ເຄື່ອງຈັກ / ຜູ້ໃຫ້ບໍລິການ',
|
||||
@@ -1606,6 +1606,11 @@ const message = {
|
||||
timePerOutputTokenChart: 'ເວລາລໍຖ້າລະຫວ່າງ Token',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'ໜ່ວຍຄວາມຈຳລະບົບທີ່ໃຊ້ຮ່ວມກັນ',
|
||||
systemMemoryUsed: 'ໜ່ວຍຄວາມຈຳລະບົບທີ່ໃຊ້ແລ້ວ',
|
||||
systemMemoryTotal: 'ໜ່ວຍຄວາມຈຳລະບົບທັງໝົດ',
|
||||
systemMemoryAvailable: 'ໜ່ວຍຄວາມຈຳລະບົບທີ່ພ້ອມໃຊ້',
|
||||
systemMemoryHelper: 'CPU ແລະ GPU ໃຊ້ຮ່ວມກັນ. ສະແດງການໃຊ້ໜ່ວຍຄວາມຈຳຂອງທັງລະບົບ ບໍ່ແມ່ນສະເພາະ GPU.',
|
||||
memoryActivity: 'ກິດຈະກຳອ່ານ/ຂຽນໜ່ວຍຄວາມຈຳ',
|
||||
hotspotTemperature: 'ອຸນຫະພູມຈຸດຮ້ອນ',
|
||||
fanRPM: 'ຄວາມໄວພັດລົມ (RPM)',
|
||||
@@ -1640,7 +1645,6 @@ const message = {
|
||||
historyAverage: 'ຄ່າສະເລ່ຍ',
|
||||
historyPeak: 'ຄ່າສູງສຸດ',
|
||||
historyAggregated: 'ລວມ {count} ຕົວຢ່າງເປັນຊ່ວງ {seconds} ວິນາທີ; ສະແດງພຽງຈຳນວນໂປຣເຊສ.',
|
||||
historyRaw: '{count} ຕົວຢ່າງຕົ້ນສະບັບ.',
|
||||
historyMetricEmpty: 'ບໍ່ມີຂໍ້ມູນຕົວຊີ້ວັດນີ້ໃນຊ່ວງເວລາທີ່ເລືອກ',
|
||||
historyLoadFailed: 'ໂຫຼດປະຫວັດບໍ່ສຳເລັດ. ກະລຸນາລອງໃໝ່.',
|
||||
collectedAt: 'ເວລາເກັບຂໍ້ມູນ',
|
||||
|
||||
@@ -1081,11 +1081,11 @@ const message = {
|
||||
onlineModel: 'Model dalam talian',
|
||||
instanceDeployment: 'Pengerahan mengikut instans',
|
||||
nvidiaDeployment: 'Pengerahan dioptimumkan untuk NVIDIA',
|
||||
engines: 'Enjin inferens',
|
||||
engines: 'Enjin inferens setempat',
|
||||
modelManagement: 'Pengurusan model',
|
||||
serviceDetails: 'Butiran perkhidmatan',
|
||||
deployments: 'Pengerahan setempat',
|
||||
files: 'Fail model',
|
||||
files: 'Hab model',
|
||||
connectExternal: 'Sambungkan perkhidmatan luaran',
|
||||
deploymentTemplates: 'Templat arahan permulaan vLLM',
|
||||
engineProvider: 'Enjin / Pembekal',
|
||||
@@ -1655,6 +1655,12 @@ const message = {
|
||||
timePerOutputTokenChart: 'Kependaman antara token',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'Memori sistem dikongsi',
|
||||
systemMemoryUsed: 'Memori sistem digunakan',
|
||||
systemMemoryTotal: 'Jumlah memori sistem',
|
||||
systemMemoryAvailable: 'Memori sistem tersedia',
|
||||
systemMemoryHelper:
|
||||
'Dikongsi oleh CPU dan GPU. Memaparkan penggunaan memori seluruh sistem, bukan GPU sahaja.',
|
||||
memoryActivity: 'Aktiviti baca/tulis memori',
|
||||
hotspotTemperature: 'Suhu titik panas',
|
||||
fanRPM: 'Kelajuan kipas (RPM)',
|
||||
@@ -1690,7 +1696,6 @@ const message = {
|
||||
historyPeak: 'Puncak',
|
||||
historyAggregated:
|
||||
'{count} sampel diagregatkan dalam sela {seconds} saat; proses hanya memaparkan bilangan.',
|
||||
historyRaw: '{count} sampel asal.',
|
||||
historyMetricEmpty: 'Tiada data untuk metrik ini dalam julat masa yang dipilih',
|
||||
historyLoadFailed: 'Gagal memuatkan sejarah. Sila cuba lagi.',
|
||||
collectedAt: 'Masa pengumpulan',
|
||||
|
||||
@@ -1077,11 +1077,11 @@ const message = {
|
||||
onlineModel: 'Modelos online',
|
||||
instanceDeployment: 'Implantação por instância',
|
||||
nvidiaDeployment: 'Implantação otimizada para NVIDIA',
|
||||
engines: 'Mecanismos de inferência',
|
||||
engines: 'Mecanismos de inferência locais',
|
||||
modelManagement: 'Gerenciamento de modelos',
|
||||
serviceDetails: 'Detalhes do serviço',
|
||||
deployments: 'Implantações locais',
|
||||
files: 'Arquivos de modelos',
|
||||
files: 'Central de modelos',
|
||||
connectExternal: 'Conectar serviço externo',
|
||||
deploymentTemplates: 'Modelos de comandos de inicialização do vLLM',
|
||||
engineProvider: 'Mecanismo / Provedor',
|
||||
@@ -1654,6 +1654,12 @@ const message = {
|
||||
timePerOutputTokenChart: 'Latência entre tokens',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'Memória compartilhada do sistema',
|
||||
systemMemoryUsed: 'Memória do sistema em uso',
|
||||
systemMemoryTotal: 'Memória total do sistema',
|
||||
systemMemoryAvailable: 'Memória disponível do sistema',
|
||||
systemMemoryHelper:
|
||||
'Compartilhada pela CPU e GPU. Exibe o uso de memória de todo o sistema, não apenas da GPU.',
|
||||
memoryActivity: 'Atividade de leitura/escrita da memória',
|
||||
hotspotTemperature: 'Temperatura do ponto quente',
|
||||
fanRPM: 'Rotação da ventoinha (RPM)',
|
||||
@@ -1689,7 +1695,6 @@ const message = {
|
||||
historyPeak: 'Pico',
|
||||
historyAggregated:
|
||||
'{count} amostras agregadas em intervalos de {seconds} segundos; processos exibem apenas contagens.',
|
||||
historyRaw: '{count} amostras originais.',
|
||||
historyMetricEmpty: 'Sem dados desta métrica no período selecionado',
|
||||
historyLoadFailed: 'Falha ao carregar o histórico. Tente novamente.',
|
||||
collectedAt: 'Data da coleta',
|
||||
|
||||
@@ -1070,11 +1070,11 @@ const message = {
|
||||
onlineModel: 'Онлайн-модели',
|
||||
instanceDeployment: 'Развертывание по экземплярам',
|
||||
nvidiaDeployment: 'Развертывание с оптимизацией для NVIDIA',
|
||||
engines: 'Движки инференса',
|
||||
engines: 'Локальные движки инференса',
|
||||
modelManagement: 'Управление моделями',
|
||||
serviceDetails: 'Сведения о сервисе',
|
||||
deployments: 'Локальные развертывания',
|
||||
files: 'Файлы моделей',
|
||||
files: 'Каталог моделей',
|
||||
connectExternal: 'Подключить внешний сервис',
|
||||
deploymentTemplates: 'Шаблоны команд запуска vLLM',
|
||||
engineProvider: 'Движок / Поставщик',
|
||||
@@ -1644,6 +1644,12 @@ const message = {
|
||||
timePerOutputTokenChart: 'Задержка между токенами',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'Общая системная память',
|
||||
systemMemoryUsed: 'Использовано системной памяти',
|
||||
systemMemoryTotal: 'Всего системной памяти',
|
||||
systemMemoryAvailable: 'Доступная системная память',
|
||||
systemMemoryHelper:
|
||||
'Память совместно используется CPU и GPU. Показано потребление всей системы, а не только GPU.',
|
||||
memoryActivity: 'Активность чтения/записи памяти',
|
||||
hotspotTemperature: 'Температура горячей точки',
|
||||
fanRPM: 'Скорость вентилятора (об/мин)',
|
||||
@@ -1679,7 +1685,6 @@ const message = {
|
||||
historyPeak: 'Максимум',
|
||||
historyAggregated:
|
||||
'{count} измерений сгруппированы по {seconds} с; для процессов показано только количество.',
|
||||
historyRaw: 'Исходных измерений: {count}.',
|
||||
historyMetricEmpty: 'Нет данных этой метрики за выбранный период',
|
||||
historyLoadFailed: 'Не удалось загрузить историю. Повторите попытку.',
|
||||
collectedAt: 'Время сбора',
|
||||
|
||||
@@ -1080,11 +1080,11 @@ const message = {
|
||||
onlineModel: 'Çevrimiçi modeller',
|
||||
instanceDeployment: 'Örnek bazında dağıtım',
|
||||
nvidiaDeployment: 'NVIDIA için optimize edilmiş dağıtım',
|
||||
engines: 'Çıkarım motorları',
|
||||
engines: 'Yerel çıkarım motorları',
|
||||
modelManagement: 'Model yönetimi',
|
||||
serviceDetails: 'Hizmet ayrıntıları',
|
||||
deployments: 'Yerel dağıtımlar',
|
||||
files: 'Model dosyaları',
|
||||
files: 'Model merkezi',
|
||||
connectExternal: 'Harici hizmete bağlan',
|
||||
deploymentTemplates: 'vLLM başlatma komutu şablonları',
|
||||
engineProvider: 'Motor / Sağlayıcı',
|
||||
@@ -1652,6 +1652,12 @@ const message = {
|
||||
timePerOutputTokenChart: 'Token arası gecikme',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: 'Paylaşılan sistem belleği',
|
||||
systemMemoryUsed: 'Kullanılan sistem belleği',
|
||||
systemMemoryTotal: 'Toplam sistem belleği',
|
||||
systemMemoryAvailable: 'Kullanılabilir sistem belleği',
|
||||
systemMemoryHelper:
|
||||
'CPU ve GPU tarafından paylaşılır. Yalnızca GPU kullanımını değil, tüm sistemin bellek kullanımını gösterir.',
|
||||
memoryActivity: 'Bellek okuma/yazma etkinliği',
|
||||
hotspotTemperature: 'Sıcak nokta sıcaklığı',
|
||||
fanRPM: 'Fan hızı (RPM)',
|
||||
@@ -1687,7 +1693,6 @@ const message = {
|
||||
historyPeak: 'En yüksek',
|
||||
historyAggregated:
|
||||
'{count} örnek {seconds} saniyelik aralıklarla toplandı; işlemler için yalnızca sayılar gösterilir.',
|
||||
historyRaw: '{count} ham örnek.',
|
||||
historyMetricEmpty: 'Seçilen zaman aralığında bu metriğe ait veri yok',
|
||||
historyLoadFailed: 'Geçmiş yüklenemedi. Lütfen tekrar deneyin.',
|
||||
collectedAt: 'Toplanma zamanı',
|
||||
|
||||
@@ -1009,11 +1009,11 @@ const message = {
|
||||
onlineModel: '線上模型',
|
||||
instanceDeployment: '按執行個體部署',
|
||||
nvidiaDeployment: 'NVIDIA 最佳化部署',
|
||||
engines: '推理引擎',
|
||||
engines: '本機推理引擎',
|
||||
modelManagement: '模型管理',
|
||||
serviceDetails: '服務詳情',
|
||||
deployments: '本機部署',
|
||||
files: '模型檔案',
|
||||
files: '模型廣場',
|
||||
connectExternal: '接入外部服務',
|
||||
deploymentTemplates: 'vLLM 啟動命令範本',
|
||||
engineProvider: '引擎 / 供應商',
|
||||
@@ -1555,6 +1555,11 @@ const message = {
|
||||
timePerOutputTokenChart: 'Token 間延遲',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: '系統共享記憶體',
|
||||
systemMemoryUsed: '系統已用記憶體',
|
||||
systemMemoryTotal: '系統總記憶體',
|
||||
systemMemoryAvailable: '系統可用記憶體',
|
||||
systemMemoryHelper: 'CPU 與 GPU 共用,顯示整機記憶體使用情況,不代表 GPU 獨占用量。',
|
||||
memoryActivity: '顯示記憶體讀寫活躍率',
|
||||
hotspotTemperature: '熱點溫度',
|
||||
fanRPM: '風扇轉速(RPM)',
|
||||
@@ -1589,7 +1594,6 @@ const message = {
|
||||
historyAverage: '平均值',
|
||||
historyPeak: '峰值',
|
||||
historyAggregated: '共 {count} 筆取樣,按 {seconds} 秒彙整;程序僅顯示統計數量。',
|
||||
historyRaw: '共 {count} 筆原始取樣。',
|
||||
historyMetricEmpty: '所選時間範圍內沒有此指標的資料',
|
||||
historyLoadFailed: '歷史記錄載入失敗,請重試',
|
||||
collectedAt: '採集時間',
|
||||
|
||||
@@ -1022,11 +1022,11 @@ const message = {
|
||||
onlineModel: '在线模型',
|
||||
instanceDeployment: '按实例部署',
|
||||
nvidiaDeployment: 'NVIDIA 优化部署',
|
||||
engines: '推理引擎',
|
||||
engines: '本地推理引擎',
|
||||
modelManagement: '模型管理',
|
||||
serviceDetails: '服务详情',
|
||||
deployments: '本地部署',
|
||||
files: '模型文件',
|
||||
files: '模型广场',
|
||||
connectExternal: '接入外部服务',
|
||||
deploymentTemplates: 'vLLM 启动命令模板',
|
||||
engineProvider: '引擎 / 供应商',
|
||||
@@ -1568,6 +1568,11 @@ const message = {
|
||||
timePerOutputTokenChart: 'Token 间延迟',
|
||||
},
|
||||
gpu: {
|
||||
systemMemory: '系统共享内存',
|
||||
systemMemoryUsed: '系统已用内存',
|
||||
systemMemoryTotal: '系统总内存',
|
||||
systemMemoryAvailable: '系统可用内存',
|
||||
systemMemoryHelper: 'CPU 与 GPU 共用,显示整机内存使用情况,不代表 GPU 独占用量。',
|
||||
memoryActivity: '显存读写活跃率',
|
||||
hotspotTemperature: '热点温度',
|
||||
fanRPM: '风扇转速(RPM)',
|
||||
@@ -1602,7 +1607,6 @@ const message = {
|
||||
historyAverage: '平均值',
|
||||
historyPeak: '峰值',
|
||||
historyAggregated: '共 {count} 条采样,按 {seconds} 秒聚合;进程仅展示统计数量。',
|
||||
historyRaw: '共 {count} 条原始采样。',
|
||||
historyMetricEmpty: '所选时间范围内没有该指标的数据',
|
||||
historyLoadFailed: '历史记录加载失败,请重试',
|
||||
collectedAt: '采集时间',
|
||||
|
||||
@@ -79,6 +79,9 @@
|
||||
>
|
||||
{{ $t('commons.button.view') }}
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="openHistory(item)">
|
||||
{{ $t('aiTools.gpu.history') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -97,7 +100,21 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="metric-item metric-primary metric-memory">
|
||||
<span>{{ $t('aiTools.gpu.memoryUsed') }}</span>
|
||||
<span>
|
||||
{{
|
||||
$t(
|
||||
item.type !== 'ascend' && item.sharedMemory
|
||||
? 'aiTools.gpu.systemMemory'
|
||||
: 'aiTools.gpu.memoryUsed',
|
||||
)
|
||||
}}
|
||||
<el-tooltip
|
||||
v-if="item.type !== 'ascend' && item.sharedMemory"
|
||||
:content="$t('aiTools.gpu.systemMemoryHelper')"
|
||||
>
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<strong>{{ formatMemory(item.memUsed, item.memTotal) }}</strong>
|
||||
<el-progress
|
||||
v-if="memoryPercentage(item) !== null"
|
||||
@@ -191,6 +208,9 @@
|
||||
>
|
||||
{{ $t('commons.button.view') }}
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="openHistory(item)">
|
||||
{{ $t('aiTools.gpu.history') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -321,6 +341,8 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { InfoFilled } from '@element-plus/icons-vue';
|
||||
import { loadGPUInfo } from '@/api/modules/ai';
|
||||
import RouterMenu from '@/views/ai/gpu/index.vue';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
@@ -328,6 +350,7 @@ import i18n from '@/lang';
|
||||
import { MsgWarning } from '@/utils/message';
|
||||
import DeviceDetails, { type DetailSection } from './components/DeviceDetails.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const loading = ref();
|
||||
const processDrawerVisible = ref(false);
|
||||
const processDeviceKey = ref('');
|
||||
@@ -574,10 +597,14 @@ const deviceDetailSections = (item: AcceleratorDevice): DetailSection[] => {
|
||||
},
|
||||
{
|
||||
id: 'memory',
|
||||
title: t('aiTools.gpu.memory'),
|
||||
title: t(item.sharedMemory ? 'aiTools.gpu.systemMemory' : 'aiTools.gpu.memory'),
|
||||
items: [
|
||||
{ label: t('aiTools.gpu.memoryActivity'), value: item.memoryActivity },
|
||||
{ label: t('aiTools.gpu.freeMemory'), value: item.memoryFree },
|
||||
{
|
||||
label: t(item.sharedMemory ? 'aiTools.gpu.systemMemoryAvailable' : 'aiTools.gpu.freeMemory'),
|
||||
value: item.memoryFree,
|
||||
help: item.sharedMemory ? t('aiTools.gpu.systemMemoryHelper') : undefined,
|
||||
},
|
||||
{ label: t('aiTools.gpu.memoryReserved'), value: item.memoryReserved },
|
||||
],
|
||||
},
|
||||
@@ -733,6 +760,26 @@ const openXPUProcesses = (deviceID: number) => {
|
||||
processDrawerVisible.value = true;
|
||||
};
|
||||
|
||||
const stableDeviceID = (vendor: string, identifier: string | undefined, fallback: string) => {
|
||||
return `${vendor}:${identifier && identifier.toUpperCase() !== 'N/A' ? identifier : fallback}`;
|
||||
};
|
||||
|
||||
const openHistory = (item: AcceleratorDevice | AI.XpuInfo['xpu'][number]) => {
|
||||
let deviceID: string;
|
||||
if ('basic' in item) {
|
||||
deviceID = stableDeviceID(
|
||||
'xpu',
|
||||
item.basic.uuid,
|
||||
stableDeviceID('pci', item.basic.pciBdfAddress, String(item.basic.deviceID)),
|
||||
);
|
||||
} else if (item.type === 'ascend') {
|
||||
deviceID = `${stableDeviceID('ascend', item.busID, String(item.npuIndex))}:${item.chipIndex}`;
|
||||
} else {
|
||||
deviceID = stableDeviceID(item.type, item.uuid, stableDeviceID('pci', item.busID, String(item.index)));
|
||||
}
|
||||
void router.push({ path: '/ai/gpu/history', query: { deviceID } });
|
||||
};
|
||||
|
||||
const deviceTitle = (item: AcceleratorDevice) => {
|
||||
return item.type === 'ascend' ? `Chip ${item.chipIndex}` : `GPU ${item.index}`;
|
||||
};
|
||||
|
||||
@@ -4,45 +4,40 @@
|
||||
<template v-if="supported">
|
||||
<el-card class="history-toolbar">
|
||||
<div class="flex flex-wrap gap-3 items-center">
|
||||
<el-date-picker
|
||||
v-model="timeRangeGlobal"
|
||||
type="datetimerange"
|
||||
style="max-width: 100%; width: 360px; flex-grow: 0"
|
||||
range-separator="-"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
:clearable="false"
|
||||
:size="isMobile ? 'small' : 'default'"
|
||||
@change="changeTimeRange"
|
||||
/>
|
||||
<el-select style="max-width: 100%" class="p-w-300" v-model="selectedDevice" @change="search">
|
||||
<el-select style="max-width: 100%" class="p-w-400" v-model="selectedDevice" @change="search">
|
||||
<template #prefix>{{ $t('aiTools.gpu.deviceInfo') }}</template>
|
||||
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-radio-group v-if="history && history.bucketSeconds > 0" v-model="aggregation" @change="search">
|
||||
<el-radio-button value="avg">{{ $t('aiTools.gpu.historyAverage') }}</el-radio-button>
|
||||
<el-radio-button value="max">{{ $t('aiTools.gpu.historyPeak') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
<TableRefresh @search="refresh" />
|
||||
<div class="ml-auto flex flex-wrap gap-3 items-center">
|
||||
<el-date-picker
|
||||
v-model="timeRangeGlobal"
|
||||
type="datetimerange"
|
||||
class="history-time-range"
|
||||
style="max-width: 100%; width: 400px; flex-grow: 0"
|
||||
:prefix-icon="timeRangePrefix"
|
||||
range-separator="-"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
:clearable="false"
|
||||
@change="changeTimeRange"
|
||||
/>
|
||||
<TableRefresh @search="refresh" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="history?.sampleCount" class="history-summary">
|
||||
<div v-if="history?.sampleCount && history.bucketSeconds > 0" class="history-summary">
|
||||
{{
|
||||
history.bucketSeconds > 0
|
||||
? $t('aiTools.gpu.historyAggregated', {
|
||||
seconds: history.bucketSeconds,
|
||||
count: history.sampleCount,
|
||||
})
|
||||
: $t('aiTools.gpu.historyRaw', { count: history.sampleCount })
|
||||
$t('aiTools.gpu.historyAggregated', {
|
||||
seconds: history.bucketSeconds,
|
||||
count: history.sampleCount,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</el-card>
|
||||
<el-alert
|
||||
v-if="currentDevice?.legacy"
|
||||
:title="$t('aiTools.gpu.legacyHistory')"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-alert
|
||||
v-if="failed"
|
||||
:title="$t('aiTools.gpu.historyLoadFailed')"
|
||||
@@ -58,7 +53,15 @@
|
||||
<el-row v-else-if="history?.sampleCount" :gutter="12">
|
||||
<el-col v-for="chart in charts" :key="chart.id" :xs="24" :md="12">
|
||||
<el-card class="card-interval">
|
||||
<template #header>{{ chart.title }}</template>
|
||||
<template #header>
|
||||
{{ chart.title }}
|
||||
<el-tooltip
|
||||
v-if="chart.id === 'gpu-memory' && currentDevice?.sharedMemory"
|
||||
:content="$t('aiTools.gpu.systemMemoryHelper')"
|
||||
>
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<v-charts :id="chart.id" height="320px" type="line" :option="chart.option" :dataZoom="true" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
@@ -90,7 +93,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { ref, computed, watch, onBeforeUnmount, h } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { InfoFilled } from '@element-plus/icons-vue';
|
||||
import { loadGPUMonitor, getGPUOptions } from '@/api/modules/ai';
|
||||
import RouterMenu from '@/views/ai/gpu/index.vue';
|
||||
import { shortcuts } from '@/utils/shortcuts';
|
||||
@@ -98,6 +103,7 @@ import i18n from '@/lang';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
|
||||
const route = useRoute();
|
||||
const { isMobile } = useGlobalStore();
|
||||
const loading = ref(false);
|
||||
const failed = ref(false);
|
||||
@@ -108,6 +114,7 @@ const aggregation = ref<'avg' | 'max'>('avg');
|
||||
const history = ref<AI.MonitorGPUData>();
|
||||
const currentDevice = computed(() => options.value.find((item) => item.value === selectedDevice.value));
|
||||
const timeRangeGlobal = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const timeRangePrefix = () => h('span', i18n.global.t('commons.table.date'));
|
||||
let followNow = true;
|
||||
let requestID = 0;
|
||||
|
||||
@@ -124,10 +131,18 @@ const charts = computed(() => {
|
||||
},
|
||||
{
|
||||
id: 'gpu-memory',
|
||||
title: 'memory',
|
||||
title: currentDevice.value?.sharedMemory ? 'systemMemory' : 'memory',
|
||||
series: [
|
||||
{ key: 'memoryUsed', label: 'memoryUsed', unit: 'MiB' },
|
||||
{ key: 'memoryTotal', label: 'memoryTotal', unit: 'MiB' },
|
||||
{
|
||||
key: 'memoryUsed',
|
||||
label: currentDevice.value?.sharedMemory ? 'systemMemoryUsed' : 'memoryUsed',
|
||||
unit: 'MiB',
|
||||
},
|
||||
{
|
||||
key: 'memoryTotal',
|
||||
label: currentDevice.value?.sharedMemory ? 'systemMemoryTotal' : 'memoryTotal',
|
||||
unit: 'MiB',
|
||||
},
|
||||
{ key: 'memoryPercent', label: 'percent', unit: '%', percent: true },
|
||||
],
|
||||
},
|
||||
@@ -338,12 +353,16 @@ const loadOptions = async () => {
|
||||
const response = await getGPUOptions();
|
||||
if (id !== requestID) return;
|
||||
supported.value = response.data.supported;
|
||||
options.value = (supported.value ? response.data.chartHide || [] : []).map((item) => ({
|
||||
...item,
|
||||
value: item.deviceID || `legacy:${item.productName}`,
|
||||
label: `${item.productName} · ${item.deviceID || i18n.global.t('aiTools.gpu.legacyDevice')}`,
|
||||
}));
|
||||
if (!currentDevice.value) selectedDevice.value = options.value[0]?.value || '';
|
||||
options.value = (supported.value ? response.data.chartHide || [] : [])
|
||||
.filter((item) => item.deviceID && !item.legacy)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
value: item.deviceID!,
|
||||
label: item.productName
|
||||
.replace(/^(\d+) - /, `${item.type === 'xpu' ? 'XPU' : 'GPU'} $1 · `)
|
||||
.replace(' - ', ' · '),
|
||||
}));
|
||||
if (!currentDevice.value && !route.query.deviceID) selectedDevice.value = options.value[0]?.value || '';
|
||||
await search();
|
||||
} catch {
|
||||
if (id === requestID) failed.value = true;
|
||||
@@ -401,7 +420,15 @@ const loadProcessType = (val: string) => {
|
||||
return val;
|
||||
};
|
||||
|
||||
onMounted(loadOptions);
|
||||
watch(
|
||||
() => [route.path, route.query.deviceID],
|
||||
() => {
|
||||
if (route.path !== '/ai/gpu/history') return;
|
||||
if (typeof route.query.deviceID === 'string') selectedDevice.value = route.query.deviceID;
|
||||
void loadOptions();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
onBeforeUnmount(() => {
|
||||
requestID++;
|
||||
});
|
||||
@@ -412,6 +439,14 @@ onBeforeUnmount(() => {
|
||||
margin: 7px 0 12px;
|
||||
--el-card-padding: 12px;
|
||||
}
|
||||
.history-toolbar :deep(.history-time-range .el-range__icon) {
|
||||
width: auto;
|
||||
flex-shrink: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 6px;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.history-summary {
|
||||
margin-top: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
? { highlightDiff: 210 }
|
||||
: view === TaskLog
|
||||
? { width: '70%' }
|
||||
: actionName === 'config'
|
||||
: ['config', 'monitor'].includes(actionName)
|
||||
? {}
|
||||
: { dialog: true }
|
||||
"
|
||||
|
||||
@@ -357,8 +357,8 @@ const isManagement = computed(
|
||||
() => isCurrentPage.value && isLocal && ['vllm', 'tensorrt'].includes(String(route.query.tab)),
|
||||
);
|
||||
const tabs = computed(() => [
|
||||
{ label: i18n.global.t('aiTools.model.services'), path: '/ai/model/account' },
|
||||
{ label: i18n.global.t('aiTools.model.files'), path: '/ai/model/files' },
|
||||
{ label: i18n.global.t('aiTools.model.services'), path: '/ai/model/account' },
|
||||
{ label: i18n.global.t('aiTools.model.engines'), path: '/ai/model/local' },
|
||||
]);
|
||||
const downloaderModules = import.meta.glob('/src/xpack/views/model-downloader/index.vue');
|
||||
|
||||
@@ -55,10 +55,16 @@
|
||||
<el-form-item :label="$t('aiTools.tensorRT.modelDir')" prop="modelDir">
|
||||
<el-input v-model="tensorRTLLM.modelDir">
|
||||
<template #prepend>
|
||||
<el-button icon="Folder" @click="modelDirRef.acceptParams({ dir: true })" />
|
||||
<el-button icon="Folder" @click="selectModelDir" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</details>
|
||||
<GPUConfig ref="gpuRef" v-model="tensorRTLLM.gpu" />
|
||||
<details open class="form-section">
|
||||
<summary class="form-section-header">{{ $t('runtime.runScript') }}</summary>
|
||||
<div class="form-section-body">
|
||||
<el-form-item :label="$t('runtime.runScript')" prop="command">
|
||||
<el-input v-model="tensorRTLLM.command"></el-input>
|
||||
<span class="input-help">
|
||||
@@ -67,7 +73,6 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
</details>
|
||||
<GPUConfig ref="gpuRef" v-model="tensorRTLLM.gpu" />
|
||||
<details open class="form-section">
|
||||
<summary class="form-section-header">{{ $t('app.advanced') }}</summary>
|
||||
<div class="form-section-body">
|
||||
@@ -103,7 +108,7 @@ import { ElForm, FormInstance } from 'element-plus';
|
||||
import { createTensorRTLLM, updateTensorRTLLM } from '@/api/modules/ai';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
const { isFxplay, isMobile } = useGlobalStore();
|
||||
const { isFxplay, isMobile, isProductPro } = useGlobalStore();
|
||||
|
||||
const loading = ref(false);
|
||||
const gpuRef = ref<InstanceType<typeof GPUConfig>>();
|
||||
@@ -189,6 +194,24 @@ const handleClose = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const downloaderModules = import.meta.glob<{
|
||||
getModelDownloaderSettings: () => Promise<{ data: { modelDir: string } }>;
|
||||
}>('/src/xpack/api/modules/model-downloader.ts');
|
||||
const selectModelDir = async () => {
|
||||
let path = tensorRTLLM.value.modelDir;
|
||||
const loadDownloader = Object.values(downloaderModules)[0];
|
||||
if (isProductPro.value && loadDownloader) {
|
||||
try {
|
||||
const { getModelDownloaderSettings } = await loadDownloader();
|
||||
const { data } = await getModelDownloaderSettings();
|
||||
path = data.modelDir;
|
||||
} catch {
|
||||
path = '/';
|
||||
}
|
||||
}
|
||||
modelDirRef.value.acceptParams({ dir: true, path });
|
||||
};
|
||||
|
||||
const getModelDir = (path: string) => {
|
||||
tensorRTLLM.value.modelDir = path;
|
||||
};
|
||||
|
||||
@@ -18,11 +18,18 @@
|
||||
<span v-for="model in modelGroups" :key="model.name">{{ model.name }} × {{ model.count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-memory">
|
||||
<div v-if="dedicatedMemoryDevices.length" class="summary-memory">
|
||||
<span class="metric-label">{{ $t('aiTools.gpu.memoryTotal') }}</span>
|
||||
<strong>{{ formatMemory(totalMemory) }}</strong>
|
||||
<span class="metric-label">{{ $t('home.used') }} {{ formatMemory(usedMemory) }}</span>
|
||||
</div>
|
||||
<div v-if="sharedMemoryDevice" class="summary-memory" :title="$t('aiTools.gpu.systemMemoryHelper')">
|
||||
<span class="metric-label">{{ $t('aiTools.gpu.systemMemory') }}</span>
|
||||
<strong>{{ formatMemory(sharedMemoryDevice.memoryTotal) }}</strong>
|
||||
<span class="metric-label">
|
||||
{{ $t('home.used') }} {{ formatMemory(sharedMemoryDevice.memoryUsed) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gpu-grid">
|
||||
@@ -59,7 +66,12 @@
|
||||
</div>
|
||||
<div class="device-metric">
|
||||
<div class="metric-heading">
|
||||
<span class="metric-label">{{ $t('aiTools.gpu.memoryUsed') }}</span>
|
||||
<span
|
||||
class="metric-label"
|
||||
:title="device.sharedMemory ? $t('aiTools.gpu.systemMemoryHelper') : undefined"
|
||||
>
|
||||
{{ $t(device.sharedMemory ? 'aiTools.gpu.systemMemory' : 'aiTools.gpu.memoryUsed') }}
|
||||
</span>
|
||||
<strong>
|
||||
{{ formatMemory(device.memoryUsed) }}
|
||||
<span class="metric-total">/ {{ formatMemory(device.memoryTotal) }}</span>
|
||||
@@ -139,6 +151,7 @@ const devices = computed(() =>
|
||||
...(props.current.gpuData || []).map((item) => ({
|
||||
key: `gpu-${item.type}-${item.busID || item.index}`,
|
||||
type: 'GPU',
|
||||
sharedMemory: !!item.sharedMemory,
|
||||
card: `${item.type}-${item.busID || item.index}`,
|
||||
id: `GPU ${item.index}`,
|
||||
name: item.productName || 'GPU',
|
||||
@@ -157,6 +170,7 @@ const devices = computed(() =>
|
||||
...(props.current.npuData || []).map((item) => ({
|
||||
key: `npu-${item.npuIndex}-${item.chipIndex}`,
|
||||
type: 'NPU',
|
||||
sharedMemory: false,
|
||||
card: item.npuIndex,
|
||||
id: `NPU ${item.npuIndex} · Chip ${item.chipIndex}`,
|
||||
name: item.productName || 'NPU',
|
||||
@@ -176,6 +190,7 @@ const devices = computed(() =>
|
||||
...(props.current.xpuData || []).map((item) => ({
|
||||
key: `xpu-${item.deviceID}`,
|
||||
type: 'XPU',
|
||||
sharedMemory: false,
|
||||
card: item.deviceID,
|
||||
id: `XPU ${item.deviceID}`,
|
||||
name: item.deviceName || 'XPU',
|
||||
@@ -222,15 +237,17 @@ const modelGroups = computed(() => {
|
||||
}
|
||||
return Array.from(models, ([name, cards]) => ({ name, count: cards.size }));
|
||||
});
|
||||
const sharedMemoryDevice = computed(() => devices.value.find((item) => item.sharedMemory));
|
||||
const dedicatedMemoryDevices = computed(() => devices.value.filter((item) => !item.sharedMemory));
|
||||
const totalMemory = computed(() =>
|
||||
devices.value.some((item) => item.memoryTotal === null)
|
||||
dedicatedMemoryDevices.value.some((item) => item.memoryTotal === null)
|
||||
? null
|
||||
: devices.value.reduce((sum, item) => sum + item.memoryTotal!, 0),
|
||||
: dedicatedMemoryDevices.value.reduce((sum, item) => sum + item.memoryTotal!, 0),
|
||||
);
|
||||
const usedMemory = computed(() =>
|
||||
devices.value.some((item) => item.memoryUsed === null)
|
||||
dedicatedMemoryDevices.value.some((item) => item.memoryUsed === null)
|
||||
? null
|
||||
: devices.value.reduce((sum, item) => sum + item.memoryUsed!, 0),
|
||||
: dedicatedMemoryDevices.value.reduce((sum, item) => sum + item.memoryUsed!, 0),
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user