mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-02-28 19:14:13 +08:00
feat: Merge code of gpu from dev (#7982)
This commit is contained in:
parent
efa2fa9eac
commit
90eca45347
256
agent/app/api/v2/ai.go
Normal file
256
agent/app/api/v2/ai.go
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Create Ollama model
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OllamaModelName true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/ollama/model [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型 [name]","formatEN":"add Ollama model [name]"}
|
||||||
|
func (b *BaseApi) CreateOllamaModel(c *gin.Context) {
|
||||||
|
var req dto.OllamaModelName
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := aiToolService.Create(req.Name); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Rereate Ollama model
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OllamaModelName true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/ollama/model/recreate [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型重试 [name]","formatEN":"re-add Ollama model [name]"}
|
||||||
|
func (b *BaseApi) RecreateOllamaModel(c *gin.Context) {
|
||||||
|
var req dto.OllamaModelName
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := aiToolService.Recreate(req.Name); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Close Ollama model conn
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OllamaModelName true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/ollama/model/close [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"关闭 Ollama 模型连接 [name]","formatEN":"close conn for Ollama model [name]"}
|
||||||
|
func (b *BaseApi) CloseOllamaModel(c *gin.Context) {
|
||||||
|
var req dto.OllamaModelName
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := aiToolService.Close(req.Name); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Sync Ollama model list
|
||||||
|
// @Success 200 {array} dto.OllamaModelDropList
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/ollama/model/sync [post]
|
||||||
|
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 Ollama 模型列表","formatEN":"sync Ollama model list"}
|
||||||
|
func (b *BaseApi) SyncOllamaModel(c *gin.Context) {
|
||||||
|
list, err := aiToolService.Sync()
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Page Ollama models
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.SearchWithPage true "request"
|
||||||
|
// @Success 200 {object} dto.PageResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/ollama/model/search [post]
|
||||||
|
func (b *BaseApi) SearchOllamaModel(c *gin.Context) {
|
||||||
|
var req dto.SearchWithPage
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total, list, err := aiToolService.Search(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Items: list,
|
||||||
|
Total: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Page Ollama models
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OllamaModelName true "request"
|
||||||
|
// @Success 200 {string} details
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/ollama/model/load [post]
|
||||||
|
func (b *BaseApi) LoadOllamaModelDetail(c *gin.Context) {
|
||||||
|
var req dto.OllamaModelName
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
detail, err := aiToolService.LoadDetail(req.Name)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Delete Ollama model
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ForceDelete true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/ollama/model/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"ollama_models","output_column":"name","output_value":"names"}],"formatZH":"删除 Ollama 模型 [names]","formatEN":"remove Ollama model [names]"}
|
||||||
|
func (b *BaseApi) DeleteOllamaModel(c *gin.Context) {
|
||||||
|
var req dto.ForceDelete
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := aiToolService.Delete(req); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Load gpu / xpu info
|
||||||
|
// @Accept json
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/gpu/load [get]
|
||||||
|
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
|
||||||
|
ok, client := gpu.New()
|
||||||
|
if ok {
|
||||||
|
info, err := client.LoadGpuInfo()
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, info)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xpuOK, xpuClient := xpu.New()
|
||||||
|
if xpuOK {
|
||||||
|
info, err := xpuClient.LoadGpuInfo()
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, info)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, &common.GpuInfo{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Bind domain
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OllamaBindDomain true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/domain/bind [post]
|
||||||
|
func (b *BaseApi) BindDomain(c *gin.Context) {
|
||||||
|
var req dto.OllamaBindDomain
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := aiToolService.BindDomain(req); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Get bind domain
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OllamaBindDomainReq true "request"
|
||||||
|
// @Success 200 {object} dto.OllamaBindDomainRes
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/domain/get [post]
|
||||||
|
func (b *BaseApi) GetBindDomain(c *gin.Context) {
|
||||||
|
var req dto.OllamaBindDomainReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := aiToolService.GetBindDomain(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tags AI
|
||||||
|
// Summary Update bind domain
|
||||||
|
// Accept json
|
||||||
|
// Param request body dto.OllamaBindDomain true "request"
|
||||||
|
// Success 200
|
||||||
|
// Security ApiKeyAuth
|
||||||
|
// Security Timestamp
|
||||||
|
// Router /ai/domain/update [post]
|
||||||
|
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
|
||||||
|
var req dto.OllamaBindDomain
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := aiToolService.UpdateBindDomain(req); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
@ -16,6 +16,8 @@ var (
|
|||||||
appService = service.NewIAppService()
|
appService = service.NewIAppService()
|
||||||
appInstallService = service.NewIAppInstalledService()
|
appInstallService = service.NewIAppInstalledService()
|
||||||
|
|
||||||
|
aiToolService = service.NewIAIToolService()
|
||||||
|
|
||||||
containerService = service.NewIContainerService()
|
containerService = service.NewIContainerService()
|
||||||
composeTemplateService = service.NewIComposeTemplateService()
|
composeTemplateService = service.NewIComposeTemplateService()
|
||||||
imageRepoService = service.NewIImageRepoService()
|
imageRepoService = service.NewIImageRepoService()
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/terminal"
|
"github.com/1Panel-dev/1Panel/agent/utils/terminal"
|
||||||
@ -165,6 +166,70 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) OllamaWsSsh(c *gin.Context) {
|
||||||
|
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer wsConn.Close()
|
||||||
|
|
||||||
|
if global.CONF.Base.IsDemo {
|
||||||
|
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := c.Query("name")
|
||||||
|
if cmd.CheckIllegal(name) {
|
||||||
|
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container, err := service.LoadContainerName()
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, " load container name for ollama failed")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commands := []string{"ollama", "run", name}
|
||||||
|
|
||||||
|
pidMap := loadMapFromDockerTop(container)
|
||||||
|
fmt.Println("pidMap")
|
||||||
|
for k, v := range pidMap {
|
||||||
|
fmt.Println(k, v)
|
||||||
|
}
|
||||||
|
itemCmds := append([]string{"exec", "-it", container}, commands...)
|
||||||
|
slave, err := terminal.NewCommand(itemCmds)
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer killBash(container, strings.Join(commands, " "), pidMap)
|
||||||
|
defer slave.Close()
|
||||||
|
|
||||||
|
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
quitChan := make(chan bool, 3)
|
||||||
|
tty.Start(quitChan)
|
||||||
|
go slave.Wait(quitChan)
|
||||||
|
|
||||||
|
<-quitChan
|
||||||
|
|
||||||
|
global.LOG.Info("websocket finished")
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func wshandleError(ws *websocket.Conn, err error) bool {
|
func wshandleError(ws *websocket.Conn, err error) bool {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("handler ws faled:, err: %v", err)
|
global.LOG.Errorf("handler ws faled:, err: %v", err)
|
||||||
|
44
agent/app/dto/ai.go
Normal file
44
agent/app/dto/ai.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type OllamaModelInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
From string `json:"from"`
|
||||||
|
LogFileExist bool `json:"logFileExist"`
|
||||||
|
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OllamaModelDropList struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OllamaModelName struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OllamaBindDomain struct {
|
||||||
|
Domain string `json:"domain" validate:"required"`
|
||||||
|
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||||
|
SSLID uint `json:"sslID"`
|
||||||
|
WebsiteID uint `json:"websiteID"`
|
||||||
|
IPList string `json:"ipList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OllamaBindDomainReq struct {
|
||||||
|
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OllamaBindDomainRes struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
SSLID uint `json:"sslID"`
|
||||||
|
AllowIPs []string `json:"allowIPs"`
|
||||||
|
WebsiteID uint `json:"websiteID"`
|
||||||
|
ConnUrl string `json:"connUrl"`
|
||||||
|
}
|
@ -71,3 +71,8 @@ type UpdateGroup struct {
|
|||||||
type OperateWithTask struct {
|
type OperateWithTask struct {
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ForceDelete struct {
|
||||||
|
IDs []uint `json:"ids"`
|
||||||
|
ForceDelete bool `json:"forceDelete"`
|
||||||
|
}
|
||||||
|
11
agent/app/model/ai.go
Normal file
11
agent/app/model/ai.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type OllamaModel struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
From string `json:"from"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
69
agent/app/repo/ai.go
Normal file
69
agent/app/repo/ai.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AiRepo struct{}
|
||||||
|
|
||||||
|
type IAiRepo interface {
|
||||||
|
Get(opts ...DBOption) (model.OllamaModel, error)
|
||||||
|
List(opts ...DBOption) ([]model.OllamaModel, error)
|
||||||
|
Page(limit, offset int, opts ...DBOption) (int64, []model.OllamaModel, error)
|
||||||
|
Create(cronjob *model.OllamaModel) error
|
||||||
|
Update(id uint, vars map[string]interface{}) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIAiRepo() IAiRepo {
|
||||||
|
return &AiRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AiRepo) Get(opts ...DBOption) (model.OllamaModel, error) {
|
||||||
|
var item model.OllamaModel
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.First(&item).Error
|
||||||
|
return item, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AiRepo) List(opts ...DBOption) ([]model.OllamaModel, error) {
|
||||||
|
var list []model.OllamaModel
|
||||||
|
db := global.DB.Model(&model.OllamaModel{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.Find(&list).Error
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AiRepo) Page(page, size int, opts ...DBOption) (int64, []model.OllamaModel, error) {
|
||||||
|
var list []model.OllamaModel
|
||||||
|
db := global.DB.Model(&model.OllamaModel{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&list).Error
|
||||||
|
return count, list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AiRepo) Create(item *model.OllamaModel) error {
|
||||||
|
return global.DB.Create(item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AiRepo) Update(id uint, vars map[string]interface{}) error {
|
||||||
|
return global.DB.Model(&model.OllamaModel{}).Where("id = ?", id).Updates(vars).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AiRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Delete(&model.OllamaModel{}).Error
|
||||||
|
}
|
393
agent/app/service/ai.go
Normal file
393
agent/app/service/ai.go
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AIToolService struct{}
|
||||||
|
|
||||||
|
type IAIToolService interface {
|
||||||
|
Search(search dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error)
|
||||||
|
Create(name string) error
|
||||||
|
Close(name string) error
|
||||||
|
Recreate(name string) error
|
||||||
|
Delete(req dto.ForceDelete) error
|
||||||
|
Sync() ([]dto.OllamaModelDropList, error)
|
||||||
|
LoadDetail(name string) (string, error)
|
||||||
|
BindDomain(req dto.OllamaBindDomain) error
|
||||||
|
GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error)
|
||||||
|
UpdateBindDomain(req dto.OllamaBindDomain) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIAIToolService() IAIToolService {
|
||||||
|
return &AIToolService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) Search(req dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error) {
|
||||||
|
var options []repo.DBOption
|
||||||
|
if len(req.Info) != 0 {
|
||||||
|
options = append(options, repo.WithByLikeName(req.Info))
|
||||||
|
}
|
||||||
|
total, list, err := aiRepo.Page(req.Page, req.PageSize, options...)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
var dtoLists []dto.OllamaModelInfo
|
||||||
|
for _, itemModel := range list {
|
||||||
|
var item dto.OllamaModelInfo
|
||||||
|
if err := copier.Copy(&item, &itemModel); err != nil {
|
||||||
|
return 0, nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||||
|
}
|
||||||
|
logPath := path.Join(global.Dir.DataDir, "log", "AITools", itemModel.Name)
|
||||||
|
if _, err := os.Stat(logPath); err == nil {
|
||||||
|
item.LogFileExist = true
|
||||||
|
}
|
||||||
|
dtoLists = append(dtoLists, item)
|
||||||
|
}
|
||||||
|
return int64(total), dtoLists, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) LoadDetail(name string) (string, error) {
|
||||||
|
if cmd.CheckIllegal(name) {
|
||||||
|
return "", buserr.New("ErrCmdIllegal")
|
||||||
|
}
|
||||||
|
containerName, err := LoadContainerName()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
stdout, err := cmd.Execf("docker exec %s ollama show %s", containerName, name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return stdout, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) Create(name string) error {
|
||||||
|
if cmd.CheckIllegal(name) {
|
||||||
|
return buserr.New("ErrCmdIllegal")
|
||||||
|
}
|
||||||
|
modelInfo, _ := aiRepo.Get(repo.WithByName(name))
|
||||||
|
if modelInfo.ID != 0 {
|
||||||
|
return buserr.New("ErrRecordExist")
|
||||||
|
}
|
||||||
|
containerName, err := LoadContainerName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logItem := path.Join(global.Dir.DataDir, "log", "AITools", name)
|
||||||
|
if _, err := os.Stat(path.Dir(logItem)); err != nil && os.IsNotExist(err) {
|
||||||
|
if err = os.MkdirAll(path.Dir(logItem), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info := model.OllamaModel{
|
||||||
|
Name: name,
|
||||||
|
From: "local",
|
||||||
|
Status: constant.StatusWaiting,
|
||||||
|
}
|
||||||
|
if err := aiRepo.Create(&info); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go pullOllamaModel(file, containerName, info)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) Close(name string) error {
|
||||||
|
if cmd.CheckIllegal(name) {
|
||||||
|
return buserr.New("ErrCmdIllegal")
|
||||||
|
}
|
||||||
|
containerName, err := LoadContainerName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stdout, err := cmd.Execf("docker exec %s ollama stop %s", containerName, name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("handle ollama stop %s failed, stdout: %s, err: %v", name, stdout, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) Recreate(name string) error {
|
||||||
|
if cmd.CheckIllegal(name) {
|
||||||
|
return buserr.New("ErrCmdIllegal")
|
||||||
|
}
|
||||||
|
modelInfo, _ := aiRepo.Get(repo.WithByName(name))
|
||||||
|
if modelInfo.ID == 0 {
|
||||||
|
return buserr.New("ErrRecordNotFound")
|
||||||
|
}
|
||||||
|
containerName, err := LoadContainerName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := aiRepo.Update(modelInfo.ID, map[string]interface{}{"status": constant.StatusWaiting, "from": "local"}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logItem := path.Join(global.Dir.DataDir, "log", "AITools", name)
|
||||||
|
if _, err := os.Stat(path.Dir(logItem)); err != nil && os.IsNotExist(err) {
|
||||||
|
if err = os.MkdirAll(path.Dir(logItem), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go pullOllamaModel(file, containerName, modelInfo)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) Delete(req dto.ForceDelete) error {
|
||||||
|
ollamaList, _ := aiRepo.List(repo.WithByIDs(req.IDs))
|
||||||
|
if len(ollamaList) == 0 {
|
||||||
|
return buserr.New("ErrRecordNotFound")
|
||||||
|
}
|
||||||
|
containerName, err := LoadContainerName()
|
||||||
|
if err != nil && !req.ForceDelete {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, item := range ollamaList {
|
||||||
|
if item.Status != constant.StatusDeleted {
|
||||||
|
stdout, err := cmd.Execf("docker exec %s ollama rm %s", containerName, item.Name)
|
||||||
|
if err != nil && !req.ForceDelete {
|
||||||
|
return fmt.Errorf("handle ollama rm %s failed, stdout: %s, err: %v", item.Name, stdout, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = aiRepo.Delete(repo.WithByID(item.ID))
|
||||||
|
logItem := path.Join(global.Dir.DataDir, "log", "AITools", item.Name)
|
||||||
|
_ = os.Remove(logItem)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {
|
||||||
|
containerName, err := LoadContainerName()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stdout, err := cmd.Execf("docker exec %s ollama list", containerName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var list []model.OllamaModel
|
||||||
|
lines := strings.Split(stdout, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) < 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if parts[0] == "NAME" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, model.OllamaModel{Name: parts[0], Size: parts[2] + " " + parts[3]})
|
||||||
|
}
|
||||||
|
listInDB, _ := aiRepo.List()
|
||||||
|
var dropList []dto.OllamaModelDropList
|
||||||
|
for _, itemModel := range listInDB {
|
||||||
|
isExit := false
|
||||||
|
for i := 0; i < len(list); i++ {
|
||||||
|
if list[i].Name == itemModel.Name {
|
||||||
|
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusSuccess, "message": "", "size": list[i].Size})
|
||||||
|
list = append(list[:i], list[(i+1):]...)
|
||||||
|
isExit = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isExit && itemModel.Status != constant.StatusWaiting {
|
||||||
|
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusDeleted, "message": "not exist", "size": ""})
|
||||||
|
dropList = append(dropList, dto.OllamaModelDropList{ID: itemModel.ID, Name: itemModel.Name})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, item := range list {
|
||||||
|
item.Status = constant.StatusSuccess
|
||||||
|
item.From = "remote"
|
||||||
|
_ = aiRepo.Create(&item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dropList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) BindDomain(req dto.OllamaBindDomain) error {
|
||||||
|
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||||
|
if nginxInstall.ID == 0 {
|
||||||
|
return buserr.New("ErrOpenrestyInstall")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
ipList []string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if len(req.IPList) > 0 {
|
||||||
|
ipList, err = common.HandleIPList(req.IPList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createWebsiteReq := request.WebsiteCreate{
|
||||||
|
Domains: []request.WebsiteDomain{{Domain: req.Domain}},
|
||||||
|
Alias: strings.ToLower(req.Domain),
|
||||||
|
Type: constant.Deployment,
|
||||||
|
AppType: constant.InstalledApp,
|
||||||
|
AppInstallID: req.AppInstallID,
|
||||||
|
}
|
||||||
|
websiteService := NewIWebsiteService()
|
||||||
|
if err := websiteService.CreateWebsite(createWebsiteReq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(ipList) > 0 {
|
||||||
|
if err = ConfigAllowIPs(ipList, website); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.SSLID > 0 {
|
||||||
|
sslReq := request.WebsiteHTTPSOp{
|
||||||
|
WebsiteID: website.ID,
|
||||||
|
Enable: true,
|
||||||
|
Type: "existed",
|
||||||
|
WebsiteSSLID: req.SSLID,
|
||||||
|
HttpConfig: "HTTPSOnly",
|
||||||
|
}
|
||||||
|
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = ConfigAIProxy(website); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error) {
|
||||||
|
install, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res := &dto.OllamaBindDomainRes{}
|
||||||
|
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(install.ID))
|
||||||
|
if website.ID == 0 {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
res.WebsiteID = website.ID
|
||||||
|
res.Domain = website.PrimaryDomain
|
||||||
|
if website.WebsiteSSLID > 0 {
|
||||||
|
res.SSLID = website.WebsiteSSLID
|
||||||
|
}
|
||||||
|
res.ConnUrl = fmt.Sprintf("%s://%s", strings.ToLower(website.Protocol), website.PrimaryDomain)
|
||||||
|
res.AllowIPs = GetAllowIps(website)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {
|
||||||
|
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||||
|
if nginxInstall.ID == 0 {
|
||||||
|
return buserr.New("ErrOpenrestyInstall")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
ipList []string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if len(req.IPList) > 0 {
|
||||||
|
ipList, err = common.HandleIPList(req.IPList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
websiteService := NewIWebsiteService()
|
||||||
|
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = ConfigAllowIPs(ipList, website); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if req.SSLID > 0 {
|
||||||
|
sslReq := request.WebsiteHTTPSOp{
|
||||||
|
WebsiteID: website.ID,
|
||||||
|
Enable: true,
|
||||||
|
Type: "existed",
|
||||||
|
WebsiteSSLID: req.SSLID,
|
||||||
|
HttpConfig: "HTTPSOnly",
|
||||||
|
}
|
||||||
|
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if website.WebsiteSSLID > 0 && req.SSLID == 0 {
|
||||||
|
sslReq := request.WebsiteHTTPSOp{
|
||||||
|
WebsiteID: website.ID,
|
||||||
|
Enable: false,
|
||||||
|
}
|
||||||
|
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadContainerName() (string, error) {
|
||||||
|
ollamaBaseInfo, err := appInstallRepo.LoadBaseInfo("ollama", "")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("ollama service is not found, err: %v", err)
|
||||||
|
}
|
||||||
|
if ollamaBaseInfo.Status != constant.Running {
|
||||||
|
return "", fmt.Errorf("container %s of ollama is not running, please check and retry!", ollamaBaseInfo.ContainerName)
|
||||||
|
}
|
||||||
|
return ollamaBaseInfo.ContainerName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pullOllamaModel(file *os.File, containerName string, info model.OllamaModel) {
|
||||||
|
defer file.Close()
|
||||||
|
cmd := exec.Command("docker", "exec", containerName, "ollama", "pull", info.Name)
|
||||||
|
multiWriter := io.MultiWriter(os.Stdout, file)
|
||||||
|
cmd.Stdout = multiWriter
|
||||||
|
cmd.Stderr = multiWriter
|
||||||
|
_ = cmd.Run()
|
||||||
|
itemSize, err := loadModelSize(info.Name, containerName)
|
||||||
|
if len(itemSize) != 0 {
|
||||||
|
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusSuccess, "size": itemSize})
|
||||||
|
} else {
|
||||||
|
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||||
|
}
|
||||||
|
_, _ = file.WriteString("ollama pull completed!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadModelSize(name string, containerName string) (string, error) {
|
||||||
|
stdout, err := cmd.Execf("docker exec %s ollama list | grep %s", containerName, name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(stdout), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) < 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return parts[2] + " " + parts[3], nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no such model %s in ollama list, std: %s", name, string(stdout))
|
||||||
|
}
|
@ -17,10 +17,11 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
"github.com/shirou/gopsutil/v3/cpu"
|
||||||
"github.com/shirou/gopsutil/v3/disk"
|
"github.com/shirou/gopsutil/v3/disk"
|
||||||
"github.com/shirou/gopsutil/v3/host"
|
"github.com/shirou/gopsutil/v3/host"
|
||||||
@ -492,7 +493,17 @@ func loadDiskInfo() []dto.DiskInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadGPUInfo() []dto.GPUInfo {
|
func loadGPUInfo() []dto.GPUInfo {
|
||||||
list := xpack.LoadGpuInfo()
|
ok, client := gpu.New()
|
||||||
|
var list []interface{}
|
||||||
|
if ok {
|
||||||
|
info, err := client.LoadGpuInfo()
|
||||||
|
if err != nil || len(info.GPUs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, item := range info.GPUs {
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(list) == 0 {
|
if len(list) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -542,7 +553,17 @@ func ArryContains(arr []string, element string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadXpuInfo() []dto.XPUInfo {
|
func loadXpuInfo() []dto.XPUInfo {
|
||||||
list := xpack.LoadXpuInfo()
|
var list []interface{}
|
||||||
|
ok, xpuClient := xpu.New()
|
||||||
|
if ok {
|
||||||
|
xpus, err := xpuClient.LoadDashData()
|
||||||
|
if err != nil || len(xpus) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, item := range xpus {
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(list) == 0 {
|
if len(list) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ var (
|
|||||||
launcherRepo = repo.NewILauncherRepo()
|
launcherRepo = repo.NewILauncherRepo()
|
||||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||||
|
|
||||||
|
aiRepo = repo.NewIAiRepo()
|
||||||
|
|
||||||
mysqlRepo = repo.NewIMysqlRepo()
|
mysqlRepo = repo.NewIMysqlRepo()
|
||||||
postgresqlRepo = repo.NewIPostgresqlRepo()
|
postgresqlRepo = repo.NewIPostgresqlRepo()
|
||||||
databaseRepo = repo.NewIDatabaseRepo()
|
databaseRepo = repo.NewIDatabaseRepo()
|
||||||
|
@ -1231,3 +1231,55 @@ func openProxyCache(website model.Website) error {
|
|||||||
proxyCachePath := fmt.Sprintf("/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:5m max_size=1g inactive=24h", website.Alias, website.Alias)
|
proxyCachePath := fmt.Sprintf("/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:5m max_size=1g inactive=24h", website.Alias, website.Alias)
|
||||||
return updateNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path", Params: []string{proxyCachePath}}}, &website)
|
return updateNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path", Params: []string{proxyCachePath}}}, &website)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConfigAllowIPs(ips []string, website model.Website) error {
|
||||||
|
nginxFull, err := getNginxFull(&website)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nginxConfig := nginxFull.SiteConfig
|
||||||
|
config := nginxFull.SiteConfig.Config
|
||||||
|
server := config.FindServers()[0]
|
||||||
|
server.RemoveDirective("allow", nil)
|
||||||
|
server.RemoveDirective("deny", nil)
|
||||||
|
if len(ips) > 0 {
|
||||||
|
server.UpdateAllowIPs(ips)
|
||||||
|
}
|
||||||
|
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nginxCheckAndReload(nginxConfig.OldContent, config.FilePath, nginxFull.Install.ContainerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllowIps(website model.Website) []string {
|
||||||
|
nginxFull, err := getNginxFull(&website)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
config := nginxFull.SiteConfig.Config
|
||||||
|
server := config.FindServers()[0]
|
||||||
|
dirs := server.GetDirectives()
|
||||||
|
var ips []string
|
||||||
|
for _, dir := range dirs {
|
||||||
|
if dir.GetName() == "allow" {
|
||||||
|
ips = append(ips, dir.GetParameters()...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfigAIProxy(website model.Website) error {
|
||||||
|
nginxFull, err := getNginxFull(&website)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
config := nginxFull.SiteConfig.Config
|
||||||
|
server := config.FindServers()[0]
|
||||||
|
dirs := server.GetDirectives()
|
||||||
|
for _, dir := range dirs {
|
||||||
|
if dir.GetName() == "location" && dir.GetParameters()[0] == "/" {
|
||||||
|
server.UpdateRootProxyForAi([]string{fmt.Sprintf("http://%s", website.Proxy)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@ func InitAgentDB() {
|
|||||||
migrations.InitBackup,
|
migrations.InitBackup,
|
||||||
migrations.UpdateAppTag,
|
migrations.UpdateAppTag,
|
||||||
migrations.UpdateApp,
|
migrations.UpdateApp,
|
||||||
|
migrations.AddOllamaModel,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
@ -265,3 +265,13 @@ var UpdateApp = &gormigrate.Migration{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddOllamaModel = &gormigrate.Migration{
|
||||||
|
ID: "20250218-add-ollama-model",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.OllamaModel{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
29
agent/router/ai.go
Normal file
29
agent/router/ai.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "github.com/1Panel-dev/1Panel/agent/app/api/v2"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AIToolsRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||||
|
aiToolsRouter := Router.Group("ai")
|
||||||
|
|
||||||
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
|
{
|
||||||
|
aiToolsRouter.GET("/ollama/exec", baseApi.OllamaWsSsh)
|
||||||
|
aiToolsRouter.POST("/ollama/close", baseApi.CloseOllamaModel)
|
||||||
|
aiToolsRouter.POST("/ollama/model", baseApi.CreateOllamaModel)
|
||||||
|
aiToolsRouter.POST("/ollama/model/recreate", baseApi.RecreateOllamaModel)
|
||||||
|
aiToolsRouter.POST("/ollama/model/search", baseApi.SearchOllamaModel)
|
||||||
|
aiToolsRouter.POST("/ollama/model/sync", baseApi.SyncOllamaModel)
|
||||||
|
aiToolsRouter.POST("/ollama/model/load", baseApi.LoadOllamaModelDetail)
|
||||||
|
aiToolsRouter.POST("/ollama/model/del", baseApi.DeleteOllamaModel)
|
||||||
|
aiToolsRouter.GET("/gpu/load", baseApi.LoadGpuInfo)
|
||||||
|
aiToolsRouter.POST("/domain/bind", baseApi.BindDomain)
|
||||||
|
aiToolsRouter.POST("/domain/get", baseApi.GetBindDomain)
|
||||||
|
aiToolsRouter.POST("/domain/update", baseApi.UpdateBindDomain)
|
||||||
|
}
|
||||||
|
}
|
@ -21,5 +21,6 @@ func commonGroups() []CommonRouter {
|
|||||||
&RuntimeRouter{},
|
&RuntimeRouter{},
|
||||||
&ProcessRouter{},
|
&ProcessRouter{},
|
||||||
&WebsiteCARouter{},
|
&WebsiteCARouter{},
|
||||||
|
&AIToolsRouter{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
agent/utils/ai_tools/gpu/common/gpu_info.go
Normal file
37
agent/utils/ai_tools/gpu/common/gpu_info.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
type GpuInfo struct {
|
||||||
|
CudaVersion string `json:"cudaVersion"`
|
||||||
|
DriverVersion string `json:"driverVersion"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
GPUs []GPU `json:"gpu"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GPU struct {
|
||||||
|
Index uint `json:"index"`
|
||||||
|
ProductName string `json:"productName"`
|
||||||
|
PersistenceMode string `json:"persistenceMode"`
|
||||||
|
BusID string `json:"busID"`
|
||||||
|
DisplayActive string `json:"displayActive"`
|
||||||
|
ECC string `json:"ecc"`
|
||||||
|
FanSpeed string `json:"fanSpeed"`
|
||||||
|
|
||||||
|
Temperature string `json:"temperature"`
|
||||||
|
PerformanceState string `json:"performanceState"`
|
||||||
|
PowerDraw string `json:"powerDraw"`
|
||||||
|
MaxPowerLimit string `json:"maxPowerLimit"`
|
||||||
|
MemUsed string `json:"memUsed"`
|
||||||
|
MemTotal string `json:"memTotal"`
|
||||||
|
GPUUtil string `json:"gpuUtil"`
|
||||||
|
ComputeMode string `json:"computeMode"`
|
||||||
|
MigMode string `json:"migMode"`
|
||||||
|
Processes []Process `json:"processes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
Pid string `json:"pid"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
ProcessName string `json:"processName"`
|
||||||
|
UsedMemory string `json:"usedMemory"`
|
||||||
|
}
|
65
agent/utils/ai_tools/gpu/gpu.go
Normal file
65
agent/utils/ai_tools/gpu/gpu.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package gpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/schema_v12"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NvidiaSMI struct{}
|
||||||
|
|
||||||
|
func New() (bool, NvidiaSMI) {
|
||||||
|
return cmd.Which("nvidia-smi"), NvidiaSMI{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NvidiaSMI) LoadGpuInfo() (*common.GpuInfo, error) {
|
||||||
|
itemData, err := cmd.ExecWithTimeOut("nvidia-smi -q -x", 5*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("calling nvidia-smi failed, err: %w", err)
|
||||||
|
}
|
||||||
|
data := []byte(itemData)
|
||||||
|
schema := "v11"
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(data)
|
||||||
|
decoder := xml.NewDecoder(buf)
|
||||||
|
for {
|
||||||
|
token, err := decoder.Token()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("reading token failed: %w", err)
|
||||||
|
}
|
||||||
|
d, ok := token.(xml.Directive)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
directive := string(d)
|
||||||
|
if !strings.HasPrefix(directive, "DOCTYPE") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Split(directive, " ")
|
||||||
|
s := strings.Trim(parts[len(parts)-1], "\" ")
|
||||||
|
if strings.HasPrefix(s, "nvsmi_device_") && strings.HasSuffix(s, ".dtd") {
|
||||||
|
schema = strings.TrimSuffix(strings.TrimPrefix(s, "nvsmi_device_"), ".dtd")
|
||||||
|
} else {
|
||||||
|
global.LOG.Debugf("Cannot find schema version in %q", directive)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema != "v12" {
|
||||||
|
return &common.GpuInfo{}, nil
|
||||||
|
}
|
||||||
|
return schema_v12.Parse(data)
|
||||||
|
}
|
55
agent/utils/ai_tools/gpu/schema_v12/parser.go
Normal file
55
agent/utils/ai_tools/gpu/schema_v12/parser.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package schema_v12
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Parse(buf []byte) (*common.GpuInfo, error) {
|
||||||
|
var (
|
||||||
|
s smi
|
||||||
|
info common.GpuInfo
|
||||||
|
)
|
||||||
|
if err := xml.Unmarshal(buf, &s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Type = "nvidia"
|
||||||
|
info.CudaVersion = s.CudaVersion
|
||||||
|
info.DriverVersion = s.DriverVersion
|
||||||
|
if len(s.Gpu) == 0 {
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s.Gpu); i++ {
|
||||||
|
var gpuItem common.GPU
|
||||||
|
gpuItem.Index = uint(i)
|
||||||
|
gpuItem.ProductName = s.Gpu[i].ProductName
|
||||||
|
gpuItem.PersistenceMode = s.Gpu[i].PersistenceMode
|
||||||
|
gpuItem.BusID = s.Gpu[i].ID
|
||||||
|
gpuItem.DisplayActive = s.Gpu[i].DisplayActive
|
||||||
|
gpuItem.ECC = s.Gpu[i].EccErrors.Volatile.DramUncorrectable
|
||||||
|
gpuItem.FanSpeed = s.Gpu[i].FanSpeed
|
||||||
|
|
||||||
|
gpuItem.Temperature = s.Gpu[i].Temperature.GpuTemp
|
||||||
|
gpuItem.PerformanceState = s.Gpu[i].PerformanceState
|
||||||
|
gpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.PowerDraw
|
||||||
|
gpuItem.MaxPowerLimit = s.Gpu[i].GpuPowerReadings.MaxPowerLimit
|
||||||
|
gpuItem.MemUsed = s.Gpu[i].FbMemoryUsage.Used
|
||||||
|
gpuItem.MemTotal = s.Gpu[i].FbMemoryUsage.Total
|
||||||
|
gpuItem.GPUUtil = s.Gpu[i].Utilization.GpuUtil
|
||||||
|
gpuItem.ComputeMode = s.Gpu[i].ComputeMode
|
||||||
|
gpuItem.MigMode = s.Gpu[i].MigMode.CurrentMig
|
||||||
|
|
||||||
|
for _, process := range s.Gpu[i].Processes.ProcessInfo {
|
||||||
|
gpuItem.Processes = append(gpuItem.Processes, common.Process{
|
||||||
|
Pid: process.Pid,
|
||||||
|
Type: process.Type,
|
||||||
|
ProcessName: process.ProcessName,
|
||||||
|
UsedMemory: process.UsedMemory,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
info.GPUs = append(info.GPUs, gpuItem)
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
294
agent/utils/ai_tools/gpu/schema_v12/types.go
Normal file
294
agent/utils/ai_tools/gpu/schema_v12/types.go
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
package schema_v12
|
||||||
|
|
||||||
|
type smi struct {
|
||||||
|
AttachedGpus string `xml:"attached_gpus"`
|
||||||
|
CudaVersion string `xml:"cuda_version"`
|
||||||
|
DriverVersion string `xml:"driver_version"`
|
||||||
|
Gpu []struct {
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
AccountedProcesses struct{} `xml:"accounted_processes"`
|
||||||
|
AccountingMode string `xml:"accounting_mode"`
|
||||||
|
AccountingModeBufferSize string `xml:"accounting_mode_buffer_size"`
|
||||||
|
AddressingMode string `xml:"addressing_mode"`
|
||||||
|
ApplicationsClocks struct {
|
||||||
|
GraphicsClock string `xml:"graphics_clock"`
|
||||||
|
MemClock string `xml:"mem_clock"`
|
||||||
|
} `xml:"applications_clocks"`
|
||||||
|
Bar1MemoryUsage struct {
|
||||||
|
Free string `xml:"free"`
|
||||||
|
Total string `xml:"total"`
|
||||||
|
Used string `xml:"used"`
|
||||||
|
} `xml:"bar1_memory_usage"`
|
||||||
|
BoardID string `xml:"board_id"`
|
||||||
|
BoardPartNumber string `xml:"board_part_number"`
|
||||||
|
CcProtectedMemoryUsage struct {
|
||||||
|
Free string `xml:"free"`
|
||||||
|
Total string `xml:"total"`
|
||||||
|
Used string `xml:"used"`
|
||||||
|
} `xml:"cc_protected_memory_usage"`
|
||||||
|
ClockPolicy struct {
|
||||||
|
AutoBoost string `xml:"auto_boost"`
|
||||||
|
AutoBoostDefault string `xml:"auto_boost_default"`
|
||||||
|
} `xml:"clock_policy"`
|
||||||
|
Clocks struct {
|
||||||
|
GraphicsClock string `xml:"graphics_clock"`
|
||||||
|
MemClock string `xml:"mem_clock"`
|
||||||
|
SmClock string `xml:"sm_clock"`
|
||||||
|
VideoClock string `xml:"video_clock"`
|
||||||
|
} `xml:"clocks"`
|
||||||
|
ClocksEventReasons struct {
|
||||||
|
ClocksEventReasonApplicationsClocksSetting string `xml:"clocks_event_reason_applications_clocks_setting"`
|
||||||
|
ClocksEventReasonDisplayClocksSetting string `xml:"clocks_event_reason_display_clocks_setting"`
|
||||||
|
ClocksEventReasonGpuIdle string `xml:"clocks_event_reason_gpu_idle"`
|
||||||
|
ClocksEventReasonHwPowerBrakeSlowdown string `xml:"clocks_event_reason_hw_power_brake_slowdown"`
|
||||||
|
ClocksEventReasonHwSlowdown string `xml:"clocks_event_reason_hw_slowdown"`
|
||||||
|
ClocksEventReasonHwThermalSlowdown string `xml:"clocks_event_reason_hw_thermal_slowdown"`
|
||||||
|
ClocksEventReasonSwPowerCap string `xml:"clocks_event_reason_sw_power_cap"`
|
||||||
|
ClocksEventReasonSwThermalSlowdown string `xml:"clocks_event_reason_sw_thermal_slowdown"`
|
||||||
|
ClocksEventReasonSyncBoost string `xml:"clocks_event_reason_sync_boost"`
|
||||||
|
} `xml:"clocks_event_reasons"`
|
||||||
|
ComputeMode string `xml:"compute_mode"`
|
||||||
|
DefaultApplicationsClocks struct {
|
||||||
|
GraphicsClock string `xml:"graphics_clock"`
|
||||||
|
MemClock string `xml:"mem_clock"`
|
||||||
|
} `xml:"default_applications_clocks"`
|
||||||
|
DeferredClocks struct {
|
||||||
|
MemClock string `xml:"mem_clock"`
|
||||||
|
} `xml:"deferred_clocks"`
|
||||||
|
DisplayActive string `xml:"display_active"`
|
||||||
|
DisplayMode string `xml:"display_mode"`
|
||||||
|
DriverModel struct {
|
||||||
|
CurrentDm string `xml:"current_dm"`
|
||||||
|
PendingDm string `xml:"pending_dm"`
|
||||||
|
} `xml:"driver_model"`
|
||||||
|
EccErrors struct {
|
||||||
|
Aggregate struct {
|
||||||
|
DramCorrectable string `xml:"dram_correctable"`
|
||||||
|
DramUncorrectable string `xml:"dram_uncorrectable"`
|
||||||
|
SramCorrectable string `xml:"sram_correctable"`
|
||||||
|
SramUncorrectable string `xml:"sram_uncorrectable"`
|
||||||
|
} `xml:"aggregate"`
|
||||||
|
Volatile struct {
|
||||||
|
DramCorrectable string `xml:"dram_correctable"`
|
||||||
|
DramUncorrectable string `xml:"dram_uncorrectable"`
|
||||||
|
SramCorrectable string `xml:"sram_correctable"`
|
||||||
|
SramUncorrectable string `xml:"sram_uncorrectable"`
|
||||||
|
} `xml:"volatile"`
|
||||||
|
} `xml:"ecc_errors"`
|
||||||
|
EccMode struct {
|
||||||
|
CurrentEcc string `xml:"current_ecc"`
|
||||||
|
PendingEcc string `xml:"pending_ecc"`
|
||||||
|
} `xml:"ecc_mode"`
|
||||||
|
EncoderStats struct {
|
||||||
|
AverageFps string `xml:"average_fps"`
|
||||||
|
AverageLatency string `xml:"average_latency"`
|
||||||
|
SessionCount string `xml:"session_count"`
|
||||||
|
} `xml:"encoder_stats"`
|
||||||
|
Fabric struct {
|
||||||
|
State string `xml:"state"`
|
||||||
|
Status string `xml:"status"`
|
||||||
|
} `xml:"fabric"`
|
||||||
|
FanSpeed string `xml:"fan_speed"`
|
||||||
|
FbMemoryUsage struct {
|
||||||
|
Free string `xml:"free"`
|
||||||
|
Reserved string `xml:"reserved"`
|
||||||
|
Total string `xml:"total"`
|
||||||
|
Used string `xml:"used"`
|
||||||
|
} `xml:"fb_memory_usage"`
|
||||||
|
FbcStats struct {
|
||||||
|
AverageFps string `xml:"average_fps"`
|
||||||
|
AverageLatency string `xml:"average_latency"`
|
||||||
|
SessionCount string `xml:"session_count"`
|
||||||
|
} `xml:"fbc_stats"`
|
||||||
|
GpuFruPartNumber string `xml:"gpu_fru_part_number"`
|
||||||
|
GpuModuleID string `xml:"gpu_module_id"`
|
||||||
|
GpuOperationMode struct {
|
||||||
|
CurrentGom string `xml:"current_gom"`
|
||||||
|
PendingGom string `xml:"pending_gom"`
|
||||||
|
} `xml:"gpu_operation_mode"`
|
||||||
|
GpuPartNumber string `xml:"gpu_part_number"`
|
||||||
|
GpuPowerReadings struct {
|
||||||
|
CurrentPowerLimit string `xml:"current_power_limit"`
|
||||||
|
DefaultPowerLimit string `xml:"default_power_limit"`
|
||||||
|
MaxPowerLimit string `xml:"max_power_limit"`
|
||||||
|
MinPowerLimit string `xml:"min_power_limit"`
|
||||||
|
PowerDraw string `xml:"power_draw"`
|
||||||
|
PowerState string `xml:"power_state"`
|
||||||
|
RequestedPowerLimit string `xml:"requested_power_limit"`
|
||||||
|
} `xml:"gpu_power_readings"`
|
||||||
|
GpuResetStatus struct {
|
||||||
|
DrainAndResetRecommended string `xml:"drain_and_reset_recommended"`
|
||||||
|
ResetRequired string `xml:"reset_required"`
|
||||||
|
} `xml:"gpu_reset_status"`
|
||||||
|
GpuVirtualizationMode struct {
|
||||||
|
HostVgpuMode string `xml:"host_vgpu_mode"`
|
||||||
|
VirtualizationMode string `xml:"virtualization_mode"`
|
||||||
|
} `xml:"gpu_virtualization_mode"`
|
||||||
|
GspFirmwareVersion string `xml:"gsp_firmware_version"`
|
||||||
|
Ibmnpu struct {
|
||||||
|
RelaxedOrderingMode string `xml:"relaxed_ordering_mode"`
|
||||||
|
} `xml:"ibmnpu"`
|
||||||
|
InforomVersion struct {
|
||||||
|
EccObject string `xml:"ecc_object"`
|
||||||
|
ImgVersion string `xml:"img_version"`
|
||||||
|
OemObject string `xml:"oem_object"`
|
||||||
|
PwrObject string `xml:"pwr_object"`
|
||||||
|
} `xml:"inforom_version"`
|
||||||
|
MaxClocks struct {
|
||||||
|
GraphicsClock string `xml:"graphics_clock"`
|
||||||
|
MemClock string `xml:"mem_clock"`
|
||||||
|
SmClock string `xml:"sm_clock"`
|
||||||
|
VideoClock string `xml:"video_clock"`
|
||||||
|
} `xml:"max_clocks"`
|
||||||
|
MaxCustomerBoostClocks struct {
|
||||||
|
GraphicsClock string `xml:"graphics_clock"`
|
||||||
|
} `xml:"max_customer_boost_clocks"`
|
||||||
|
MigDevices struct {
|
||||||
|
MigDevice []struct {
|
||||||
|
Index string `xml:"index"`
|
||||||
|
GpuInstanceID string `xml:"gpu_instance_id"`
|
||||||
|
ComputeInstanceID string `xml:"compute_instance_id"`
|
||||||
|
EccErrorCount struct {
|
||||||
|
Text string `xml:",chardata" json:"text"`
|
||||||
|
VolatileCount struct {
|
||||||
|
SramUncorrectable string `xml:"sram_uncorrectable"`
|
||||||
|
} `xml:"volatile_count" json:"volatile_count"`
|
||||||
|
} `xml:"ecc_error_count" json:"ecc_error_count"`
|
||||||
|
FbMemoryUsage struct {
|
||||||
|
Total string `xml:"total"`
|
||||||
|
Reserved string `xml:"reserved"`
|
||||||
|
Used string `xml:"used"`
|
||||||
|
Free string `xml:"free"`
|
||||||
|
} `xml:"fb_memory_usage" json:"fb_memory_usage"`
|
||||||
|
Bar1MemoryUsage struct {
|
||||||
|
Total string `xml:"total"`
|
||||||
|
Used string `xml:"used"`
|
||||||
|
Free string `xml:"free"`
|
||||||
|
} `xml:"bar1_memory_usage" json:"bar1_memory_usage"`
|
||||||
|
} `xml:"mig_device" json:"mig_device"`
|
||||||
|
} `xml:"mig_devices" json:"mig_devices"`
|
||||||
|
MigMode struct {
|
||||||
|
CurrentMig string `xml:"current_mig"`
|
||||||
|
PendingMig string `xml:"pending_mig"`
|
||||||
|
} `xml:"mig_mode"`
|
||||||
|
MinorNumber string `xml:"minor_number"`
|
||||||
|
ModulePowerReadings struct {
|
||||||
|
CurrentPowerLimit string `xml:"current_power_limit"`
|
||||||
|
DefaultPowerLimit string `xml:"default_power_limit"`
|
||||||
|
MaxPowerLimit string `xml:"max_power_limit"`
|
||||||
|
MinPowerLimit string `xml:"min_power_limit"`
|
||||||
|
PowerDraw string `xml:"power_draw"`
|
||||||
|
PowerState string `xml:"power_state"`
|
||||||
|
RequestedPowerLimit string `xml:"requested_power_limit"`
|
||||||
|
} `xml:"module_power_readings"`
|
||||||
|
MultigpuBoard string `xml:"multigpu_board"`
|
||||||
|
Pci struct {
|
||||||
|
AtomicCapsInbound string `xml:"atomic_caps_inbound"`
|
||||||
|
AtomicCapsOutbound string `xml:"atomic_caps_outbound"`
|
||||||
|
PciBridgeChip struct {
|
||||||
|
BridgeChipFw string `xml:"bridge_chip_fw"`
|
||||||
|
BridgeChipType string `xml:"bridge_chip_type"`
|
||||||
|
} `xml:"pci_bridge_chip"`
|
||||||
|
PciBus string `xml:"pci_bus"`
|
||||||
|
PciBusID string `xml:"pci_bus_id"`
|
||||||
|
PciDevice string `xml:"pci_device"`
|
||||||
|
PciDeviceID string `xml:"pci_device_id"`
|
||||||
|
PciDomain string `xml:"pci_domain"`
|
||||||
|
PciGpuLinkInfo struct {
|
||||||
|
LinkWidths struct {
|
||||||
|
CurrentLinkWidth string `xml:"current_link_width"`
|
||||||
|
MaxLinkWidth string `xml:"max_link_width"`
|
||||||
|
} `xml:"link_widths"`
|
||||||
|
PcieGen struct {
|
||||||
|
CurrentLinkGen string `xml:"current_link_gen"`
|
||||||
|
DeviceCurrentLinkGen string `xml:"device_current_link_gen"`
|
||||||
|
MaxDeviceLinkGen string `xml:"max_device_link_gen"`
|
||||||
|
MaxHostLinkGen string `xml:"max_host_link_gen"`
|
||||||
|
MaxLinkGen string `xml:"max_link_gen"`
|
||||||
|
} `xml:"pcie_gen"`
|
||||||
|
} `xml:"pci_gpu_link_info"`
|
||||||
|
PciSubSystemID string `xml:"pci_sub_system_id"`
|
||||||
|
ReplayCounter string `xml:"replay_counter"`
|
||||||
|
ReplayRolloverCounter string `xml:"replay_rollover_counter"`
|
||||||
|
RxUtil string `xml:"rx_util"`
|
||||||
|
TxUtil string `xml:"tx_util"`
|
||||||
|
} `xml:"pci"`
|
||||||
|
PerformanceState string `xml:"performance_state"`
|
||||||
|
PersistenceMode string `xml:"persistence_mode"`
|
||||||
|
PowerReadings struct {
|
||||||
|
PowerState string `xml:"power_state"`
|
||||||
|
PowerManagement string `xml:"power_management"`
|
||||||
|
PowerDraw string `xml:"power_draw"`
|
||||||
|
PowerLimit string `xml:"power_limit"`
|
||||||
|
DefaultPowerLimit string `xml:"default_power_limit"`
|
||||||
|
EnforcedPowerLimit string `xml:"enforced_power_limit"`
|
||||||
|
MinPowerLimit string `xml:"min_power_limit"`
|
||||||
|
MaxPowerLimit string `xml:"max_power_limit"`
|
||||||
|
} `xml:"power_readings"`
|
||||||
|
Processes struct {
|
||||||
|
ProcessInfo []struct {
|
||||||
|
Pid string `xml:"pid"`
|
||||||
|
Type string `xml:"type"`
|
||||||
|
ProcessName string `xml:"process_name"`
|
||||||
|
UsedMemory string `xml:"used_memory"`
|
||||||
|
} `xml:"process_info"`
|
||||||
|
} `xml:"processes"`
|
||||||
|
ProductArchitecture string `xml:"product_architecture"`
|
||||||
|
ProductBrand string `xml:"product_brand"`
|
||||||
|
ProductName string `xml:"product_name"`
|
||||||
|
RemappedRows struct {
|
||||||
|
// Manually added
|
||||||
|
Correctable string `xml:"remapped_row_corr"`
|
||||||
|
Uncorrectable string `xml:"remapped_row_unc"`
|
||||||
|
Pending string `xml:"remapped_row_pending"`
|
||||||
|
Failure string `xml:"remapped_row_failure"`
|
||||||
|
} `xml:"remapped_rows"`
|
||||||
|
RetiredPages struct {
|
||||||
|
DoubleBitRetirement struct {
|
||||||
|
RetiredCount string `xml:"retired_count"`
|
||||||
|
RetiredPagelist string `xml:"retired_pagelist"`
|
||||||
|
} `xml:"double_bit_retirement"`
|
||||||
|
MultipleSingleBitRetirement struct {
|
||||||
|
RetiredCount string `xml:"retired_count"`
|
||||||
|
RetiredPagelist string `xml:"retired_pagelist"`
|
||||||
|
} `xml:"multiple_single_bit_retirement"`
|
||||||
|
PendingBlacklist string `xml:"pending_blacklist"`
|
||||||
|
PendingRetirement string `xml:"pending_retirement"`
|
||||||
|
} `xml:"retired_pages"`
|
||||||
|
Serial string `xml:"serial"`
|
||||||
|
SupportedClocks struct {
|
||||||
|
SupportedMemClock []struct {
|
||||||
|
SupportedGraphicsClock []string `xml:"supported_graphics_clock"`
|
||||||
|
Value string `xml:"value"`
|
||||||
|
} `xml:"supported_mem_clock"`
|
||||||
|
} `xml:"supported_clocks"`
|
||||||
|
SupportedGpuTargetTemp struct {
|
||||||
|
GpuTargetTempMax string `xml:"gpu_target_temp_max"`
|
||||||
|
GpuTargetTempMin string `xml:"gpu_target_temp_min"`
|
||||||
|
} `xml:"supported_gpu_target_temp"`
|
||||||
|
Temperature struct {
|
||||||
|
GpuTargetTemperature string `xml:"gpu_target_temperature"`
|
||||||
|
GpuTemp string `xml:"gpu_temp"`
|
||||||
|
GpuTempMaxGpuThreshold string `xml:"gpu_temp_max_gpu_threshold"`
|
||||||
|
GpuTempMaxMemThreshold string `xml:"gpu_temp_max_mem_threshold"`
|
||||||
|
GpuTempMaxThreshold string `xml:"gpu_temp_max_threshold"`
|
||||||
|
GpuTempSlowThreshold string `xml:"gpu_temp_slow_threshold"`
|
||||||
|
GpuTempTlimit string `xml:"gpu_temp_tlimit"`
|
||||||
|
MemoryTemp string `xml:"memory_temp"`
|
||||||
|
} `xml:"temperature"`
|
||||||
|
Utilization struct {
|
||||||
|
DecoderUtil string `xml:"decoder_util"`
|
||||||
|
EncoderUtil string `xml:"encoder_util"`
|
||||||
|
GpuUtil string `xml:"gpu_util"`
|
||||||
|
JpegUtil string `xml:"jpeg_util"`
|
||||||
|
MemoryUtil string `xml:"memory_util"`
|
||||||
|
OfaUtil string `xml:"ofa_util"`
|
||||||
|
} `xml:"utilization"`
|
||||||
|
UUID string `xml:"uuid"`
|
||||||
|
VbiosVersion string `xml:"vbios_version"`
|
||||||
|
Voltage struct {
|
||||||
|
GraphicsVolt string `xml:"graphics_volt"`
|
||||||
|
} `xml:"voltage"`
|
||||||
|
} `xml:"gpu"`
|
||||||
|
Timestamp string `xml:"timestamp"`
|
||||||
|
}
|
43
agent/utils/ai_tools/xpu/types.go
Normal file
43
agent/utils/ai_tools/xpu/types.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package xpu
|
||||||
|
|
||||||
|
type DeviceUtilByProc struct {
|
||||||
|
DeviceID int `json:"device_id"`
|
||||||
|
MemSize float64 `json:"mem_size"`
|
||||||
|
ProcessID int `json:"process_id"`
|
||||||
|
ProcessName string `json:"process_name"`
|
||||||
|
SharedMemSize float64 `json:"shared_mem_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceUtilByProcList struct {
|
||||||
|
DeviceUtilByProcList []DeviceUtilByProc `json:"device_util_by_proc_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
DeviceFunctionType string `json:"device_function_type"`
|
||||||
|
DeviceID int `json:"device_id"`
|
||||||
|
DeviceName string `json:"device_name"`
|
||||||
|
DeviceType string `json:"device_type"`
|
||||||
|
DrmDevice string `json:"drm_device"`
|
||||||
|
PciBdfAddress string `json:"pci_bdf_address"`
|
||||||
|
PciDeviceID string `json:"pci_device_id"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
VendorName string `json:"vendor_name"`
|
||||||
|
|
||||||
|
MemoryPhysicalSizeByte string `json:"memory_physical_size_byte"`
|
||||||
|
MemoryFreeSizeByte string `json:"memory_free_size_byte"`
|
||||||
|
DriverVersion string `json:"driver_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceInfo struct {
|
||||||
|
DeviceList []Device `json:"device_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceLevelMetric struct {
|
||||||
|
MetricsType string `json:"metrics_type"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceStats struct {
|
||||||
|
DeviceID int `json:"device_id"`
|
||||||
|
DeviceLevel []DeviceLevelMetric `json:"device_level"`
|
||||||
|
}
|
254
agent/utils/ai_tools/xpu/xpu.go
Normal file
254
agent/utils/ai_tools/xpu/xpu.go
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
package xpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
baseGlobal "github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XpuSMI struct{}
|
||||||
|
|
||||||
|
func New() (bool, XpuSMI) {
|
||||||
|
return cmd.Which("xpu-smi"), XpuSMI{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x XpuSMI) loadDeviceData(device Device, wg *sync.WaitGroup, res *[]XPUSimpleInfo, mu *sync.Mutex) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
var xpu XPUSimpleInfo
|
||||||
|
xpu.DeviceID = device.DeviceID
|
||||||
|
xpu.DeviceName = device.DeviceName
|
||||||
|
|
||||||
|
var xpuData, statsData string
|
||||||
|
var xpuErr, statsErr error
|
||||||
|
|
||||||
|
var wgCmd sync.WaitGroup
|
||||||
|
wgCmd.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wgCmd.Done()
|
||||||
|
xpuData, xpuErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi discovery -d %d -j", device.DeviceID), 5*time.Second)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wgCmd.Done()
|
||||||
|
statsData, statsErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi stats -d %d -j", device.DeviceID), 5*time.Second)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wgCmd.Wait()
|
||||||
|
|
||||||
|
if xpuErr != nil {
|
||||||
|
baseGlobal.LOG.Errorf("calling xpu-smi discovery failed for device %d, err: %v\n", device.DeviceID, xpuErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var info Device
|
||||||
|
if err := json.Unmarshal([]byte(xpuData), &info); err != nil {
|
||||||
|
baseGlobal.LOG.Errorf("xpuData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
baseGlobal.LOG.Errorf("Error parsing memory size for device %d, err: %v\n", device.DeviceID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xpu.Memory = fmt.Sprintf("%.1f MB", float64(bytes)/(1024*1024))
|
||||||
|
|
||||||
|
if statsErr != nil {
|
||||||
|
baseGlobal.LOG.Errorf("calling xpu-smi stats failed for device %d, err: %v\n", device.DeviceID, statsErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var stats DeviceStats
|
||||||
|
if err := json.Unmarshal([]byte(statsData), &stats); err != nil {
|
||||||
|
baseGlobal.LOG.Errorf("statsData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stat := range stats.DeviceLevel {
|
||||||
|
switch stat.MetricsType {
|
||||||
|
case "XPUM_STATS_POWER":
|
||||||
|
xpu.Power = fmt.Sprintf("%.1fW", stat.Value)
|
||||||
|
case "XPUM_STATS_GPU_CORE_TEMPERATURE":
|
||||||
|
xpu.Temperature = fmt.Sprintf("%.1f°C", stat.Value)
|
||||||
|
case "XPUM_STATS_MEMORY_USED":
|
||||||
|
xpu.MemoryUsed = fmt.Sprintf("%.1fMB", stat.Value)
|
||||||
|
case "XPUM_STATS_MEMORY_UTILIZATION":
|
||||||
|
xpu.MemoryUtil = fmt.Sprintf("%.1f%%", stat.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
*res = append(*res, xpu)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x XpuSMI) LoadDashData() ([]XPUSimpleInfo, error) {
|
||||||
|
data, err := cmd.ExecWithTimeOut("xpu-smi discovery -j", 5*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("calling xpu-smi failed, err: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var deviceInfo DeviceInfo
|
||||||
|
if err := json.Unmarshal([]byte(data), &deviceInfo); err != nil {
|
||||||
|
return nil, fmt.Errorf("deviceInfo json unmarshal failed, err: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []XPUSimpleInfo
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
for _, device := range deviceInfo.DeviceList {
|
||||||
|
wg.Add(1)
|
||||||
|
go x.loadDeviceData(device, &wg, &res, &mu)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
sort.Slice(res, func(i, j int) bool {
|
||||||
|
return res[i].DeviceID < res[j].DeviceID
|
||||||
|
})
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x XpuSMI) LoadGpuInfo() (*XpuInfo, error) {
|
||||||
|
data, err := cmd.ExecWithTimeOut("xpu-smi discovery -j", 5*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("calling xpu-smi failed, err: %w", err)
|
||||||
|
}
|
||||||
|
var deviceInfo DeviceInfo
|
||||||
|
if err := json.Unmarshal([]byte(data), &deviceInfo); err != nil {
|
||||||
|
return nil, fmt.Errorf("deviceInfo json unmarshal failed, err: %w", err)
|
||||||
|
}
|
||||||
|
res := &XpuInfo{
|
||||||
|
Type: "xpu",
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
for _, device := range deviceInfo.DeviceList {
|
||||||
|
wg.Add(1)
|
||||||
|
go x.loadDeviceInfo(device, &wg, res, &mu)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
processData, err := cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi ps -j"), 5*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("calling xpu-smi ps failed, err: %w", err)
|
||||||
|
}
|
||||||
|
var psList DeviceUtilByProcList
|
||||||
|
if err := json.Unmarshal([]byte(processData), &psList); err != nil {
|
||||||
|
return nil, fmt.Errorf("processData json unmarshal failed, err: %w", err)
|
||||||
|
}
|
||||||
|
for _, ps := range psList.DeviceUtilByProcList {
|
||||||
|
process := Process{
|
||||||
|
PID: ps.ProcessID,
|
||||||
|
Command: ps.ProcessName,
|
||||||
|
}
|
||||||
|
if ps.SharedMemSize > 0 {
|
||||||
|
process.SHR = fmt.Sprintf("%.1f MB", ps.SharedMemSize/1024)
|
||||||
|
}
|
||||||
|
if ps.MemSize > 0 {
|
||||||
|
process.Memory = fmt.Sprintf("%.1f MB", ps.MemSize/1024)
|
||||||
|
}
|
||||||
|
for index, xpu := range res.Xpu {
|
||||||
|
if xpu.Basic.DeviceID == ps.DeviceID {
|
||||||
|
res.Xpu[index].Processes = append(res.Xpu[index].Processes, process)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x XpuSMI) loadDeviceInfo(device Device, wg *sync.WaitGroup, res *XpuInfo, mu *sync.Mutex) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
xpu := Xpu{
|
||||||
|
Basic: Basic{
|
||||||
|
DeviceID: device.DeviceID,
|
||||||
|
DeviceName: device.DeviceName,
|
||||||
|
VendorName: device.VendorName,
|
||||||
|
PciBdfAddress: device.PciBdfAddress,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var xpuData, statsData string
|
||||||
|
var xpuErr, statsErr error
|
||||||
|
|
||||||
|
var wgCmd sync.WaitGroup
|
||||||
|
wgCmd.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wgCmd.Done()
|
||||||
|
xpuData, xpuErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi discovery -d %d -j", device.DeviceID), 5*time.Second)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wgCmd.Done()
|
||||||
|
statsData, statsErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi stats -d %d -j", device.DeviceID), 5*time.Second)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wgCmd.Wait()
|
||||||
|
|
||||||
|
if xpuErr != nil {
|
||||||
|
baseGlobal.LOG.Errorf("calling xpu-smi discovery failed for device %d, err: %v\n", device.DeviceID, xpuErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var info Device
|
||||||
|
if err := json.Unmarshal([]byte(xpuData), &info); err != nil {
|
||||||
|
baseGlobal.LOG.Errorf("xpuData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.DriverVersion = info.DriverVersion
|
||||||
|
xpu.Basic.DriverVersion = info.DriverVersion
|
||||||
|
|
||||||
|
bytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
baseGlobal.LOG.Errorf("Error parsing memory size for device %d, err: %v\n", device.DeviceID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xpu.Basic.Memory = fmt.Sprintf("%.1f MB", float64(bytes)/(1024*1024))
|
||||||
|
xpu.Basic.FreeMemory = info.MemoryFreeSizeByte
|
||||||
|
|
||||||
|
if statsErr != nil {
|
||||||
|
baseGlobal.LOG.Errorf("calling xpu-smi stats failed for device %d, err: %v\n", device.DeviceID, statsErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var stats DeviceStats
|
||||||
|
if err := json.Unmarshal([]byte(statsData), &stats); err != nil {
|
||||||
|
baseGlobal.LOG.Errorf("statsData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stat := range stats.DeviceLevel {
|
||||||
|
switch stat.MetricsType {
|
||||||
|
case "XPUM_STATS_POWER":
|
||||||
|
xpu.Stats.Power = fmt.Sprintf("%.1fW", stat.Value)
|
||||||
|
case "XPUM_STATS_GPU_FREQUENCY":
|
||||||
|
xpu.Stats.Frequency = fmt.Sprintf("%.1fMHz", stat.Value)
|
||||||
|
case "XPUM_STATS_GPU_CORE_TEMPERATURE":
|
||||||
|
xpu.Stats.Temperature = fmt.Sprintf("%.1f°C", stat.Value)
|
||||||
|
case "XPUM_STATS_MEMORY_USED":
|
||||||
|
xpu.Stats.MemoryUsed = fmt.Sprintf("%.1fMB", stat.Value)
|
||||||
|
case "XPUM_STATS_MEMORY_UTILIZATION":
|
||||||
|
xpu.Stats.MemoryUtil = fmt.Sprintf("%.1f%%", stat.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
res.Xpu = append(res.Xpu, xpu)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
49
agent/utils/ai_tools/xpu/xpu_info.go
Normal file
49
agent/utils/ai_tools/xpu/xpu_info.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package xpu
|
||||||
|
|
||||||
|
type XpuInfo struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
DriverVersion string `json:"driverVersion"`
|
||||||
|
|
||||||
|
Xpu []Xpu `json:"xpu"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Xpu struct {
|
||||||
|
Basic Basic `json:"basic"`
|
||||||
|
Stats Stats `json:"stats"`
|
||||||
|
Processes []Process `json:"processes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Basic struct {
|
||||||
|
DeviceID int `json:"deviceID"`
|
||||||
|
DeviceName string `json:"deviceName"`
|
||||||
|
VendorName string `json:"vendorName"`
|
||||||
|
DriverVersion string `json:"driverVersion"`
|
||||||
|
Memory string `json:"memory"`
|
||||||
|
FreeMemory string `json:"freeMemory"`
|
||||||
|
PciBdfAddress string `json:"pciBdfAddress"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
Power string `json:"power"`
|
||||||
|
Frequency string `json:"frequency"`
|
||||||
|
Temperature string `json:"temperature"`
|
||||||
|
MemoryUsed string `json:"memoryUsed"`
|
||||||
|
MemoryUtil string `json:"memoryUtil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
PID int `json:"pid"`
|
||||||
|
Command string `json:"command"`
|
||||||
|
SHR string `json:"shr"`
|
||||||
|
Memory string `json:"memory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XPUSimpleInfo struct {
|
||||||
|
DeviceID int `json:"deviceID"`
|
||||||
|
DeviceName string `json:"deviceName"`
|
||||||
|
Memory string `json:"memory"`
|
||||||
|
Temperature string `json:"temperature"`
|
||||||
|
MemoryUsed string `json:"memoryUsed"`
|
||||||
|
Power string `json:"power"`
|
||||||
|
MemoryUtil string `json:"memoryUtil"`
|
||||||
|
}
|
@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
"golang.org/x/net/idna"
|
"golang.org/x/net/idna"
|
||||||
)
|
)
|
||||||
@ -357,3 +358,22 @@ func GetLang(c *gin.Context) string {
|
|||||||
}
|
}
|
||||||
return lang
|
return lang
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HandleIPList(content string) ([]string, error) {
|
||||||
|
ipList := strings.Split(content, "\n")
|
||||||
|
var res []string
|
||||||
|
for _, ip := range ipList {
|
||||||
|
if ip == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if net.ParseIP(ip) != nil {
|
||||||
|
res = append(res, ip)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, _, err := net.ParseCIDR(ip); err != nil {
|
||||||
|
return nil, buserr.New("ErrParseIP")
|
||||||
|
}
|
||||||
|
res = append(res, ip)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
@ -260,6 +260,53 @@ func (s *Server) UpdateRoot(path string) {
|
|||||||
s.UpdateDirective("root", []string{path})
|
s.UpdateDirective("root", []string{path})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) UpdateRootProxyForAi(proxy []string) {
|
||||||
|
newDir := Directive{
|
||||||
|
Name: "location",
|
||||||
|
Parameters: []string{"/"},
|
||||||
|
Block: &Block{},
|
||||||
|
}
|
||||||
|
block := &Block{}
|
||||||
|
block.Directives = []IDirective{
|
||||||
|
&Directive{
|
||||||
|
Name: "proxy_buffering",
|
||||||
|
Parameters: []string{
|
||||||
|
"off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&Directive{
|
||||||
|
Name: "proxy_cache",
|
||||||
|
Parameters: []string{
|
||||||
|
"off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&Directive{
|
||||||
|
Name: "proxy_http_version",
|
||||||
|
Parameters: []string{
|
||||||
|
"1.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&Directive{
|
||||||
|
Name: "proxy_set_header",
|
||||||
|
Parameters: []string{
|
||||||
|
"Connection", "''",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&Directive{
|
||||||
|
Name: "chunked_transfer_encoding",
|
||||||
|
Parameters: []string{
|
||||||
|
"off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
block.Directives = append(block.Directives, &Directive{
|
||||||
|
Name: "proxy_pass",
|
||||||
|
Parameters: proxy,
|
||||||
|
})
|
||||||
|
newDir.Block = block
|
||||||
|
s.UpdateDirectiveBySecondKey("location", "/", newDir)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) UpdateRootLocation() {
|
func (s *Server) UpdateRootLocation() {
|
||||||
newDir := Directive{
|
newDir := Directive{
|
||||||
Name: "location",
|
Name: "location",
|
||||||
@ -393,3 +440,30 @@ func (s *Server) AddHTTP2HTTPS() {
|
|||||||
newDir.Block = block
|
newDir.Block = block
|
||||||
s.UpdateDirectiveBySecondKey("if", "($scheme", newDir)
|
s.UpdateDirectiveBySecondKey("if", "($scheme", newDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) UpdateAllowIPs(ips []string) {
|
||||||
|
index := -1
|
||||||
|
for i, directive := range s.Directives {
|
||||||
|
if directive.GetName() == "location" && directive.GetParameters()[0] == "/" {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipDirectives := make([]IDirective, 0)
|
||||||
|
for _, ip := range ips {
|
||||||
|
ipDirectives = append(ipDirectives, &Directive{
|
||||||
|
Name: "allow",
|
||||||
|
Parameters: []string{ip},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ipDirectives = append(ipDirectives, &Directive{
|
||||||
|
Name: "deny",
|
||||||
|
Parameters: []string{"all"},
|
||||||
|
})
|
||||||
|
if index != -1 {
|
||||||
|
newDirectives := append(ipDirectives, s.Directives[index:]...)
|
||||||
|
s.Directives = append(s.Directives[:index], newDirectives...)
|
||||||
|
} else {
|
||||||
|
s.Directives = append(s.Directives, ipDirectives...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,6 +47,10 @@ var WebUrlMap = map[string]struct{}{
|
|||||||
"/apps/upgrade": {},
|
"/apps/upgrade": {},
|
||||||
"/apps/setting": {},
|
"/apps/setting": {},
|
||||||
|
|
||||||
|
"/ai": {},
|
||||||
|
"/ai/model": {},
|
||||||
|
"/ai/gpu": {},
|
||||||
|
|
||||||
"/containers": {},
|
"/containers": {},
|
||||||
"/containers/container": {},
|
"/containers/container": {},
|
||||||
"/containers/image": {},
|
"/containers/image": {},
|
||||||
|
111
frontend/src/api/interface/ai.ts
Normal file
111
frontend/src/api/interface/ai.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { ReqPage } from '.';
|
||||||
|
|
||||||
|
export namespace AI {
|
||||||
|
export interface OllamaModelInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
size: string;
|
||||||
|
from: string;
|
||||||
|
logFileExist: boolean;
|
||||||
|
status: string;
|
||||||
|
message: string;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
export interface OllamaModelDropInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export interface OllamaModelSearch extends ReqPage {
|
||||||
|
info: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Info {
|
||||||
|
cudaVersion: string;
|
||||||
|
driverVersion: string;
|
||||||
|
type: string;
|
||||||
|
gpu: GPU[];
|
||||||
|
}
|
||||||
|
export interface GPU {
|
||||||
|
index: number;
|
||||||
|
productName: string;
|
||||||
|
persistenceMode: string;
|
||||||
|
busID: string;
|
||||||
|
displayActive: string;
|
||||||
|
ecc: string;
|
||||||
|
fanSpeed: string;
|
||||||
|
|
||||||
|
temperature: string;
|
||||||
|
performanceState: string;
|
||||||
|
powerDraw: string;
|
||||||
|
maxPowerLimit: string;
|
||||||
|
memUsed: string;
|
||||||
|
memTotal: string;
|
||||||
|
gpuUtil: string;
|
||||||
|
computeMode: string;
|
||||||
|
migMode: string;
|
||||||
|
processes: Process[];
|
||||||
|
}
|
||||||
|
export interface Process {
|
||||||
|
pid: string;
|
||||||
|
type: string;
|
||||||
|
processName: string;
|
||||||
|
usedMemory: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface XpuInfo {
|
||||||
|
type: string;
|
||||||
|
driverVersion: string;
|
||||||
|
xpu: Xpu[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Xpu {
|
||||||
|
basic: Basic;
|
||||||
|
stats: Stats;
|
||||||
|
processes: XpuProcess[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Basic {
|
||||||
|
deviceID: number;
|
||||||
|
deviceName: string;
|
||||||
|
vendorName: string;
|
||||||
|
driverVersion: string;
|
||||||
|
memory: string;
|
||||||
|
freeMemory: string;
|
||||||
|
pciBdfAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Stats {
|
||||||
|
power: string;
|
||||||
|
frequency: string;
|
||||||
|
temperature: string;
|
||||||
|
memoryUsed: string;
|
||||||
|
memoryUtil: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface XpuProcess {
|
||||||
|
pid: number;
|
||||||
|
command: string;
|
||||||
|
shr: string;
|
||||||
|
memory: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BindDomain {
|
||||||
|
domain: string;
|
||||||
|
sslID: number;
|
||||||
|
ipList: string;
|
||||||
|
appInstallID: number;
|
||||||
|
websiteID?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BindDomainReq {
|
||||||
|
appInstallID: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BindDomainRes {
|
||||||
|
domain: string;
|
||||||
|
sslID: number;
|
||||||
|
allowIPs: string[];
|
||||||
|
websiteID?: number;
|
||||||
|
connUrl: string;
|
||||||
|
}
|
||||||
|
}
|
41
frontend/src/api/modules/ai.ts
Normal file
41
frontend/src/api/modules/ai.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { AI } from '@/api/interface/ai';
|
||||||
|
import http from '@/api';
|
||||||
|
import { ResPage } from '../interface';
|
||||||
|
|
||||||
|
export const createOllamaModel = (name: string) => {
|
||||||
|
return http.post(`/ai/ollama/model`, { name: name });
|
||||||
|
};
|
||||||
|
export const recreateOllamaModel = (name: string) => {
|
||||||
|
return http.post(`/ai/ollama/model/recreate`, { name: name });
|
||||||
|
};
|
||||||
|
export const deleteOllamaModel = (ids: Array<number>, force: boolean) => {
|
||||||
|
return http.post(`/ai/ollama/model/del`, { ids: ids, forceDelete: force });
|
||||||
|
};
|
||||||
|
export const searchOllamaModel = (params: AI.OllamaModelSearch) => {
|
||||||
|
return http.post<ResPage<AI.OllamaModelInfo>>(`/ai/ollama/model/search`, params);
|
||||||
|
};
|
||||||
|
export const loadOllamaModel = (name: string) => {
|
||||||
|
return http.post<string>(`/ai/ollama/model/load`, { name: name });
|
||||||
|
};
|
||||||
|
export const syncOllamaModel = () => {
|
||||||
|
return http.post<Array<AI.OllamaModelDropInfo>>(`/ai/ollama/model/sync`);
|
||||||
|
};
|
||||||
|
export const closeOllamaModel = (name: string) => {
|
||||||
|
return http.post(`/ai/ollama/close`, { name: name });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadGPUInfo = () => {
|
||||||
|
return http.get<any>(`/ai/gpu/load`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bindDomain = (req: AI.BindDomain) => {
|
||||||
|
return http.post(`/ai/domain/bind`, req);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBindDomain = (req: AI.BindDomainReq) => {
|
||||||
|
return http.post<AI.BindDomainRes>(`/ai/domain/get`, req);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateBindDomain = (req: AI.BindDomain) => {
|
||||||
|
return http.post(`/ai/domain/update`, req);
|
||||||
|
};
|
@ -1,9 +1,9 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4776196 */
|
font-family: "iconfont"; /* Project id 4776196 */
|
||||||
src: url('iconfont.woff2?t=1740384606757') format('woff2'),
|
src: url('iconfont.woff2?t=1740392092454') format('woff2'),
|
||||||
url('iconfont.woff?t=1740384606757') format('woff'),
|
url('iconfont.woff?t=1740392092454') format('woff'),
|
||||||
url('iconfont.ttf?t=1740384606757') format('truetype'),
|
url('iconfont.ttf?t=1740392092454') format('truetype'),
|
||||||
url('iconfont.svg?t=1740384606757#iconfont') format('svg');
|
url('iconfont.svg?t=1740392092454#iconfont') format('svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@ -14,6 +14,10 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p-jiqiren2:before {
|
||||||
|
content: "\e61b";
|
||||||
|
}
|
||||||
|
|
||||||
.p-terminal2:before {
|
.p-terminal2:before {
|
||||||
content: "\e82f";
|
content: "\e82f";
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -5,6 +5,13 @@
|
|||||||
"css_prefix_text": "p-",
|
"css_prefix_text": "p-",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "10505865",
|
||||||
|
"name": "机器人",
|
||||||
|
"font_class": "jiqiren2",
|
||||||
|
"unicode": "e61b",
|
||||||
|
"unicode_decimal": 58907
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "5127551",
|
"icon_id": "5127551",
|
||||||
"name": "terminal",
|
"name": "terminal",
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
/>
|
/>
|
||||||
<missing-glyph />
|
<missing-glyph />
|
||||||
|
|
||||||
|
<glyph glyph-name="jiqiren2" unicode="" d="M554.667 533.333h-320V64h554.666V533.333H554.667z m-85.334 85.334V704H448a21.333 21.333 0 0 0-21.333 21.333V810.667A21.333 21.333 0 0 0 448 832h128a21.333 21.333 0 0 0 21.333-21.333v-85.334A21.333 21.333 0 0 0 576 704h-21.333v-85.333H832A42.667 42.667 0 0 0 874.667 576v-554.667A42.667 42.667 0 0 0 832-21.333H192a42.667 42.667 0 0 0-42.667 42.666V576A42.667 42.667 0 0 0 192 618.667h277.333zM21.333 384H64a21.333 21.333 0 0 0 21.333-21.333v-128A21.333 21.333 0 0 0 64 213.333H21.333A21.333 21.333 0 0 0 0 234.667v128A21.333 21.333 0 0 0 21.333 384zM320 362.667h128v-128H320v128z m256 0h128v-128H576v128zM960 384h42.667A21.333 21.333 0 0 0 1024 362.667v-128a21.333 21.333 0 0 0-21.333-21.334H960a21.333 21.333 0 0 0-21.333 21.334v128A21.333 21.333 0 0 0 960 384z" horiz-adv-x="1024" />
|
||||||
|
|
||||||
<glyph glyph-name="terminal2" unicode="" d="M512 298.666667h256v-85.333334h-256z m-183.168-72.832l128 128a42.624 42.624 0 0 1 0 60.330666l-128 128-60.330667-60.330666L366.336 384l-97.834667-97.834667 60.330667-60.330666zM896 725.333333H128a42.666667 42.666667 0 0 1-42.666667-42.666666v-597.333334a42.666667 42.666667 0 0 1 42.666667-42.666666h768a42.666667 42.666667 0 0 1 42.666667 42.666666V682.666667a42.666667 42.666667 0 0 1-42.666667 42.666666z m-42.666667-597.333333H170.666667V640h682.666666v-512z" horiz-adv-x="1024" />
|
<glyph glyph-name="terminal2" unicode="" d="M512 298.666667h256v-85.333334h-256z m-183.168-72.832l128 128a42.624 42.624 0 0 1 0 60.330666l-128 128-60.330667-60.330666L366.336 384l-97.834667-97.834667 60.330667-60.330666zM896 725.333333H128a42.666667 42.666667 0 0 1-42.666667-42.666666v-597.333334a42.666667 42.666667 0 0 1 42.666667-42.666666h768a42.666667 42.666667 0 0 1 42.666667 42.666666V682.666667a42.666667 42.666667 0 0 1-42.666667 42.666666z m-42.666667-597.333333H170.666667V640h682.666666v-512z" horiz-adv-x="1024" />
|
||||||
|
|
||||||
<glyph glyph-name="tuijian" unicode="" d="M64 832m128 0l640 0q128 0 128-128l0-640q0-128-128-128l-640 0q-128 0-128 128l0 640q0 128 128 128ZM585.6 448h88.96a64 64 0 0 0 19.2 0 50.56 50.56 0 0 0 29.44-64c-10.88-55.68-22.4-110.72-33.28-165.76a64 64 0 0 0-19.2-39.68 37.12 37.12 0 0 0-23.04-9.6H410.24V423.68c0 5.12 0 7.04 6.4 8.32A107.52 107.52 0 0 1 499.2 529.92a112.64 112.64 0 0 0 6.4 38.4 41.6 41.6 0 0 0 52.48 24.96 39.68 39.68 0 0 0 17.92-10.88 54.4 54.4 0 0 0 16-33.28A238.72 238.72 0 0 0 586.24 448z m-198.4-38.4c0 13.44-7.04 21.76-19.2 21.76H320a17.92 17.92 0 0 1-18.56-17.92V192a17.92 17.92 0 0 1 16-20.48h49.28c14.72 0 21.76 7.04 21.76 24.32V407.68z" horiz-adv-x="1024" />
|
<glyph glyph-name="tuijian" unicode="" d="M64 832m128 0l640 0q128 0 128-128l0-640q0-128-128-128l-640 0q-128 0-128 128l0 640q0 128 128 128ZM585.6 448h88.96a64 64 0 0 0 19.2 0 50.56 50.56 0 0 0 29.44-64c-10.88-55.68-22.4-110.72-33.28-165.76a64 64 0 0 0-19.2-39.68 37.12 37.12 0 0 0-23.04-9.6H410.24V423.68c0 5.12 0 7.04 6.4 8.32A107.52 107.52 0 0 1 499.2 529.92a112.64 112.64 0 0 0 6.4 38.4 41.6 41.6 0 0 0 52.48 24.96 39.68 39.68 0 0 0 17.92-10.88 54.4 54.4 0 0 0 16-33.28A238.72 238.72 0 0 0 586.24 448z m-198.4-38.4c0 13.44-7.04 21.76-19.2 21.76H320a17.92 17.92 0 0 1-18.56-17.92V192a17.92 17.92 0 0 1 16-20.48h49.28c14.72 0 21.76 7.04 21.76 24.32V407.68z" horiz-adv-x="1024" />
|
||||||
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 166 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -30,7 +30,7 @@
|
|||||||
>
|
>
|
||||||
{{ $t('commons.operate.restart') }}
|
{{ $t('commons.operate.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider v-if="!hideSetting" direction="vertical" />
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
@ -42,6 +42,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-divider v-if="data.app === 'OpenResty'" direction="vertical" />
|
<el-divider v-if="data.app === 'OpenResty'" direction="vertical" />
|
||||||
<el-button
|
<el-button
|
||||||
|
v-if="!hideSetting"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="setting"
|
@click="setting"
|
||||||
link
|
link
|
||||||
@ -89,6 +90,10 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
hideSetting: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let key = ref('');
|
let key = ref('');
|
||||||
|
@ -56,7 +56,6 @@ interface DialogProps {
|
|||||||
upgradeVersion: string;
|
upgradeVersion: string;
|
||||||
}
|
}
|
||||||
const acceptParams = (params: DialogProps): void => {
|
const acceptParams = (params: DialogProps): void => {
|
||||||
console.log(params);
|
|
||||||
upgradeInfo.value = params.upgradeInfo;
|
upgradeInfo.value = params.upgradeInfo;
|
||||||
upgradeVersion.value = params.upgradeVersion;
|
upgradeVersion.value = params.upgradeVersion;
|
||||||
drawerVisible.value = true;
|
drawerVisible.value = true;
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
lingxia: 'Lingxia',
|
lingxia: 'Lingxia',
|
||||||
colon: ': ',
|
colon: ': ',
|
||||||
button: {
|
button: {
|
||||||
|
run: 'Run',
|
||||||
prev: 'Previous',
|
prev: 'Previous',
|
||||||
next: 'Next',
|
next: 'Next',
|
||||||
create: 'Create ',
|
create: 'Create ',
|
||||||
@ -333,6 +334,7 @@ const message = {
|
|||||||
firewall: 'Firewall',
|
firewall: 'Firewall',
|
||||||
ssl: 'Certificate',
|
ssl: 'Certificate',
|
||||||
database: 'Database',
|
database: 'Database',
|
||||||
|
aiTools: 'AI',
|
||||||
container: 'Container',
|
container: 'Container',
|
||||||
cronjob: 'Cronjob',
|
cronjob: 'Cronjob',
|
||||||
host: 'Host',
|
host: 'Host',
|
||||||
@ -589,6 +591,67 @@ const message = {
|
|||||||
remoteConnHelper2: 'Use this address for non-container or external connections',
|
remoteConnHelper2: 'Use this address for non-container or external connections',
|
||||||
localIP: 'Local IP',
|
localIP: 'Local IP',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: 'Model',
|
||||||
|
create: 'Add Model',
|
||||||
|
create_helper: 'Pull "{0}"',
|
||||||
|
ollama_doc: 'You can visit the Ollama official website to search and find more models.',
|
||||||
|
container_conn_helper: 'Use this address for inter-container access or connection',
|
||||||
|
ollama_sync: 'Syncing Ollama model found the following models do not exist, do you want to delete them?',
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'AI Proxy Enhancement',
|
||||||
|
proxyHelper1: 'Bind domain and enable HTTPS for enhanced transmission security',
|
||||||
|
proxyHelper2: 'Limit IP access to prevent exposure on the public internet',
|
||||||
|
proxyHelper3: 'Enable streaming',
|
||||||
|
proxyHelper4: 'Once created, you can view and manage it in the website list',
|
||||||
|
proxyHelper5:
|
||||||
|
'After enabling, you can disable external access to the port in the App Store - Installed - Ollama - Parameters to improve security.',
|
||||||
|
proxyHelper6: 'To disable proxy configuration, you can delete it from the website list.',
|
||||||
|
whiteListHelper: 'Restrict access to only IPs in the whitelist',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'GPU Monitor',
|
||||||
|
base: 'Basic Information',
|
||||||
|
gpuHelper: 'NVIDIA-SMI or XPU-SMI command not detected on the current system. Please check and try again!',
|
||||||
|
driverVersion: 'Driver Version',
|
||||||
|
cudaVersion: 'CUDA Version',
|
||||||
|
process: 'Process Information',
|
||||||
|
type: 'Type',
|
||||||
|
typeG: 'Graphics',
|
||||||
|
typeC: 'Compute',
|
||||||
|
typeCG: 'Compute + Graphics',
|
||||||
|
processName: 'Process Name',
|
||||||
|
processMemoryUsage: 'Memory Usage',
|
||||||
|
temperatureHelper: 'High GPU temperature can cause GPU frequency throttling',
|
||||||
|
performanceStateHelper: 'From P0 (maximum performance) to P12 (minimum performance)',
|
||||||
|
busID: 'Bus ID',
|
||||||
|
persistenceMode: 'Persistence Mode',
|
||||||
|
enabled: 'Enabled',
|
||||||
|
disabled: 'Disabled',
|
||||||
|
persistenceModeHelper:
|
||||||
|
'Persistence mode allows quicker task responses but increases standby power consumption.',
|
||||||
|
displayActive: 'Graphics Card Initialized',
|
||||||
|
displayActiveT: 'Yes',
|
||||||
|
displayActiveF: 'No',
|
||||||
|
ecc: 'Error Correction and Check Technology',
|
||||||
|
computeMode: 'Compute Mode',
|
||||||
|
default: 'Default',
|
||||||
|
exclusiveProcess: 'Exclusive Process',
|
||||||
|
exclusiveThread: 'Exclusive Thread',
|
||||||
|
prohibited: 'Prohibited',
|
||||||
|
defaultHelper: 'Default: Processes can execute concurrently',
|
||||||
|
exclusiveProcessHelper:
|
||||||
|
'Exclusive Process: Only one CUDA context can use the GPU, but can be shared by multiple threads',
|
||||||
|
exclusiveThreadHelper: 'Exclusive Thread: Only one thread in a CUDA context can use the GPU',
|
||||||
|
prohibitedHelper: 'Prohibited: Processes are not allowed to execute simultaneously',
|
||||||
|
migModeHelper: 'Used to create MIG instances for physical isolation of the GPU at the user level.',
|
||||||
|
migModeNA: 'Not Supported',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: 'Create',
|
create: 'Create',
|
||||||
createByCommand: 'Create by command',
|
createByCommand: 'Create by command',
|
||||||
@ -1807,7 +1870,6 @@ const message = {
|
|||||||
waf: 'Upgrading to the professional version can provide features such as interception map, logs, block records, geographical location blocking, custom rules, custom interception pages, etc.',
|
waf: 'Upgrading to the professional version can provide features such as interception map, logs, block records, geographical location blocking, custom rules, custom interception pages, etc.',
|
||||||
tamper: 'Upgrading to the professional version can protect websites from unauthorized modifications or tampering.',
|
tamper: 'Upgrading to the professional version can protect websites from unauthorized modifications or tampering.',
|
||||||
tamperHelper: 'Operation failed, the file or folder has tamper protection enabled. Please check and try again!',
|
tamperHelper: 'Operation failed, the file or folder has tamper protection enabled. Please check and try again!',
|
||||||
gpu: 'Upgrading to the professional version can help users visually monitor important parameters of GPU such as workload, temperature, memory usage in real time.',
|
|
||||||
setting:
|
setting:
|
||||||
'Upgrading to the professional version allows customization of panel logo, welcome message, and other information.',
|
'Upgrading to the professional version allows customization of panel logo, welcome message, and other information.',
|
||||||
monitor:
|
monitor:
|
||||||
@ -2995,44 +3057,6 @@ const message = {
|
|||||||
disableHelper:
|
disableHelper:
|
||||||
'The anti-tampering function of website {0} is about to be disabled. Do you want to continue?',
|
'The anti-tampering function of website {0} is about to be disabled. Do you want to continue?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'GPU Monitor',
|
|
||||||
base: 'Basic Information',
|
|
||||||
gpuHelper: 'NVIDIA-SMI or XPU-SMI command not detected on the current system. Please check and try again!',
|
|
||||||
driverVersion: 'Driver Version',
|
|
||||||
cudaVersion: 'CUDA Version',
|
|
||||||
process: 'Process Information',
|
|
||||||
type: 'Type',
|
|
||||||
typeG: 'Graphics',
|
|
||||||
typeC: 'Compute',
|
|
||||||
typeCG: 'Compute + Graphics',
|
|
||||||
processName: 'Process Name',
|
|
||||||
processMemoryUsage: 'Memory Usage',
|
|
||||||
temperatureHelper: 'High GPU temperature can cause GPU frequency throttling',
|
|
||||||
performanceStateHelper: 'From P0 (maximum performance) to P12 (minimum performance)',
|
|
||||||
busID: 'Bus ID',
|
|
||||||
persistenceMode: 'Persistence Mode',
|
|
||||||
enabled: 'Enabled',
|
|
||||||
disabled: 'Disabled',
|
|
||||||
persistenceModeHelper:
|
|
||||||
'Persistence mode allows quicker task responses but increases standby power consumption.',
|
|
||||||
displayActive: 'Graphics Card Initialized',
|
|
||||||
displayActiveT: 'Yes',
|
|
||||||
displayActiveF: 'No',
|
|
||||||
ecc: 'Error Correction and Check Technology',
|
|
||||||
computeMode: 'Compute Mode',
|
|
||||||
default: 'Default',
|
|
||||||
exclusiveProcess: 'Exclusive Process',
|
|
||||||
exclusiveThread: 'Exclusive Thread',
|
|
||||||
prohibited: 'Prohibited',
|
|
||||||
defaultHelper: 'Default: Processes can execute concurrently',
|
|
||||||
exclusiveProcessHelper:
|
|
||||||
'Exclusive Process: Only one CUDA context can use the GPU, but can be shared by multiple threads',
|
|
||||||
exclusiveThreadHelper: 'Exclusive Thread: Only one thread in a CUDA context can use the GPU',
|
|
||||||
prohibitedHelper: 'Prohibited: Processes are not allowed to execute simultaneously',
|
|
||||||
migModeHelper: 'Used to create MIG instances for physical isolation of the GPU at the user level.',
|
|
||||||
migModeNA: 'Not Supported',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: 'Panel Settings',
|
setting: 'Panel Settings',
|
||||||
title: 'Panel Description',
|
title: 'Panel Description',
|
||||||
@ -3076,11 +3100,6 @@ const message = {
|
|||||||
tamperContent4:
|
tamperContent4:
|
||||||
'Record file access and operation logs for subsequent auditing and analysis by administrators, as well as to identify potential security threats.',
|
'Record file access and operation logs for subsequent auditing and analysis by administrators, as well as to identify potential security threats.',
|
||||||
|
|
||||||
gpuTitle1: 'Overview Monitoring',
|
|
||||||
gpuContent1: 'Display the current GPU usage on the overview page.',
|
|
||||||
gpuTitle2: 'GPU Details',
|
|
||||||
gpuContent2: 'Show GPU parameters in finer detail.',
|
|
||||||
|
|
||||||
settingTitle1: 'Custom Welcome Message',
|
settingTitle1: 'Custom Welcome Message',
|
||||||
settingContent1: 'Set a custom welcome message on the 1Panel login page.',
|
settingContent1: 'Set a custom welcome message on the 1Panel login page.',
|
||||||
settingTitle2: 'Custom Logo',
|
settingTitle2: 'Custom Logo',
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
fit2cloud: 'FIT2CLOUD',
|
fit2cloud: 'FIT2CLOUD',
|
||||||
lingxia: 'Lingxia',
|
lingxia: 'Lingxia',
|
||||||
button: {
|
button: {
|
||||||
|
run: '実行',
|
||||||
create: '作成する',
|
create: '作成する',
|
||||||
add: '追加',
|
add: '追加',
|
||||||
save: '保存',
|
save: '保存',
|
||||||
@ -327,6 +328,7 @@ const message = {
|
|||||||
firewall: 'ファイアウォール',
|
firewall: 'ファイアウォール',
|
||||||
ssl: '証明書|証明書',
|
ssl: '証明書|証明書',
|
||||||
database: 'データベース|データベース',
|
database: 'データベース|データベース',
|
||||||
|
aiTools: 'AI',
|
||||||
container: 'コンテナ|コンテナ',
|
container: 'コンテナ|コンテナ',
|
||||||
cronjob: 'クロンジョブ|クロンの仕事',
|
cronjob: 'クロンジョブ|クロンの仕事',
|
||||||
host: 'ホスト|ホスト',
|
host: 'ホスト|ホスト',
|
||||||
@ -581,6 +583,67 @@ const message = {
|
|||||||
'この接続アドレスは、非コンテナまたは外部アプリケーションで実行されているアプリケーションで使用できます。',
|
'この接続アドレスは、非コンテナまたは外部アプリケーションで実行されているアプリケーションで使用できます。',
|
||||||
localIP: 'ローカルIP',
|
localIP: 'ローカルIP',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: 'モデル',
|
||||||
|
create: 'モデルを追加',
|
||||||
|
create_helper: 'を取得 "{0}"',
|
||||||
|
ollama_doc: 'Ollama の公式ウェブサイトを訪れて、さらに多くのモデルを検索して見つけることができます。',
|
||||||
|
container_conn_helper: 'コンテナ間のアクセスまたは接続にこのアドレスを使用',
|
||||||
|
ollama_sync: 'Ollamaモデルの同期中に、以下のモデルが存在しないことが判明しました。削除しますか?',
|
||||||
|
from_remote: 'このモデルは1Panelを介してダウンロードされておらず、関連するプルログはありません。',
|
||||||
|
no_logs: 'このモデルのプルログは削除されており、関連するログを表示できません。',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'AI プロキシ強化',
|
||||||
|
proxyHelper1: 'ドメインをバインドし、HTTPS を有効にして通信のセキュリティを強化',
|
||||||
|
proxyHelper2: 'IP アクセスを制限し、パブリックインターネットでの露出を防止',
|
||||||
|
proxyHelper3: 'ストリーミングを有効にする',
|
||||||
|
proxyHelper4: '作成後、ウェブサイトリストで確認および管理できます',
|
||||||
|
proxyHelper5:
|
||||||
|
'有効にすると、アプリストア - インストール済み - Ollama - パラメータでポートの外部アクセスを無効にし、セキュリティを向上させることができます。',
|
||||||
|
proxyHelper6: 'プロキシ設定を無効にするには、ウェブサイトリストから削除できます。',
|
||||||
|
whiteListHelper: 'ホワイトリスト内のIPのみアクセスを許可する',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'GPUモニター',
|
||||||
|
base: '基本情報',
|
||||||
|
gpuHelper:
|
||||||
|
'現在のシステムでNVIDIA-SMIまたはXPU-SMIコマンドが検出されませんでした。確認して再試行してください!',
|
||||||
|
driverVersion: 'ドライバーバージョン',
|
||||||
|
cudaVersion: 'CUDAバージョン',
|
||||||
|
process: 'プロセス情報',
|
||||||
|
type: 'タイプ',
|
||||||
|
typeG: 'グラフィックス',
|
||||||
|
typeC: 'コンピュート',
|
||||||
|
typeCG: 'コンピュート + グラフィックス',
|
||||||
|
processName: 'プロセス名',
|
||||||
|
processMemoryUsage: 'メモリ使用量',
|
||||||
|
temperatureHelper: '高いGPU温度はGPUの周波数制限を引き起こす可能性があります',
|
||||||
|
performanceStateHelper: 'P0(最大性能)からP12(最小性能)まで',
|
||||||
|
busID: 'バスID',
|
||||||
|
persistenceMode: '永続モード',
|
||||||
|
enabled: '有効',
|
||||||
|
disabled: '無効',
|
||||||
|
persistenceModeHelper: '永続モードはタスクの応答速度を速くしますが、待機時の消費電力が増加します。',
|
||||||
|
displayActive: 'グラフィックカード初期化済み',
|
||||||
|
displayActiveT: 'はい',
|
||||||
|
displayActiveF: 'いいえ',
|
||||||
|
ecc: 'エラー訂正およびチェック技術',
|
||||||
|
computeMode: 'コンピュートモード',
|
||||||
|
default: 'デフォルト',
|
||||||
|
exclusiveProcess: '専用プロセス',
|
||||||
|
exclusiveThread: '専用スレッド',
|
||||||
|
prohibited: '禁止',
|
||||||
|
defaultHelper: 'デフォルト:プロセスは並行して実行できます',
|
||||||
|
exclusiveProcessHelper:
|
||||||
|
'専用プロセス:1つのCUDAコンテキストのみがGPUを使用できますが、複数のスレッドで共有できます',
|
||||||
|
exclusiveThreadHelper: '専用スレッド:CUDAコンテキスト内の1つのスレッドのみがGPUを使用できます',
|
||||||
|
prohibitedHelper: '禁止:プロセスは同時に実行できません',
|
||||||
|
migModeHelper: 'ユーザーレベルでGPUの物理的分離を行うためのMIGインスタンスを作成するために使用されます。',
|
||||||
|
migModeNA: 'サポートされていません',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: 'コンテナを作成します',
|
create: 'コンテナを作成します',
|
||||||
edit: 'コンテナを編集します',
|
edit: 'コンテナを編集します',
|
||||||
@ -1670,7 +1733,6 @@ const message = {
|
|||||||
introduce: '機能の紹介',
|
introduce: '機能の紹介',
|
||||||
waf: 'プロフェッショナルバージョンにアップグレードすると、インターセプトマップ、ログ、ブロックレコード、地理的位置ブロッキング、カスタムルール、カスタムインターセプトページなどの機能を提供できます。',
|
waf: 'プロフェッショナルバージョンにアップグレードすると、インターセプトマップ、ログ、ブロックレコード、地理的位置ブロッキング、カスタムルール、カスタムインターセプトページなどの機能を提供できます。',
|
||||||
tamper: 'プロのバージョンにアップグレードすると、不正な変更や改ざんからWebサイトを保護できます。',
|
tamper: 'プロのバージョンにアップグレードすると、不正な変更や改ざんからWebサイトを保護できます。',
|
||||||
gpu: 'プロのバージョンにアップグレードすることで、ユーザーはワークロード、温度、メモリ使用量などのGPUの重要なパラメーターをリアルタイムで視覚的に監視するのに役立ちます。',
|
|
||||||
setting:
|
setting:
|
||||||
'プロのバージョンにアップグレードすることで、パネルロゴ、ウェルカムメッセージ、その他の情報のカスタマイズが可能になります。',
|
'プロのバージョンにアップグレードすることで、パネルロゴ、ウェルカムメッセージ、その他の情報のカスタマイズが可能になります。',
|
||||||
monitor:
|
monitor:
|
||||||
@ -2806,44 +2868,6 @@ const message = {
|
|||||||
'ウェブサイト {0} の改ざん防止機能が有効になろうとしています。セキュリティを強化するために続行しますか?',
|
'ウェブサイト {0} の改ざん防止機能が有効になろうとしています。セキュリティを強化するために続行しますか?',
|
||||||
disableHelper: 'ウェブサイト {0} の改ざん防止機能が無効になろうとしています。続行しますか?',
|
disableHelper: 'ウェブサイト {0} の改ざん防止機能が無効になろうとしています。続行しますか?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'GPUモニター',
|
|
||||||
base: '基本情報',
|
|
||||||
gpuHelper:
|
|
||||||
'現在のシステムでNVIDIA-SMIまたはXPU-SMIコマンドが検出されませんでした。確認して再試行してください!',
|
|
||||||
driverVersion: 'ドライバーバージョン',
|
|
||||||
cudaVersion: 'CUDAバージョン',
|
|
||||||
process: 'プロセス情報',
|
|
||||||
type: 'タイプ',
|
|
||||||
typeG: 'グラフィックス',
|
|
||||||
typeC: 'コンピュート',
|
|
||||||
typeCG: 'コンピュート + グラフィックス',
|
|
||||||
processName: 'プロセス名',
|
|
||||||
processMemoryUsage: 'メモリ使用量',
|
|
||||||
temperatureHelper: '高いGPU温度はGPUの周波数制限を引き起こす可能性があります',
|
|
||||||
performanceStateHelper: 'P0(最大性能)からP12(最小性能)まで',
|
|
||||||
busID: 'バスID',
|
|
||||||
persistenceMode: '永続モード',
|
|
||||||
enabled: '有効',
|
|
||||||
disabled: '無効',
|
|
||||||
persistenceModeHelper: '永続モードはタスクの応答速度を速くしますが、待機時の消費電力が増加します。',
|
|
||||||
displayActive: 'グラフィックカード初期化済み',
|
|
||||||
displayActiveT: 'はい',
|
|
||||||
displayActiveF: 'いいえ',
|
|
||||||
ecc: 'エラー訂正およびチェック技術',
|
|
||||||
computeMode: 'コンピュートモード',
|
|
||||||
default: 'デフォルト',
|
|
||||||
exclusiveProcess: '専用プロセス',
|
|
||||||
exclusiveThread: '専用スレッド',
|
|
||||||
prohibited: '禁止',
|
|
||||||
defaultHelper: 'デフォルト:プロセスは並行して実行できます',
|
|
||||||
exclusiveProcessHelper:
|
|
||||||
'専用プロセス:1つのCUDAコンテキストのみがGPUを使用できますが、複数のスレッドで共有できます',
|
|
||||||
exclusiveThreadHelper: '専用スレッド:CUDAコンテキスト内の1つのスレッドのみがGPUを使用できます',
|
|
||||||
prohibitedHelper: '禁止:プロセスは同時に実行できません',
|
|
||||||
migModeHelper: 'ユーザーレベルでGPUの物理的分離を行うためのMIGインスタンスを作成するために使用されます。',
|
|
||||||
migModeNA: 'サポートされていません',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: 'パネル設定',
|
setting: 'パネル設定',
|
||||||
title: 'パネルの説明',
|
title: 'パネルの説明',
|
||||||
@ -2887,10 +2911,6 @@ const message = {
|
|||||||
tamperTitle4: 'ログ記録と分析',
|
tamperTitle4: 'ログ記録と分析',
|
||||||
tamperContent4:
|
tamperContent4:
|
||||||
'ファイルのアクセスおよび操作ログを記録し、後の監査および分析に使用、また潜在的なセキュリティ脅威を特定。',
|
'ファイルのアクセスおよび操作ログを記録し、後の監査および分析に使用、また潜在的なセキュリティ脅威を特定。',
|
||||||
gpuTitle1: '概要ページモニタリング',
|
|
||||||
gpuContent1: '概要ページでGPUの現在の使用状況を表示します。',
|
|
||||||
gpuTitle2: 'GPU詳細情報',
|
|
||||||
gpuContent2: 'GPUの各パラメータをより詳細に表示します。',
|
|
||||||
|
|
||||||
settingTitle1: 'カスタムウェルカムメッセージ',
|
settingTitle1: 'カスタムウェルカムメッセージ',
|
||||||
settingContent1: '1Panelのログインページにカスタムウェルカムメッセージを設定。',
|
settingContent1: '1Panelのログインページにカスタムウェルカムメッセージを設定。',
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
fit2cloud: 'FIT2CLOUD',
|
fit2cloud: 'FIT2CLOUD',
|
||||||
lingxia: 'Lingxia',
|
lingxia: 'Lingxia',
|
||||||
button: {
|
button: {
|
||||||
|
run: '실행',
|
||||||
create: '생성',
|
create: '생성',
|
||||||
add: '추가',
|
add: '추가',
|
||||||
save: '저장',
|
save: '저장',
|
||||||
@ -328,6 +329,7 @@ const message = {
|
|||||||
firewall: '방화벽',
|
firewall: '방화벽',
|
||||||
ssl: '인증서 | 인증서들',
|
ssl: '인증서 | 인증서들',
|
||||||
database: '데이터베이스 | 데이터베이스들',
|
database: '데이터베이스 | 데이터베이스들',
|
||||||
|
aiTools: 'AI',
|
||||||
container: '컨테이너 | 컨테이너들',
|
container: '컨테이너 | 컨테이너들',
|
||||||
cronjob: '크론 작업 | 크론 작업들',
|
cronjob: '크론 작업 | 크론 작업들',
|
||||||
host: '호스트 | 호스트들',
|
host: '호스트 | 호스트들',
|
||||||
@ -577,6 +579,66 @@ const message = {
|
|||||||
'이 연결 주소는 컨테이너 외부 또는 외부 애플리케이션에서 실행 중인 애플리케이션에서 사용할 수 있습니다.',
|
'이 연결 주소는 컨테이너 외부 또는 외부 애플리케이션에서 실행 중인 애플리케이션에서 사용할 수 있습니다.',
|
||||||
localIP: '로컬 IP',
|
localIP: '로컬 IP',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: '모델',
|
||||||
|
create: '모델 추가',
|
||||||
|
create_helper: '가져오기 "{0}"',
|
||||||
|
ollama_doc: 'Ollama 공식 웹사이트를 방문하여 더 많은 모델을 검색하고 찾을 수 있습니다.',
|
||||||
|
container_conn_helper: '컨테이너 간 접근 또는 연결에 이 주소를 사용',
|
||||||
|
ollama_sync: 'Ollama 모델 동기화 중 다음 모델이 존재하지 않음을 발견했습니다. 삭제하시겠습니까?',
|
||||||
|
from_remote: '이 모델은 1Panel을 통해 다운로드되지 않았으며 관련 풀 로그가 없습니다.',
|
||||||
|
no_logs: '이 모델의 풀 로그가 삭제되어 관련 로그를 볼 수 없습니다.',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'AI 프록시 강화',
|
||||||
|
proxyHelper1: '도메인을 바인딩하고 HTTPS를 활성화하여 전송 보안을 강화',
|
||||||
|
proxyHelper2: 'IP 접근을 제한하여 공용 인터넷에서의 노출을 방지',
|
||||||
|
proxyHelper3: '스트리밍을 활성화',
|
||||||
|
proxyHelper4: '생성 후, 웹사이트 목록에서 이를 보고 관리할 수 있습니다',
|
||||||
|
proxyHelper5:
|
||||||
|
'활성화한 후, 앱 스토어 - 설치됨 - Ollama - 매개변수에서 포트 외부 접근을 비활성화하여 보안을 강화할 수 있습니다.',
|
||||||
|
proxyHelper6: '프록시 구성을 비활성화하려면 웹사이트 목록에서 삭제할 수 있습니다.',
|
||||||
|
whiteListHelper: '화이트리스트에 있는 IP만 접근 허용',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'GPU 모니터',
|
||||||
|
base: '기본 정보',
|
||||||
|
gpuHelper: '현재 시스템에서 NVIDIA-SMI 또는 XPU-SMI 명령이 감지되지 않았습니다. 확인 후 다시 시도하세요!',
|
||||||
|
driverVersion: '드라이버 버전',
|
||||||
|
cudaVersion: 'CUDA 버전',
|
||||||
|
process: '프로세스 정보',
|
||||||
|
type: '유형',
|
||||||
|
typeG: '그래픽',
|
||||||
|
typeC: '연산',
|
||||||
|
typeCG: '연산 + 그래픽',
|
||||||
|
processName: '프로세스 이름',
|
||||||
|
processMemoryUsage: '메모리 사용량',
|
||||||
|
temperatureHelper: 'GPU 온도가 높으면 GPU 주파수 제한이 발생할 수 있습니다.',
|
||||||
|
performanceStateHelper: 'P0(최대 성능)부터 P12(최소 성능)까지',
|
||||||
|
busID: '버스 ID',
|
||||||
|
persistenceMode: '지속 모드',
|
||||||
|
enabled: '활성화됨',
|
||||||
|
disabled: '비활성화됨',
|
||||||
|
persistenceModeHelper: '지속 모드는 작업 응답 속도를 빠르게 하지만 대기 전력 소비를 증가시킵니다.',
|
||||||
|
displayActive: '그래픽 카드 초기화됨',
|
||||||
|
displayActiveT: '예',
|
||||||
|
displayActiveF: '아니요',
|
||||||
|
ecc: '오류 감지 및 수정 기술',
|
||||||
|
computeMode: '연산 모드',
|
||||||
|
default: '기본값',
|
||||||
|
exclusiveProcess: '단독 프로세스',
|
||||||
|
exclusiveThread: '단독 스레드',
|
||||||
|
prohibited: '금지됨',
|
||||||
|
defaultHelper: '기본값: 프로세스가 동시에 실행될 수 있음',
|
||||||
|
exclusiveProcessHelper:
|
||||||
|
'단독 프로세스: 하나의 CUDA 컨텍스트만 GPU 를 사용할 수 있지만, 여러 스레드에서 공유 가능',
|
||||||
|
exclusiveThreadHelper: '단독 스레드: CUDA 컨텍스트의 하나의 스레드만 GPU 를 사용할 수 있음',
|
||||||
|
prohibitedHelper: '금지됨: 프로세스가 동시에 실행되는 것이 허용되지 않음',
|
||||||
|
migModeHelper: '사용자 수준에서 GPU 를 물리적으로 분리하는 MIG 인스턴스를 생성하는 데 사용됩니다.',
|
||||||
|
migModeNA: '지원되지 않음',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: '컨테이너 만들기',
|
create: '컨테이너 만들기',
|
||||||
edit: '컨테이너 편집',
|
edit: '컨테이너 편집',
|
||||||
@ -1643,7 +1705,6 @@ const message = {
|
|||||||
introduce: '기능 소개',
|
introduce: '기능 소개',
|
||||||
waf: '전문 버전으로 업그레이드하면 차단 맵, 로그, 차단 기록, 지리적 위치 차단, 사용자 정의 규칙, 사용자 정의 차단 페이지 등의 기능을 제공받을 수 있습니다.',
|
waf: '전문 버전으로 업그레이드하면 차단 맵, 로그, 차단 기록, 지리적 위치 차단, 사용자 정의 규칙, 사용자 정의 차단 페이지 등의 기능을 제공받을 수 있습니다.',
|
||||||
tamper: '전문 버전으로 업그레이드하면 웹사이트를 무단 수정이나 변조로부터 보호할 수 있습니다.',
|
tamper: '전문 버전으로 업그레이드하면 웹사이트를 무단 수정이나 변조로부터 보호할 수 있습니다.',
|
||||||
gpu: '전문 버전으로 업그레이드하면 GPU 의 작업 부하, 온도, 메모리 사용량 등 중요한 매개변수를 실시간으로 시각적으로 모니터링할 수 있습니다.',
|
|
||||||
setting: '전문 버전으로 업그레이드하면 패널 로고, 환영 메시지 등 정보를 사용자 정의할 수 있습니다.',
|
setting: '전문 버전으로 업그레이드하면 패널 로고, 환영 메시지 등 정보를 사용자 정의할 수 있습니다.',
|
||||||
monitor:
|
monitor:
|
||||||
'전문 버전으로 업그레이드하면 웹사이트의 실시간 상태, 방문자 트렌드, 방문자 출처, 요청 로그 등 정보를 확인할 수 있습니다.',
|
'전문 버전으로 업그레이드하면 웹사이트의 실시간 상태, 방문자 트렌드, 방문자 출처, 요청 로그 등 정보를 확인할 수 있습니다.',
|
||||||
@ -2763,43 +2824,6 @@ const message = {
|
|||||||
'웹사이트 {0}의 방지 조작 기능을 활성화하여 웹사이트 보안을 강화하려고 합니다. 계속하시겠습니까?',
|
'웹사이트 {0}의 방지 조작 기능을 활성화하여 웹사이트 보안을 강화하려고 합니다. 계속하시겠습니까?',
|
||||||
disableHelper: '웹사이트 {0}의 방지 조작 기능을 비활성화하려고 합니다. 계속하시겠습니까?',
|
disableHelper: '웹사이트 {0}의 방지 조작 기능을 비활성화하려고 합니다. 계속하시겠습니까?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'GPU 모니터',
|
|
||||||
base: '기본 정보',
|
|
||||||
gpuHelper: '현재 시스템에서 NVIDIA-SMI 또는 XPU-SMI 명령이 감지되지 않았습니다. 확인 후 다시 시도하세요!',
|
|
||||||
driverVersion: '드라이버 버전',
|
|
||||||
cudaVersion: 'CUDA 버전',
|
|
||||||
process: '프로세스 정보',
|
|
||||||
type: '유형',
|
|
||||||
typeG: '그래픽',
|
|
||||||
typeC: '연산',
|
|
||||||
typeCG: '연산 + 그래픽',
|
|
||||||
processName: '프로세스 이름',
|
|
||||||
processMemoryUsage: '메모리 사용량',
|
|
||||||
temperatureHelper: 'GPU 온도가 높으면 GPU 주파수 제한이 발생할 수 있습니다.',
|
|
||||||
performanceStateHelper: 'P0(최대 성능)부터 P12(최소 성능)까지',
|
|
||||||
busID: '버스 ID',
|
|
||||||
persistenceMode: '지속 모드',
|
|
||||||
enabled: '활성화됨',
|
|
||||||
disabled: '비활성화됨',
|
|
||||||
persistenceModeHelper: '지속 모드는 작업 응답 속도를 빠르게 하지만 대기 전력 소비를 증가시킵니다.',
|
|
||||||
displayActive: '그래픽 카드 초기화됨',
|
|
||||||
displayActiveT: '예',
|
|
||||||
displayActiveF: '아니요',
|
|
||||||
ecc: '오류 감지 및 수정 기술',
|
|
||||||
computeMode: '연산 모드',
|
|
||||||
default: '기본값',
|
|
||||||
exclusiveProcess: '단독 프로세스',
|
|
||||||
exclusiveThread: '단독 스레드',
|
|
||||||
prohibited: '금지됨',
|
|
||||||
defaultHelper: '기본값: 프로세스가 동시에 실행될 수 있음',
|
|
||||||
exclusiveProcessHelper:
|
|
||||||
'단독 프로세스: 하나의 CUDA 컨텍스트만 GPU 를 사용할 수 있지만, 여러 스레드에서 공유 가능',
|
|
||||||
exclusiveThreadHelper: '단독 스레드: CUDA 컨텍스트의 하나의 스레드만 GPU 를 사용할 수 있음',
|
|
||||||
prohibitedHelper: '금지됨: 프로세스가 동시에 실행되는 것이 허용되지 않음',
|
|
||||||
migModeHelper: '사용자 수준에서 GPU 를 물리적으로 분리하는 MIG 인스턴스를 생성하는 데 사용됩니다.',
|
|
||||||
migModeNA: '지원되지 않음',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: '패널 설정',
|
setting: '패널 설정',
|
||||||
title: '패널 설명',
|
title: '패널 설명',
|
||||||
@ -2840,11 +2864,6 @@ const message = {
|
|||||||
tamperContent4:
|
tamperContent4:
|
||||||
'파일 접근 및 작업 로그를 기록하여 관리자가 감사 및 분석을 수행할 수 있도록 하고, 잠재적 보안 위협을 식별합니다.',
|
'파일 접근 및 작업 로그를 기록하여 관리자가 감사 및 분석을 수행할 수 있도록 하고, 잠재적 보안 위협을 식별합니다.',
|
||||||
|
|
||||||
gpuTitle1: '개요 모니터링',
|
|
||||||
gpuContent1: '개요 페이지에서 현재 GPU 사용량을 표시합니다.',
|
|
||||||
gpuTitle2: 'GPU 세부 정보',
|
|
||||||
gpuContent2: 'GPU 매개변수를 더 세부적으로 표시합니다.',
|
|
||||||
|
|
||||||
settingTitle1: '사용자 정의 환영 메시지',
|
settingTitle1: '사용자 정의 환영 메시지',
|
||||||
settingContent1: '1Panel 로그인 페이지에 사용자 정의 환영 메시지를 설정합니다.',
|
settingContent1: '1Panel 로그인 페이지에 사용자 정의 환영 메시지를 설정합니다.',
|
||||||
settingTitle2: '사용자 정의 로고',
|
settingTitle2: '사용자 정의 로고',
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
fit2cloud: 'FIT2CLOUD',
|
fit2cloud: 'FIT2CLOUD',
|
||||||
lingxia: 'Lingxia',
|
lingxia: 'Lingxia',
|
||||||
button: {
|
button: {
|
||||||
|
run: 'Jalankan',
|
||||||
create: 'Cipta',
|
create: 'Cipta',
|
||||||
add: 'Tambah',
|
add: 'Tambah',
|
||||||
save: 'Simpan',
|
save: 'Simpan',
|
||||||
@ -334,6 +335,7 @@ const message = {
|
|||||||
firewall: 'Firewall',
|
firewall: 'Firewall',
|
||||||
ssl: 'Certificate | Certificates',
|
ssl: 'Certificate | Certificates',
|
||||||
database: 'Database | Databases',
|
database: 'Database | Databases',
|
||||||
|
aiTools: 'AI',
|
||||||
container: 'Container | Containers',
|
container: 'Container | Containers',
|
||||||
cronjob: 'Cron Job | Cron Jobs',
|
cronjob: 'Cron Job | Cron Jobs',
|
||||||
host: 'Host | Hosts',
|
host: 'Host | Hosts',
|
||||||
@ -592,6 +594,68 @@ const message = {
|
|||||||
'Alamat sambungan ini boleh digunakan oleh aplikasi yang berjalan di luar kontena atau aplikasi luaran.',
|
'Alamat sambungan ini boleh digunakan oleh aplikasi yang berjalan di luar kontena atau aplikasi luaran.',
|
||||||
localIP: 'IP Tempatan',
|
localIP: 'IP Tempatan',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: 'Model',
|
||||||
|
create: 'Tambah Model',
|
||||||
|
create_helper: 'Tarik "{0}"',
|
||||||
|
ollama_doc: 'Anda boleh melawat laman web rasmi Ollama untuk mencari dan menemui lebih banyak model.',
|
||||||
|
container_conn_helper: 'Gunakan alamat ini untuk akses atau sambungan antara kontena',
|
||||||
|
ollama_sync:
|
||||||
|
'Sincronizando o modelo Ollama, encontrou que os seguintes modelos não existem, deseja excluí-los?',
|
||||||
|
from_remote: 'Este modelo não foi baixado via 1Panel, sem logs de pull relacionados.',
|
||||||
|
no_logs: 'Os logs de pull deste modelo foram excluídos e não podem ser visualizados.',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'Peningkatan Proksi AI',
|
||||||
|
proxyHelper1: 'Ikatkan domain dan aktifkan HTTPS untuk meningkatkan keselamatan penghantaran',
|
||||||
|
proxyHelper2: 'Hadkan akses IP untuk mengelakkan pendedahan di internet awam',
|
||||||
|
proxyHelper3: 'Aktifkan penstriman',
|
||||||
|
proxyHelper4: 'Setelah selesai, anda boleh melihat dan mengurusnya dalam senarai laman web',
|
||||||
|
proxyHelper5:
|
||||||
|
'Selepas diaktifkan, anda boleh melumpuhkan akses luaran ke port dalam App Store - Dipasang - Ollama - Parameter untuk meningkatkan keselamatan.',
|
||||||
|
proxyHelper6: 'Untuk melumpuhkan konfigurasi proksi, anda boleh memadamnya dari senarai laman web.',
|
||||||
|
whiteListHelper: 'Hadkan akses kepada hanya IP dalam senarai putih',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'Monitor GPU',
|
||||||
|
base: 'Maklumat Asas',
|
||||||
|
gpuHelper: 'Perintah NVIDIA-SMI atau XPU-SMI tidak dikesan pada sistem semasa. Sila periksa dan cuba lagi!',
|
||||||
|
driverVersion: 'Versi Pemacu',
|
||||||
|
cudaVersion: 'Versi CUDA',
|
||||||
|
process: 'Maklumat Proses',
|
||||||
|
type: 'Jenis',
|
||||||
|
typeG: 'Grafik',
|
||||||
|
typeC: 'Pengiraan',
|
||||||
|
typeCG: 'Pengiraan + Grafik',
|
||||||
|
processName: 'Nama Proses',
|
||||||
|
processMemoryUsage: 'Penggunaan Memori',
|
||||||
|
temperatureHelper: 'Suhu GPU yang tinggi boleh menyebabkan pelambatan frekuensi GPU',
|
||||||
|
performanceStateHelper: 'Dari P0 (prestasi maksimum) hingga P12 (prestasi minimum)',
|
||||||
|
busID: 'ID Bas',
|
||||||
|
persistenceMode: 'Mod Ketekalan',
|
||||||
|
enabled: 'Diaktifkan',
|
||||||
|
disabled: 'Dilumpuhkan',
|
||||||
|
persistenceModeHelper:
|
||||||
|
'Mod ketekalan membolehkan respons tugas lebih cepat tetapi meningkatkan penggunaan kuasa sedia.',
|
||||||
|
displayActive: 'Kad Grafik Dimulakan',
|
||||||
|
displayActiveT: 'Ya',
|
||||||
|
displayActiveF: 'Tidak',
|
||||||
|
ecc: 'Teknologi Pemeriksaan dan Pembetulan Ralat',
|
||||||
|
computeMode: 'Mod Pengiraan',
|
||||||
|
default: 'Asal',
|
||||||
|
exclusiveProcess: 'Proses Eksklusif',
|
||||||
|
exclusiveThread: 'Thread Eksklusif',
|
||||||
|
prohibited: 'Dilarang',
|
||||||
|
defaultHelper: 'Asal: Proses boleh dilaksanakan secara serentak',
|
||||||
|
exclusiveProcessHelper:
|
||||||
|
'Proses Eksklusif: Hanya satu konteks CUDA boleh menggunakan GPU, tetapi boleh dikongsi oleh berbilang thread',
|
||||||
|
exclusiveThreadHelper: 'Thread Eksklusif: Hanya satu thread dalam konteks CUDA boleh menggunakan GPU',
|
||||||
|
prohibitedHelper: 'Dilarang: Proses tidak dibenarkan dilaksanakan serentak',
|
||||||
|
migModeHelper: 'Digunakan untuk membuat contoh MIG bagi pengasingan fizikal GPU pada tahap pengguna.',
|
||||||
|
migModeNA: 'Tidak Disokong',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: 'Cipta kontena',
|
create: 'Cipta kontena',
|
||||||
edit: 'Sunting kontena',
|
edit: 'Sunting kontena',
|
||||||
@ -1726,7 +1790,6 @@ const message = {
|
|||||||
introduce: 'Pengenalan Ciri',
|
introduce: 'Pengenalan Ciri',
|
||||||
waf: 'Menaik taraf ke versi profesional boleh menyediakan ciri seperti peta pencegahan, log, rekod blok, sekatan lokasi geografi, peraturan tersuai, halaman pencegahan tersuai, dan sebagainya.',
|
waf: 'Menaik taraf ke versi profesional boleh menyediakan ciri seperti peta pencegahan, log, rekod blok, sekatan lokasi geografi, peraturan tersuai, halaman pencegahan tersuai, dan sebagainya.',
|
||||||
tamper: 'Menaik taraf ke versi profesional boleh melindungi laman web daripada pengubahsuaian atau manipulasi tanpa kebenaran.',
|
tamper: 'Menaik taraf ke versi profesional boleh melindungi laman web daripada pengubahsuaian atau manipulasi tanpa kebenaran.',
|
||||||
gpu: 'Menaik taraf ke versi profesional boleh membantu pengguna memantau parameter penting GPU secara visual seperti beban kerja, suhu, penggunaan memori secara masa nyata.',
|
|
||||||
setting:
|
setting:
|
||||||
'Menaik taraf ke versi profesional membolehkan penyesuaian logo panel, mesej selamat datang, dan maklumat lain.',
|
'Menaik taraf ke versi profesional membolehkan penyesuaian logo panel, mesej selamat datang, dan maklumat lain.',
|
||||||
monitor:
|
monitor:
|
||||||
@ -2871,44 +2934,6 @@ const message = {
|
|||||||
'Ciri anti-pemalsuan laman web {0} akan diaktifkan untuk meningkatkan keselamatan laman web. Adakah anda mahu meneruskan?',
|
'Ciri anti-pemalsuan laman web {0} akan diaktifkan untuk meningkatkan keselamatan laman web. Adakah anda mahu meneruskan?',
|
||||||
disableHelper: 'Ciri anti-pemalsuan laman web {0} akan dilumpuhkan. Adakah anda mahu meneruskan?',
|
disableHelper: 'Ciri anti-pemalsuan laman web {0} akan dilumpuhkan. Adakah anda mahu meneruskan?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'Monitor GPU',
|
|
||||||
base: 'Maklumat Asas',
|
|
||||||
gpuHelper: 'Perintah NVIDIA-SMI atau XPU-SMI tidak dikesan pada sistem semasa. Sila periksa dan cuba lagi!',
|
|
||||||
driverVersion: 'Versi Pemacu',
|
|
||||||
cudaVersion: 'Versi CUDA',
|
|
||||||
process: 'Maklumat Proses',
|
|
||||||
type: 'Jenis',
|
|
||||||
typeG: 'Grafik',
|
|
||||||
typeC: 'Pengiraan',
|
|
||||||
typeCG: 'Pengiraan + Grafik',
|
|
||||||
processName: 'Nama Proses',
|
|
||||||
processMemoryUsage: 'Penggunaan Memori',
|
|
||||||
temperatureHelper: 'Suhu GPU yang tinggi boleh menyebabkan pelambatan frekuensi GPU',
|
|
||||||
performanceStateHelper: 'Dari P0 (prestasi maksimum) hingga P12 (prestasi minimum)',
|
|
||||||
busID: 'ID Bas',
|
|
||||||
persistenceMode: 'Mod Ketekalan',
|
|
||||||
enabled: 'Diaktifkan',
|
|
||||||
disabled: 'Dilumpuhkan',
|
|
||||||
persistenceModeHelper:
|
|
||||||
'Mod ketekalan membolehkan respons tugas lebih cepat tetapi meningkatkan penggunaan kuasa sedia.',
|
|
||||||
displayActive: 'Kad Grafik Dimulakan',
|
|
||||||
displayActiveT: 'Ya',
|
|
||||||
displayActiveF: 'Tidak',
|
|
||||||
ecc: 'Teknologi Pemeriksaan dan Pembetulan Ralat',
|
|
||||||
computeMode: 'Mod Pengiraan',
|
|
||||||
default: 'Asal',
|
|
||||||
exclusiveProcess: 'Proses Eksklusif',
|
|
||||||
exclusiveThread: 'Thread Eksklusif',
|
|
||||||
prohibited: 'Dilarang',
|
|
||||||
defaultHelper: 'Asal: Proses boleh dilaksanakan secara serentak',
|
|
||||||
exclusiveProcessHelper:
|
|
||||||
'Proses Eksklusif: Hanya satu konteks CUDA boleh menggunakan GPU, tetapi boleh dikongsi oleh berbilang thread',
|
|
||||||
exclusiveThreadHelper: 'Thread Eksklusif: Hanya satu thread dalam konteks CUDA boleh menggunakan GPU',
|
|
||||||
prohibitedHelper: 'Dilarang: Proses tidak dibenarkan dilaksanakan serentak',
|
|
||||||
migModeHelper: 'Digunakan untuk membuat contoh MIG bagi pengasingan fizikal GPU pada tahap pengguna.',
|
|
||||||
migModeNA: 'Tidak Disokong',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: 'Tetapan Panel',
|
setting: 'Tetapan Panel',
|
||||||
title: 'Deskripsi Panel',
|
title: 'Deskripsi Panel',
|
||||||
@ -2952,11 +2977,6 @@ const message = {
|
|||||||
tamperContent4:
|
tamperContent4:
|
||||||
'Rekod log akses dan operasi fail untuk audit dan analisis selanjutnya oleh pentadbir serta mengenal pasti potensi ancaman keselamatan.',
|
'Rekod log akses dan operasi fail untuk audit dan analisis selanjutnya oleh pentadbir serta mengenal pasti potensi ancaman keselamatan.',
|
||||||
|
|
||||||
gpuTitle1: 'Pemantauan Gambaran Keseluruhan',
|
|
||||||
gpuContent1: 'Papar penggunaan semasa GPU pada halaman gambaran keseluruhan.',
|
|
||||||
gpuTitle2: 'Butiran GPU',
|
|
||||||
gpuContent2: 'Tunjukkan parameter GPU dengan lebih terperinci.',
|
|
||||||
|
|
||||||
settingTitle1: 'Mesej Selamat Datang Tersuai',
|
settingTitle1: 'Mesej Selamat Datang Tersuai',
|
||||||
settingContent1: 'Tetapkan mesej selamat datang tersuai pada halaman log masuk 1Panel.',
|
settingContent1: 'Tetapkan mesej selamat datang tersuai pada halaman log masuk 1Panel.',
|
||||||
settingTitle2: 'Logo Tersuai',
|
settingTitle2: 'Logo Tersuai',
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
fit2cloud: 'FIT2CLOUD',
|
fit2cloud: 'FIT2CLOUD',
|
||||||
lingxia: 'Lingxia',
|
lingxia: 'Lingxia',
|
||||||
button: {
|
button: {
|
||||||
|
run: 'Executar',
|
||||||
create: 'Criar',
|
create: 'Criar',
|
||||||
add: 'Adicionar',
|
add: 'Adicionar',
|
||||||
save: 'Salvar',
|
save: 'Salvar',
|
||||||
@ -332,6 +333,7 @@ const message = {
|
|||||||
firewall: 'Firewall',
|
firewall: 'Firewall',
|
||||||
ssl: 'Certificado | Certificados',
|
ssl: 'Certificado | Certificados',
|
||||||
database: 'Banco de Dados | Bancos de Dados',
|
database: 'Banco de Dados | Bancos de Dados',
|
||||||
|
aiTools: 'AI',
|
||||||
container: 'Container | Containers',
|
container: 'Container | Containers',
|
||||||
cronjob: 'Tarefa Cron | Tarefas Cron',
|
cronjob: 'Tarefa Cron | Tarefas Cron',
|
||||||
host: 'Host | Hosts',
|
host: 'Host | Hosts',
|
||||||
@ -589,6 +591,67 @@ const message = {
|
|||||||
'Este endereço de conexão pode ser utilizado por aplicações que estão fora do contêiner ou por aplicações externas.',
|
'Este endereço de conexão pode ser utilizado por aplicações que estão fora do contêiner ou por aplicações externas.',
|
||||||
localIP: 'IP local',
|
localIP: 'IP local',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: 'Modelo',
|
||||||
|
create: 'Adicionar Modelo',
|
||||||
|
create_helper: 'Puxar "{0}"',
|
||||||
|
ollama_doc: 'Você pode visitar o site oficial da Ollama para pesquisar e encontrar mais modelos.',
|
||||||
|
container_conn_helper: 'Use este endereço para acesso ou conexão entre contêineres',
|
||||||
|
ollama_sync:
|
||||||
|
'Menyelaraskan model Ollama mendapati model berikut tidak wujud, adakah anda ingin memadamnya?',
|
||||||
|
from_remote: 'Model ini tidak dimuat turun melalui 1Panel, tiada log pengambilan berkaitan.',
|
||||||
|
no_logs: 'Log pengambilan untuk model ini telah dipadam dan tidak dapat dilihat.',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'Melhoria de Proxy AI',
|
||||||
|
proxyHelper1: 'Vincule o domínio e habilite o HTTPS para aumentar a segurança na transmissão',
|
||||||
|
proxyHelper2: 'Limite o acesso por IP para evitar exposição na internet pública',
|
||||||
|
proxyHelper3: 'Habilite a transmissão em fluxo',
|
||||||
|
proxyHelper4: 'Após a criação, você pode visualizar e gerenciar no lista de sites',
|
||||||
|
proxyHelper6: 'Para desativar a configuração de proxy, você pode excluí-la da lista de sites.',
|
||||||
|
whiteListHelper: 'Restringir o acesso apenas aos IPs na lista branca',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'Monitor de GPU',
|
||||||
|
base: 'Informações Básicas',
|
||||||
|
gpuHelper:
|
||||||
|
'Comando NVIDIA-SMI ou XPU-SMI não detectado no sistema atual. Por favor, verifique e tente novamente!',
|
||||||
|
driverVersion: 'Versão do Driver',
|
||||||
|
cudaVersion: 'Versão do CUDA',
|
||||||
|
process: 'Informações do Processo',
|
||||||
|
type: 'Tipo',
|
||||||
|
typeG: 'Gráficos',
|
||||||
|
typeC: 'Cálculo',
|
||||||
|
typeCG: 'Cálculo + Gráficos',
|
||||||
|
processName: 'Nome do Processo',
|
||||||
|
processMemoryUsage: 'Uso de Memória',
|
||||||
|
temperatureHelper: 'Temperaturas altas da GPU podem causar limitação de frequência da GPU.',
|
||||||
|
performanceStateHelper: 'De P0 (máximo desempenho) a P12 (mínimo desempenho).',
|
||||||
|
busID: 'ID do Barramento',
|
||||||
|
persistenceMode: 'Modo de Persistência',
|
||||||
|
enabled: 'Ativado',
|
||||||
|
disabled: 'Desativado',
|
||||||
|
persistenceModeHelper:
|
||||||
|
'O modo de persistência permite respostas mais rápidas às tarefas, mas aumenta o consumo de energia em standby.',
|
||||||
|
displayActive: 'Placa Gráfica Inicializada',
|
||||||
|
displayActiveT: 'Sim',
|
||||||
|
displayActiveF: 'Não',
|
||||||
|
ecc: 'Tecnologia de Correção e Verificação de Erros',
|
||||||
|
computeMode: 'Modo de Cálculo',
|
||||||
|
default: 'Padrão',
|
||||||
|
exclusiveProcess: 'Processo Exclusivo',
|
||||||
|
exclusiveThread: 'Thread Exclusivo',
|
||||||
|
prohibited: 'Proibido',
|
||||||
|
defaultHelper: 'Padrão: Processos podem ser executados simultaneamente.',
|
||||||
|
exclusiveProcessHelper:
|
||||||
|
'Processo Exclusivo: Apenas um contexto CUDA pode usar a GPU, mas pode ser compartilhado por múltiplas threads.',
|
||||||
|
exclusiveThreadHelper: 'Thread Exclusivo: Apenas uma thread em um contexto CUDA pode usar a GPU.',
|
||||||
|
prohibitedHelper: 'Proibido: Não é permitido que processos sejam executados simultaneamente.',
|
||||||
|
migModeHelper: 'Usado para criar instâncias MIG para isolamento físico da GPU no nível do usuário.',
|
||||||
|
migModeNA: 'Não Suportado',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: 'Criar contêiner',
|
create: 'Criar contêiner',
|
||||||
edit: 'Editar contêiner',
|
edit: 'Editar contêiner',
|
||||||
@ -1714,7 +1777,6 @@ const message = {
|
|||||||
introduce: 'Introdução de recursos',
|
introduce: 'Introdução de recursos',
|
||||||
waf: 'O upgrade para a versão profissional pode fornecer recursos como mapa de intercepção, logs, registros de bloqueio, bloqueio por localização geográfica, regras personalizadas, páginas de intercepção personalizadas, etc.',
|
waf: 'O upgrade para a versão profissional pode fornecer recursos como mapa de intercepção, logs, registros de bloqueio, bloqueio por localização geográfica, regras personalizadas, páginas de intercepção personalizadas, etc.',
|
||||||
tamper: 'O upgrade para a versão profissional pode proteger sites contra modificações ou adulterações não autorizadas.',
|
tamper: 'O upgrade para a versão profissional pode proteger sites contra modificações ou adulterações não autorizadas.',
|
||||||
gpu: 'O upgrade para a versão profissional pode ajudar os usuários a monitorar visualmente parâmetros importantes da GPU, como carga de trabalho, temperatura e uso de memória em tempo real.',
|
|
||||||
setting:
|
setting:
|
||||||
'O upgrade para a versão profissional permite a personalização do logo do painel, mensagem de boas-vindas e outras informações.',
|
'O upgrade para a versão profissional permite a personalização do logo do painel, mensagem de boas-vindas e outras informações.',
|
||||||
monitor:
|
monitor:
|
||||||
@ -2875,45 +2937,6 @@ const message = {
|
|||||||
'A função de anti-alteração do site {0} está prestes a ser ativada para aumentar a segurança do site. Deseja continuar?',
|
'A função de anti-alteração do site {0} está prestes a ser ativada para aumentar a segurança do site. Deseja continuar?',
|
||||||
disableHelper: 'A função de anti-alteração do site {0} está prestes a ser desativada. Deseja continuar?',
|
disableHelper: 'A função de anti-alteração do site {0} está prestes a ser desativada. Deseja continuar?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'Monitor de GPU',
|
|
||||||
base: 'Informações Básicas',
|
|
||||||
gpuHelper:
|
|
||||||
'Comando NVIDIA-SMI ou XPU-SMI não detectado no sistema atual. Por favor, verifique e tente novamente!',
|
|
||||||
driverVersion: 'Versão do Driver',
|
|
||||||
cudaVersion: 'Versão do CUDA',
|
|
||||||
process: 'Informações do Processo',
|
|
||||||
type: 'Tipo',
|
|
||||||
typeG: 'Gráficos',
|
|
||||||
typeC: 'Cálculo',
|
|
||||||
typeCG: 'Cálculo + Gráficos',
|
|
||||||
processName: 'Nome do Processo',
|
|
||||||
processMemoryUsage: 'Uso de Memória',
|
|
||||||
temperatureHelper: 'Temperaturas altas da GPU podem causar limitação de frequência da GPU.',
|
|
||||||
performanceStateHelper: 'De P0 (máximo desempenho) a P12 (mínimo desempenho).',
|
|
||||||
busID: 'ID do Barramento',
|
|
||||||
persistenceMode: 'Modo de Persistência',
|
|
||||||
enabled: 'Ativado',
|
|
||||||
disabled: 'Desativado',
|
|
||||||
persistenceModeHelper:
|
|
||||||
'O modo de persistência permite respostas mais rápidas às tarefas, mas aumenta o consumo de energia em standby.',
|
|
||||||
displayActive: 'Placa Gráfica Inicializada',
|
|
||||||
displayActiveT: 'Sim',
|
|
||||||
displayActiveF: 'Não',
|
|
||||||
ecc: 'Tecnologia de Correção e Verificação de Erros',
|
|
||||||
computeMode: 'Modo de Cálculo',
|
|
||||||
default: 'Padrão',
|
|
||||||
exclusiveProcess: 'Processo Exclusivo',
|
|
||||||
exclusiveThread: 'Thread Exclusivo',
|
|
||||||
prohibited: 'Proibido',
|
|
||||||
defaultHelper: 'Padrão: Processos podem ser executados simultaneamente.',
|
|
||||||
exclusiveProcessHelper:
|
|
||||||
'Processo Exclusivo: Apenas um contexto CUDA pode usar a GPU, mas pode ser compartilhado por múltiplas threads.',
|
|
||||||
exclusiveThreadHelper: 'Thread Exclusivo: Apenas uma thread em um contexto CUDA pode usar a GPU.',
|
|
||||||
prohibitedHelper: 'Proibido: Não é permitido que processos sejam executados simultaneamente.',
|
|
||||||
migModeHelper: 'Usado para criar instâncias MIG para isolamento físico da GPU no nível do usuário.',
|
|
||||||
migModeNA: 'Não Suportado',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: 'Configurações do Painel',
|
setting: 'Configurações do Painel',
|
||||||
title: 'Descrição do Painel',
|
title: 'Descrição do Painel',
|
||||||
@ -2958,11 +2981,6 @@ const message = {
|
|||||||
tamperContent4:
|
tamperContent4:
|
||||||
'Registra logs de acesso e operações em arquivos para auditoria e análise posteriores, ajudando administradores a identificar ameaças de segurança potenciais.',
|
'Registra logs de acesso e operações em arquivos para auditoria e análise posteriores, ajudando administradores a identificar ameaças de segurança potenciais.',
|
||||||
|
|
||||||
gpuTitle1: 'Monitoramento Geral',
|
|
||||||
gpuContent1: 'Exibir o uso atual do GPU na página de visão geral.',
|
|
||||||
gpuTitle2: 'Detalhes do GPU',
|
|
||||||
gpuContent2: 'Mostrar os parâmetros do GPU com mais detalhes.',
|
|
||||||
|
|
||||||
settingTitle1: 'Mensagem de Boas-vindas Personalizada',
|
settingTitle1: 'Mensagem de Boas-vindas Personalizada',
|
||||||
settingContent1: 'Defina uma mensagem de boas-vindas personalizada na página de login do 1Panel.',
|
settingContent1: 'Defina uma mensagem de boas-vindas personalizada na página de login do 1Panel.',
|
||||||
settingTitle2: 'Logo Personalizado',
|
settingTitle2: 'Logo Personalizado',
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
fit2cloud: 'FIT2CLOUD',
|
fit2cloud: 'FIT2CLOUD',
|
||||||
lingxia: 'Lingxia',
|
lingxia: 'Lingxia',
|
||||||
button: {
|
button: {
|
||||||
|
run: 'Запуск',
|
||||||
create: 'Создать ',
|
create: 'Создать ',
|
||||||
add: 'Добавить ',
|
add: 'Добавить ',
|
||||||
save: 'Сохранить ',
|
save: 'Сохранить ',
|
||||||
@ -328,6 +329,7 @@ const message = {
|
|||||||
firewall: 'Firewall',
|
firewall: 'Firewall',
|
||||||
ssl: 'Сертификат | Сертификаты',
|
ssl: 'Сертификат | Сертификаты',
|
||||||
database: 'База данных | Базы данных',
|
database: 'База данных | Базы данных',
|
||||||
|
aiTools: 'AI',
|
||||||
container: 'Контейнер | Контейнеры',
|
container: 'Контейнер | Контейнеры',
|
||||||
cronjob: 'Cron | Задачи Cron',
|
cronjob: 'Cron | Задачи Cron',
|
||||||
host: 'Хост | Хосты',
|
host: 'Хост | Хосты',
|
||||||
@ -586,6 +588,69 @@ const message = {
|
|||||||
'Этот адрес подключения может использоваться приложениями, работающими вне контейнера или внешними приложениями.',
|
'Этот адрес подключения может использоваться приложениями, работающими вне контейнера или внешними приложениями.',
|
||||||
localIP: 'Локальный IP',
|
localIP: 'Локальный IP',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: 'Модель',
|
||||||
|
create: 'Добавить модель',
|
||||||
|
create_helper: 'Загрузить "{0}"',
|
||||||
|
ollama_doc: 'Вы можете посетить официальный сайт Ollama, чтобы искать и находить больше моделей.',
|
||||||
|
container_conn_helper: 'Используйте этот адрес для доступа или подключения между контейнерами',
|
||||||
|
ollama_sync:
|
||||||
|
'Синхронизация модели Ollama обнаружила, что следующие модели не существуют, хотите удалить их?',
|
||||||
|
from_remote: 'Эта модель не была загружена через 1Panel, нет связанных журналов извлечения.',
|
||||||
|
no_logs: 'Журналы извлечения для этой модели были удалены и не могут быть просмотрены.',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'Усиление AI-прокси',
|
||||||
|
proxyHelper1: 'Привяжите домен и включите HTTPS для повышения безопасности передачи данных',
|
||||||
|
proxyHelper2: 'Ограничьте доступ по IP, чтобы предотвратить утечку данных в публичной сети',
|
||||||
|
proxyHelper3: 'Включите потоковую передачу',
|
||||||
|
proxyHelper4: 'После создания вы можете просматривать и управлять этим в списке сайтов',
|
||||||
|
proxyHelper5:
|
||||||
|
'После включения вы можете отключить внешний доступ к порту в Магазине приложений - Установленные - Ollama - Параметры для повышения безопасности.',
|
||||||
|
proxyHelper6: 'Чтобы отключить настройку прокси, вы можете удалить её из списка сайтов.',
|
||||||
|
whiteListHelper: 'Ограничить доступ только для IP-адресов из белого списка',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'Мониторинг GPU',
|
||||||
|
base: 'Основная информация',
|
||||||
|
gpuHelper: 'Команда NVIDIA-SMI или XPU-SMI не обнаружена в текущей системе. Проверьте и попробуйте снова!',
|
||||||
|
driverVersion: 'Версия драйвера',
|
||||||
|
cudaVersion: 'Версия CUDA',
|
||||||
|
process: 'Информация о процессе',
|
||||||
|
type: 'Тип',
|
||||||
|
typeG: 'Графика',
|
||||||
|
typeC: 'Вычисления',
|
||||||
|
typeCG: 'Вычисления + Графика',
|
||||||
|
processName: 'Имя процесса',
|
||||||
|
processMemoryUsage: 'Использование памяти',
|
||||||
|
temperatureHelper: 'Высокая температура GPU может вызвать снижение частоты GPU',
|
||||||
|
performanceStateHelper: 'От P0 (максимальная производительность) до P12 (минимальная производительность)',
|
||||||
|
busID: 'ID шины',
|
||||||
|
persistenceMode: 'Режим постоянства',
|
||||||
|
enabled: 'Включен',
|
||||||
|
disabled: 'Выключен',
|
||||||
|
persistenceModeHelper:
|
||||||
|
'Режим постоянства позволяет быстрее реагировать на задачи, но увеличивает потребление энергии в режиме ожидания.',
|
||||||
|
displayActive: 'Инициализация видеокарты',
|
||||||
|
displayActiveT: 'Да',
|
||||||
|
displayActiveF: 'Нет',
|
||||||
|
ecc: 'Технология проверки и коррекции ошибок (ECC)',
|
||||||
|
computeMode: 'Режим вычислений',
|
||||||
|
default: 'По умолчанию',
|
||||||
|
exclusiveProcess: 'Исключительный процесс',
|
||||||
|
exclusiveThread: 'Исключительный поток',
|
||||||
|
prohibited: 'Запрещено',
|
||||||
|
defaultHelper: 'По умолчанию: процессы могут выполняться одновременно',
|
||||||
|
exclusiveProcessHelper:
|
||||||
|
'Исключительный процесс: только один контекст CUDA может использовать GPU, но его могут разделять несколько потоков',
|
||||||
|
exclusiveThreadHelper: 'Исключительный поток: только один поток в контексте CUDA может использовать GPU',
|
||||||
|
prohibitedHelper: 'Запрещено: процессам не разрешено выполняться одновременно',
|
||||||
|
migModeHelper:
|
||||||
|
'Используется для создания MIG-инстансов для физической изоляции GPU на уровне пользователя.',
|
||||||
|
migModeNA: 'Не поддерживается',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: 'Создать контейнер',
|
create: 'Создать контейнер',
|
||||||
edit: 'Редактировать контейнер',
|
edit: 'Редактировать контейнер',
|
||||||
@ -1710,7 +1775,6 @@ const message = {
|
|||||||
introduce: 'Описание функций',
|
introduce: 'Описание функций',
|
||||||
waf: 'Обновление до профессиональной версии предоставляет такие функции, как карта перехватов, логи, записи блокировок, блокировка по географическому положению, пользовательские правила, пользовательские страницы перехвата и т.д.',
|
waf: 'Обновление до профессиональной версии предоставляет такие функции, как карта перехватов, логи, записи блокировок, блокировка по географическому положению, пользовательские правила, пользовательские страницы перехвата и т.д.',
|
||||||
tamper: 'Обновление до профессиональной версии может защитить веб-сайты от несанкционированных изменений или подделок.',
|
tamper: 'Обновление до профессиональной версии может защитить веб-сайты от несанкционированных изменений или подделок.',
|
||||||
gpu: 'Обновление до профессиональной версии помогает пользователям визуально отслеживать важные параметры GPU, такие как нагрузка, температура, использование памяти в реальном времени.',
|
|
||||||
setting:
|
setting:
|
||||||
'Обновление до профессиональной версии позволяет настраивать логотип панели, приветственное сообщение и другую информацию.',
|
'Обновление до профессиональной версии позволяет настраивать логотип панели, приветственное сообщение и другую информацию.',
|
||||||
monitor:
|
monitor:
|
||||||
@ -2864,45 +2928,6 @@ const message = {
|
|||||||
'Функция защиты от модификации для сайта {0} будет включена для повышения безопасности сайта. Вы хотите продолжить?',
|
'Функция защиты от модификации для сайта {0} будет включена для повышения безопасности сайта. Вы хотите продолжить?',
|
||||||
disableHelper: 'Функция защиты от модификации для сайта {0} будет отключена. Вы хотите продолжить?',
|
disableHelper: 'Функция защиты от модификации для сайта {0} будет отключена. Вы хотите продолжить?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'Мониторинг GPU',
|
|
||||||
base: 'Основная информация',
|
|
||||||
gpuHelper: 'Команда NVIDIA-SMI или XPU-SMI не обнаружена в текущей системе. Проверьте и попробуйте снова!',
|
|
||||||
driverVersion: 'Версия драйвера',
|
|
||||||
cudaVersion: 'Версия CUDA',
|
|
||||||
process: 'Информация о процессе',
|
|
||||||
type: 'Тип',
|
|
||||||
typeG: 'Графика',
|
|
||||||
typeC: 'Вычисления',
|
|
||||||
typeCG: 'Вычисления + Графика',
|
|
||||||
processName: 'Имя процесса',
|
|
||||||
processMemoryUsage: 'Использование памяти',
|
|
||||||
temperatureHelper: 'Высокая температура GPU может вызвать снижение частоты GPU',
|
|
||||||
performanceStateHelper: 'От P0 (максимальная производительность) до P12 (минимальная производительность)',
|
|
||||||
busID: 'ID шины',
|
|
||||||
persistenceMode: 'Режим постоянства',
|
|
||||||
enabled: 'Включен',
|
|
||||||
disabled: 'Выключен',
|
|
||||||
persistenceModeHelper:
|
|
||||||
'Режим постоянства позволяет быстрее реагировать на задачи, но увеличивает потребление энергии в режиме ожидания.',
|
|
||||||
displayActive: 'Инициализация видеокарты',
|
|
||||||
displayActiveT: 'Да',
|
|
||||||
displayActiveF: 'Нет',
|
|
||||||
ecc: 'Технология проверки и коррекции ошибок (ECC)',
|
|
||||||
computeMode: 'Режим вычислений',
|
|
||||||
default: 'По умолчанию',
|
|
||||||
exclusiveProcess: 'Исключительный процесс',
|
|
||||||
exclusiveThread: 'Исключительный поток',
|
|
||||||
prohibited: 'Запрещено',
|
|
||||||
defaultHelper: 'По умолчанию: процессы могут выполняться одновременно',
|
|
||||||
exclusiveProcessHelper:
|
|
||||||
'Исключительный процесс: только один контекст CUDA может использовать GPU, но его могут разделять несколько потоков',
|
|
||||||
exclusiveThreadHelper: 'Исключительный поток: только один поток в контексте CUDA может использовать GPU',
|
|
||||||
prohibitedHelper: 'Запрещено: процессам не разрешено выполняться одновременно',
|
|
||||||
migModeHelper:
|
|
||||||
'Используется для создания MIG-инстансов для физической изоляции GPU на уровне пользователя.',
|
|
||||||
migModeNA: 'Не поддерживается',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: 'Настройки Панели',
|
setting: 'Настройки Панели',
|
||||||
title: 'Описание Панели',
|
title: 'Описание Панели',
|
||||||
@ -2947,11 +2972,6 @@ const message = {
|
|||||||
tamperContent4:
|
tamperContent4:
|
||||||
'Записывайте логи доступа и операций с файлами для последующего аудита и анализа администраторами, а также для выявления потенциальных угроз безопасности.',
|
'Записывайте логи доступа и операций с файлами для последующего аудита и анализа администраторами, а также для выявления потенциальных угроз безопасности.',
|
||||||
|
|
||||||
gpuTitle1: 'Мониторинг обзора',
|
|
||||||
gpuContent1: 'Отображение текущего использования GPU на странице обзора.',
|
|
||||||
gpuTitle2: 'Детали GPU',
|
|
||||||
gpuContent2: 'Показать параметры GPU с большей точностью.',
|
|
||||||
|
|
||||||
settingTitle1: 'Пользовательское Приветственное Сообщение',
|
settingTitle1: 'Пользовательское Приветственное Сообщение',
|
||||||
settingContent1: 'Установите пользовательское приветственное сообщение на странице входа в 1Panel.',
|
settingContent1: 'Установите пользовательское приветственное сообщение на странице входа в 1Panel.',
|
||||||
settingTitle2: 'Пользовательский Логотип',
|
settingTitle2: 'Пользовательский Логотип',
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
lingxia: '凌霞',
|
lingxia: '凌霞',
|
||||||
colon: ': ',
|
colon: ': ',
|
||||||
button: {
|
button: {
|
||||||
|
run: '執行',
|
||||||
prev: '上一步',
|
prev: '上一步',
|
||||||
next: '下一步',
|
next: '下一步',
|
||||||
create: '創建',
|
create: '創建',
|
||||||
@ -327,6 +328,7 @@ const message = {
|
|||||||
firewall: '防火墻',
|
firewall: '防火墻',
|
||||||
ssl: '證書',
|
ssl: '證書',
|
||||||
database: '數據庫',
|
database: '數據庫',
|
||||||
|
aiTools: 'AI',
|
||||||
container: '容器',
|
container: '容器',
|
||||||
cronjob: '計劃任務',
|
cronjob: '計劃任務',
|
||||||
host: '主機',
|
host: '主機',
|
||||||
@ -570,6 +572,64 @@ const message = {
|
|||||||
remoteConnHelper2: '非容器或外部連接使用此地址',
|
remoteConnHelper2: '非容器或外部連接使用此地址',
|
||||||
localIP: '本機 IP',
|
localIP: '本機 IP',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: '模型',
|
||||||
|
create: '新增模型',
|
||||||
|
create_helper: '拉取 "{0}"',
|
||||||
|
ollama_doc: '您可以瀏覽 Ollama 官方網站,搜尋並查找更多模型。',
|
||||||
|
container_conn_helper: '容器間瀏覽或連接使用此地址',
|
||||||
|
ollama_sync: '同步 Ollama 模型發現下列模型不存在,是否刪除?',
|
||||||
|
from_remote: '該模型並非透過 1Panel 下載,無相關拉取日誌。',
|
||||||
|
no_logs: '該模型的拉取日誌已被刪除,無法查看相關日誌。',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'AI 代理增強',
|
||||||
|
proxyHelper1: '綁定域名並啟用 HTTPS,提高傳輸安全性',
|
||||||
|
proxyHelper2: '限制 IP 瀏覽,防止在網路上暴露',
|
||||||
|
proxyHelper3: '啟用即時串流',
|
||||||
|
proxyHelper4: '創建後,您可以在網站列表中查看並管理',
|
||||||
|
proxyHelper5: '啟用後,您可以在應用商店 - 已安裝 - Ollama - 參數中取消埠外部瀏覽以提高安全性',
|
||||||
|
proxyHelper6: '如需關閉代理配置,可以在網站列表中刪除',
|
||||||
|
whiteListHelper: '限制僅白名單中的 IP 可瀏覽',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'GPU 監控',
|
||||||
|
base: '基礎資訊',
|
||||||
|
gpuHelper: '目前系統未檢測到 NVIDIA-SMI 或者 XPU-SMI 指令,請檢查後重試!',
|
||||||
|
driverVersion: '驅動版本',
|
||||||
|
cudaVersion: 'CUDA 版本',
|
||||||
|
process: '行程資訊',
|
||||||
|
type: '類型',
|
||||||
|
typeG: '圖形',
|
||||||
|
typeC: '計算',
|
||||||
|
typeCG: '計算+圖形',
|
||||||
|
processName: '行程名稱',
|
||||||
|
processMemoryUsage: '記憶體使用',
|
||||||
|
temperatureHelper: 'GPU 溫度過高會導致 GPU 頻率下降',
|
||||||
|
performanceStateHelper: '從 P0 (最大性能) 到 P12 (最小性能)',
|
||||||
|
busID: '匯流排地址',
|
||||||
|
persistenceMode: '持續模式',
|
||||||
|
enabled: '開啟',
|
||||||
|
disabled: '關閉',
|
||||||
|
persistenceModeHelper: '持續模式能更加快速地響應任務,但相應待機功耗也會增加',
|
||||||
|
displayActive: '顯卡初始化',
|
||||||
|
displayActiveT: '是',
|
||||||
|
displayActiveF: '否',
|
||||||
|
ecc: '是否開啟錯誤檢查和糾正技術',
|
||||||
|
computeMode: '計算模式',
|
||||||
|
default: '預設',
|
||||||
|
exclusiveProcess: '行程排他',
|
||||||
|
exclusiveThread: '執行緒排他',
|
||||||
|
prohibited: '禁止',
|
||||||
|
defaultHelper: '預設: 行程可以並發執行',
|
||||||
|
exclusiveProcessHelper: '行程排他: 只有一個 CUDA 上下文可以使用 GPU, 但可以由多個執行緒共享',
|
||||||
|
exclusiveThreadHelper: '執行緒排他: 只有一個執行緒在 CUDA 上下文中可以使用 GPU',
|
||||||
|
prohibitedHelper: '禁止: 不允許行程同時執行',
|
||||||
|
migModeHelper: '用於建立 MIG 實例,在用戶層實現 GPU 的物理隔離。',
|
||||||
|
migModeNA: '不支援',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: '創建容器',
|
create: '創建容器',
|
||||||
createByCommand: '命令創建',
|
createByCommand: '命令創建',
|
||||||
@ -1697,7 +1757,6 @@ const message = {
|
|||||||
waf: '升級專業版可以獲得攔截地圖、日誌、封鎖記錄、地理位置封禁、自定義規則、自定義攔截頁面等功能。',
|
waf: '升級專業版可以獲得攔截地圖、日誌、封鎖記錄、地理位置封禁、自定義規則、自定義攔截頁面等功能。',
|
||||||
tamper: '升級專業版可以保護網站免受未經授權的修改或篡改。',
|
tamper: '升級專業版可以保護網站免受未經授權的修改或篡改。',
|
||||||
tamperHelper: '操作失敗,該文件或文件夾已經開啟防篡改,請檢查後重試!',
|
tamperHelper: '操作失敗,該文件或文件夾已經開啟防篡改,請檢查後重試!',
|
||||||
gpu: '升級專業版可以幫助用戶實時直觀查看到 GPU 的工作負載、溫度、顯存等重要參數。',
|
|
||||||
setting: '升級專業版可以自定義面板 Logo、歡迎簡介等信息。',
|
setting: '升級專業版可以自定義面板 Logo、歡迎簡介等信息。',
|
||||||
monitor: '升級專業版可以查看網站的即時狀態、訪客趨勢、訪客來源、請求日誌等資訊。 ',
|
monitor: '升級專業版可以查看網站的即時狀態、訪客趨勢、訪客來源、請求日誌等資訊。 ',
|
||||||
alert: '陞級專業版可通過簡訊接收告警資訊,並查看告警日誌,全面掌控各類關鍵事件,確保系統運行無憂。',
|
alert: '陞級專業版可通過簡訊接收告警資訊,並查看告警日誌,全面掌控各類關鍵事件,確保系統運行無憂。',
|
||||||
@ -2774,42 +2833,6 @@ const message = {
|
|||||||
enableHelper: '即將啟用 {0} 網站的防篡改功能,以提升網站安全性,是否繼續?',
|
enableHelper: '即將啟用 {0} 網站的防篡改功能,以提升網站安全性,是否繼續?',
|
||||||
disableHelper: '即將關閉 {0} 網站的防篡改功能,是否繼續?',
|
disableHelper: '即將關閉 {0} 網站的防篡改功能,是否繼續?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'GPU 监控',
|
|
||||||
base: '基礎資訊',
|
|
||||||
gpuHelper: '目前系統未檢測到 NVIDIA-SMI或者XPU-SMI 指令,請檢查後重試!',
|
|
||||||
driverVersion: '驅動版本',
|
|
||||||
cudaVersion: 'CUDA 版本',
|
|
||||||
process: '行程資訊',
|
|
||||||
type: '類型',
|
|
||||||
typeG: '圖形',
|
|
||||||
typeC: '計算',
|
|
||||||
typeCG: '計算+圖形',
|
|
||||||
processName: '行程名稱',
|
|
||||||
processMemoryUsage: '顯存使用',
|
|
||||||
temperatureHelper: 'GPU 溫度過高會導致 GPU 頻率下降',
|
|
||||||
performanceStateHelper: '從 P0 (最大性能) 到 P12 (最小性能)',
|
|
||||||
busID: '總線地址',
|
|
||||||
persistenceMode: '持續模式',
|
|
||||||
enabled: '開啟',
|
|
||||||
disabled: '關閉',
|
|
||||||
persistenceModeHelper: '持續模式能更加快速地響應任務,但相應待機功耗也會增加',
|
|
||||||
displayActive: '顯卡初始化',
|
|
||||||
displayActiveT: '是',
|
|
||||||
displayActiveF: '否',
|
|
||||||
ecc: '是否開啟錯誤檢查和紀正技術',
|
|
||||||
computeMode: '計算模式',
|
|
||||||
default: '預設',
|
|
||||||
exclusiveProcess: '行程排他',
|
|
||||||
exclusiveThread: '線程排他',
|
|
||||||
prohibited: '禁止',
|
|
||||||
defaultHelper: '預設: 行程可以並發執行',
|
|
||||||
exclusiveProcessHelper: '行程排他: 只有一個 CUDA 上下文可以使用 GPU, 但可以由多個線程共享',
|
|
||||||
exclusiveThreadHelper: '線程排他: 只有一個線程在 CUDA 上下文中可以使用 GPU',
|
|
||||||
prohibitedHelper: '禁止: 不允許行程同時執行',
|
|
||||||
migModeHelper: '用於建立 MIG 實例,在用戶層實現 GPU 的物理隔離。',
|
|
||||||
migModeNA: '不支援',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: '界面設定',
|
setting: '界面設定',
|
||||||
title: '面板描述',
|
title: '面板描述',
|
||||||
@ -2847,11 +2870,6 @@ const message = {
|
|||||||
tamperTitle4: '日誌紀錄與分析',
|
tamperTitle4: '日誌紀錄與分析',
|
||||||
tamperContent4: '紀錄檔案瀏覽和操作日誌,以便管理員進行後續的審計和分析,以及發現潛在的安全威脅。',
|
tamperContent4: '紀錄檔案瀏覽和操作日誌,以便管理員進行後續的審計和分析,以及發現潛在的安全威脅。',
|
||||||
|
|
||||||
gpuTitle1: '概覽頁監控',
|
|
||||||
gpuContent1: '在概覽頁上顯示 GPU 當前使用情況。',
|
|
||||||
gpuTitle2: 'GPU 詳細資訊',
|
|
||||||
gpuContent2: '更加細緻地顯示 GPU 各項參數。',
|
|
||||||
|
|
||||||
settingTitle1: '自訂歡迎語',
|
settingTitle1: '自訂歡迎語',
|
||||||
settingContent1: '在 1Panel 登入頁上設定自訂的歡迎語。',
|
settingContent1: '在 1Panel 登入頁上設定自訂的歡迎語。',
|
||||||
settingTitle2: '自訂 Logo',
|
settingTitle2: '自訂 Logo',
|
||||||
|
@ -9,6 +9,7 @@ const message = {
|
|||||||
lingxia: '凌霞',
|
lingxia: '凌霞',
|
||||||
colon: ': ',
|
colon: ': ',
|
||||||
button: {
|
button: {
|
||||||
|
run: '运行',
|
||||||
prev: '上一步',
|
prev: '上一步',
|
||||||
next: '下一步',
|
next: '下一步',
|
||||||
create: '创建',
|
create: '创建',
|
||||||
@ -325,6 +326,7 @@ const message = {
|
|||||||
firewall: '防火墙',
|
firewall: '防火墙',
|
||||||
ssl: '证书',
|
ssl: '证书',
|
||||||
database: '数据库',
|
database: '数据库',
|
||||||
|
aiTools: 'AI',
|
||||||
container: '容器',
|
container: '容器',
|
||||||
cronjob: '计划任务',
|
cronjob: '计划任务',
|
||||||
host: '主机',
|
host: '主机',
|
||||||
@ -568,6 +570,65 @@ const message = {
|
|||||||
remoteConnHelper2: '非容器或外部连接使用此地址',
|
remoteConnHelper2: '非容器或外部连接使用此地址',
|
||||||
localIP: '本机 IP',
|
localIP: '本机 IP',
|
||||||
},
|
},
|
||||||
|
aiTools: {
|
||||||
|
model: {
|
||||||
|
model: '模型',
|
||||||
|
create: '添加模型',
|
||||||
|
create_helper: '拉取 "{0}"',
|
||||||
|
ollama_doc: '您可以访问 Ollama 官网,搜索并查找更多模型。',
|
||||||
|
container_conn_helper: '容器间访问或连接使用此地址',
|
||||||
|
ollama_sync: '同步 Ollama 模型发现下列模型不存在,是否删除?',
|
||||||
|
from_remote: '该模型并非通过 1Panel 下载,无相关拉取日志。',
|
||||||
|
no_logs: '该模型的拉取日志已被删除,无法查看相关日志。',
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
proxy: 'AI 代理增强',
|
||||||
|
proxyHelper1: '绑定域名并开启 HTTPS,增强传输安全性',
|
||||||
|
proxyHelper2: '限制 IP 访问,防止在公网暴露',
|
||||||
|
proxyHelper3: '开启流式传输',
|
||||||
|
proxyHelper4: '创建完成之后可以在网站列表中查看并管理',
|
||||||
|
proxyHelper5: '创建完成之后可以在应用商店 - 已安装 - ollama - 参数中取消端口外部访问以提高安全性',
|
||||||
|
proxyHelper6: '如需关闭代理配置,可以在网站列表中删除',
|
||||||
|
whiteListHelper: '限制仅白名单中的 IP 可访问',
|
||||||
|
},
|
||||||
|
gpu: {
|
||||||
|
gpu: 'GPU 监控',
|
||||||
|
base: '基础信息',
|
||||||
|
gpuHelper: '当前系统未检测到 NVIDIA-SMI或者XPU-SMI 指令,请检查后重试!',
|
||||||
|
driverVersion: '驱动版本',
|
||||||
|
cudaVersion: 'CUDA 版本',
|
||||||
|
process: '进程信息',
|
||||||
|
type: '类型',
|
||||||
|
typeG: '图形',
|
||||||
|
typeC: '计算',
|
||||||
|
typeCG: '计算+图形',
|
||||||
|
processName: '进程名称',
|
||||||
|
processMemoryUsage: '显存使用',
|
||||||
|
temperatureHelper: 'GPU 温度过高会导致 GPU 频率下降',
|
||||||
|
performanceStateHelper: '从 P0 (最大性能) 到 P12 (最小性能)',
|
||||||
|
busID: '总线地址',
|
||||||
|
persistenceMode: '持续模式',
|
||||||
|
enabled: '开启',
|
||||||
|
disabled: '关闭',
|
||||||
|
persistenceModeHelper: '持续模式能更加快速地响应任务,但相应待机功耗也会增加',
|
||||||
|
displayActive: '显卡初始化',
|
||||||
|
displayActiveT: '是',
|
||||||
|
displayActiveF: '否',
|
||||||
|
ecc: '是否开启错误检查和纠正技术',
|
||||||
|
computeMode: '计算模式',
|
||||||
|
default: '默认',
|
||||||
|
exclusiveProcess: '进程排他',
|
||||||
|
exclusiveThread: '线程排他',
|
||||||
|
prohibited: '禁止',
|
||||||
|
defaultHelper: '默认: 进程可以并发执行',
|
||||||
|
exclusiveProcessHelper: '进程排他: 只有一个 CUDA 上下文可以使用 GPU, 但可以由多个线程共享',
|
||||||
|
exclusiveThreadHelper: '线程排他: 只有一个线程在 CUDA 上下文中可以使用 GPU',
|
||||||
|
prohibitedHelper: '禁止: 不允许进程同时执行',
|
||||||
|
migModeHelper: '用于创建 MIG 实例,在用户层实现 GPU 的物理隔离。',
|
||||||
|
migModeNA: '不支持',
|
||||||
|
shr: '共享显存',
|
||||||
|
},
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
create: '创建容器',
|
create: '创建容器',
|
||||||
createByCommand: '命令创建',
|
createByCommand: '命令创建',
|
||||||
@ -1665,7 +1726,6 @@ const message = {
|
|||||||
waf: '升级专业版可以获得拦截地图、日志、封锁记录、地理位置封禁、自定义规则、自定义拦截页面等功能。',
|
waf: '升级专业版可以获得拦截地图、日志、封锁记录、地理位置封禁、自定义规则、自定义拦截页面等功能。',
|
||||||
tamper: '升级专业版可以保护网站免受未经授权的修改或篡改。',
|
tamper: '升级专业版可以保护网站免受未经授权的修改或篡改。',
|
||||||
tamperHelper: '操作失败,该文件或文件夹已经开启防篡改,请检查后重试!',
|
tamperHelper: '操作失败,该文件或文件夹已经开启防篡改,请检查后重试!',
|
||||||
gpu: '升级专业版可以帮助用户实时直观查看到 GPU 的工作负载、温度、显存等重要参数。',
|
|
||||||
setting: '升级专业版可以自定义面板 Logo、欢迎简介等信息。',
|
setting: '升级专业版可以自定义面板 Logo、欢迎简介等信息。',
|
||||||
monitor: '升级专业版可以查看网站的实时状态、访客趋势、访客来源、请求日志等信息。',
|
monitor: '升级专业版可以查看网站的实时状态、访客趋势、访客来源、请求日志等信息。',
|
||||||
alert: '升级专业版可通过短信接收告警信息,并查看告警日志,全面掌控各类关键事件,确保系统运行无忧。',
|
alert: '升级专业版可通过短信接收告警信息,并查看告警日志,全面掌控各类关键事件,确保系统运行无忧。',
|
||||||
@ -2803,38 +2863,6 @@ const message = {
|
|||||||
enableHelper: '即将启用下列网站的防篡改功能,以提升网站安全性,是否继续?',
|
enableHelper: '即将启用下列网站的防篡改功能,以提升网站安全性,是否继续?',
|
||||||
disableHelper: '即将关闭下列网站的防篡改功能,是否继续?',
|
disableHelper: '即将关闭下列网站的防篡改功能,是否继续?',
|
||||||
},
|
},
|
||||||
gpu: {
|
|
||||||
gpu: 'GPU 监控',
|
|
||||||
base: '基础信息',
|
|
||||||
gpuHelper: '当前系统未检测到 NVIDIA-SMI 指令,请检查后重试!',
|
|
||||||
driverVersion: '驱动版本',
|
|
||||||
cudaVersion: 'CUDA 版本',
|
|
||||||
process: '进程信息',
|
|
||||||
typeG: '图形',
|
|
||||||
typeC: '计算',
|
|
||||||
typeCG: '计算+图形',
|
|
||||||
processName: '进程名称',
|
|
||||||
processMemoryUsage: '显存使用',
|
|
||||||
temperatureHelper: 'GPU 温度过高会导致 GPU 频率下降',
|
|
||||||
performanceStateHelper: '从 P0 (最大性能) 到 P12 (最小性能)',
|
|
||||||
busID: '总线地址',
|
|
||||||
persistenceMode: '持续模式',
|
|
||||||
persistenceModeHelper: '持续模式能更加快速地响应任务,但相应待机功耗也会增加',
|
|
||||||
displayActive: '显卡初始化',
|
|
||||||
displayActiveT: '是',
|
|
||||||
displayActiveF: '否',
|
|
||||||
ecc: '是否开启错误检查和纠正技术',
|
|
||||||
computeMode: '计算模式',
|
|
||||||
exclusiveProcess: '进程排他',
|
|
||||||
exclusiveThread: '线程排他',
|
|
||||||
prohibited: '禁止',
|
|
||||||
defaultHelper: '默认: 进程可以并发执行',
|
|
||||||
exclusiveProcessHelper: '进程排他: 只有一个 CUDA 上下文可以使用 GPU, 但可以由多个线程共享',
|
|
||||||
exclusiveThreadHelper: '线程排他: 只有一个线程在 CUDA 上下文中可以使用 GPU',
|
|
||||||
prohibitedHelper: '禁止: 不允许进程同时执行',
|
|
||||||
migModeHelper: '用于创建 MIG 实例,在用户层实现 GPU 的物理隔离。',
|
|
||||||
migModeNA: '不支持',
|
|
||||||
},
|
|
||||||
setting: {
|
setting: {
|
||||||
setting: '界面设置',
|
setting: '界面设置',
|
||||||
title: '面板描述',
|
title: '面板描述',
|
||||||
@ -2870,11 +2898,6 @@ const message = {
|
|||||||
tamperTitle4: '日志记录与分析',
|
tamperTitle4: '日志记录与分析',
|
||||||
tamperContent4: '记录文件访问和操作日志,以便管理员进行后续的审计和分析,以及发现潜在的安全威胁。',
|
tamperContent4: '记录文件访问和操作日志,以便管理员进行后续的审计和分析,以及发现潜在的安全威胁。',
|
||||||
|
|
||||||
gpuTitle1: '概览页监控',
|
|
||||||
gpuContent1: '在概览页上显示 GPU 当前使用情况。',
|
|
||||||
gpuTitle2: 'GPU 详细信息',
|
|
||||||
gpuContent2: '更加细粒度的显示出 GPU 各项参数。',
|
|
||||||
|
|
||||||
settingTitle1: '自定义欢迎语',
|
settingTitle1: '自定义欢迎语',
|
||||||
settingContent1: '在 1Panel 登录页上设置自定义的欢迎语。',
|
settingContent1: '在 1Panel 登录页上设置自定义的欢迎语。',
|
||||||
settingTitle2: '自定义 Logo',
|
settingTitle2: '自定义 Logo',
|
||||||
|
34
frontend/src/routers/modules/ai.ts
Normal file
34
frontend/src/routers/modules/ai.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
|
const databaseRouter = {
|
||||||
|
sort: 4,
|
||||||
|
path: '/ai',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/ai/model',
|
||||||
|
meta: {
|
||||||
|
icon: 'p-jiqiren2',
|
||||||
|
title: 'menu.aiTools',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/ai/model',
|
||||||
|
name: 'OllamaModel',
|
||||||
|
component: () => import('@/views/ai/model/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'aiTools.model.model',
|
||||||
|
requiresAuth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ai/gpu',
|
||||||
|
name: 'GPU',
|
||||||
|
component: () => import('@/views/ai/gpu/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'aiTools.gpu.gpu',
|
||||||
|
requiresAuth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default databaseRouter;
|
@ -1,7 +1,7 @@
|
|||||||
import { Layout } from '@/routers/constant';
|
import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
const containerRouter = {
|
const containerRouter = {
|
||||||
sort: 5,
|
sort: 6,
|
||||||
path: '/containers',
|
path: '/containers',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/containers/container',
|
redirect: '/containers/container',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Layout } from '@/routers/constant';
|
import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
const databaseRouter = {
|
const databaseRouter = {
|
||||||
sort: 4,
|
sort: 5,
|
||||||
path: '/databases',
|
path: '/databases',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/databases/mysql',
|
redirect: '/databases/mysql',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Layout } from '@/routers/constant';
|
import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
const hostRouter = {
|
const hostRouter = {
|
||||||
sort: 6,
|
sort: 7,
|
||||||
path: '/hosts',
|
path: '/hosts',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/hosts/security',
|
redirect: '/hosts/security',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Layout } from '@/routers/constant';
|
import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
const terminalRouter = {
|
const terminalRouter = {
|
||||||
sort: 7,
|
sort: 8,
|
||||||
path: '/terminal',
|
path: '/terminal',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/terminal',
|
redirect: '/terminal',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Layout } from '@/routers/constant';
|
import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
const toolboxRouter = {
|
const toolboxRouter = {
|
||||||
sort: 8,
|
sort: 9,
|
||||||
path: '/toolbox',
|
path: '/toolbox',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/toolbox/supervisor',
|
redirect: '/toolbox/supervisor',
|
||||||
|
367
frontend/src/views/ai/gpu/index.vue
Normal file
367
frontend/src/views/ai/gpu/index.vue
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<RouterButton
|
||||||
|
:buttons="[
|
||||||
|
{
|
||||||
|
label: $t('aiTools.gpu.gpu'),
|
||||||
|
path: '/xpack/gpu',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-if="gpuType == 'nvidia'">
|
||||||
|
<LayoutContent
|
||||||
|
v-loading="loading"
|
||||||
|
:title="$t('aiTools.gpu.gpu')"
|
||||||
|
:divider="true"
|
||||||
|
v-if="gpuInfo.driverVersion.length !== 0 && !loading"
|
||||||
|
>
|
||||||
|
<template #toolbar>
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16" />
|
||||||
|
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8">
|
||||||
|
<TableSetting @search="refresh()" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<el-descriptions direction="vertical" :column="14" border>
|
||||||
|
<el-descriptions-item :label="$t('aiTools.gpu.driverVersion')" width="50%" :span="7">
|
||||||
|
{{ gpuInfo.driverVersion }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('aiTools.gpu.cudaVersion')" :span="7">
|
||||||
|
{{ gpuInfo.cudaVersion }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<el-collapse v-model="activeNames" class="mt-5">
|
||||||
|
<el-collapse-item v-for="item in gpuInfo.gpu" :key="item.index" :name="item.index">
|
||||||
|
<template #title>
|
||||||
|
<span class="name-class">{{ item.index + '. ' + item.productName }}</span>
|
||||||
|
</template>
|
||||||
|
<span class="title-class">{{ $t('aiTools.gpu.base') }}</span>
|
||||||
|
<el-descriptions direction="vertical" :column="6" border size="small" class="mt-2">
|
||||||
|
<el-descriptions-item :label="$t('monitor.gpuUtil')">
|
||||||
|
{{ item.gpuUtil }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
{{ $t('monitor.temperature') }}
|
||||||
|
<el-tooltip placement="top" :content="$t('aiTools.gpu.temperatureHelper')">
|
||||||
|
<el-icon class="icon-item"><InfoFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ item.temperature.replaceAll('C', '°C') }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
{{ $t('monitor.performanceState') }}
|
||||||
|
<el-tooltip
|
||||||
|
placement="top"
|
||||||
|
:content="$t('aiTools.gpu.performanceStateHelper')"
|
||||||
|
>
|
||||||
|
<el-icon class="icon-item"><InfoFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ item.performanceState }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('monitor.powerUsage')">
|
||||||
|
{{ item.powerDraw }} / {{ item.maxPowerLimit }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('monitor.memoryUsage')">
|
||||||
|
{{ item.memUsed }} / {{ item.memTotal }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('monitor.fanSpeed')">
|
||||||
|
{{ item.fanSpeed }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
|
||||||
|
<el-descriptions-item :label="$t('aiTools.gpu.busID')">
|
||||||
|
{{ item.busID }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
{{ $t('aiTools.gpu.persistenceMode') }}
|
||||||
|
<el-tooltip
|
||||||
|
placement="top"
|
||||||
|
:content="$t('aiTools.gpu.persistenceModeHelper')"
|
||||||
|
>
|
||||||
|
<el-icon class="icon-item"><InfoFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ $t('aiTools.gpu.' + item.persistenceMode.toLowerCase()) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('aiTools.gpu.displayActive')">
|
||||||
|
{{
|
||||||
|
lowerCase(item.displayActive) === 'disabled'
|
||||||
|
? $t('aiTools.gpu.displayActiveF')
|
||||||
|
: $t('aiTools.gpu.displayActiveT')
|
||||||
|
}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
Uncorr. ECC
|
||||||
|
<el-tooltip placement="top" :content="$t('aiTools.gpu.ecc')">
|
||||||
|
<el-icon class="icon-item"><InfoFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ loadEcc(item.ecc) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('aiTools.gpu.computeMode')">
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
{{ $t('aiTools.gpu.computeMode') }}
|
||||||
|
<el-tooltip placement="top">
|
||||||
|
<template #content>
|
||||||
|
{{ $t('aiTools.gpu.defaultHelper') }}
|
||||||
|
<br />
|
||||||
|
{{ $t('aiTools.gpu.exclusiveProcessHelper') }}
|
||||||
|
<br />
|
||||||
|
{{ $t('aiTools.gpu.exclusiveThreadHelper') }}
|
||||||
|
<br />
|
||||||
|
{{ $t('aiTools.gpu.prohibitedHelper') }}
|
||||||
|
</template>
|
||||||
|
<el-icon class="icon-item"><InfoFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ loadComputeMode(item.computeMode) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="MIG.M">
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
MIG M.
|
||||||
|
<el-tooltip placement="top">
|
||||||
|
<template #content>
|
||||||
|
{{ $t('aiTools.gpu.migModeHelper') }}
|
||||||
|
</template>
|
||||||
|
<el-icon class="icon-item"><InfoFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{
|
||||||
|
item.migMode === 'N/A'
|
||||||
|
? $t('aiTools.gpu.migModeNA')
|
||||||
|
: $t('aiTools.gpu.' + lowerCase(item.migMode))
|
||||||
|
}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<div class="mt-5">
|
||||||
|
<span class="title-class">{{ $t('aiTools.gpu.process') }}</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="item.processes" v-if="item.processes?.length !== 0">
|
||||||
|
<el-table-column label="PID" prop="pid" />
|
||||||
|
<el-table-column :label="$t('aiTools.gpu.type')" prop="type">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ loadProcessType(row.type) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('aiTools.gpu.processName')" prop="processName" />
|
||||||
|
<el-table-column :label="$t('aiTools.gpu.processMemoryUsage')" prop="usedMemory" />
|
||||||
|
</el-table>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<LayoutContent
|
||||||
|
v-loading="loading"
|
||||||
|
:title="$t('aiTools.gpu.gpu')"
|
||||||
|
:divider="true"
|
||||||
|
v-if="xpuInfo.driverVersion.length !== 0 && !loading"
|
||||||
|
>
|
||||||
|
<template #toolbar>
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16" />
|
||||||
|
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8">
|
||||||
|
<TableSetting @search="refresh()" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<el-descriptions direction="vertical" :column="14" border>
|
||||||
|
<el-descriptions-item :label="$t('aiTools.gpu.driverVersion')" width="50%" :span="7">
|
||||||
|
{{ xpuInfo.driverVersion }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<el-collapse v-model="activeNames" class="mt-5">
|
||||||
|
<el-collapse-item
|
||||||
|
v-for="item in xpuInfo.xpu"
|
||||||
|
:key="item.basic.deviceID"
|
||||||
|
:name="item.basic.deviceID"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span class="name-class">{{ item.basic.deviceID + '. ' + item.basic.deviceName }}</span>
|
||||||
|
</template>
|
||||||
|
<span class="title-class">{{ $t('aiTools.gpu.base') }}</span>
|
||||||
|
<el-descriptions direction="vertical" :column="6" border size="small" class="mt-2">
|
||||||
|
<el-descriptions-item :label="$t('monitor.gpuUtil')">
|
||||||
|
{{ item.stats.memoryUtil }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
{{ $t('monitor.temperature') }}
|
||||||
|
<el-tooltip placement="top" :content="$t('aiTools.gpu.temperatureHelper')">
|
||||||
|
<el-icon class="icon-item"><InfoFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ item.stats.temperature }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('monitor.powerUsage')">
|
||||||
|
{{ item.stats.power }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('monitor.memoryUsage')">
|
||||||
|
{{ item.stats.memoryUsed }} / {{ item.basic.memory }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('aiTools.gpu.busID')">
|
||||||
|
{{ item.basic.pciBdfAddress }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<div class="mt-5">
|
||||||
|
<span class="title-class">{{ $t('aiTools.gpu.process') }}</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="item.processes" v-if="item.processes?.length !== 0">
|
||||||
|
<el-table-column label="PID" prop="pid" />
|
||||||
|
<el-table-column :label="$t('aiTools.gpu.processName')" prop="command" />
|
||||||
|
<el-table-column :label="$t('aiTools.gpu.shr')" prop="shr" />
|
||||||
|
<el-table-column :label="$t('aiTools.gpu.processMemoryUsage')" prop="memory" />
|
||||||
|
</el-table>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
</div>
|
||||||
|
<LayoutContent
|
||||||
|
:title="$t('aiTools.gpu.gpu')"
|
||||||
|
:divider="true"
|
||||||
|
v-if="gpuInfo.driverVersion.length === 0 && xpuInfo.driverVersion.length == 0 && !loading"
|
||||||
|
>
|
||||||
|
<template #main>
|
||||||
|
<div class="app-warn">
|
||||||
|
<div class="flx-center">
|
||||||
|
<span>{{ $t('aiTools.gpu.gpuHelper') }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img src="@/assets/images/no_app.svg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { loadGPUInfo } from '@/api/modules/ai';
|
||||||
|
import { AI } from '@/api/interface/ai';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
|
||||||
|
const loading = ref();
|
||||||
|
const activeNames = ref(0);
|
||||||
|
const gpuInfo = ref<AI.Info>({
|
||||||
|
cudaVersion: '',
|
||||||
|
driverVersion: '',
|
||||||
|
type: 'nvidia',
|
||||||
|
gpu: [],
|
||||||
|
});
|
||||||
|
const xpuInfo = ref<AI.XpuInfo>({
|
||||||
|
driverVersion: '',
|
||||||
|
type: 'xpu',
|
||||||
|
xpu: [],
|
||||||
|
});
|
||||||
|
const gpuType = ref('nvidia');
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await loadGPUInfo()
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
gpuType.value = res.data.type;
|
||||||
|
if (res.data.type == 'nvidia') {
|
||||||
|
gpuInfo.value = res.data;
|
||||||
|
} else {
|
||||||
|
xpuInfo.value = res.data;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const refresh = async () => {
|
||||||
|
const res = await loadGPUInfo();
|
||||||
|
gpuInfo.value = res.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const lowerCase = (val: string) => {
|
||||||
|
return val.toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadComputeMode = (val: string) => {
|
||||||
|
switch (val) {
|
||||||
|
case 'Default':
|
||||||
|
return i18n.global.t('aiTools.gpu.default');
|
||||||
|
case 'Exclusive Process':
|
||||||
|
return i18n.global.t('aiTools.gpu.exclusiveProcess');
|
||||||
|
case 'Exclusive Thread':
|
||||||
|
return i18n.global.t('aiTools.gpu.exclusiveThread');
|
||||||
|
case 'Prohibited':
|
||||||
|
return i18n.global.t('aiTools.gpu.prohibited');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadEcc = (val: string) => {
|
||||||
|
if (val === 'N/A') {
|
||||||
|
return i18n.global.t('aiTools.gpu.migModeNA');
|
||||||
|
}
|
||||||
|
if (val === 'Disabled') {
|
||||||
|
return i18n.global.t('aiTools.gpu.disabled');
|
||||||
|
}
|
||||||
|
if (val === 'Enabled') {
|
||||||
|
return i18n.global.t('aiTools.gpu.enabled');
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadProcessType = (val: string) => {
|
||||||
|
if (val === 'C' || val === 'G') {
|
||||||
|
return i18n.global.t('aiTools.gpu.type' + val);
|
||||||
|
}
|
||||||
|
if (val === 'C+G') {
|
||||||
|
return i18n.global.t('aiTools.gpu.typeCG');
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.name-class {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.title-class {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.cell-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.icon-item {
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
88
frontend/src/views/ai/model/add/index.vue
Normal file
88
frontend/src/views/ai/model/add/index.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<DrawerPro v-model="drawerVisible" :header="$t('aiTools.model.create')" :back="handleClose">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-alert type="info" :closable="false">
|
||||||
|
<template #title>
|
||||||
|
<span class="flx-align-center">
|
||||||
|
{{ $t('aiTools.model.ollama_doc') }}
|
||||||
|
<el-button link class="ml-5" icon="Position" @click="goSearch()" type="primary">
|
||||||
|
{{ $t('firewall.quickJump') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
<el-form ref="formRef" label-position="top" class="mt-5" :model="form">
|
||||||
|
<el-form-item :label="$t('commons.table.name')" :rules="Rules.requiredInput" prop="name">
|
||||||
|
<el-input v-model.trim="form.name" />
|
||||||
|
<span class="input-help" v-if="form.name">
|
||||||
|
{{
|
||||||
|
$t('aiTools.model.create_helper', [
|
||||||
|
form.name.replaceAll('ollama run ', '').replaceAll('ollama pull ', ''),
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="drawerVisible = false">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit(formRef)">
|
||||||
|
{{ $t('commons.button.add') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</DrawerPro>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm } from 'element-plus';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { createOllamaModel } from '@/api/modules/ai';
|
||||||
|
|
||||||
|
const drawerVisible = ref(false);
|
||||||
|
const form = reactive({
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const acceptParams = async (): Promise<void> => {
|
||||||
|
form.name = '';
|
||||||
|
drawerVisible.value = true;
|
||||||
|
};
|
||||||
|
const emit = defineEmits(['search', 'log']);
|
||||||
|
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
let itemName = form.name.replaceAll('ollama run ', '').replaceAll('ollama pull ', '');
|
||||||
|
await createOllamaModel(itemName);
|
||||||
|
drawerVisible.value = false;
|
||||||
|
emit('search');
|
||||||
|
emit('log', { logFileExist: true, name: itemName, from: 'local' });
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const goSearch = () => {
|
||||||
|
window.open('https://ollama.com/search', '_blank', 'noopener,noreferrer');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
drawerVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
139
frontend/src/views/ai/model/conn/index.vue
Normal file
139
frontend/src/views/ai/model/conn/index.vue
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<template>
|
||||||
|
<DrawerPro v-model="drawerVisible" :header="$t('database.databaseConnInfo')" :back="handleClose" size="small">
|
||||||
|
<el-form @submit.prevent v-loading="loading" :model="form" label-position="top">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-form-item :label="$t('database.containerConn')">
|
||||||
|
<el-card class="mini-border-card">
|
||||||
|
<el-descriptions :column="1">
|
||||||
|
<el-descriptions-item :label="$t('database.connAddress')">
|
||||||
|
{{ form.containerName }}
|
||||||
|
<CopyButton :content="form.containerName" type="icon" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('commons.table.port')">
|
||||||
|
11434
|
||||||
|
<CopyButton content="11434" type="icon" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-card>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('aiTools.model.container_conn_helper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('setting.proxyUrl')" v-if="bindDomain.connUrl != ''">
|
||||||
|
<el-card class="mini-border-card">
|
||||||
|
<el-descriptions :column="1">
|
||||||
|
<el-descriptions-item :label="$t('database.connAddress')">
|
||||||
|
{{ bindDomain.connUrl }}
|
||||||
|
<CopyButton :content="bindDomain.connUrl" type="icon" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-card>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('database.remoteConnHelper2') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('database.remoteConn')" v-else>
|
||||||
|
<el-card class="mini-border-card">
|
||||||
|
<el-descriptions :column="1">
|
||||||
|
<el-descriptions-item :label="$t('database.connAddress')">
|
||||||
|
{{ form.systemIP }}
|
||||||
|
<CopyButton :content="form.systemIP" type="icon" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="$t('commons.table.port')">
|
||||||
|
{{ form.port }}
|
||||||
|
<CopyButton :content="form.port + ''" type="icon" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-card>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('database.remoteConnHelper2') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button :disabled="loading" @click="drawerVisible = false">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</DrawerPro>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm } from 'element-plus';
|
||||||
|
import { getSettingInfo } from '@/api/modules/setting';
|
||||||
|
import { getBindDomain } from '@/api/modules/ai';
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const drawerVisible = ref(false);
|
||||||
|
const form = reactive({
|
||||||
|
systemIP: '',
|
||||||
|
containerName: '',
|
||||||
|
port: 0,
|
||||||
|
|
||||||
|
remoteIP: '',
|
||||||
|
});
|
||||||
|
const bindDomain = ref({
|
||||||
|
connUrl: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
port: number;
|
||||||
|
containerName: string;
|
||||||
|
appinstallID: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const acceptParams = (param: DialogProps): void => {
|
||||||
|
form.containerName = param.containerName;
|
||||||
|
form.port = param.port;
|
||||||
|
loadSystemIP();
|
||||||
|
loadBindDomain(param.appinstallID);
|
||||||
|
drawerVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
drawerVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSystemIP = async () => {
|
||||||
|
const res = await getSettingInfo();
|
||||||
|
form.systemIP = res.data.systemIP || i18n.global.t('database.localIP');
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadBindDomain = async (appInstallID: number) => {
|
||||||
|
if (appInstallID == undefined || appInstallID <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await getBindDomain({
|
||||||
|
appInstallID: appInstallID,
|
||||||
|
});
|
||||||
|
if (res.data.websiteID > 0) {
|
||||||
|
bindDomain.value.connUrl = res.data.connUrl;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.copy_button {
|
||||||
|
border-radius: 0px;
|
||||||
|
border-left-width: 0px;
|
||||||
|
}
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
}
|
||||||
|
</style>
|
105
frontend/src/views/ai/model/del/index.vue
Normal file
105
frontend/src/views/ai/model/del/index.vue
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<DialogPro v-model="open" :title="$t('commons.button.sync')" size="small" @close="handleClose">
|
||||||
|
<div v-loading="loading">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-alert class="mt-2" :show-icon="true" type="warning" :closable="false">
|
||||||
|
{{ $t('aiTools.model.ollama_sync') }}
|
||||||
|
</el-alert>
|
||||||
|
<el-checkbox
|
||||||
|
class="mt-2"
|
||||||
|
v-model="checkAll"
|
||||||
|
:indeterminate="isIndeterminate"
|
||||||
|
@change="handleCheckAllChange"
|
||||||
|
>
|
||||||
|
{{ $t('setting.all') }}
|
||||||
|
</el-checkbox>
|
||||||
|
<el-checkbox-group v-model="checkedItems" @change="handleCheckedChange">
|
||||||
|
<el-checkbox v-for="(item, index) in list" :key="index" :label="item.name" :value="item.id" />
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="handleClose()" :disabled="loading">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="onConfirm" :disabled="loading || checkedItems.length === 0">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</DialogPro>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AI } from '@/api/interface/ai';
|
||||||
|
import { deleteOllamaModel } from '@/api/modules/ai';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { CheckboxValueType } from 'element-plus';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'OpDialog' });
|
||||||
|
|
||||||
|
const checkAll = ref(false);
|
||||||
|
const isIndeterminate = ref(true);
|
||||||
|
const checkedItems = ref([]);
|
||||||
|
const list = ref([]);
|
||||||
|
|
||||||
|
const loading = ref();
|
||||||
|
const open = ref();
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
list: Array<AI.OllamaModelDropInfo>;
|
||||||
|
}
|
||||||
|
const acceptParams = (props: DialogProps): void => {
|
||||||
|
list.value = props.list;
|
||||||
|
checkAll.value = true;
|
||||||
|
handleCheckAllChange(true);
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const emit = defineEmits(['search']);
|
||||||
|
|
||||||
|
const handleCheckAllChange = (val: CheckboxValueType) => {
|
||||||
|
checkedItems.value = [];
|
||||||
|
if (val) {
|
||||||
|
for (const item of list.value) {
|
||||||
|
checkedItems.value.push(item.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isIndeterminate.value = false;
|
||||||
|
};
|
||||||
|
const handleCheckedChange = (value: CheckboxValueType[]) => {
|
||||||
|
const checkedCount = value.length;
|
||||||
|
checkAll.value = checkedCount === list.value.length;
|
||||||
|
isIndeterminate.value = checkedCount > 0 && checkedCount < list.value.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirm = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await deleteOllamaModel(checkedItems.value, true)
|
||||||
|
.then(() => {
|
||||||
|
emit('search');
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
open.value = false;
|
||||||
|
loading.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit('search');
|
||||||
|
open.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
260
frontend/src/views/ai/model/domain/index.vue
Normal file
260
frontend/src/views/ai/model/domain/index.vue
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<template>
|
||||||
|
<DrawerPro v-model="open" :title="$t('aiTools.proxy.proxy')" :back="handleClose" size="large">
|
||||||
|
<div v-loading="loading">
|
||||||
|
<el-form ref="formRef" label-position="top" @submit.prevent :model="req" :rules="rules">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-alert class="common-prompt" :closable="false" type="warning">
|
||||||
|
<template #default>
|
||||||
|
<ul>
|
||||||
|
<li>{{ $t('aiTools.proxy.proxyHelper1') }}</li>
|
||||||
|
<li>{{ $t('aiTools.proxy.proxyHelper2') }}</li>
|
||||||
|
<li>{{ $t('aiTools.proxy.proxyHelper3') }}</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
<el-form-item :label="$t('website.domain')" prop="domain">
|
||||||
|
<el-input v-model.trim="req.domain" :disabled="operate === 'update'" />
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('aiTools.proxy.proxyHelper4') }}
|
||||||
|
</span>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('aiTools.proxy.proxyHelper6') }}
|
||||||
|
<el-link
|
||||||
|
class="pageRoute"
|
||||||
|
icon="Position"
|
||||||
|
@click="toWebsite(req.websiteID)"
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{{ $t('firewall.quickJump') }}
|
||||||
|
</el-link>
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('xpack.waf.whiteList') + ' IP'" prop="ipList">
|
||||||
|
<el-input
|
||||||
|
:rows="3"
|
||||||
|
type="textarea"
|
||||||
|
clearable
|
||||||
|
v-model="req.ipList"
|
||||||
|
:placeholder="$t('xpack.waf.ipGroupHelper')"
|
||||||
|
/>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('aiTools.proxy.whiteListHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="req.enableSSL" @change="changeSSL">
|
||||||
|
{{ $t('website.enable') + ' ' + 'HTTPS' }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('website.acmeAccountManage')"
|
||||||
|
prop="acmeAccountID"
|
||||||
|
v-if="req.enableSSL"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="req.acmeAccountID"
|
||||||
|
:placeholder="$t('website.selectAcme')"
|
||||||
|
@change="listSSL"
|
||||||
|
>
|
||||||
|
<el-option :key="0" :label="$t('website.imported')" :value="0"></el-option>
|
||||||
|
<el-option
|
||||||
|
v-for="(acme, index) in acmeAccounts"
|
||||||
|
:key="index"
|
||||||
|
:label="acme.email"
|
||||||
|
:value="acme.id"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{ acme.email }}
|
||||||
|
<el-tag class="ml-5">{{ getAccountName(acme.type) }}</el-tag>
|
||||||
|
</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('website.ssl')" prop="sslID" v-if="req.enableSSL">
|
||||||
|
<el-select
|
||||||
|
v-model="req.sslID"
|
||||||
|
:placeholder="$t('website.selectSSL')"
|
||||||
|
@change="changeSSl(req.sslID)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(ssl, index) in ssls"
|
||||||
|
:key="index"
|
||||||
|
:label="ssl.primaryDomain"
|
||||||
|
:value="ssl.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-alert :closable="false">
|
||||||
|
{{ $t('aiTools.proxy.proxyHelper5') }}
|
||||||
|
<el-link class="pageRoute" icon="Position" @click="toInstalled()" type="primary">
|
||||||
|
{{ $t('firewall.quickJump') }}
|
||||||
|
</el-link>
|
||||||
|
</el-alert>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit(formRef)">
|
||||||
|
{{ $t('commons.button.add') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</DrawerPro>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Website } from '@/api/interface/website';
|
||||||
|
import { listSSL, searchAcmeAccount } from '@/api/modules/website';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import { FormInstance, FormRules } from 'element-plus';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { getAccountName } from '@/utils/util';
|
||||||
|
import { bindDomain, getBindDomain, updateBindDomain } from '@/api/modules/ai';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
|
||||||
|
const open = ref(false);
|
||||||
|
const operate = ref('create');
|
||||||
|
const loading = ref(false);
|
||||||
|
const ssls = ref([]);
|
||||||
|
const websiteSSL = ref<Website.SSL>();
|
||||||
|
const acmeAccounts = ref();
|
||||||
|
const formRef = ref();
|
||||||
|
const req = ref({
|
||||||
|
domain: '',
|
||||||
|
sslID: undefined,
|
||||||
|
ipList: '',
|
||||||
|
acmeAccountID: 0,
|
||||||
|
enableSSL: false,
|
||||||
|
allowIPs: [],
|
||||||
|
appInstallID: 0,
|
||||||
|
websiteID: 0,
|
||||||
|
});
|
||||||
|
const rules = reactive<FormRules>({
|
||||||
|
domain: [Rules.domainWithPort],
|
||||||
|
sslID: [Rules.requiredSelectBusiness],
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['search']);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit('search');
|
||||||
|
open.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = (installID: number) => {
|
||||||
|
req.value.appInstallID = installID;
|
||||||
|
search(installID);
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeSSl = (sslid: number) => {
|
||||||
|
const res = ssls.value.filter((element: Website.SSL) => {
|
||||||
|
return element.id == sslid;
|
||||||
|
});
|
||||||
|
websiteSSL.value = res[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeSSL = () => {
|
||||||
|
if (!req.value.enableSSL) {
|
||||||
|
req.value.sslID = undefined;
|
||||||
|
} else {
|
||||||
|
listAcmeAccount();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSSL = () => {
|
||||||
|
const sslReq = {
|
||||||
|
acmeAccountID: String(req.value.acmeAccountID),
|
||||||
|
};
|
||||||
|
listSSL(sslReq).then((res) => {
|
||||||
|
ssls.value = res.data || [];
|
||||||
|
if (ssls.value.length > 0) {
|
||||||
|
let exist = false;
|
||||||
|
for (const ssl of ssls.value) {
|
||||||
|
if (ssl.id === req.value.sslID) {
|
||||||
|
exist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exist) {
|
||||||
|
req.value.sslID = ssls.value[0].id;
|
||||||
|
}
|
||||||
|
changeSSl(req.value.sslID);
|
||||||
|
} else {
|
||||||
|
req.value.sslID = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const listAcmeAccount = () => {
|
||||||
|
searchAcmeAccount({ page: 1, pageSize: 100 }).then((res) => {
|
||||||
|
acmeAccounts.value = res.data.items || [];
|
||||||
|
if (acmeAccounts.value.length > 0) {
|
||||||
|
req.value.acmeAccountID = acmeAccounts.value[0].id;
|
||||||
|
}
|
||||||
|
loadSSL();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
if (operate.value === 'update') {
|
||||||
|
await updateBindDomain(req.value);
|
||||||
|
} else {
|
||||||
|
await bindDomain(req.value);
|
||||||
|
}
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
handleClose();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async (appInstallID: number) => {
|
||||||
|
try {
|
||||||
|
const res = await getBindDomain({
|
||||||
|
appInstallID: appInstallID,
|
||||||
|
});
|
||||||
|
if (res.data.websiteID > 0) {
|
||||||
|
operate.value = 'update';
|
||||||
|
req.value.domain = res.data.domain;
|
||||||
|
req.value.websiteID = res.data.websiteID;
|
||||||
|
if (res.data.allowIPs && res.data.allowIPs.length > 0) {
|
||||||
|
req.value.ipList = res.data.allowIPs.join('\n');
|
||||||
|
}
|
||||||
|
if (res.data.sslID > 0) {
|
||||||
|
req.value.enableSSL = true;
|
||||||
|
req.value.sslID = res.data.sslID;
|
||||||
|
listAcmeAccount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toWebsite = (websiteID: number) => {
|
||||||
|
if (websiteID != undefined && websiteID > 0) {
|
||||||
|
window.location.href = `/websites/${websiteID}/config/basic`;
|
||||||
|
} else {
|
||||||
|
window.location.href = '/websites';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toInstalled = () => {
|
||||||
|
window.location.href = '/apps/installed';
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.pageRoute {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
457
frontend/src/views/ai/model/index.vue
Normal file
457
frontend/src/views/ai/model/index.vue
Normal file
@ -0,0 +1,457 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<RouterButton
|
||||||
|
:buttons="[
|
||||||
|
{
|
||||||
|
label: i18n.global.t('aiTools.model.model'),
|
||||||
|
path: '/ai-tools/model',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<LayoutContent title="Ollama">
|
||||||
|
<template #app>
|
||||||
|
<AppStatus
|
||||||
|
app-key="ollama"
|
||||||
|
v-model:loading="loading"
|
||||||
|
:hide-setting="true"
|
||||||
|
v-model:mask-show="maskShow"
|
||||||
|
v-model:appInstallID="appInstallID"
|
||||||
|
@is-exist="checkExist"
|
||||||
|
ref="appStatusRef"
|
||||||
|
></AppStatus>
|
||||||
|
</template>
|
||||||
|
<template #prompt>
|
||||||
|
<el-alert type="info" :closable="false">
|
||||||
|
<template #title>
|
||||||
|
<span>{{ $t('runtime.systemRestartHelper') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</template>
|
||||||
|
<template #leftToolBar>
|
||||||
|
<el-button :disabled="modelInfo.status !== 'Running'" type="primary" @click="onCreate()">
|
||||||
|
{{ $t('aiTools.model.create') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button plain type="primary" :disabled="modelInfo.status !== 'Running'" @click="bindDomain">
|
||||||
|
{{ $t('aiTools.proxy.proxy') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="modelInfo.status !== 'Running'" @click="onLoadConn" type="primary" plain>
|
||||||
|
{{ $t('database.databaseConnInfo') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="modelInfo.status !== 'Running'" type="primary" plain @click="onSync()">
|
||||||
|
{{ $t('database.loadFromRemote') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
:disabled="modelInfo.status !== 'Running'"
|
||||||
|
icon="Position"
|
||||||
|
@click="goDashboard()"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
>
|
||||||
|
OpenWebUI
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-button plain :disabled="selects.length === 0" type="primary" @click="onDelete(null)">
|
||||||
|
{{ $t('commons.button.delete') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #rightToolBar>
|
||||||
|
<TableSearch @search="search()" v-model:searchName="searchName" />
|
||||||
|
<TableRefresh @search="search()" />
|
||||||
|
<TableSetting title="model-refresh" @search="search()" />
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<ComplexTable
|
||||||
|
:pagination-config="paginationConfig"
|
||||||
|
v-model:selects="selects"
|
||||||
|
:class="{ mask: maskShow }"
|
||||||
|
@sort-change="search"
|
||||||
|
@search="search"
|
||||||
|
:data="data"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" :selectable="selectable" fix />
|
||||||
|
<el-table-column :label="$t('aiTools.model.model')" prop="name" min-width="90">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-text v-if="row.size" type="primary" class="cursor-pointer" @click="onLoad(row.name)">
|
||||||
|
{{ row.name }}
|
||||||
|
</el-text>
|
||||||
|
<span v-else>{{ row.name }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('file.size')" prop="size">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span>{{ row.size || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.status === 'Success'" type="success">
|
||||||
|
{{ $t('commons.status.success') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="row.status === 'Deleted'" type="info">
|
||||||
|
{{ $t('database.isDelete') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="row.status === 'Canceled'" type="danger">
|
||||||
|
{{ $t('commons.status.systemrestart') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="row.status === 'Failed'" type="danger">
|
||||||
|
{{ $t('commons.status.failed') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="row.status === 'Waiting'">
|
||||||
|
<el-icon v-if="row.status === 'Waiting'" class="is-loading">
|
||||||
|
<Loading />
|
||||||
|
</el-icon>
|
||||||
|
{{ $t('commons.status.waiting') }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.button.log')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button @click="onLoadLog(row)" link type="primary">
|
||||||
|
{{ $t('website.check') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
min-width="80"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
prop="createdAt"
|
||||||
|
:formatter="dateFormat"
|
||||||
|
/>
|
||||||
|
<fu-table-operations
|
||||||
|
:ellipsis="mobile ? 0 : 10"
|
||||||
|
:min-width="mobile ? 'auto' : 200"
|
||||||
|
:buttons="buttons"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fixed="right"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
|
||||||
|
<el-card v-if="modelInfo.status != 'Running' && !loading && maskShow" class="mask-prompt">
|
||||||
|
<span v-if="modelInfo.isExist">
|
||||||
|
{{ $t('commons.service.serviceNotStarted', ['Ollama']) }}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ $t('app.checkInstalledWarn', ['Ollama']) }}
|
||||||
|
<el-button @click="goInstall('ollama')" link icon="Position" type="primary">
|
||||||
|
{{ $t('database.goInstall') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
|
||||||
|
<DialogPro v-model="dashboardVisible" :title="$t('app.checkTitle')" size="mini">
|
||||||
|
<div class="flex justify-center items-center gap-2 flex-wrap">
|
||||||
|
{{ $t('app.checkInstalledWarn', ['OpenWebUI']) }}
|
||||||
|
<el-link icon="Position" @click="goInstall('ollama-webui')" type="primary">
|
||||||
|
{{ $t('database.goInstall') }}
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dashboardVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</DialogPro>
|
||||||
|
|
||||||
|
<OpDialog ref="opRef" @search="search" @submit="onSubmitDelete()">
|
||||||
|
<template #content>
|
||||||
|
<el-form class="mt-4 mb-1" ref="deleteForm" label-position="left">
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="forceDelete" :label="$t('website.forceDelete')" />
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('website.forceDeleteHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
</OpDialog>
|
||||||
|
<AddDialog ref="addRef" @search="search" @log="onLoadLog" />
|
||||||
|
<Log ref="logRef" @close="search" />
|
||||||
|
<Del ref="delRef" @search="search" />
|
||||||
|
<Terminal ref="terminalRef" />
|
||||||
|
<Conn ref="connRef" />
|
||||||
|
<CodemirrorDialog ref="detailRef" />
|
||||||
|
<PortJumpDialog ref="dialogPortJumpRef" />
|
||||||
|
<BindDomain ref="bindDomainRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import AppStatus from '@/components/app-status/index.vue';
|
||||||
|
import AddDialog from '@/views/ai/model/add/index.vue';
|
||||||
|
import Conn from '@/views/ai/model/conn/index.vue';
|
||||||
|
import Terminal from '@/views/ai/model/terminal/index.vue';
|
||||||
|
import Del from '@/views/ai/model/del/index.vue';
|
||||||
|
import Log from '@/components/log-dialog/index.vue';
|
||||||
|
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||||
|
import CodemirrorDialog from '@/components/codemirror-dialog/index.vue';
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { GlobalStore } from '@/store';
|
||||||
|
import {
|
||||||
|
deleteOllamaModel,
|
||||||
|
loadOllamaModel,
|
||||||
|
recreateOllamaModel,
|
||||||
|
searchOllamaModel,
|
||||||
|
syncOllamaModel,
|
||||||
|
} from '@/api/modules/ai';
|
||||||
|
import { AI } from '@/api/interface/ai';
|
||||||
|
import { getAppPort } from '@/api/modules/app';
|
||||||
|
import { dateFormat } from '@/utils/util';
|
||||||
|
import router from '@/routers';
|
||||||
|
import { MsgInfo, MsgSuccess } from '@/utils/message';
|
||||||
|
import BindDomain from '@/views/ai/model/domain/index.vue';
|
||||||
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const selects = ref<any>([]);
|
||||||
|
const maskShow = ref(true);
|
||||||
|
const addRef = ref();
|
||||||
|
const logRef = ref();
|
||||||
|
const detailRef = ref();
|
||||||
|
const delRef = ref();
|
||||||
|
const connRef = ref();
|
||||||
|
const terminalRef = ref();
|
||||||
|
const openWebUIPort = ref();
|
||||||
|
const dashboardVisible = ref(false);
|
||||||
|
const dialogPortJumpRef = ref();
|
||||||
|
const appStatusRef = ref();
|
||||||
|
const bindDomainRef = ref();
|
||||||
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
cacheSizeKey: 'model-page-size',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: Number(localStorage.getItem('page-size')) || 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
const searchName = ref();
|
||||||
|
const appInstallID = ref(0);
|
||||||
|
|
||||||
|
const opRef = ref();
|
||||||
|
const operateIDs = ref();
|
||||||
|
const forceDelete = ref();
|
||||||
|
|
||||||
|
const modelInfo = reactive({
|
||||||
|
status: '',
|
||||||
|
container: '',
|
||||||
|
isExist: null,
|
||||||
|
version: '',
|
||||||
|
port: 11434,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mobile = computed(() => {
|
||||||
|
return globalStore.isMobile();
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectable(row) {
|
||||||
|
return row.status !== 'Waiting';
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
let params = {
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
info: searchName.value,
|
||||||
|
};
|
||||||
|
loading.value = true;
|
||||||
|
await searchOllamaModel(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
data.value = res.data.items || [];
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCreate = async () => {
|
||||||
|
addRef.value.acceptParams();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSync = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await syncOllamaModel()
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.data) {
|
||||||
|
delRef.value.acceptParams({ list: res.data });
|
||||||
|
} else {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
search();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoadConn = async () => {
|
||||||
|
connRef.value.acceptParams({
|
||||||
|
port: modelInfo.port,
|
||||||
|
containerName: modelInfo.container,
|
||||||
|
appinstallID: appInstallID.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoad = async (name: string) => {
|
||||||
|
const res = await loadOllamaModel(name);
|
||||||
|
let detailInfo = res.data;
|
||||||
|
let param = {
|
||||||
|
header: i18n.global.t('commons.button.view'),
|
||||||
|
detailInfo: detailInfo,
|
||||||
|
};
|
||||||
|
detailRef.value!.acceptParams(param);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goDashboard = async () => {
|
||||||
|
if (openWebUIPort.value === 0) {
|
||||||
|
dashboardVisible.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dialogPortJumpRef.value.acceptParams({ port: openWebUIPort.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const bindDomain = () => {
|
||||||
|
bindDomainRef.value.acceptParams(appInstallID.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goInstall = (name: string) => {
|
||||||
|
router.push({ name: 'AppAll', query: { install: name } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadWebUIPort = async () => {
|
||||||
|
const res = await getAppPort('ollama-webui', '');
|
||||||
|
openWebUIPort.value = res.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkExist = (data: App.CheckInstalled) => {
|
||||||
|
modelInfo.isExist = data.isExist;
|
||||||
|
modelInfo.status = data.status;
|
||||||
|
modelInfo.version = data.version;
|
||||||
|
modelInfo.container = data.containerName;
|
||||||
|
modelInfo.port = data.httpPort;
|
||||||
|
|
||||||
|
if (modelInfo.isExist && modelInfo.status === 'Running') {
|
||||||
|
search();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmitDelete = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await deleteOllamaModel(operateIDs.value, forceDelete.value)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReCreate = async (name: string) => {
|
||||||
|
loading.value = true;
|
||||||
|
await recreateOllamaModel(name)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = async (row: AI.OllamaModelInfo) => {
|
||||||
|
let names = [];
|
||||||
|
let ids = [];
|
||||||
|
if (row) {
|
||||||
|
ids = [row.id];
|
||||||
|
names = [row.name];
|
||||||
|
} else {
|
||||||
|
for (const item of selects.value) {
|
||||||
|
names.push(item.name);
|
||||||
|
ids.push(item.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operateIDs.value = ids;
|
||||||
|
opRef.value.acceptParams({
|
||||||
|
title: i18n.global.t('commons.button.delete'),
|
||||||
|
names: names,
|
||||||
|
msg: i18n.global.t('commons.msg.operatorHelper', [
|
||||||
|
i18n.global.t('cronjob.cronTask'),
|
||||||
|
i18n.global.t('commons.button.delete'),
|
||||||
|
]),
|
||||||
|
api: null,
|
||||||
|
params: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoadLog = (row: any) => {
|
||||||
|
if (row.from === 'remote') {
|
||||||
|
MsgInfo(i18n.global.t('aiTools.model.from_remote'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!row.logFileExist) {
|
||||||
|
MsgInfo(i18n.global.t('aiTools.model.no_logs'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logRef.value.acceptParams({ id: 0, type: 'ollama-model', name: row.name, tail: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.run'),
|
||||||
|
click: (row: AI.OllamaModelInfo) => {
|
||||||
|
terminalRef.value.acceptParams({ name: row.name });
|
||||||
|
},
|
||||||
|
disabled: (row: any) => {
|
||||||
|
return row.status !== 'Success';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.retry'),
|
||||||
|
click: (row: AI.OllamaModelInfo) => {
|
||||||
|
onReCreate(row.name);
|
||||||
|
},
|
||||||
|
disabled: (row: any) => {
|
||||||
|
return row.status === 'Success' || row.status === 'Waiting';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: (row: AI.OllamaModelInfo) => {
|
||||||
|
onDelete(row);
|
||||||
|
},
|
||||||
|
disabled: (row: any) => {
|
||||||
|
return row.status === 'Waiting';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadWebUIPort();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.iconInTable {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
.jumpAdd {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.tagClass {
|
||||||
|
float: right;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
76
frontend/src/views/ai/model/terminal/index.vue
Normal file
76
frontend/src/views/ai/model/terminal/index.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<DrawerPro
|
||||||
|
v-model="open"
|
||||||
|
:title="$t('menu.terminal')"
|
||||||
|
:back="handleClose"
|
||||||
|
:resource="title"
|
||||||
|
:size="globalStore.isFullScreen ? 'full' : 'large'"
|
||||||
|
>
|
||||||
|
<el-alert type="error" :closable="false">
|
||||||
|
<template #title>
|
||||||
|
<span>{{ $t('commons.msg.disConn', ['/bye exit']) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
<Terminal class="mt-2" style="height: calc(100vh - 225px)" ref="terminalRef"></Terminal>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="handleClose">
|
||||||
|
{{ $t('commons.button.disConn') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</DrawerPro>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
import Terminal from '@/components/terminal/index.vue';
|
||||||
|
import { closeOllamaModel } from '@/api/modules/ai';
|
||||||
|
import { GlobalStore } from '@/store';
|
||||||
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
|
const title = ref();
|
||||||
|
const open = ref(false);
|
||||||
|
const itemName = ref();
|
||||||
|
const terminalRef = ref();
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||||
|
itemName.value = params.name;
|
||||||
|
open.value = true;
|
||||||
|
initTerm();
|
||||||
|
};
|
||||||
|
|
||||||
|
const initTerm = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
terminalRef.value.acceptParams({
|
||||||
|
endpoint: '/api/v1/ai/ollama/exec',
|
||||||
|
args: `name=${itemName.value}`,
|
||||||
|
error: '',
|
||||||
|
initCmd: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = async () => {
|
||||||
|
await closeOllamaModel(itemName.value)
|
||||||
|
.then(() => {
|
||||||
|
terminalRef.value?.onClose();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
terminalRef.value?.onClose();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
onClose();
|
||||||
|
open.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
x
Reference in New Issue
Block a user