Compare commits

...
12 Commits
29 changed files with 392 additions and 84 deletions
+2
View File
@@ -14,6 +14,8 @@ type TensorRTLLMCreate struct {
ModelDir string `json:"modelDir" validate:"required"`
Image string `json:"image" validate:"required"`
Command string `json:"command" validate:"required"`
ModelType string `json:"modelType"`
ModelSpeedup bool `json:"modelSpeedup"`
DockerConfig
}
+2
View File
@@ -43,6 +43,8 @@ type Cronjob struct {
EntryIDs string `json:"entryIDs"`
Records []JobRecords `json:"records"`
Secret string `json:"secret"`
Config string `json:"config"`
}
type JobRecords struct {
+7 -3
View File
@@ -2,14 +2,18 @@ package model
type MonitorBase struct {
BaseModel
Cpu float64 `json:"cpu"`
Cpu float64 `json:"cpu"`
TopCPU string `json:"topCPU"`
TopCPUItems interface{} `gorm:"-" json:"topCPUItems"`
Memory float64 `json:"memory"`
TopMem string `json:"topMem"`
TopMemItems interface{} `gorm:"-" json:"topMemItems"`
LoadUsage float64 `json:"loadUsage"`
CpuLoad1 float64 `json:"cpuLoad1"`
CpuLoad5 float64 `json:"cpuLoad5"`
CpuLoad15 float64 `json:"cpuLoad15"`
Memory float64 `json:"memory"`
}
type MonitorIO struct {
+5 -4
View File
@@ -6,8 +6,9 @@ type TensorRTLLM struct {
DockerCompose string `json:"dockerCompose"`
ContainerName string `json:"containerName"`
Message string `json:"message"`
//Port int `json:"port"`
Status string `json:"status"`
Env string `json:"env"`
TaskID string `json:"taskID"`
Status string `json:"status"`
Env string `json:"env"`
TaskID string `json:"taskID"`
ModelType string `json:"modelType"`
ModelSpeedup bool `json:"modelSpeedup"`
}
+103 -48
View File
@@ -1,6 +1,7 @@
package service
import (
"errors"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
@@ -12,10 +13,13 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/env"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -52,7 +56,7 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR
serverDTO.ModelDir = envs["MODEL_PATH"]
serverDTO.Dir = path.Join(global.Dir.TensorRTLLMDir, item.Name)
serverDTO.Image = envs["IMAGE"]
serverDTO.Command = envs["COMMAND"]
serverDTO.Command = getCommand(item.Env)
for k, v := range envs {
if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") {
@@ -81,31 +85,25 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR
}
composeByte, err := files.NewFileOp().GetContent(path.Join(global.Dir.TensorRTLLMDir, item.Name, "docker-compose.yml"))
if err != nil {
continue
}
serverDTO.Environments, err = getDockerComposeEnvironments(composeByte)
if err != nil {
continue
if err == nil {
serverDTO.Environments, _ = getDockerComposeEnvironments(composeByte)
}
volumes, err := getDockerComposeVolumes(composeByte)
if err != nil {
continue
}
var defaultVolumes = map[string]string{
"${MODEL_PATH}": "/models",
}
for _, volume := range volumes {
exist := false
for key, value := range defaultVolumes {
if key == volume.Source && value == volume.Target {
exist = true
break
}
if err == nil {
var defaultVolumes = map[string]string{
"${MODEL_PATH}": "${MODEL_PATH}",
}
if !exist {
serverDTO.Volumes = append(serverDTO.Volumes, volume)
for _, volume := range volumes {
exist := false
for key, value := range defaultVolumes {
if key == volume.Source && value == volume.Target {
exist = true
break
}
}
if !exist {
serverDTO.Volumes = append(serverDTO.Volumes, volume)
}
}
}
items = append(items, serverDTO)
@@ -159,16 +157,15 @@ func handleLLMParams(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) e
delete(serviceValue, "environment")
var environments []interface{}
environments = append(environments, fmt.Sprintf("MODEL_PATH=%s", create.ModelDir))
for _, e := range create.Environments {
environments = append(environments, fmt.Sprintf("%s=%s", e.Key, e.Value))
}
if len(environments) > 0 {
serviceValue["environment"] = environments
}
serviceValue["environment"] = environments
var volumes []interface{}
var defaultVolumes = map[string]string{
"${MODEL_PATH}": "/models",
"${MODEL_PATH}": "${MODEL_PATH}",
}
for k, v := range defaultVolumes {
volumes = append(volumes, fmt.Sprintf("%s:%s", k, v))
@@ -188,23 +185,24 @@ func handleLLMParams(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) e
}
func handleLLMEnv(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) gotenv.Env {
env := make(gotenv.Env)
env["CONTAINER_NAME"] = create.ContainerName
env["MODEL_PATH"] = create.ModelDir
env["VERSION"] = create.Version
env["IMAGE"] = create.Image
env["COMMAND"] = create.Command
envMap := make(gotenv.Env)
envMap["CONTAINER_NAME"] = create.ContainerName
envMap["MODEL_PATH"] = create.ModelDir
envMap["VERSION"] = create.Version
envMap["IMAGE"] = create.Image
envMap["COMMAND"] = create.Command
for i, port := range create.ExposedPorts {
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
hostPortStr := fmt.Sprintf("HOST_PORT_%d", i)
hostIPStr := fmt.Sprintf("HOST_IP_%d", i)
env[containerPortStr] = strconv.Itoa(port.ContainerPort)
env[hostPortStr] = strconv.Itoa(port.HostPort)
env[hostIPStr] = port.HostIP
envMap[containerPortStr] = strconv.Itoa(port.ContainerPort)
envMap[hostPortStr] = strconv.Itoa(port.HostPort)
envMap[hostIPStr] = port.HostIP
}
envStr, _ := gotenv.Marshal(env)
orders := []string{"MODEL_PATH", "COMMAND"}
envStr, _ := env.MarshalWithOrder(envMap, orders)
llm.Env = envStr
return env
return envMap
}
func (t TensorRTLLMService) Create(create request.TensorRTLLMCreate) error {
@@ -227,27 +225,34 @@ func (t TensorRTLLMService) Create(create request.TensorRTLLMCreate) error {
}
tensorrtLLMDir := path.Join(global.Dir.TensorRTLLMDir, create.Name)
filesOP := files.NewFileOp()
if !filesOP.Stat(tensorrtLLMDir) {
_ = filesOP.CreateDir(tensorrtLLMDir, 0644)
filesOp := files.NewFileOp()
if !filesOp.Stat(tensorrtLLMDir) {
_ = filesOp.CreateDir(tensorrtLLMDir, 0644)
}
if create.ModelSpeedup {
if err := handleModelArchive(create.ModelType, create.ModelDir); err != nil {
return err
}
}
tensorrtLLM := &model.TensorRTLLM{
Name: create.Name,
ContainerName: create.ContainerName,
Status: constant.StatusStarting,
ModelType: create.ModelType,
ModelSpeedup: create.ModelSpeedup,
}
if err := handleLLMParams(tensorrtLLM, create); err != nil {
return err
}
env := handleLLMEnv(tensorrtLLM, create)
envMap := handleLLMEnv(tensorrtLLM, create)
llmDir := path.Join(global.Dir.TensorRTLLMDir, create.Name)
envPath := path.Join(llmDir, ".env")
if err := gotenv.Write(env, envPath); err != nil {
if err := env.WriteWithOrder(envMap, envPath, []string{"MODEL_PATH", "COMMAND"}); err != nil {
return err
}
dockerComposePath := path.Join(llmDir, "docker-compose.yml")
if err := filesOP.SaveFile(dockerComposePath, tensorrtLLM.DockerCompose, 0644); err != nil {
if err := filesOp.SaveFile(dockerComposePath, tensorrtLLM.DockerCompose, 0644); err != nil {
return err
}
tensorrtLLM.Status = constant.StatusStarting
@@ -269,18 +274,20 @@ func (t TensorRTLLMService) Update(req request.TensorRTLLMUpdate) error {
return err
}
}
tensorrtLLM.ModelType = req.ModelType
tensorrtLLM.ModelSpeedup = req.ModelSpeedup
tensorrtLLM.ContainerName = req.ContainerName
if err := handleLLMParams(tensorrtLLM, req.TensorRTLLMCreate); err != nil {
return err
}
env := handleLLMEnv(tensorrtLLM, req.TensorRTLLMCreate)
envStr, _ := gotenv.Marshal(env)
envMap := handleLLMEnv(tensorrtLLM, req.TensorRTLLMCreate)
envStr, _ := gotenv.Marshal(envMap)
tensorrtLLM.Env = envStr
llmDir := path.Join(global.Dir.TensorRTLLMDir, tensorrtLLM.Name)
envPath := path.Join(llmDir, ".env")
if err := gotenv.Write(env, envPath); err != nil {
if err := env.WriteWithOrder(envMap, envPath, []string{"MODEL_PATH", "COMMAND"}); err != nil {
return err
}
dockerComposePath := path.Join(llmDir, "docker-compose.yml")
@@ -381,3 +388,51 @@ func syncTensorRTLLMContainerStatus(tensorrtLLM *model.TensorRTLLM) error {
}
return tensorrtLLMRepo.Save(tensorrtLLM)
}
func findModelArchive(modelType string) (string, error) {
const baseDir = "/home/models"
prefix := fmt.Sprintf("FusionXplay_%s_Accelerator", modelType)
entries, err := os.ReadDir(baseDir)
if err != nil {
return "", fmt.Errorf("failed to read %s: %w", baseDir, err)
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if strings.HasPrefix(name, prefix) && strings.HasSuffix(name, ".tar.gz") {
return filepath.Join(baseDir, name), nil
}
}
return "", errors.New(fmt.Sprintf("no FusionXplay_%s_Accelerator*.tar.gz found in /home/models", modelType))
}
func handleModelArchive(modelType string, modelDir string) error {
filePath, err := findModelArchive(modelType)
if err != nil {
return err
}
fileOp := files.NewFileOp()
if err = fileOp.TarGzExtractPro(filePath, modelDir, ""); err != nil {
return err
}
if err = fileOp.ChmodR(path.Join(modelDir, "fusionxpark_accelerator"), 0755, false); err != nil {
return err
}
return nil
}
func getCommand(envStr string) string {
lines := strings.Split(envStr, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "COMMAND=") {
return strings.TrimPrefix(line, "COMMAND=")
}
}
return ""
}
+1 -1
View File
@@ -14,7 +14,7 @@ services:
networks:
- 1panel-network
volumes:
- ${MODEL_PATH}:/models
- ${MODEL_PATH}:${MODEL_PATH}
ipc: host
ulimits:
memlock:
+4
View File
@@ -47,6 +47,10 @@ func InitAgentDB() {
migrations.UpdateCronjobSpec,
migrations.UpdateWebsiteSSLAddColumn,
migrations.AddTensorRTLLMModel,
migrations.UpdateMonitorInterval,
migrations.AddMonitorProcess,
migrations.UpdateCronJob,
migrations.UpdateTensorrtLLM,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+46
View File
@@ -6,6 +6,7 @@ import (
"os"
"os/user"
"path"
"strconv"
"strings"
"time"
@@ -634,3 +635,48 @@ var AddTensorRTLLMModel = &gormigrate.Migration{
return tx.AutoMigrate(&model.TensorRTLLM{})
},
}
var UpdateMonitorInterval = &gormigrate.Migration{
ID: "20251026-update-monitor-interval",
Migrate: func(tx *gorm.DB) error {
var monitorInterval model.Setting
if err := tx.Where("key = ?", "MonitorInterval").First(&monitorInterval).Error; err != nil {
return err
}
interval, _ := strconv.Atoi(monitorInterval.Value)
if interval == 0 {
interval = 300
}
if err := tx.Model(&model.Setting{}).
Where("key = ?", "MonitorInterval").
Updates(map[string]interface{}{"value": fmt.Sprintf("%v", interval*60)}).
Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "DefaultIO", Value: "all"}).Error; err != nil {
return err
}
return nil
},
}
var AddMonitorProcess = &gormigrate.Migration{
ID: "20251202-add-monitor-process",
Migrate: func(tx *gorm.DB) error {
return global.MonitorDB.AutoMigrate(&model.MonitorBase{})
},
}
var UpdateCronJob = &gormigrate.Migration{
ID: "20251202-update-cronjob",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.Cronjob{})
},
}
var UpdateTensorrtLLM = &gormigrate.Migration{
ID: "20251110-update-tensorrt-llm",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.TensorRTLLM{})
},
}
+57 -3
View File
@@ -10,7 +10,7 @@ import (
)
func Write(envMap map[string]string, filename string) error {
content, err := marshal(envMap)
content, err := Marshal(envMap)
if err != nil {
return err
}
@@ -26,7 +26,7 @@ func Write(envMap map[string]string, filename string) error {
return file.Sync()
}
func marshal(envMap map[string]string) (string, error) {
func Marshal(envMap map[string]string) (string, error) {
lines := make([]string, 0, len(envMap))
for k, v := range envMap {
if d, err := strconv.Atoi(v); err == nil && !isStartWithZero(v) {
@@ -37,10 +37,64 @@ func marshal(envMap map[string]string) (string, error) {
lines = append(lines, fmt.Sprintf(`%s="%s"`, k, v))
}
}
sort.Strings(lines)
return strings.Join(lines, "\n"), nil
}
func WriteWithOrder(envMap map[string]string, filename string, orders []string) error {
content, err := MarshalWithOrder(envMap, orders)
if err != nil {
return err
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(content + "\n")
if err != nil {
return err
}
return file.Sync()
}
func MarshalWithOrder(envMap map[string]string, orders []string) (string, error) {
lines := make([]string, 0, len(envMap))
for _, k := range orders {
if v, ok := envMap[k]; ok {
lines = append(lines, formatEnvLine(k, v))
}
}
extraKeys := make([]string, 0)
for k := range envMap {
found := false
for _, okk := range orders {
if k == okk {
found = true
break
}
}
if !found {
extraKeys = append(extraKeys, k)
}
}
sort.Strings(extraKeys)
for _, k := range extraKeys {
lines = append(lines, formatEnvLine(k, envMap[k]))
}
return strings.Join(lines, "\n"), nil
}
func formatEnvLine(k, v string) string {
if d, err := strconv.Atoi(v); err == nil && !isStartWithZero(v) {
return fmt.Sprintf(`%s=%d`, k, d)
} else if hasEvenDoubleQuotes(v) {
return fmt.Sprintf(`%s='%s'`, k, v)
} else {
return fmt.Sprintf(`%s="%s"`, k, v)
}
}
func GetEnvValueByKey(envPath, key string) (string, error) {
envMap, err := godotenv.Read(envPath)
if err != nil {
+3 -3
View File
@@ -17,11 +17,11 @@ download_resources() {
fi
if [ ! -f "1panel-core.service" ]; then
wget https://github.com/1Panel-dev/installer/raw/v2/1panel-core.service
wget https://github.com/1Panel-dev/installer/raw/v2/initscript/1panel-core.service
fi
if [ ! -f "1panel-agent.service" ]; then
wget https://github.com/1Panel-dev/installer/raw/v2/1panel-agent.service
wget https://github.com/1Panel-dev/installer/raw/v2/initscript/1panel-agent.service
fi
if [ ! -d "lang" ]; then
@@ -68,4 +68,4 @@ if [ "$1" = "compress_binary" ]; then
compress_binary "$2" "$3"
else
download_resources
fi
fi
+16
View File
@@ -2,6 +2,8 @@ package v2
import (
"encoding/base64"
"os"
"path"
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/app/dto"
@@ -125,6 +127,20 @@ func (b *BaseApi) GetResponsePage(c *gin.Context) {
helper.SuccessWithData(c, pageCode)
}
func (b *BaseApi) GetWelcomePage(c *gin.Context) {
count, _, _ := logService.PageLoginLog(c, dto.SearchLgLogWithPage{PageInfo: dto.PageInfo{Page: 1, PageSize: 10}})
if count != 1 {
helper.Success(c)
return
}
file, err := os.ReadFile(path.Join(global.CONF.Base.InstallDir, "1panel/welcome/index.html"))
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, string(file))
}
// @Tags Auth
// @Summary Get Setting For Login
// @Success 200 {object} dto.SystemSetting
+1
View File
@@ -16,5 +16,6 @@ func (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {
baseRouter.POST("/login", baseApi.Login)
baseRouter.POST("/logout", baseApi.LogOut)
baseRouter.GET("/setting", baseApi.GetLoginSetting)
baseRouter.GET("/welcome", baseApi.GetWelcomePage)
}
}
+7 -2
View File
@@ -9,6 +9,7 @@ import (
"net/http"
"os"
"path"
"time"
"github.com/1Panel-dev/1Panel/core/init/db"
"github.com/1Panel-dev/1Panel/core/init/geo"
@@ -56,8 +57,12 @@ func Start() {
global.CONF.Conn.BindAddress = fmt.Sprintf("[%s]", global.CONF.Conn.BindAddress)
}
server := &http.Server{
Addr: global.CONF.Conn.BindAddress + ":" + global.CONF.Conn.Port,
Handler: rootRouter,
Addr: global.CONF.Conn.BindAddress + ":" + global.CONF.Conn.Port,
Handler: rootRouter,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 600 * time.Second,
WriteTimeout: 600 * time.Second,
IdleTimeout: 240 * time.Second,
}
ln, err := net.Listen(tcpItem, server.Addr)
if err != nil {
+4
View File
@@ -20,3 +20,7 @@ export const logOutApi = () => {
export const getLoginSetting = () => {
return http.get<Login.LoginSetting>('/core/auth/setting');
};
export const getWelcomePage = () => {
return http.get<string>('/core/auth/welcome');
};
@@ -109,7 +109,7 @@ const mobile = computed(() => {
return globalStore.isMobile();
});
const tableRef = ref();
const tableHeight = ref(0);
const tableHeight = ref<number | string>('');
const menuRef = ref<HTMLElement | null>(null);
const leftSelect = ref(false);
+3 -1
View File
@@ -728,9 +728,11 @@ const message = {
llm: 'TensorRT LLM',
modelDir: 'Model Directory',
commandHelper:
'After /models in the startup command, the model name needs to be completed; if external access is required, set the port in the command to be the same as the application port',
'If external access is needed, set the port in the command to be the same as the application port',
imageAlert:
'Due to the large image size, it is recommended to manually download the image to the server before installation',
modelSpeedup: 'Enable model acceleration',
modelType: 'Model type',
},
},
container: {
+3 -1
View File
@@ -729,9 +729,11 @@ const message = {
llm: 'TensorRT LLM',
modelDir: 'Directorio del Modelo',
commandHelper:
'Después de /models en el comando de inicio, se debe completar el nombre del modelo; si se requiere acceso externo, configure el puerto en el comando para que sea el mismo que el puerto de la aplicación',
'Si se necesita acceso externo, establezca el puerto en el comando para que sea el mismo que el puerto de la aplicación',
imageAlert:
'Debido al gran tamaño de la imagen, se recomienda descargar manualmente la imagen al servidor antes de la instalación',
modelSpeedup: 'Habilitar aceleración de modelo',
modelType: 'Tipo de modelo',
},
},
container: {
+3 -1
View File
@@ -716,9 +716,11 @@ const message = {
llm: 'TensorRT LLM',
modelDir: 'モデルディレクトリ',
commandHelper:
'起動コマンドの /models の後にはモデル名を補完する必要があります;外部アクセスが必要な場合は、コマンド内のポートをアプリケーションポートと同じに設定してください',
'外部アクセスが必要な場合は、コマンド内のポートをアプリケーションポートと同じに設定してください',
imageAlert:
'イメージサイズが大きいため、インストール前にサーバーにイメージを手動でダウンロードすることをお勧めします',
modelSpeedup: 'モデル加速を有効化',
modelType: 'モデルタイプ',
},
},
container: {
+3 -2
View File
@@ -711,9 +711,10 @@ const message = {
tensorRT: {
llm: 'TensorRT LLM',
modelDir: '모델 디렉토리',
commandHelper:
'시작 명령의 /models 뒤에는 모델 이름을 완성해야 합니다; 외부 액세스가 필요한 경우 명령의 포트를 애플리케이션 포트와 동일하게 설정하세요',
commandHelper: '외부 액세스가 필요한 경우 명령에서 포트를 애플리케이션 포트와 동일하게 설정하십시오',
imageAlert: '이미지 크기가 크므로 설치 전에 서버에 이미지를 수동으로 다운로드하는 것이 좋습니다',
modelSpeedup: '모델 가속 활성화',
modelType: '모델 유형',
},
},
container: {
+3 -2
View File
@@ -728,10 +728,11 @@ const message = {
tensorRT: {
llm: 'TensorRT LLM',
modelDir: 'Direktori Model',
commandHelper:
'Selepas /models dalam arahan permulaan, nama model perlu dilengkapkan; jika akses luar diperlukan, tetapkan port dalam arahan sama dengan port aplikasi',
commandHelper: 'Jika akses luar diperlukan, tetapkan port dalam arahan sama dengan port aplikasi',
imageAlert:
'Disebabkan saiz imej yang besar, disyorkan untuk memuat turun imej secara manual ke pelayan sebelum pemasangan',
modelSpeedup: 'Dayakan pecutan model',
modelType: 'Jenis model',
},
},
container: {
+3 -1
View File
@@ -725,9 +725,11 @@ const message = {
llm: 'TensorRT LLM',
modelDir: 'Diretório do Modelo',
commandHelper:
'Após /models no comando de inicialização, o nome do modelo precisa ser completado; se for necessário acesso externo, defina a porta no comando para ser a mesma que a porta do aplicativo',
'Se for necessário acesso externo, defina a porta no comando para ser a mesma que a porta do aplicativo',
imageAlert:
'Devido ao grande tamanho da imagem, recomenda-se baixar manualmente a imagem para o servidor antes da instalação',
modelSpeedup: 'Ativar aceleração de modelo',
modelType: 'Tipo de modelo',
},
},
container: {
+3 -2
View File
@@ -722,10 +722,11 @@ const message = {
tensorRT: {
llm: 'TensorRT LLM',
modelDir: 'Каталог модели',
commandHelper:
'После /models в команде запуска необходимо указать имя модели; если требуется внешний доступ, установите порт в команде таким же, как порт приложения',
commandHelper: 'Если требуется внешний доступ, установите порт в команде таким же, как порт приложения',
imageAlert:
'Из-за большого размера образа рекомендуется вручную загрузить образ на сервер перед установкой',
modelSpeedup: 'Включить ускорение модели',
modelType: 'Тип модели',
},
},
container: {
+3 -1
View File
@@ -737,9 +737,11 @@ const message = {
llm: 'TensorRT LLM',
modelDir: 'Model Dizini',
commandHelper:
'Başlatma komutundaki /models sonrasında model adı tamamlanmalıdır; harici erişim gerekiyorsa, komuttaki bağlantı noktasını uygulama bağlantı noktasıyla aynı olacak şekilde ayarlayın',
'Harici erişim gerekiyorsa, komuttaki bağlantı noktasını uygulama bağlantı noktasıyla aynı olacak şekilde ayarlayın',
imageAlert:
'Görüntü boyutu büyük olduğundan, kurulumdan önce görüntüyü sunucuya manuel olarak indirmeniz önerilir',
modelSpeedup: 'Model hızlandırmayı etkinleştir',
modelType: 'Model türü',
},
},
container: {
+3 -1
View File
@@ -700,8 +700,10 @@ const message = {
tensorRT: {
llm: 'TensorRT LLM',
modelDir: '模型目錄',
commandHelper: '啟動指令中的 /models 後需補全模型名稱;若需外部訪問,請將指令中的埠設定為與應用埠相同',
commandHelper: '若需外部訪問,請將命令中的端口設置為與應用端口相同',
imageAlert: '由於鏡像較大,建議先手動將鏡像下載到伺服器後再進行安裝',
modelSpeedup: '啟用模型加速',
modelType: '模型類型',
},
},
container: {
+3 -1
View File
@@ -700,8 +700,10 @@ const message = {
tensorRT: {
llm: 'TensorRT LLM',
modelDir: '模型目录',
commandHelper: '启动命令中的 /models 后需补全模型名称;若需外部访问,请将命令中的端口设置为与应用端口相同',
commandHelper: '若需外部访问,请将命令中的端口设置为与应用端口相同',
imageAlert: '由于镜像较大,建议先手动将镜像下载到服务器后再进行安装',
modelSpeedup: '启用模型加速',
modelType: '模型类型',
},
},
container: {
@@ -192,7 +192,7 @@ const buttons = [
{
label: i18n.global.t('commons.button.start'),
disabled: (row: AI.TensorRTLLM) => {
return row.status === 'running';
return row.status === 'Running';
},
click: (row: AI.TensorRTLLM) => {
operate(row, 'start');
@@ -201,7 +201,7 @@ const buttons = [
{
label: i18n.global.t('commons.button.stop'),
disabled: (row: AI.TensorRTLLM) => {
return row.status !== 'running';
return row.status !== 'Running';
},
click: (row: AI.TensorRTLLM) => {
operate(row, 'stop');
@@ -209,9 +209,6 @@ const buttons = [
},
{
label: i18n.global.t('commons.button.restart'),
disabled: (row: AI.TensorRTLLM) => {
return row.status !== 'running';
},
click: (row: AI.TensorRTLLM) => {
operate(row, 'restart');
},
@@ -14,6 +14,20 @@
<el-form-item :label="$t('app.version')" prop="version">
<el-input v-model.trim="tensorRTLLM.version" />
</el-form-item>
<div v-if="isFxplay">
<el-form-item :label="$t('aiTools.tensorRT.modelSpeedup')" prop="modelSpeedup">
<el-switch v-model="tensorRTLLM.modelSpeedup" @change="changeModelSpeedup"></el-switch>
</el-form-item>
<el-form-item
:label="$t('aiTools.tensorRT.modelType')"
prop="modelType"
v-if="tensorRTLLM.modelSpeedup"
>
<el-select v-model="tensorRTLLM.modelType">
<el-option label="Qwen3" value="Qwen3" />
</el-select>
</el-form-item>
</div>
<el-form-item :label="$t('aiTools.tensorRT.modelDir')" prop="modelDir">
<el-input v-model="tensorRTLLM.modelDir">
<template #prepend>
@@ -62,6 +76,8 @@ import i18n from '@/lang';
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 } = useGlobalStore();
const loading = ref(false);
const mode = ref('create');
@@ -73,10 +89,12 @@ const newTensorRTLLM = () => {
version: '1.2.0rc0',
modelDir: '',
image: 'nvcr.io/nvidia/tensorrt-llm/release',
command: 'bash -c "trtllm-serve /models/ --host 0.0.0.0 --port 8000"',
command: 'bash -c "trtllm-serve ${MODEL_PATH} --host 0.0.0.0 --port 8000"',
exposedPorts: [],
environments: [],
volumes: [],
modelSpeedup: false,
modelType: 'Qwen3',
};
};
const modelDirRef = ref();
@@ -101,6 +119,7 @@ const openEdit = (rowData: any): void => {
if (tensorRTLLM.value.exposedPorts == null) {
tensorRTLLM.value.exposedPorts = [];
}
tensorRTLLM.value.command = rowData.command.slice(1, -1).replace(/^'|'$/g, '').replace(/\\"/g, '"');
drawerVisiable.value = true;
};
@@ -112,6 +131,15 @@ const getModelDir = (path: string) => {
tensorRTLLM.value.modelDir = path;
};
const changeModelSpeedup = () => {
if (tensorRTLLM.value.modelSpeedup) {
tensorRTLLM.value.command =
'bash -c "${MODEL_PATH}/fusionxpark_accelerator --model_path ${MODEL_PATH} --host 0.0.0.0 --port 8000"';
} else {
tensorRTLLM.value.command = 'bash -c "trtllm-serve ${MODEL_PATH} --host 0.0.0.0 --port 8000"';
}
};
const rules = reactive({
name: [Rules.requiredInput],
version: [Rules.requiredInput],
@@ -119,6 +147,7 @@ const rules = reactive({
containerName: [Rules.requiredInput],
image: [Rules.requiredInput],
command: [Rules.requiredInput],
modelType: [Rules.requiredSelect],
});
const formRef = ref<FormInstance>();
+26
View File
@@ -302,6 +302,10 @@
<LicenseImport ref="licenseRef" />
<QuickJump @search="onLoadBaseInfo(false, 'all')" ref="quickJumpRef" />
<DialogPro v-model="welcomeOpen" size="w-70" id="welcomeDialog">
<div ref="shadowContainer" />
</DialogPro>
</div>
</template>
@@ -323,6 +327,7 @@ import { getSettingInfo, listAllSimpleNodes, loadUpgradeInfo } from '@/api/modul
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
import { routerToFileWithPath, routerToPath } from '@/utils/router';
import { getWelcomePage } from '@/api/modules/auth';
const router = useRouter();
const globalStore = GlobalStore();
@@ -331,6 +336,9 @@ const appRef = ref();
const isSafety = ref();
const welcomeOpen = ref();
const shadowContainer = ref();
const chartOption = ref('network');
let timer: NodeJS.Timer | null = null;
let isInit = ref<boolean>(true);
@@ -692,6 +700,21 @@ const fetchData = () => {
onLoadSimpleNode();
};
const loadWelcome = async () => {
await getWelcomePage().then((res) => {
if (res.data) {
welcomeOpen.value = true;
nextTick(() => {
const shadowRoot = shadowContainer.value.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = res.data;
});
localStorage.setItem('welcomeShow', 'false');
} else {
localStorage.setItem('welcomeShow', 'false');
}
});
};
onBeforeRouteUpdate((to, from, next) => {
if (to.name === 'home') {
clearTimer();
@@ -707,6 +730,9 @@ const clearTimer = () => {
onMounted(() => {
fetchData();
if (localStorage.getItem('welcomeShow') !== 'false') {
loadWelcome();
}
});
onBeforeUnmount(() => {
@@ -0,0 +1,45 @@
<template>
<el-tabs type="border-card">
<el-tab-pane :label="$t('commons.table.port')">
<PortConfig :exposedPorts="runtime.exposedPorts" />
</el-tab-pane>
<el-tab-pane :label="$t('runtime.environment')">
<Environment :environments="runtime.environments" />
</el-tab-pane>
<el-tab-pane :label="$t('container.mount')">
<Volumes :volumes="runtime.volumes" />
</el-tab-pane>
<el-tab-pane :label="$t('runtime.extraHosts')">
<ExtraHosts :extraHosts="runtime.extraHosts" />
</el-tab-pane>
</el-tabs>
</template>
<script lang="ts" setup>
import PortConfig from '@/views/website/runtime/components/port/index.vue';
import Environment from '@/views/website/runtime/components/environment/index.vue';
import Volumes from '@/views/website/runtime/components/volume/index.vue';
import ExtraHosts from '@/views/website/runtime/components/extra_hosts/index.vue';
import { useVModel } from '@vueuse/core';
import type { PropType } from 'vue';
interface RuntimeConfigFields {
exposedPorts: any[];
environments: any[];
volumes: any[];
extraHosts: any[];
}
const props = defineProps({
modelValue: {
type: Object as PropType<RuntimeConfigFields>,
required: true,
},
});
const emit = defineEmits<{
'update:modelValue': [value: RuntimeConfigFields];
}>();
const runtime = useVModel(props, 'modelValue', emit);
</script>