From 2878979dfeb99898f4c12a29b6e0a87348451c45 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 <1paneldev@sina.com> Date: Thu, 27 Aug 2026 17:16:48 +0800 Subject: [PATCH 1/2] feat: add gb10 vllm image support --- agent/app/dto/request/app.go | 9 ++ agent/app/service/app.go | 12 +- agent/app/service/app_install.go | 106 ++++++++++++++++-- agent/app/service/app_utils.go | 37 ++++-- .../src/components/advanced-setting/index.vue | 54 +++++---- frontend/src/lang/modules/en.ts | 9 +- frontend/src/lang/modules/es-es.ts | 9 +- frontend/src/lang/modules/fa.ts | 9 +- frontend/src/lang/modules/ja.ts | 9 +- frontend/src/lang/modules/ko.ts | 9 +- frontend/src/lang/modules/lo.ts | 9 +- frontend/src/lang/modules/ms.ts | 9 +- frontend/src/lang/modules/pt-br.ts | 9 +- frontend/src/lang/modules/ru.ts | 9 +- frontend/src/lang/modules/tr.ts | 9 +- frontend/src/lang/modules/zh-Hant.ts | 9 +- frontend/src/lang/modules/zh.ts | 9 +- 17 files changed, 252 insertions(+), 74 deletions(-) diff --git a/agent/app/dto/request/app.go b/agent/app/dto/request/app.go index f2519636d896..0091a50f6f80 100644 --- a/agent/app/dto/request/app.go +++ b/agent/app/dto/request/app.go @@ -50,6 +50,10 @@ type AppContainerConfig struct { Type string `json:"type"` SpecifyIP string `json:"specifyIP"` RestartPolicy string `json:"restartPolicy" validate:"omitempty,oneof=always unless-stopped no on-failure"` + + KeepServiceName bool `json:"-"` + SkipComposeCommonConfig bool `json:"-"` + UseLifecycleScripts bool `json:"-"` } type AppInstalledSearch struct { @@ -92,6 +96,8 @@ type AppInstalledOperate struct { TaskID string `json:"taskID"` DeleteImage bool `json:"deleteImage"` Favorite bool `json:"favorite"` + + UseLifecycleScripts bool `json:"-"` } type AppInstallUpgrade struct { @@ -111,11 +117,14 @@ type AppInstallDelete struct { DeleteDB bool `json:"deleteDB"` DeleteImage bool `json:"deleteImage"` TaskID string `json:"taskID"` + + UseLifecycleScripts bool `json:"-"` } type AppInstalledUpdate struct { InstallId uint `json:"installId" validate:"required"` Params map[string]interface{} `json:"params" validate:"required"` + TaskID string `json:"-"` AppContainerConfig } diff --git a/agent/app/service/app.go b/agent/app/service/app.go index 63d538bfcd07..6417ab1862d5 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -483,15 +483,17 @@ func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript index++ } newServiceName := strings.ToLower(appInstall.Name) - if app.Limit == 0 && newServiceName != serviceName && len(servicesMap) == 1 { + if app.Limit == 0 && newServiceName != serviceName && len(servicesMap) == 1 && !req.KeepServiceName { servicesMap[newServiceName] = servicesMap[serviceName] delete(servicesMap, serviceName) serviceName = newServiceName } appInstall.ServiceName = serviceName - if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil { - return + if !req.SkipComposeCommonConfig { + if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil { + return + } } var ( composeByte []byte @@ -559,7 +561,7 @@ func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript return err } } - if executeScript { + if executeScript || req.UseLifecycleScripts { if err = runScript(t, appInstall, "init"); err != nil { return err } @@ -572,7 +574,7 @@ func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript return err } } - if err = upApp(t, appInstall, req.PullImage); err != nil { + if err = upApp(t, appInstall, req.PullImage, req.UseLifecycleScripts); err != nil { return err } updateToolApp(appInstall) diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index df5145c9f69a..ec8b959f75de 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -13,12 +13,14 @@ import ( "sort" "strconv" "strings" + "time" "github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/dto/request" "github.com/1Panel-dev/1Panel/agent/app/dto/response" "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/app/task" "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" @@ -252,6 +254,9 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error { return buserr.New("ErrInstallDirNotFound") } dockerComposePath := install.GetComposePath() + if req.UseLifecycleScripts && (req.Operate == constant.Start || req.Operate == constant.Stop || req.Operate == constant.Restart) { + return operateAppWithLifecycleScripts(install, req) + } switch req.Operate { case constant.Rebuild: return rebuildApp(install) @@ -275,12 +280,13 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error { return syncAppInstallStatus(&install, false) case constant.Delete: deleteReq := request.AppInstallDelete{ - Install: install, - DeleteBackup: req.DeleteBackup, - ForceDelete: req.ForceDelete, - DeleteDB: req.DeleteDB, - DeleteImage: req.DeleteImage, - TaskID: req.TaskID, + Install: install, + DeleteBackup: req.DeleteBackup, + ForceDelete: req.ForceDelete, + DeleteDB: req.DeleteDB, + DeleteImage: req.DeleteImage, + TaskID: req.TaskID, + UseLifecycleScripts: req.UseLifecycleScripts, } if err = deleteAppInstall(deleteReq); err != nil && !req.ForceDelete { return err @@ -312,6 +318,66 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error { } } +func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppInstalledOperate) error { + taskType := task.TaskUpdate + switch req.Operate { + case constant.Start: + install.Status = constant.StatusStarting + case constant.Restart: + taskType = task.TaskRestart + install.Status = constant.StatusRestarting + case constant.Stop: + install.Status = constant.StatusWaiting + default: + return errors.New("lifecycle script operation not supported") + } + install.Message = "" + if err := appInstallRepo.Save(context.Background(), &install); err != nil { + return err + } + + operationTask, err := task.NewTaskWithOps(install.Name, taskType, task.TaskScopeApp, req.TaskID, install.ID) + if err != nil { + return err + } + operation := string(req.Operate) + operationTask.AddSubTaskWithOps( + task.GetTaskName(install.Name, taskType, task.TaskScopeApp), + func(t *task.Task) error { + if err := runScript(t, &install, operation); err != nil { + return err + } + if req.Operate == constant.Stop { + install.Status = constant.StatusStopped + install.Message = "" + return appInstallRepo.Save(context.Background(), &install) + } + containerNames, err := getContainerNames(install) + if err != nil { + return err + } + if len(containerNames) == 0 { + return buserr.WithName("ErrContainerNotFound", install.Name) + } + install.ContainerName = strings.Join(containerNames, ",") + install.Status = constant.StatusRunning + install.Message = "" + return appInstallRepo.Save(context.Background(), &install) + }, + func(t *task.Task) { + install.Status = constant.StatusUpErr + install.Message = t.Task.ErrorMsg + _ = appInstallRepo.Save(context.Background(), &install) + }, + 0, + time.Hour, + ) + go func() { + _ = operationTask.Execute() + }() + return nil +} + func (a *AppInstallService) UpdateAppConfig(req request.AppConfigUpdate) error { installed, err := appInstallRepo.GetFirst(repo.WithByID(req.InstallID)) if err != nil { @@ -374,8 +440,10 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error { return err } } - if err = addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil { - return err + if !req.SkipComposeCommonConfig { + if err = addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil { + return err + } } composeByte, err := yaml.Marshal(composeMap) if err != nil { @@ -420,13 +488,25 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error { } fileOp := files.NewFileOp() _ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), constant.DirPerm) - if err := rebuildApp(installed); err != nil { + if req.UseLifecycleScripts { + err = operateAppWithLifecycleScripts(installed, request.AppInstalledOperate{ + InstallId: installed.ID, + Operate: constant.Restart, + TaskID: req.TaskID, + UseLifecycleScripts: true, + }) + } else { + err = rebuildApp(installed) + } + if err != nil { _ = env.Write(backupEnvMaps, envPath) _ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), constant.DirPerm) return err } - installed.Status = constant.StatusRunning - _ = appInstallRepo.Save(context.Background(), &installed) + if !req.UseLifecycleScripts { + installed.Status = constant.StatusRunning + _ = appInstallRepo.Save(context.Background(), &installed) + } proxyChanged := hasAppInstallProxyPassChanged(&oldInstalled, &installed) currentProxy, currentProxyErr := getAppInstallProxyPass(&installed) @@ -836,7 +916,9 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) { } func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error { - if appInstall.Status == constant.StatusInstalling || appInstall.Status == constant.StatusRebuilding || appInstall.Status == constant.StatusUpgrading || appInstall.Status == constant.StatusUninstalling { + switch appInstall.Status { + case constant.StatusInstalling, constant.StatusRebuilding, constant.StatusUpgrading, constant.StatusUninstalling, + constant.StatusStarting, constant.StatusRestarting, constant.StatusWaiting: return nil } cli, err := docker.NewClient() diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index a3c213bb6eb5..cb275fcc3cab 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -353,15 +353,21 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error { logStr := i18n.GetMsgByKey("Stop") + i18n.GetMsgByKey("App") t.Log(logStr) - out, err := compose.Down(install.GetComposePath()) - if err != nil && !deleteReq.ForceDelete { - return handleErr(install, err, out) + if deleteReq.UseLifecycleScripts { + if err = runScript(t, &install, "uninstall"); err != nil { + return err + } + } else { + out, err := compose.Down(install.GetComposePath()) + if err != nil && !deleteReq.ForceDelete { + return handleErr(install, err, out) + } + if err = runScript(t, &install, "uninstall"); err != nil { + _, _ = compose.Up(install.GetComposePath()) + return err + } } t.LogSuccess(logStr) - if err = runScript(t, &install, "uninstall"); err != nil { - _, _ = compose.Up(install.GetComposePath()) - return err - } if deleteReq.DeleteImage { content, err := op.GetContent(install.GetEnvPath()) if err != nil { @@ -999,6 +1005,12 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er scriptPath = path.Join(workDir, "scripts", "upgrade.sh") case "uninstall": scriptPath = path.Join(workDir, "scripts", "uninstall.sh") + case "start": + scriptPath = path.Join(workDir, "scripts", "start.sh") + case "stop": + scriptPath = path.Join(workDir, "scripts", "stop.sh") + case "restart": + scriptPath = path.Join(workDir, "scripts", "restart.sh") } fileOp := files.NewFileOp() if !fileOp.Stat(scriptPath) { @@ -1008,7 +1020,11 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er logStr := i18n.GetWithName("ExecShell", operate) task.LogStart(logStr) - cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir)) + timeout := 10 * time.Minute + if operate == "start" || operate == "restart" { + timeout = time.Hour + } + cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout), cmd.WithWorkDir(workDir), cmd.WithTask(*task)) if err := cmdMgr.Run("bash", scriptPath); err != nil { task.LogFailedWithErr(logStr, err) return err @@ -1043,12 +1059,15 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) { return false, nil } -func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error { +func upApp(task *task.Task, appInstall *model.AppInstall, pullImages, useLifecycleScripts bool) error { upProject := func(appInstall *model.AppInstall) (err error) { var ( out string errMsg string ) + if useLifecycleScripts { + return runScript(task, appInstall, "start") + } if pullImages && appInstall.App.Type != "php" { envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath()) if err != nil { diff --git a/frontend/src/components/advanced-setting/index.vue b/frontend/src/components/advanced-setting/index.vue index 4b9ebd471044..0db052796f5f 100644 --- a/frontend/src/components/advanced-setting/index.vue +++ b/frontend/src/components/advanced-setting/index.vue @@ -3,7 +3,7 @@
- + @@ -22,29 +22,31 @@ - - - - - - {{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }} - - - - - - - {{ $t('container.limitHelper', [limits.memory]) }}{{ form.memoryUnit }}B - + {{ $t('app.pullImageHelper') }} @@ -83,7 +85,9 @@ const props = withDefaults( }; showAllowPort?: boolean; showSpecifyIP?: boolean; + showContainerName?: boolean; showRestartPolicy?: boolean; + showResourceLimit?: boolean; showPullImage?: boolean; showCompose?: boolean; autoLoadLimit?: boolean; @@ -91,7 +95,9 @@ const props = withDefaults( { showAllowPort: true, showSpecifyIP: true, + showContainerName: true, showRestartPolicy: true, + showResourceLimit: true, showPullImage: true, showCompose: true, autoLoadLimit: true, diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 81b0e32894ff..86c054eb668a 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -949,6 +949,7 @@ const message = { ascendVisibleDevices: 'Ascend Visible Devices', 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', modelAccountAddressHelper: 'Container address is for agent containers; 127.0.0.1, system IP, or custom address is for AI Gateway or external services.', @@ -1054,9 +1055,13 @@ const message = { accountMaxConcurrency: 'Account Max Concurrency', groupMaxConcurrency: 'User Group Max Concurrency', apiKeyMaxConcurrency: 'Per-API-Key Max Concurrency', - gatewayConcurrency: 'Gateway Concurrency', - waitingQueue: 'Waiting Queue', + gatewayConcurrency: 'Current Gateway Concurrency', + waitingQueue: 'Current Waiting Queue', + currentActiveUsers: 'Current Active Users', + activeStreamingRequests: 'Active Streaming Requests', modelAccountConcurrency: 'Model Account Concurrency', + accountAvailability: 'Model Account Availability', + capacityFull: 'At Capacity', groupConcurrency: 'User Group Concurrency', apiKeyConcurrency: 'API Key Concurrency', currentLimit: 'Current / Limit', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 796f8dc32d9d..9aa7727bfe19 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -953,6 +953,7 @@ const message = { ascendVisibleDevices: 'Dispositivos Ascend visibles', 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', 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.', @@ -1059,9 +1060,13 @@ const message = { accountMaxConcurrency: 'Concurrencia máxima de la cuenta', groupMaxConcurrency: 'Concurrencia máxima del grupo de usuarios', apiKeyMaxConcurrency: 'Concurrencia máxima por API Key', - gatewayConcurrency: 'Concurrencia del gateway', - waitingQueue: 'Cola de espera', + gatewayConcurrency: 'Concurrencia actual del gateway', + waitingQueue: 'Cola de espera actual', + currentActiveUsers: 'Usuarios activos actuales', + activeStreamingRequests: 'Solicitudes de streaming activas', modelAccountConcurrency: 'Concurrencia de cuentas de modelo', + accountAvailability: 'Disponibilidad de cuentas de modelo', + capacityFull: 'Capacidad completa', groupConcurrency: 'Concurrencia de grupos de usuarios', apiKeyConcurrency: 'Concurrencia de API Keys', currentLimit: 'Actual / Límite', diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts index b2f23c4be81e..cfd8b7fdced5 100644 --- a/frontend/src/lang/modules/fa.ts +++ b/frontend/src/lang/modules/fa.ts @@ -936,6 +936,7 @@ const message = { ascendVisibleDevices: 'دستگاه‌های Ascend قابل مشاهده', vllmCommandPortHelper: 'فرمان راه‌اندازی باید از پورت {0} استفاده کند؛ در غیر این صورت سرویس قابل دسترسی نخواهد بود.', + ascendVisibleDevices: 'دستگاه‌های قابل مشاهده Ascend (ASCEND_RT_VISIBLE_DEVICES)', syncModelAccount: 'همگام‌سازی با حساب مدل', modelAccountAddressHelper: 'آدرس کانتینر برای کانتینرهای عامل است؛ 127.0.0.1، IP سیستم یا آدرس سفارشی برای دروازه AI یا خدمات خارجی است.', @@ -1041,9 +1042,13 @@ const message = { accountMaxConcurrency: 'حداکثر هم‌روندی حساب', groupMaxConcurrency: 'حداکثر هم‌روندی گروه کاربر', apiKeyMaxConcurrency: 'حداکثر هم‌روندی برای هر API Key', - gatewayConcurrency: 'هم‌روندی دروازه', - waitingQueue: 'صف انتظار', + gatewayConcurrency: 'هم‌روندی فعلی دروازه', + waitingQueue: 'صف انتظار فعلی', + currentActiveUsers: 'کاربران فعال فعلی', + activeStreamingRequests: 'درخواست‌های جریانی فعال', modelAccountConcurrency: 'هم‌روندی حساب مدل', + accountAvailability: 'دسترس‌پذیری حساب مدل', + capacityFull: 'ظرفیت تکمیل', groupConcurrency: 'هم‌روندی گروه کاربر', apiKeyConcurrency: 'هم‌روندی API Key', currentLimit: 'فعلی / حد', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 1204ce2717dd..73e95980f3fd 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -940,6 +940,7 @@ const message = { ascendVisibleDevices: 'Ascend 可視デバイス', vllmCommandPortHelper: '起動コマンドではポート {0} を使用する必要があります。使用しない場合、サービスにアクセスできません。', + ascendVisibleDevices: 'Ascend 可視デバイス(ASCEND_RT_VISIBLE_DEVICES)', syncModelAccount: 'モデルアカウントに同期', modelAccountAddressHelper: 'コンテナアドレスはエージェントコンテナ向けです。127.0.0.1、システム IP、カスタムアドレスは AI Gateway または外部サービス向けです。', @@ -1044,9 +1045,13 @@ const message = { accountMaxConcurrency: 'アカウント最大同時実行数', groupMaxConcurrency: 'ユーザーグループ最大同時実行数', apiKeyMaxConcurrency: 'API Key ごとの最大同時実行数', - gatewayConcurrency: 'ゲートウェイ同時実行数', - waitingQueue: '待機キュー', + gatewayConcurrency: '現在のゲートウェイ同時実行数', + waitingQueue: '現在の待機キュー', + currentActiveUsers: '現在のアクティブユーザー', + activeStreamingRequests: '実行中のストリーミングリクエスト', modelAccountConcurrency: 'モデルアカウント同時実行数', + accountAvailability: 'モデルアカウント可用性', + capacityFull: '上限到達', groupConcurrency: 'ユーザーグループ同時実行数', apiKeyConcurrency: 'API Key 同時実行数', currentLimit: '現在 / 上限', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index d0389e58889a..28774b85487e 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -929,6 +929,7 @@ const message = { vllmVersionHelper: 'FusionXpark GB 10 서버는 -cu130 버전을 선택하세요.', ascendVisibleDevices: 'Ascend 표시 장치', vllmCommandPortHelper: '시작 명령은 {0} 포트를 사용해야 하며, 그렇지 않으면 서비스에 접근할 수 없습니다.', + ascendVisibleDevices: 'Ascend 표시 장치 (ASCEND_RT_VISIBLE_DEVICES)', syncModelAccount: '모델 계정에 동기화', modelAccountAddressHelper: '컨테이너 주소는 에이전트 컨테이너 호출에 적합합니다. 127.0.0.1, 시스템 IP 또는 사용자 지정 주소는 AI Gateway 또는 외부 서비스 호출에 적합합니다.', @@ -1033,9 +1034,13 @@ const message = { accountMaxConcurrency: '계정 최대 동시성', groupMaxConcurrency: '사용자 그룹 최대 동시성', apiKeyMaxConcurrency: 'API Key별 최대 동시성', - gatewayConcurrency: '게이트웨이 동시성', - waitingQueue: '대기열', + gatewayConcurrency: '현재 게이트웨이 동시성', + waitingQueue: '현재 대기열', + currentActiveUsers: '현재 활성 사용자', + activeStreamingRequests: '활성 스트리밍 요청', modelAccountConcurrency: '모델 계정 동시성', + accountAvailability: '모델 계정 가용성', + capacityFull: '용량 가득 참', groupConcurrency: '사용자 그룹 동시성', apiKeyConcurrency: 'API Key 동시성', currentLimit: '현재 / 한도', diff --git a/frontend/src/lang/modules/lo.ts b/frontend/src/lang/modules/lo.ts index c153f0325b3f..e5958ad85ced 100644 --- a/frontend/src/lang/modules/lo.ts +++ b/frontend/src/lang/modules/lo.ts @@ -929,6 +929,7 @@ const message = { vllmVersionHelper: 'ສຳລັບເຊີເວີ FusionXpark GB 10, ກະລຸນາເລືອກເວີຊັນ -cu130.', ascendVisibleDevices: 'ອຸປະກອນ Ascend ທີ່ເຫັນໄດ້', vllmCommandPortHelper: 'ຄຳສັ່ງເລີ່ມຕົ້ນຕ້ອງໃຊ້ພອດ {0}; ບໍ່ດັ່ງນັ້ນຈະບໍ່ສາມາດເຂົ້າເຖິງບໍລິການໄດ້.', + ascendVisibleDevices: 'ອຸປະກອນ Ascend ທີ່ເຫັນໄດ້ (ASCEND_RT_VISIBLE_DEVICES)', syncModelAccount: 'ຊິ້ງຄ໌ໄປຍັງບັນຊີໂມເດວ', modelAccountAddressHelper: 'ທີ່ຢູ່ຄອນເທນເນີແມ່ນສຳລັບຄອນເທນເນີຕົວແທນ; 127.0.0.1, IP ລະບົບ, ຫຼື ທີ່ຢູ່ກຳນົດເອງແມ່ນສຳລັບ AI Gateway ຫຼື ບໍລິການພາຍນອກ.', @@ -1033,9 +1034,13 @@ const message = { accountMaxConcurrency: 'ຈຳນວນພ້ອມກັນສູງສຸດຂອງບັນຊີ', groupMaxConcurrency: 'ຈຳນວນພ້ອມກັນສູງສຸດຂອງກຸ່ມຜູ້ໃຊ້', apiKeyMaxConcurrency: 'ຈຳນວນພ້ອມກັນສູງສຸດຕໍ່ API Key', - gatewayConcurrency: 'ການເຮັດວຽກພ້ອມກັນຂອງເກດເວ', - waitingQueue: 'ຄິວລໍຖ້າ', + gatewayConcurrency: 'ການເຮັດວຽກພ້ອມກັນຂອງເກດເວປັດຈຸບັນ', + waitingQueue: 'ຄິວລໍຖ້າປັດຈຸບັນ', + currentActiveUsers: 'ຜູ້ໃຊ້ທີ່ກຳລັງໃຊ້ງານ', + activeStreamingRequests: 'ຄຳຮ້ອງຂໍສະຕຣີມທີ່ກຳລັງໃຊ້ງານ', modelAccountConcurrency: 'ການເຮັດວຽກພ້ອມກັນຂອງບັນຊີໂມເດວ', + accountAvailability: 'ຄວາມພ້ອມໃຊ້ງານຂອງບັນຊີໂມເດວ', + capacityFull: 'ຄວາມຈຸເຕັມ', groupConcurrency: 'ການເຮັດວຽກພ້ອມກັນຂອງກຸ່ມຜູ້ໃຊ້', apiKeyConcurrency: 'ການເຮັດວຽກພ້ອມກັນຂອງ API Key', currentLimit: 'ປັດຈຸບັນ / ຂີດຈຳກັດ', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index d228360c246d..a99cf317dc14 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -961,6 +961,7 @@ const message = { ascendVisibleDevices: 'Peranti Ascend yang kelihatan', 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', modelAccountAddressHelper: 'Alamat kontena sesuai untuk kontena agen; 127.0.0.1, IP sistem, atau alamat tersuai sesuai untuk AI Gateway atau perkhidmatan luaran.', @@ -1066,9 +1067,13 @@ const message = { accountMaxConcurrency: 'Keserentakan maksimum akaun', groupMaxConcurrency: 'Keserentakan maksimum kumpulan pengguna', apiKeyMaxConcurrency: 'Keserentakan maksimum setiap API Key', - gatewayConcurrency: 'Keserentakan gateway', - waitingQueue: 'Baris gilir menunggu', + gatewayConcurrency: 'Keserentakan gateway semasa', + waitingQueue: 'Baris gilir menunggu semasa', + currentActiveUsers: 'Pengguna aktif semasa', + activeStreamingRequests: 'Permintaan penstriman aktif', modelAccountConcurrency: 'Keserentakan akaun model', + accountAvailability: 'Ketersediaan akaun model', + capacityFull: 'Kapasiti penuh', groupConcurrency: 'Keserentakan kumpulan pengguna', apiKeyConcurrency: 'Keserentakan API Key', currentLimit: 'Semasa / Had', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 9376682c7d57..db2cbc2b445d 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -957,6 +957,7 @@ const message = { ascendVisibleDevices: 'Dispositivos Ascend visíveis', 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', 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.', @@ -1062,9 +1063,13 @@ const message = { accountMaxConcurrency: 'Concorrência máxima da conta', groupMaxConcurrency: 'Concorrência máxima do grupo de usuários', apiKeyMaxConcurrency: 'Concorrência máxima por API Key', - gatewayConcurrency: 'Concorrência do gateway', - waitingQueue: 'Fila de espera', + gatewayConcurrency: 'Concorrência atual do gateway', + waitingQueue: 'Fila de espera atual', + currentActiveUsers: 'Usuários ativos agora', + activeStreamingRequests: 'Solicitações de streaming ativas', modelAccountConcurrency: 'Concorrência das contas de modelo', + accountAvailability: 'Disponibilidade das contas de modelo', + capacityFull: 'Capacidade esgotada', groupConcurrency: 'Concorrência dos grupos de usuários', apiKeyConcurrency: 'Concorrência das API Keys', currentLimit: 'Atual / Limite', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 84f473b42609..1b15c5f30ebb 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -949,6 +949,7 @@ const message = { vllmVersionHelper: 'Для серверов FusionXpark GB 10 выберите версию -cu130.', ascendVisibleDevices: 'Видимые устройства Ascend', vllmCommandPortHelper: 'Команда запуска должна использовать порт {0}, иначе сервис будет недоступен.', + ascendVisibleDevices: 'Видимые устройства Ascend (ASCEND_RT_VISIBLE_DEVICES)', syncModelAccount: 'Синхронизировать с аккаунтом модели', modelAccountAddressHelper: 'Адрес контейнера подходит для контейнеров агентов; 127.0.0.1, системный IP или пользовательский адрес подходит для AI Gateway или внешних сервисов.', @@ -1053,9 +1054,13 @@ const message = { accountMaxConcurrency: 'Макс. параллельность аккаунта', groupMaxConcurrency: 'Макс. параллельность группы', apiKeyMaxConcurrency: 'Макс. параллельность на API Key', - gatewayConcurrency: 'Параллельность шлюза', - waitingQueue: 'Очередь ожидания', + gatewayConcurrency: 'Текущая параллельность шлюза', + waitingQueue: 'Текущая очередь ожидания', + currentActiveUsers: 'Активные пользователи сейчас', + activeStreamingRequests: 'Активные потоковые запросы', modelAccountConcurrency: 'Параллельность модельных аккаунтов', + accountAvailability: 'Доступность модельных аккаунтов', + capacityFull: 'Лимит исчерпан', groupConcurrency: 'Параллельность групп пользователей', apiKeyConcurrency: 'Параллельность API Key', currentLimit: 'Текущее / Лимит', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 10dc946cc7f6..8536122d27e4 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -959,6 +959,7 @@ const message = { ascendVisibleDevices: 'Görünür Ascend cihazları', 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', modelAccountAddressHelper: 'Konteyner adresi ajan konteynerleri içindir; 127.0.0.1, sistem IP veya özel adres AI Gateway ya da harici servisler içindir.', @@ -1063,9 +1064,13 @@ const message = { accountMaxConcurrency: 'Hesap maksimum eşzamanlılığı', groupMaxConcurrency: 'Kullanıcı grubu maksimum eşzamanlılığı', apiKeyMaxConcurrency: 'API Key başına maksimum eşzamanlılık', - gatewayConcurrency: 'Ağ geçidi eşzamanlılığı', - waitingQueue: 'Bekleme kuyruğu', + gatewayConcurrency: 'Mevcut ağ geçidi eşzamanlılığı', + waitingQueue: 'Mevcut bekleme kuyruğu', + currentActiveUsers: 'Şu anda aktif kullanıcılar', + activeStreamingRequests: 'Aktif akış istekleri', modelAccountConcurrency: 'Model hesabı eşzamanlılığı', + accountAvailability: 'Model hesabı kullanılabilirliği', + capacityFull: 'Kapasite dolu', groupConcurrency: 'Kullanıcı grubu eşzamanlılığı', apiKeyConcurrency: 'API Key eşzamanlılığı', currentLimit: 'Mevcut / Sınır', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 1632442b38ee..244a4c2e1b41 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -900,6 +900,7 @@ const message = { vllmVersionHelper: 'FusionXpark GB 10 伺服器請選擇 -cu130 版本', ascendVisibleDevices: 'Ascend 可見裝置', vllmCommandPortHelper: '啟動命令必須使用 {0} 連接埠,否則服務將無法存取。', + ascendVisibleDevices: 'Ascend 可見裝置(ASCEND_RT_VISIBLE_DEVICES)', syncModelAccount: '同步到模型帳號', modelAccountAddressHelper: '容器地址適合智能體容器呼叫;127.0.0.1、本機 IP 或自訂地址適合 AI 閘道或外部服務呼叫。', @@ -1000,9 +1001,13 @@ const message = { accountMaxConcurrency: '帳號最大併發', groupMaxConcurrency: '使用者群組最大併發', apiKeyMaxConcurrency: '單一 API Key 最大併發', - gatewayConcurrency: '網關併發', - waitingQueue: '等待佇列', + gatewayConcurrency: '目前網關併發', + waitingQueue: '目前等待佇列', + currentActiveUsers: '目前活躍使用者', + activeStreamingRequests: '目前串流請求', modelAccountConcurrency: '模型帳號併發', + accountAvailability: '模型帳號可用性', + capacityFull: '已滿載', groupConcurrency: '使用者群組併發', apiKeyConcurrency: 'API Key 併發', currentLimit: '目前 / 上限', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 7506ca240d40..e18557f59e0a 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -913,6 +913,7 @@ const message = { vllmVersionHelper: 'FusionXpark GB 10 服务器请选择 -cu130 版本', ascendVisibleDevices: 'Ascend 可见设备', vllmCommandPortHelper: '启动命令必须使用 {0} 端口,否则服务将无法访问。', + ascendVisibleDevices: 'Ascend 可见设备(ASCEND_RT_VISIBLE_DEVICES)', syncModelAccount: '同步到模型账号', modelAccountAddressHelper: '容器地址适合智能体容器调用;127.0.0.1、本机 IP 或自定义地址适合 AI 网关或外部服务调用。', @@ -1014,9 +1015,13 @@ const message = { accountMaxConcurrency: '账号最大并发', groupMaxConcurrency: '用户组最大并发', apiKeyMaxConcurrency: '单 API Key 最大并发', - gatewayConcurrency: '网关并发', - waitingQueue: '等待队列', + gatewayConcurrency: '当前网关并发', + waitingQueue: '当前等待队列', + currentActiveUsers: '当前活跃用户', + activeStreamingRequests: '当前流式请求', modelAccountConcurrency: '模型账号并发', + accountAvailability: '模型账号可用性', + capacityFull: '满载', groupConcurrency: '用户组并发', apiKeyConcurrency: 'API Key 并发', currentLimit: '当前 / 上限', From ea52faab4b9ef07b401be9f6bbc0e5026eaed393 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 <1paneldev@sina.com> Date: Thu, 27 Aug 2026 18:36:04 +0800 Subject: [PATCH 2/2] feat: add gb10 vllm image support --- agent/app/service/app_install.go | 36 +++++++++++++++++++++----------- agent/app/service/app_utils.go | 3 ++- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index ec8b959f75de..048ecd944b17 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "maps" "math" "net/http" "os" @@ -255,7 +256,7 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error { } dockerComposePath := install.GetComposePath() if req.UseLifecycleScripts && (req.Operate == constant.Start || req.Operate == constant.Stop || req.Operate == constant.Restart) { - return operateAppWithLifecycleScripts(install, req) + return operateAppWithLifecycleScripts(install, req, nil) } switch req.Operate { case constant.Rebuild: @@ -318,7 +319,7 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error { } } -func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppInstalledOperate) error { +func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppInstalledOperate, onFailure func(error)) error { taskType := task.TaskUpdate switch req.Operate { case constant.Start: @@ -364,16 +365,20 @@ func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppIns install.Message = "" return appInstallRepo.Save(context.Background(), &install) }, - func(t *task.Task) { - install.Status = constant.StatusUpErr - install.Message = t.Task.ErrorMsg - _ = appInstallRepo.Save(context.Background(), &install) - }, + nil, 0, time.Hour, ) go func() { - _ = operationTask.Execute() + if taskErr := operationTask.Execute(); taskErr != nil { + if onFailure != nil { + onFailure(taskErr) + return + } + install.Status = constant.StatusUpErr + install.Message = taskErr.Error() + _ = appInstallRepo.Save(context.Background(), &install) + } }() return nil } @@ -476,7 +481,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error { if err != nil { return err } - backupEnvMaps := oldEnvMaps + backupEnvMaps := maps.Clone(oldEnvMaps) handleMap(req.Params, oldEnvMaps) paramByte, err := json.Marshal(oldEnvMaps) if err != nil { @@ -488,19 +493,26 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error { } fileOp := files.NewFileOp() _ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), constant.DirPerm) + restoreConfig := func(operationErr error) { + _ = env.Write(backupEnvMaps, envPath) + _ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), constant.DirPerm) + failed := oldInstalled + failed.Status = constant.StatusUpErr + failed.Message = operationErr.Error() + _ = appInstallRepo.Save(context.Background(), &failed) + } if req.UseLifecycleScripts { err = operateAppWithLifecycleScripts(installed, request.AppInstalledOperate{ InstallId: installed.ID, Operate: constant.Restart, TaskID: req.TaskID, UseLifecycleScripts: true, - }) + }, restoreConfig) } else { err = rebuildApp(installed) } if err != nil { - _ = env.Write(backupEnvMaps, envPath) - _ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), constant.DirPerm) + restoreConfig(err) return err } if !req.UseLifecycleScripts { diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index cb275fcc3cab..b2b0bf45c996 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -1394,7 +1394,8 @@ func handleErr(install model.AppInstall, err error, out string) error { func doNotNeedSync(installed model.AppInstall) bool { return installed.Status == constant.StatusInstalling || installed.Status == constant.StatusRebuilding || installed.Status == constant.StatusUpgrading || - installed.Status == constant.StatusSyncing || installed.Status == constant.StatusUninstalling || installed.Status == constant.StatusInstallErr + installed.Status == constant.StatusSyncing || installed.Status == constant.StatusUninstalling || installed.Status == constant.StatusInstallErr || + installed.Status == constant.StatusStarting || installed.Status == constant.StatusRestarting || installed.Status == constant.StatusWaiting } func synAppInstall(containers map[string]container.Summary, appInstall *model.AppInstall, force bool) {