diff --git a/agent/app/dto/agents.go b/agent/app/dto/agents.go index 28fda50b1..5cac7ece8 100644 --- a/agent/app/dto/agents.go +++ b/agent/app/dto/agents.go @@ -347,17 +347,18 @@ type AgentAccountCreateReq struct { } type AgentAccountUpdateReq struct { - ID uint `json:"id" validate:"required"` - Name string `json:"name" validate:"required"` - APIKey string `json:"apiKey" validate:"required"` - RememberAPIKey bool `json:"rememberApiKey"` - BaseURL string `json:"baseURL"` - APIType string `json:"apiType" validate:"required"` - AuthMode string `json:"authMode"` - VerifyModel string `json:"verifyModel"` - ValidateAvailability *bool `json:"validateAvailability"` - Remark string `json:"remark"` - SyncAgents bool `json:"syncAgents"` + ID uint `json:"id" validate:"required"` + Name string `json:"name" validate:"required"` + APIKey string `json:"apiKey" validate:"required"` + RememberAPIKey bool `json:"rememberApiKey"` + BaseURL string `json:"baseURL"` + APIType string `json:"apiType" validate:"required"` + AuthMode string `json:"authMode"` + VerifyModel string `json:"verifyModel"` + ValidateAvailability *bool `json:"validateAvailability"` + Remark string `json:"remark"` + SyncAgents bool `json:"syncAgents"` + Models []AgentAccountModel `json:"models"` } type AgentAccountVerifyReq struct { diff --git a/agent/app/repo/agent_account.go b/agent/app/repo/agent_account.go index a5cc1b50c..08fcedae9 100644 --- a/agent/app/repo/agent_account.go +++ b/agent/app/repo/agent_account.go @@ -20,7 +20,7 @@ type IAgentAccountRepo interface { List(opts ...DBOption) ([]model.AgentAccount, error) Count() (int64, error) CountTextByProviders(providers []string) (map[string]int64, error) - SaveLocal(account *model.AgentAccount, models []model.AgentAccountModel) error + SaveWithModels(account *model.AgentAccount, models []model.AgentAccountModel) error } func NewIAgentAccountRepo() IAgentAccountRepo { @@ -106,7 +106,7 @@ func (a AgentAccountRepo) CountTextByProviders(providers []string) (map[string]i return counts, nil } -func (a AgentAccountRepo) SaveLocal(account *model.AgentAccount, models []model.AgentAccountModel) error { +func (a AgentAccountRepo) SaveWithModels(account *model.AgentAccount, models []model.AgentAccountModel) error { return global.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Save(account).Error; err != nil { return err diff --git a/agent/app/service/agents.go b/agent/app/service/agents.go index ca2d36fba..9796114d7 100644 --- a/agent/app/service/agents.go +++ b/agent/app/service/agents.go @@ -1105,9 +1105,23 @@ func (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error { if err != nil { return err } + if req.Models != nil { + if account.AppInstallID != 0 { + return buserr.WithDetail("ErrInvalidParams", "local model pool is managed by the deployment", nil) + } + models, err = normalizeAgentAccountModels(account, req.Models) + if err != nil { + return err + } + if err := ensureAccountModelsNotBound(account, models); err != nil { + return err + } + } requestedVerifyModel := req.VerifyModel if strings.TrimSpace(requestedVerifyModel) == "" { - requestedVerifyModel = account.VerifyModel + if _, ok := findAgentAccountModelForProvider(provider, models, account.VerifyModel); ok { + requestedVerifyModel = account.VerifyModel + } } verifyModel := "" if account.AppInstallID == 0 || len(models) > 0 { @@ -1131,7 +1145,16 @@ func (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error { account.Remark = req.Remark account.Verified = true - if err := agentAccountRepo.Save(account); err != nil { + if req.Models != nil { + rows := make([]model.AgentAccountModel, 0, len(models)) + for _, item := range models { + rows = append(rows, model.AgentAccountModel{Model: item.ID, Name: item.Name}) + } + err = agentAccountRepo.SaveWithModels(account, rows) + } else { + err = agentAccountRepo.Save(account) + } + if err != nil { return err } terminalai.InvalidateTerminalRuntimeCache() diff --git a/agent/app/service/agents_local.go b/agent/app/service/agents_local.go index f05d5d240..885acd0c4 100644 --- a/agent/app/service/agents_local.go +++ b/agent/app/service/agents_local.go @@ -88,7 +88,7 @@ func SyncLocalModelAccount(install model.AppInstall, accountID uint, baseURL str } } if modelsChanged { - err = agentAccountRepo.SaveLocal(account, rows) + err = agentAccountRepo.SaveWithModels(account, rows) } else if accountChanged { err = agentAccountRepo.Save(account) } else { diff --git a/agent/app/service/model_gpu.go b/agent/app/service/model_gpu.go index dbbdba4f2..bd0cc9d85 100644 --- a/agent/app/service/model_gpu.go +++ b/agent/app/service/model_gpu.go @@ -1,6 +1,7 @@ package service import ( + "context" "fmt" "os" "path/filepath" @@ -9,6 +10,7 @@ import ( "strings" "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/utils/ai_tools/accelerator" "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/compose-spec/compose-go/v2/interpolation" "github.com/compose-spec/compose-go/v2/template" @@ -16,76 +18,72 @@ import ( func LoadModelGPUDevices() dto.ModelGPUDevices { result := dto.ModelGPUDevices{Devices: []dto.ModelGPUDevice{}, Warnings: []string{}} - uuidPattern := re.GetRegex(re.NvidiaGPUUUIDPattern) - renderPattern := re.GetRegex(re.IntelGPURenderNamePattern) - ascendPattern := re.GetRegex(re.AscendGPUDeviceNamePattern) - for _, directory := range []string{"/proc/driver/nvidia/gpus", "/sys/class/drm", "/dev"} { - entries, err := os.ReadDir(directory) - if err != nil { - if !os.IsNotExist(err) { - result.Warnings = append(result.Warnings, err.Error()) - } + ok, client := accelerator.New() + if !ok { + return result + } + snapshot, err := client.Collect(context.Background()) + if err != nil { + result.Warnings = append(result.Warnings, err.Error()) + return result + } + result.Warnings = append(result.Warnings, snapshot.Info.Warnings...) + for _, monitored := range snapshot.Devices { + if monitored.ParentID != "" { continue } - for _, entry := range entries { - var device dto.ModelGPUDevice - var devicePath string - switch directory { - case "/proc/driver/nvidia/gpus": - informationPath := filepath.Join(directory, entry.Name(), "information") - content, err := os.ReadFile(informationPath) + device := dto.ModelGPUDevice{Available: true} + devicePath := "" + switch { + case monitored.GPU != nil: + if monitored.Vendor == "nvidia" && !re.GetRegex(re.NvidiaGPUUUIDPattern).MatchString(monitored.GPU.UUID) { + result.Warnings = append(result.Warnings, fmt.Sprintf("invalid NVIDIA GPU UUID from nvidia-smi: %s", monitored.GPU.UUID)) + continue + } + device.ID = monitored.ID + device.Name = fmt.Sprintf("GPU %d · %s", monitored.Index, monitored.Name) + device.Type = monitored.Vendor + device.UUID = monitored.GPU.UUID + case monitored.XPU != nil: + devicePath = monitored.XPU.Basic.DrmDevice + if !re.GetRegex(re.IntelGPUDevicePathPattern).MatchString(devicePath) { + directory := filepath.Join("/sys/bus/pci/devices", filepath.Base(monitored.BusID), "drm") + entries, err := os.ReadDir(directory) if err != nil { - result.Warnings = append(result.Warnings, err.Error()) + result.Warnings = append(result.Warnings, fmt.Sprintf("resolve Intel GPU %d render device: %v", monitored.Index, err)) continue } - fields := map[string]string{} - for _, line := range strings.Split(string(content), "\n") { - key, value, found := strings.Cut(line, ":") - if found { - fields[strings.TrimSpace(key)] = strings.TrimSpace(value) + devicePath = "" + for _, entry := range entries { + if re.GetRegex(re.IntelGPURenderNamePattern).MatchString(entry.Name()) { + devicePath = filepath.Join("/dev/dri", entry.Name()) + break } } - uuid := fields["GPU UUID"] - minor, err := strconv.Atoi(fields["Device Minor"]) - if !uuidPattern.MatchString(uuid) || err != nil || minor < 0 { - result.Warnings = append(result.Warnings, fmt.Sprintf("invalid NVIDIA GPU UUID or device minor in %s", informationPath)) + if devicePath == "" { + result.Warnings = append(result.Warnings, fmt.Sprintf("Intel GPU %d has no render device", monitored.Index)) continue } - name := fields["Model"] - if name == "" { - name = "NVIDIA" - } - device = dto.ModelGPUDevice{ID: "nvidia:" + uuid, Name: name + " · " + entry.Name(), Type: "nvidia", UUID: uuid} - devicePath = filepath.Join("/dev", "nvidia"+strconv.Itoa(minor)) - case "/sys/class/drm": - if !renderPattern.MatchString(entry.Name()) { - continue - } - vendor, err := os.ReadFile(filepath.Join(directory, entry.Name(), "device/vendor")) - if err != nil { - result.Warnings = append(result.Warnings, err.Error()) - continue - } - if strings.TrimSpace(string(vendor)) != "0x8086" { - continue - } - devicePath = filepath.Join("/dev/dri", entry.Name()) - device = dto.ModelGPUDevice{ID: "intel:" + devicePath, Name: "Intel · " + entry.Name(), Type: "intel"} - case "/dev": - if !ascendPattern.MatchString(entry.Name()) { - continue - } - devicePath = filepath.Join(directory, entry.Name()) - device = dto.ModelGPUDevice{ID: "ascend:" + strings.TrimPrefix(entry.Name(), "davinci"), Name: "Ascend · " + entry.Name(), Type: "ascend"} } + device.ID = "intel:" + devicePath + device.Name = fmt.Sprintf("XPU %d · %s", monitored.Index, monitored.Name) + device.Type = "intel" + case monitored.NPU != nil: + device.ID = fmt.Sprintf("ascend:%d", monitored.Index) + device.Name = fmt.Sprintf("NPU %d · Chip %d · %s", monitored.NPUIndex, monitored.ChipIndex, monitored.Name) + device.Type = "ascend" + devicePath = fmt.Sprintf("/dev/davinci%d", monitored.Index) + default: + continue + } + if devicePath != "" { info, err := os.Stat(devicePath) + device.Available = err == nil && info.Mode()&os.ModeCharDevice != 0 if err != nil { result.Warnings = append(result.Warnings, err.Error()) - } else { - device.Available = info.Mode()&os.ModeCharDevice != 0 } - result.Devices = append(result.Devices, device) } + result.Devices = append(result.Devices, device) } return result } @@ -649,9 +647,17 @@ func resolveModelGPUValue(value string, params map[string]interface{}) (string, func loadModelAscendDeviceIDs() []string { ids := []string{} - for _, device := range LoadModelGPUDevices().Devices { - if device.Type == "ascend" && device.Available { - ids = append(ids, strings.TrimPrefix(device.ID, "ascend:")) + entries, err := os.ReadDir("/dev") + if err != nil { + return ids + } + for _, entry := range entries { + if !re.GetRegex(re.AscendGPUDeviceNamePattern).MatchString(entry.Name()) { + continue + } + info, err := os.Stat(filepath.Join("/dev", entry.Name())) + if err == nil && info.Mode()&os.ModeCharDevice != 0 { + ids = append(ids, strings.TrimPrefix(entry.Name(), "davinci")) } } slices.SortFunc(ids, func(a, b string) int { diff --git a/agent/utils/ai_tools/xpu/types.go b/agent/utils/ai_tools/xpu/types.go index 94f50369e..3c272ebb7 100644 --- a/agent/utils/ai_tools/xpu/types.go +++ b/agent/utils/ai_tools/xpu/types.go @@ -72,7 +72,8 @@ type Device struct { } type Basic struct { - UUID string `json:"uuid"` + DrmDevice string `json:"-"` + UUID string `json:"uuid"` DeviceID int `json:"deviceID"` DeviceName string `json:"deviceName"` diff --git a/agent/utils/ai_tools/xpu/xpu.go b/agent/utils/ai_tools/xpu/xpu.go index b45c10d6e..e8bb1bd75 100644 --- a/agent/utils/ai_tools/xpu/xpu.go +++ b/agent/utils/ai_tools/xpu/xpu.go @@ -93,6 +93,7 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg * xpu := Device{ ProcessStatus: "unavailable", Basic: Basic{ + DrmDevice: device.DrmDevice, UUID: device.UUID, DeviceID: device.DeviceID, DeviceName: device.DeviceName, @@ -137,6 +138,9 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg * return } + if info.DrmDevice != "" { + xpu.Basic.DrmDevice = info.DrmDevice + } xpu.Basic.DriverVersion = info.DriverVersion bytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64) diff --git a/core/constant/common.go b/core/constant/common.go index 0f45ef815..d40066cd1 100644 --- a/core/constant/common.go +++ b/core/constant/common.go @@ -71,6 +71,7 @@ var WebUrlMap = map[string]struct{}{ "/ai/skills-hub": {}, "/ai/skills-hub/targets": {}, "/ai/benchmark": {}, + "/ai/model": {}, "/ai/model/account": {}, "/ai/model/local": {}, "/ai/model/local/templates": {}, diff --git a/frontend/src/api/interface/ai.ts b/frontend/src/api/interface/ai.ts index 1e76c0468..5100e601f 100644 --- a/frontend/src/api/interface/ai.ts +++ b/frontend/src/api/interface/ai.ts @@ -737,6 +737,7 @@ export namespace AI { baseURL: string; apiType: string; authMode: string; + models?: AgentAccountModel[]; verifyModel: string; validateAvailability: boolean; remark: string; diff --git a/frontend/src/components/router-button/index.vue b/frontend/src/components/router-button/index.vue index fe1b8de44..e44d0e15d 100644 --- a/frontend/src/components/router-button/index.vue +++ b/frontend/src/components/router-button/index.vue @@ -92,12 +92,15 @@ const expiresInfo = ref(0); const expiresAlertVisible = ref(false); const expiresAlertKey = computed(() => (isEnterprise.value ? 'xpack.expiresEnterpriseAlert' : 'xpack.expiresProAlert')); -const handleChange = (label: string) => { +const handleChange = async (label: string) => { const btn = buttonArray.value.find((btn) => btn.label === label); if (!btn) return; - if (btn.path) routerToPathWithQuery(btn.path, { uncached: 'true' }); - else if (btn.name) routerToNameWithQuery(btn.name, { uncached: 'true' }); - activeName.value = btn.label; + try { + if (btn.path) await routerToPathWithQuery(btn.path, { uncached: 'true' }); + else if (btn.name) await routerToNameWithQuery(btn.name, { uncached: 'true' }); + } finally { + syncActiveName(); + } }; onMounted(() => { @@ -154,7 +157,9 @@ function syncActiveName() { if (buttonArray.value.length) { let isPathExist = false; const btn = buttonArray.value.find((btn) => { - return btn.path && router.currentRoute.value.path.startsWith(btn.path); + return btn.path + ? router.currentRoute.value.path.startsWith(btn.path) + : btn.name && router.currentRoute.value.name === btn.name; }); if (btn) { isPathExist = true; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 73d3daccf..ba78691a0 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1092,14 +1092,19 @@ const message = { container_conn_helper: 'Use this address for inter-container access or connection', from_remote: 'This model was not downloaded via 1Panel, no related pull logs.', no_logs: 'The pull logs for this model have been deleted and cannot be viewed.', - vllmVersionHelper: 'For FusionXpark GB10 servers, please select the -cu130 version.', + vllmVersionHelper: { + default: 'Official standard version with CUDA 13.0, for newer drivers and newer GPU architectures.', + cuda: 'Legacy architecture compatibility version with CUDA 12.9, for older GPU architectures using CUDA versions below 13.0.', + ubuntu: 'NVIDIA GB10 standard version with CUDA 13.0, for NVIDIA GB10 devices.', + dspark: 'NVIDIA GB10 dual-device custom version, using a custom image maintained by 1Panel.', + }, vllmDeployProfile: 'Deployment model', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'The startup command must use port {0}; otherwise, the service will be inaccessible.', ascendVisibleDevices: 'Ascend visible devices (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'Sync to model account', + syncModelAccount: 'Sync to model service', modelAccountAddressHelper: 'Container address is for agent containers; 127.0.0.1, system IP, or custom address is for AI Gateway or external services.', containerAddress: 'Container address', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 1cd09fda2..2b41b80ee 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -1094,14 +1094,20 @@ const message = { container_conn_helper: 'Utilice esta dirección para el acceso o conexión entre contenedores', from_remote: 'Este modelo no fue descargado vía 1Panel, no hay registros de descarga relacionados.', no_logs: 'Los registros de descarga de este modelo han sido eliminados y no se pueden consultar.', - vllmVersionHelper: 'Para servidores FusionXpark GB10, seleccione la versión -cu130.', + vllmVersionHelper: { + default: + 'Versión oficial estándar con CUDA 13.0, para controladores recientes y GPU de nuevas arquitecturas.', + cuda: 'Versión compatible con arquitecturas antiguas con CUDA 12.9, para GPU antiguas que utilizan versiones de CUDA anteriores a 13.0.', + ubuntu: 'Versión estándar de NVIDIA GB10 con CUDA 13.0, para dispositivos NVIDIA GB10.', + dspark: 'Versión personalizada de NVIDIA GB10 para dos dispositivos, con una imagen mantenida por 1Panel.', + }, vllmDeployProfile: 'Modelo de despliegue', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'El comando de inicio debe usar el puerto {0}; de lo contrario, no se podrá acceder al servicio.', ascendVisibleDevices: 'Dispositivos Ascend visibles (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'Sincronizar con cuenta de modelo', + syncModelAccount: 'Sincronizar con servicio de modelo', modelAccountAddressHelper: 'La dirección del contenedor es para contenedores de agentes; 127.0.0.1, IP del sistema o dirección personalizada es para AI Gateway o servicios externos.', containerAddress: 'Dirección del contenedor', diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts index f76aa9820..b86d16aa5 100644 --- a/frontend/src/lang/modules/fa.ts +++ b/frontend/src/lang/modules/fa.ts @@ -1076,14 +1076,19 @@ const message = { container_conn_helper: 'از این آدرس برای دسترسی بین کانتینرها یا اتصال استفاده کنید', from_remote: 'این مدل از طریق 1Panel دانلود نشده است، لاگ مربوط به دریافت وجود ندارد.', no_logs: 'لاگ دریافت این مدل حذف شده است و قابل مشاهده نیست.', - vllmVersionHelper: 'برای سرورهای FusionXpark GB10، لطفاً نسخه -cu130 را انتخاب کنید.', + vllmVersionHelper: { + default: 'نسخه استاندارد رسمی با CUDA 13.0، مناسب درایورهای جدید و معماریهای جدید GPU.', + cuda: 'نسخه سازگار با معماریهای قدیمی با CUDA 12.9، مناسب GPUهای قدیمی که از نسخههای CUDA پایینتر از 13.0 استفاده میکنند.', + ubuntu: 'نسخه استاندارد NVIDIA GB10 با CUDA 13.0، مناسب دستگاههای NVIDIA GB10.', + dspark: 'نسخه سفارشی NVIDIA GB10 برای دو دستگاه، با ایمیج سفارشی که توسط 1Panel نگهداری میشود.', + }, vllmDeployProfile: 'مدل استقرار', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'فرمان راهاندازی باید از پورت {0} استفاده کند؛ در غیر این صورت سرویس قابل دسترسی نخواهد بود.', ascendVisibleDevices: 'دستگاههای قابل مشاهده Ascend (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'همگامسازی با حساب مدل', + syncModelAccount: 'همگامسازی با سرویس مدل', modelAccountAddressHelper: 'آدرس کانتینر برای کانتینرهای عامل است؛ 127.0.0.1، IP سیستم یا آدرس سفارشی برای دروازه AI یا خدمات خارجی است.', containerAddress: 'آدرس کانتینر', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 5c8a8060e..e6c505574 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -1080,14 +1080,20 @@ const message = { container_conn_helper: 'コンテナ間のアクセスまたは接続にこのアドレスを使用', from_remote: 'このモデルは1Panelを介してダウンロードされておらず、関連するプルログはありません。', no_logs: 'このモデルのプルログは削除されており、関連するログを表示できません。', - vllmVersionHelper: 'FusionXpark GB10 サーバーでは -cu130 バージョンを選択してください。', + vllmVersionHelper: { + default: + '公式の標準バージョン。CUDA 13.0 を使用し、新しいドライバーと新しい GPU アーキテクチャに対応します。', + cuda: '旧アーキテクチャ向け互換バージョン。CUDA 12.9 を使用し、CUDA 13.0 未満を使用する旧世代 GPU に対応します。', + ubuntu: 'NVIDIA GB10 標準版。CUDA 13.0 を使用し、NVIDIA GB10 デバイスに対応します。', + dspark: 'NVIDIA GB10 の 2 台構成向けカスタム版。1Panel が独自に保守するカスタムイメージです。', + }, vllmDeployProfile: 'デプロイモデル', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: '起動コマンドではポート {0} を使用する必要があります。使用しない場合、サービスにアクセスできません。', ascendVisibleDevices: 'Ascend 可視デバイス(ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'モデルアカウントに同期', + syncModelAccount: 'モデルサービスに同期', modelAccountAddressHelper: 'コンテナアドレスはエージェントコンテナ向けです。127.0.0.1、システム IP、カスタムアドレスは AI Gateway または外部サービス向けです。', containerAddress: 'コンテナアドレス', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 6ba3c8378..7fd7cf28c 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -1070,13 +1070,18 @@ const message = { container_conn_helper: '컨테이너 간 접근 또는 연결에 이 주소를 사용', from_remote: '이 모델은 1Panel을 통해 다운로드되지 않았으며 관련 풀 로그가 없습니다.', no_logs: '이 모델의 풀 로그가 삭제되어 관련 로그를 볼 수 없습니다.', - vllmVersionHelper: 'FusionXpark GB10 서버는 -cu130 버전을 선택하세요.', + vllmVersionHelper: { + default: '공식 표준 버전으로, CUDA 13.0을 사용하며 최신 드라이버와 새로운 GPU 아키텍처에 적합합니다.', + cuda: '이전 아키텍처 호환 버전으로, CUDA 12.9를 사용하며 CUDA 13.0 미만을 사용하는 이전 GPU 아키텍처에 적합합니다.', + ubuntu: 'NVIDIA GB10 표준 버전으로, CUDA 13.0을 사용하며 NVIDIA GB10 장치에 적합합니다.', + dspark: 'NVIDIA GB10 장치 2대 구성용 맞춤 버전으로, 1Panel이 자체 유지 관리하는 맞춤 이미지입니다.', + }, vllmDeployProfile: '배포 모델', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: '시작 명령은 {0} 포트를 사용해야 하며, 그렇지 않으면 서비스에 접근할 수 없습니다.', ascendVisibleDevices: 'Ascend 표시 장치 (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: '모델 계정에 동기화', + syncModelAccount: '모델 서비스에 동기화', modelAccountAddressHelper: '컨테이너 주소는 에이전트 컨테이너 호출에 적합합니다. 127.0.0.1, 시스템 IP 또는 사용자 지정 주소는 AI Gateway 또는 외부 서비스 호출에 적합합니다.', containerAddress: '컨테이너 주소', diff --git a/frontend/src/lang/modules/lo.ts b/frontend/src/lang/modules/lo.ts index 74d563566..efcbda156 100644 --- a/frontend/src/lang/modules/lo.ts +++ b/frontend/src/lang/modules/lo.ts @@ -1067,13 +1067,18 @@ const message = { container_conn_helper: 'ໃຊ້ທີ່ຢູ່ນີ້ສຳລັບການເຂົ້າເຖິງ ຫຼື ເຊື່ອມຕໍ່ລະຫວ່າງຄອນເທນເນີ', from_remote: 'ໂມເດວນີ້ບໍ່ໄດ້ດາວໂຫຼດຜ່ານ 1Panel, ບໍ່ມີລັອກການດຶງຂໍ້ມູນທີ່ກ່ຽວຂ້ອງ.', no_logs: 'ລັອກການດຶງຂໍ້ມູນຂອງໂມເດວນີ້ຖືກລຶບແລ້ວ ແລະ ບໍ່ສາມາດເບິ່ງໄດ້.', - vllmVersionHelper: 'ສຳລັບເຊີເວີ FusionXpark GB10, ກະລຸນາເລືອກເວີຊັນ -cu130.', + vllmVersionHelper: { + default: 'ເວີຊັນມາດຕະຖານທາງການທີ່ໃຊ້ CUDA 13.0, ເໝາະສຳລັບໄດຣເວີໃໝ່ ແລະ GPU ສະຖາປັດຕະຍະກຳໃໝ່.', + cuda: 'ເວີຊັນຮອງຮັບສະຖາປັດຕະຍະກຳເກົ່າທີ່ໃຊ້ CUDA 12.9, ເໝາະສຳລັບ GPU ເກົ່າທີ່ໃຊ້ CUDA ຕ່ຳກວ່າ 13.0.', + ubuntu: 'ເວີຊັນມາດຕະຖານ NVIDIA GB10 ທີ່ໃຊ້ CUDA 13.0, ເໝາະສຳລັບອຸປະກອນ NVIDIA GB10.', + dspark: 'ເວີຊັນປັບແຕ່ງ NVIDIA GB10 ສຳລັບສອງອຸປະກອນ, ໃຊ້ອິມເມຈປັບແຕ່ງທີ່ 1Panel ດູແລເອງ.', + }, vllmDeployProfile: 'ໂມເດວສຳລັບຕິດຕັ້ງ', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'ຄຳສັ່ງເລີ່ມຕົ້ນຕ້ອງໃຊ້ພອດ {0}; ບໍ່ດັ່ງນັ້ນຈະບໍ່ສາມາດເຂົ້າເຖິງບໍລິການໄດ້.', ascendVisibleDevices: 'ອຸປະກອນ Ascend ທີ່ເຫັນໄດ້ (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'ຊິ້ງຄ໌ໄປຍັງບັນຊີໂມເດວ', + syncModelAccount: 'ຊິ້ງຄ໌ໄປຍັງບໍລິການໂມເດວ', modelAccountAddressHelper: 'ທີ່ຢູ່ຄອນເທນເນີແມ່ນສຳລັບຄອນເທນເນີຕົວແທນ; 127.0.0.1, IP ລະບົບ, ຫຼື ທີ່ຢູ່ກຳນົດເອງແມ່ນສຳລັບ AI Gateway ຫຼື ບໍລິການພາຍນອກ.', containerAddress: 'ທີ່ຢູ່ຄອນເທນເນີ', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 77109e7d7..29ccfa760 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -1102,14 +1102,19 @@ const message = { container_conn_helper: 'Gunakan alamat ini untuk akses atau sambungan antara kontena', from_remote: 'Model ini tidak dimuat turun melalui 1Panel; tiada log muat turun berkaitan.', no_logs: 'Log muat turun model ini telah dipadam dan tidak boleh dilihat.', - vllmVersionHelper: 'Untuk pelayan FusionXpark GB10, sila pilih versi -cu130.', + vllmVersionHelper: { + default: 'Versi standard rasmi dengan CUDA 13.0, untuk pemacu baharu dan seni bina GPU baharu.', + cuda: 'Versi keserasian seni bina lama dengan CUDA 12.9, untuk GPU lama yang menggunakan versi CUDA di bawah 13.0.', + ubuntu: 'Versi standard NVIDIA GB10 dengan CUDA 13.0, untuk peranti NVIDIA GB10.', + dspark: 'Versi tersuai NVIDIA GB10 untuk dua peranti, menggunakan imej tersuai yang diselenggara oleh 1Panel.', + }, vllmDeployProfile: 'Model penggunaan', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'Perintah permulaan mesti menggunakan port {0}; jika tidak, perkhidmatan tidak dapat diakses.', ascendVisibleDevices: 'Peranti Ascend boleh dilihat (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'Segerakkan ke akaun model', + syncModelAccount: 'Segerakkan ke perkhidmatan model', modelAccountAddressHelper: 'Alamat kontena sesuai untuk kontena agen; 127.0.0.1, IP sistem, atau alamat tersuai sesuai untuk AI Gateway atau perkhidmatan luaran.', containerAddress: 'Alamat kontena', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index b0882e19b..39ea98415 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -1098,14 +1098,19 @@ const message = { container_conn_helper: 'Use este endereço para acesso ou conexão entre contêineres', from_remote: 'Este modelo não foi baixado pelo 1Panel; não há logs de download relacionados.', no_logs: 'Os logs de download deste modelo foram excluídos e não podem ser visualizados.', - vllmVersionHelper: 'Para servidores FusionXpark GB10, selecione a versão -cu130.', + vllmVersionHelper: { + default: 'Versão padrão oficial com CUDA 13.0, para drivers recentes e GPUs de novas arquiteturas.', + cuda: 'Versão de compatibilidade com arquiteturas antigas com CUDA 12.9, para GPUs antigas que usam versões do CUDA inferiores à 13.0.', + ubuntu: 'Versão padrão do NVIDIA GB10 com CUDA 13.0, para dispositivos NVIDIA GB10.', + dspark: 'Versão personalizada do NVIDIA GB10 para dois dispositivos, com uma imagem mantida pelo 1Panel.', + }, vllmDeployProfile: 'Modelo de implantação', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'O comando de inicialização deve usar a porta {0}; caso contrário, o serviço ficará inacessível.', ascendVisibleDevices: 'Dispositivos Ascend visíveis (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'Sincronizar com conta de modelo', + syncModelAccount: 'Sincronizar com serviço de modelo', modelAccountAddressHelper: 'Use o endereço do contêiner para agentes; 127.0.0.1, IP do sistema ou endereço personalizado para AI Gateway ou serviços externos.', containerAddress: 'Endereço do contêiner', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index ca15716ca..60f029c72 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -1091,13 +1091,19 @@ const message = { container_conn_helper: 'Используйте этот адрес для доступа или подключения между контейнерами', from_remote: 'Эта модель не была загружена через 1Panel, нет связанных журналов извлечения.', no_logs: 'Журналы извлечения для этой модели были удалены и не могут быть просмотрены.', - vllmVersionHelper: 'Для серверов FusionXpark GB10 выберите версию -cu130.', + vllmVersionHelper: { + default: + 'Официальная стандартная версия с CUDA 13.0 для новых драйверов и видеокарт с новой архитектурой.', + cuda: 'Версия совместимости со старыми архитектурами с CUDA 12.9 для видеокарт, использующих версии CUDA ниже 13.0.', + ubuntu: 'Стандартная версия NVIDIA GB10 с CUDA 13.0 для устройств NVIDIA GB10.', + dspark: 'Специальная версия NVIDIA GB10 для двух устройств с образом, поддерживаемым 1Panel.', + }, vllmDeployProfile: 'Модель развертывания', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'Команда запуска должна использовать порт {0}, иначе сервис будет недоступен.', ascendVisibleDevices: 'Видимые устройства Ascend (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'Синхронизировать с аккаунтом модели', + syncModelAccount: 'Синхронизировать с сервисом модели', modelAccountAddressHelper: 'Адрес контейнера подходит для контейнеров агентов; 127.0.0.1, системный IP или пользовательский адрес подходит для AI Gateway или внешних сервисов.', containerAddress: 'Адрес контейнера', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 31d4e01b1..9d236f310 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -1101,14 +1101,19 @@ const message = { container_conn_helper: 'Konteynerler arası erişim veya bağlantı için bu adresi kullanın', from_remote: 'Bu model 1Panel aracılığıyla indirilmedi, ilgili çekme logları yok.', no_logs: 'Bu modelin çekme logları silindi ve görüntülenemiyor.', - vllmVersionHelper: 'FusionXpark GB10 sunucuları için lütfen -cu130 sürümünü seçin.', + vllmVersionHelper: { + default: 'Yeni sürücüler ve yeni GPU mimarileri için CUDA 13.0 içeren resmi standart sürüm.', + cuda: '13.0 altındaki CUDA sürümlerini kullanan eski GPU mimarileri için CUDA 12.9 içeren uyumluluk sürümü.', + ubuntu: 'NVIDIA GB10 cihazları için CUDA 13.0 içeren NVIDIA GB10 standart sürümü.', + dspark: '1Panel tarafından bakımı yapılan özel imajı kullanan, iki cihaz için NVIDIA GB10 özel sürümü.', + }, vllmDeployProfile: 'Dağıtım modeli', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: 'Başlatma komutu {0} numaralı bağlantı noktasını kullanmalıdır; aksi halde hizmete erişilemez.', ascendVisibleDevices: 'Görünür Ascend cihazları (ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: 'Model hesabına senkronize et', + syncModelAccount: 'Model hizmetine senkronize et', modelAccountAddressHelper: 'Konteyner adresi ajan konteynerleri içindir; 127.0.0.1, sistem IP veya özel adres AI Gateway ya da harici servisler içindir.', containerAddress: 'Konteyner adresi', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 288df09e7..288116a1f 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -1030,13 +1030,18 @@ const message = { container_conn_helper: '容器間瀏覽或連接使用此地址', from_remote: '該模型並非透過 1Panel 下載,無相關拉取日誌。', no_logs: '該模型的拉取日誌已被刪除,無法檢視相關日誌。', - vllmVersionHelper: 'FusionXpark GB10 伺服器請選擇 -cu130 版本', + vllmVersionHelper: { + default: '官方標準版本,CUDA 13.0,適用於新驅動程式和新架構顯示卡。', + cuda: '舊架構相容版本,CUDA 12.9,適用於 CUDA 版本低於 13.0 的舊架構顯示卡。', + ubuntu: 'NVIDIA GB10 標準版,CUDA 13.0,適用於 NVIDIA GB10 裝置。', + dspark: 'NVIDIA GB10 雙裝置客製版,由 1Panel 自行維護的客製映像。', + }, vllmDeployProfile: '部署模型', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: '啟動命令必須使用 {0} 連接埠,否則服務將無法存取。', ascendVisibleDevices: 'Ascend 可見裝置(ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: '同步到模型帳號', + syncModelAccount: '同步到模型服務', modelAccountAddressHelper: '容器地址適合智能體容器呼叫;127.0.0.1、本機 IP 或自訂地址適合 AI 閘道或外部服務呼叫。', containerAddress: '容器地址', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index e5ef6a43d..3a6c602dd 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1043,13 +1043,18 @@ const message = { container_conn_helper: '容器间访问或连接使用此地址', from_remote: '该模型并非通过 1Panel 下载,无相关拉取日志。', no_logs: '该模型的拉取日志已被删除,无法查看相关日志。', - vllmVersionHelper: 'FusionXpark GB10 服务器请选择 -cu130 版本', + vllmVersionHelper: { + default: '官方标准版本,CUDA 13.0,适用于新驱动和新架构显卡。', + cuda: '旧架构兼容版本,CUDA 12.9,适用于 CUDA 版本低于 13.0 的旧架构显卡。', + ubuntu: 'NVIDIA GB10 标准版,CUDA 13.0,适用于 NVIDIA GB10 设备。', + dspark: 'NVIDIA GB10 双设备定制版,1Panel 自行维护的定制镜像。', + }, vllmDeployProfile: '部署模型', vllmProfileFlash0731: 'DeepSeek V4 Flash 0731', vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp', vllmCommandPortHelper: '启动命令必须使用 {0} 端口,否则服务将无法访问。', ascendVisibleDevices: 'Ascend 可见设备(ASCEND_RT_VISIBLE_DEVICES)', - syncModelAccount: '同步到模型账号', + syncModelAccount: '同步到模型服务', modelAccountAddressHelper: '容器地址适合智能体容器调用;127.0.0.1、本机 IP 或自定义地址适合 AI 网关或外部服务调用。', containerAddress: '容器地址', diff --git a/frontend/src/routers/index.ts b/frontend/src/routers/index.ts index dfa683912..fa8c26b18 100644 --- a/frontend/src/routers/index.ts +++ b/frontend/src/routers/index.ts @@ -31,8 +31,15 @@ const clearLoginStatus = () => { }; router.beforeEach(async (to, from) => { - const { entrance, isEnterprise, isEnterpriseLicenseLoaded, isEnterpriseLicensed, isLogin, isOnRestart } = - useGlobalStore(); + const { + entrance, + isEnterprise, + isEnterpriseLicenseLoaded, + isEnterpriseLicensed, + isProductPro, + isLogin, + isOnRestart, + } = useGlobalStore(); NProgress.start(); axiosCanceler.removeAllPending(); @@ -133,6 +140,23 @@ router.beforeEach(async (to, from) => { removePanelCache('tabs', activeMenuKey); } + if (to.name === 'ModelFiles') { + if (!isEnterprise.value) { + try { + await loadProductProFromDB(); + } catch (error) { + console.warn('Failed to refresh model marketplace license status', error); + } + } + if (!isProductPro.value) { + if (cachedRoute === to.path) { + removePanelCache('tabs', activeMenuKey); + } + NProgress.done(); + return { path: '/ai/model/account', query: to.query, replace: true }; + } + } + if (!hasRouteAccess(to)) { MsgError(i18n.global.t('commons.res.forbidden')); NProgress.done(); diff --git a/frontend/src/routers/modules/ai.ts b/frontend/src/routers/modules/ai.ts index e545d9e43..c2d2f97e1 100644 --- a/frontend/src/routers/modules/ai.ts +++ b/frontend/src/routers/modules/ai.ts @@ -6,30 +6,20 @@ const aiRouter = { path: '/ai', name: 'AI-Menu', component: Layout, - redirect: '/ai/model/account', + redirect: '/ai/model', meta: { icon: 'p-jiqiren2', title: 'menu.aiTools', }, children: [ { - path: '/ai/model/account', + path: '/ai/model', name: 'AIModel', - component: () => import('@/views/ai/model/index.vue'), + redirect: '/ai/model/files', meta: { icon: 'p-moxing-menu', title: 'aiTools.model.model', - permission: 'ai_model_view', - }, - }, - { - path: '/ai/model/local', - hidden: true, - name: 'LocalModel', - component: () => import('@/views/ai/model/index.vue'), - meta: { - title: 'aiTools.model.deployments', - activeMenu: '/ai/model/account', + activeMenu: '/ai/model', permission: 'ai_model_view', }, }, @@ -40,7 +30,29 @@ const aiRouter = { component: () => import('@/views/ai/model/index.vue'), meta: { title: 'aiTools.model.files', - activeMenu: '/ai/model/account', + activeMenu: '/ai/model', + permission: 'ai_model_view', + }, + }, + { + path: '/ai/model/account', + hidden: true, + name: 'ModelServices', + component: () => import('@/views/ai/model/index.vue'), + meta: { + title: 'aiTools.model.services', + activeMenu: '/ai/model', + permission: 'ai_model_view', + }, + }, + { + path: '/ai/model/local', + hidden: true, + name: 'LocalModel', + component: () => import('@/views/ai/model/index.vue'), + meta: { + title: 'aiTools.model.deployments', + activeMenu: '/ai/model', permission: 'ai_model_view', }, }, diff --git a/frontend/src/views/ai/model/components/account-form/index.vue b/frontend/src/views/ai/model/components/account-form/index.vue index ecb3f7d88..d8b0eda8a 100644 --- a/frontend/src/views/ai/model/components/account-form/index.vue +++ b/frontend/src/views/ai/model/components/account-form/index.vue @@ -91,7 +91,7 @@