mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-02-28 19:14:13 +08:00
feat: Support for terminal operation with the Ollama model (#7907)
This commit is contained in:
parent
c3685f6b74
commit
c329fb6599
@ -18,7 +18,7 @@ import (
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加模型 [name]","formatEN":"add Ollama model [name]"}
|
||||
// @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 {
|
||||
@ -40,7 +40,7 @@ func (b *BaseApi) CreateOllamaModel(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/recreate [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加模型重试 [name]","formatEN":"re-add Ollama model [name]"}
|
||||
// @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 {
|
||||
@ -54,6 +54,28 @@ func (b *BaseApi) RecreateOllamaModel(c *gin.Context) {
|
||||
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.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Sync Ollama model list
|
||||
// @Success 200 {array} dto.OllamaModelDropList
|
||||
@ -127,7 +149,7 @@ func (b *BaseApi) LoadOllamaModelDetail(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"ollama_models","output_column":"name","output_value":"name"}],"formatZH":"删除 ollama 模型 [name]","formatEN":"remove ollama model [name]"}
|
||||
// @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 {
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
@ -146,6 +147,70 @@ func (b *BaseApi) RedisWsSsh(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.System.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 (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
|
@ -28,6 +28,7 @@ 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)
|
||||
@ -69,7 +70,7 @@ func (u *AIToolService) LoadDetail(name string) (string, error) {
|
||||
if cmd.CheckIllegal(name) {
|
||||
return "", buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
containerName, err := loadContainerName()
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -88,7 +89,7 @@ func (u *AIToolService) Create(name string) error {
|
||||
if modelInfo.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
containerName, err := loadContainerName()
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -114,6 +115,21 @@ func (u *AIToolService) Create(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) Close(name string) error {
|
||||
if cmd.CheckIllegal(name) {
|
||||
return buserr.New(constant.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(constant.ErrCmdIllegal)
|
||||
@ -122,7 +138,7 @@ func (u *AIToolService) Recreate(name string) error {
|
||||
if modelInfo.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
containerName, err := loadContainerName()
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -148,7 +164,7 @@ func (u *AIToolService) Delete(req dto.ForceDelete) error {
|
||||
if len(ollamaList) == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
containerName, err := loadContainerName()
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
@ -167,7 +183,7 @@ func (u *AIToolService) Delete(req dto.ForceDelete) error {
|
||||
}
|
||||
|
||||
func (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {
|
||||
containerName, err := loadContainerName()
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -335,7 +351,7 @@ func (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadContainerName() (string, error) {
|
||||
func LoadContainerName() (string, error) {
|
||||
ollamaBaseInfo, err := appInstallRepo.LoadBaseInfo("ollama", "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ollama service is not found, err: %v", err)
|
||||
|
@ -15,6 +15,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
|
||||
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)
|
||||
|
@ -156,7 +156,51 @@ const docTemplate = `{
|
||||
"name"
|
||||
],
|
||||
"formatEN": "add Ollama model [name]",
|
||||
"formatZH": "添加模型 [name]",
|
||||
"formatZH": "添加 Ollama 模型 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai/ollama/model/close": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AI"
|
||||
],
|
||||
"summary": "Close Ollama model conn",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.OllamaModelName"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"name"
|
||||
],
|
||||
"formatEN": "close conn for Ollama model [name]",
|
||||
"formatZH": "关闭 Ollama 模型连接 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@ -199,17 +243,17 @@ const docTemplate = `{
|
||||
{
|
||||
"db": "ollama_models",
|
||||
"input_column": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"input_value": "ids",
|
||||
"isList": true,
|
||||
"output_column": "name",
|
||||
"output_value": "name"
|
||||
"output_value": "names"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
"ids"
|
||||
],
|
||||
"formatEN": "remove ollama model [name]",
|
||||
"formatZH": "删除 ollama 模型 [name]",
|
||||
"formatEN": "remove Ollama model [names]",
|
||||
"formatZH": "删除 Ollama 模型 [names]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@ -291,7 +335,7 @@ const docTemplate = `{
|
||||
"name"
|
||||
],
|
||||
"formatEN": "re-add Ollama model [name]",
|
||||
"formatZH": "添加模型重试 [name]",
|
||||
"formatZH": "添加 Ollama 模型重试 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@ -20633,18 +20677,15 @@ const docTemplate = `{
|
||||
"domain"
|
||||
],
|
||||
"properties": {
|
||||
"allowIPs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"appInstallID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipList": {
|
||||
"type": "string"
|
||||
},
|
||||
"sslID": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -20673,6 +20714,9 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"connUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -153,7 +153,51 @@
|
||||
"name"
|
||||
],
|
||||
"formatEN": "add Ollama model [name]",
|
||||
"formatZH": "添加模型 [name]",
|
||||
"formatZH": "添加 Ollama 模型 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai/ollama/model/close": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AI"
|
||||
],
|
||||
"summary": "Close Ollama model conn",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.OllamaModelName"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"name"
|
||||
],
|
||||
"formatEN": "close conn for Ollama model [name]",
|
||||
"formatZH": "关闭 Ollama 模型连接 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@ -196,17 +240,17 @@
|
||||
{
|
||||
"db": "ollama_models",
|
||||
"input_column": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"input_value": "ids",
|
||||
"isList": true,
|
||||
"output_column": "name",
|
||||
"output_value": "name"
|
||||
"output_value": "names"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
"ids"
|
||||
],
|
||||
"formatEN": "remove ollama model [name]",
|
||||
"formatZH": "删除 ollama 模型 [name]",
|
||||
"formatEN": "remove Ollama model [names]",
|
||||
"formatZH": "删除 Ollama 模型 [names]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@ -288,7 +332,7 @@
|
||||
"name"
|
||||
],
|
||||
"formatEN": "re-add Ollama model [name]",
|
||||
"formatZH": "添加模型重试 [name]",
|
||||
"formatZH": "添加 Ollama 模型重试 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@ -20630,18 +20674,15 @@
|
||||
"domain"
|
||||
],
|
||||
"properties": {
|
||||
"allowIPs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"appInstallID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipList": {
|
||||
"type": "string"
|
||||
},
|
||||
"sslID": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -20670,6 +20711,9 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"connUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -2457,14 +2457,12 @@ definitions:
|
||||
- ProxyCache
|
||||
dto.OllamaBindDomain:
|
||||
properties:
|
||||
allowIPs:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
appInstallID:
|
||||
type: integer
|
||||
domain:
|
||||
type: string
|
||||
ipList:
|
||||
type: string
|
||||
sslID:
|
||||
type: integer
|
||||
websiteID:
|
||||
@ -2486,6 +2484,8 @@ definitions:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
connUrl:
|
||||
type: string
|
||||
domain:
|
||||
type: string
|
||||
sslID:
|
||||
@ -6583,7 +6583,34 @@ paths:
|
||||
bodyKeys:
|
||||
- name
|
||||
formatEN: add Ollama model [name]
|
||||
formatZH: 添加模型 [name]
|
||||
formatZH: 添加 Ollama 模型 [name]
|
||||
paramKeys: []
|
||||
/ai/ollama/model/close:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.OllamaModelName'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
- Timestamp: []
|
||||
summary: Close Ollama model conn
|
||||
tags:
|
||||
- AI
|
||||
x-panel-log:
|
||||
BeforeFunctions: []
|
||||
bodyKeys:
|
||||
- name
|
||||
formatEN: close conn for Ollama model [name]
|
||||
formatZH: 关闭 Ollama 模型连接 [name]
|
||||
paramKeys: []
|
||||
/ai/ollama/model/del:
|
||||
post:
|
||||
@ -6609,14 +6636,14 @@ paths:
|
||||
BeforeFunctions:
|
||||
- db: ollama_models
|
||||
input_column: id
|
||||
input_value: id
|
||||
isList: false
|
||||
input_value: ids
|
||||
isList: true
|
||||
output_column: name
|
||||
output_value: name
|
||||
output_value: names
|
||||
bodyKeys:
|
||||
- id
|
||||
formatEN: remove ollama model [name]
|
||||
formatZH: 删除 ollama 模型 [name]
|
||||
- ids
|
||||
formatEN: remove Ollama model [names]
|
||||
formatZH: 删除 Ollama 模型 [names]
|
||||
paramKeys: []
|
||||
/ai/ollama/model/load:
|
||||
post:
|
||||
@ -6665,7 +6692,7 @@ paths:
|
||||
bodyKeys:
|
||||
- name
|
||||
formatEN: re-add Ollama model [name]
|
||||
formatZH: 添加模型重试 [name]
|
||||
formatZH: 添加 Ollama 模型重试 [name]
|
||||
paramKeys: []
|
||||
/ai/ollama/model/search:
|
||||
post:
|
||||
|
@ -20,6 +20,9 @@ export const loadOllamaModel = (name: string) => {
|
||||
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`);
|
||||
|
@ -15,6 +15,7 @@ const message = {
|
||||
fit2cloud: 'FIT2CLOUD',
|
||||
lingxia: 'Lingxia',
|
||||
button: {
|
||||
run: 'Run',
|
||||
create: 'Create ',
|
||||
add: 'Add ',
|
||||
save: 'Save ',
|
||||
@ -122,6 +123,8 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'No data available',
|
||||
disConn:
|
||||
'If you want to disconnect the terminal, please click the disconnect button directly, do not enter exit commands like {0}.',
|
||||
delete: `This operation delete can't be undone. Do you want to continue?`,
|
||||
clean: `This operation clean can't be undone. Do you want to continue?`,
|
||||
deleteTitle: 'Delete',
|
||||
@ -1229,6 +1232,7 @@ const message = {
|
||||
resource: 'Resource',
|
||||
operate: 'Operate',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: 'Group',
|
||||
hosts: 'Host',
|
||||
apps: 'App',
|
||||
|
@ -14,6 +14,7 @@ const message = {
|
||||
fit2cloud: 'FIT2CLOUD',
|
||||
lingxia: 'Lingxia',
|
||||
button: {
|
||||
run: '実行',
|
||||
create: '作成する',
|
||||
add: '追加',
|
||||
save: '保存',
|
||||
@ -121,6 +122,8 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: '利用可能なデータはありません',
|
||||
disConn:
|
||||
'端末を切断する場合は、切断ボタンを直接クリックしてください。{0} のような終了コマンドを入力しないでください。',
|
||||
delete: `この操作削除は元に戻すことはできません。続けたいですか?`,
|
||||
clean: `この操作は取り消すことはできません。続けたいですか?`,
|
||||
deleteTitle: '消去',
|
||||
@ -1209,6 +1212,7 @@ const message = {
|
||||
resource: 'リソース',
|
||||
operate: '動作します',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: 'グループ',
|
||||
hosts: 'ホスト',
|
||||
apps: 'アプリ',
|
||||
|
@ -15,6 +15,7 @@ const message = {
|
||||
fit2cloud: 'FIT2CLOUD',
|
||||
lingxia: 'Lingxia',
|
||||
button: {
|
||||
run: '실행',
|
||||
create: '생성',
|
||||
add: '추가',
|
||||
save: '저장',
|
||||
@ -122,6 +123,7 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: '데이터가 없습니다',
|
||||
disConn: '터미널을 끊으려면 직접 끊기 버튼을 클릭하세요. {0} 과 같은 종료 명령을 입력하지 마세요.',
|
||||
delete: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
||||
clean: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
||||
deleteTitle: '삭제',
|
||||
@ -1196,6 +1198,7 @@ const message = {
|
||||
resource: '자원',
|
||||
operate: '작업',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: '그룹',
|
||||
hosts: '호스트',
|
||||
apps: '애플리케이션',
|
||||
|
@ -15,6 +15,7 @@ const message = {
|
||||
fit2cloud: 'FIT2CLOUD',
|
||||
lingxia: 'Lingxia',
|
||||
button: {
|
||||
run: 'Jalankan',
|
||||
create: 'Cipta',
|
||||
add: 'Tambah',
|
||||
save: 'Simpan',
|
||||
@ -122,6 +123,8 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'Tiada data tersedia',
|
||||
disConn:
|
||||
'Jika anda ingin memutuskan sambungan terminal, sila klik butang putus sambungan secara langsung, jangan masukkan arahan keluar seperti {0}.',
|
||||
delete: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
||||
clean: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
||||
deleteTitle: 'Padam',
|
||||
@ -1251,6 +1254,7 @@ const message = {
|
||||
resource: 'Sumber',
|
||||
operate: 'Operasi',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: 'Kumpulan',
|
||||
hosts: 'Hos',
|
||||
apps: 'Aplikasi',
|
||||
|
@ -15,6 +15,7 @@ const message = {
|
||||
fit2cloud: 'FIT2CLOUD',
|
||||
lingxia: 'Lingxia',
|
||||
button: {
|
||||
run: 'Executar',
|
||||
create: 'Criar',
|
||||
add: 'Adicionar',
|
||||
save: 'Salvar',
|
||||
@ -122,6 +123,8 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'Nenhum dado disponível',
|
||||
disConn:
|
||||
'Se você deseja desconectar o terminal, clique diretamente no botão de desconexão, não insira comandos de saída como {0}.',
|
||||
delete: 'Esta operação de exclusão não pode ser desfeita. Deseja continuar?',
|
||||
clean: 'Esta operação de limpeza não pode ser desfeita. Deseja continuar?',
|
||||
deleteTitle: 'Excluir',
|
||||
@ -1236,6 +1239,7 @@ const message = {
|
||||
resource: 'Recurso',
|
||||
operate: 'Operar',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: 'Grupo',
|
||||
hosts: 'Host',
|
||||
apps: 'Aplicativo',
|
||||
|
@ -15,6 +15,7 @@ const message = {
|
||||
fit2cloud: 'FIT2CLOUD',
|
||||
lingxia: 'Lingxia',
|
||||
button: {
|
||||
run: 'Запуск',
|
||||
create: 'Создать ',
|
||||
add: 'Добавить ',
|
||||
save: 'Сохранить ',
|
||||
@ -123,6 +124,8 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'Нет данных',
|
||||
disConn:
|
||||
'Если вы хотите отключить терминал, пожалуйста, нажмите кнопку отключения напрямую, не вводите команды выхода, такие как {0}.',
|
||||
delete: 'Эта операция удаления не может быть отменена. Хотите продолжить?',
|
||||
clean: 'Эта операция очистки не может быть отменена. Хотите продолжить?',
|
||||
deleteTitle: 'Удалить',
|
||||
@ -1242,6 +1245,7 @@ const message = {
|
||||
resource: 'Ресурс',
|
||||
operate: 'Операция',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: 'Группа',
|
||||
hosts: 'Хост',
|
||||
apps: 'Приложение',
|
||||
|
@ -14,6 +14,7 @@ const message = {
|
||||
fit2cloud: '飛致雲',
|
||||
lingxia: '凌霞',
|
||||
button: {
|
||||
run: '運行',
|
||||
create: '建立',
|
||||
add: '新增',
|
||||
save: '儲存',
|
||||
@ -122,6 +123,7 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: '暫無資料',
|
||||
disConn: '如果要斷開終端,請直接點擊斷開按鈕,不要輸入 {0} 等退出命令。',
|
||||
delete: '刪除 操作不可復原,是否繼續?',
|
||||
clean: '清空 操作不可復原,是否繼續?',
|
||||
deleteTitle: '刪除',
|
||||
@ -1169,6 +1171,7 @@ const message = {
|
||||
resource: '資源',
|
||||
operate: '操作',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: '分組',
|
||||
hosts: '主機',
|
||||
apps: '應用',
|
||||
|
@ -14,6 +14,7 @@ const message = {
|
||||
fit2cloud: '飞致云',
|
||||
lingxia: '凌霞',
|
||||
button: {
|
||||
run: '运行',
|
||||
create: '创建',
|
||||
add: '添加',
|
||||
save: '保存',
|
||||
@ -120,6 +121,7 @@ const message = {
|
||||
Rollbacking: '快照回滚中,请稍候...',
|
||||
},
|
||||
msg: {
|
||||
disConn: '如果要断开终端请直接点击断开按钮,不要输入 {0} 等退出命令',
|
||||
noneData: '暂无数据',
|
||||
delete: '删除 操作不可回滚,是否继续?',
|
||||
clean: '清空 操作不可回滚,是否继续?',
|
||||
@ -1171,6 +1173,7 @@ const message = {
|
||||
resource: '资源',
|
||||
operate: '操作',
|
||||
detail: {
|
||||
ai: 'AI',
|
||||
groups: '分组',
|
||||
hosts: '主机',
|
||||
apps: '应用',
|
||||
|
@ -172,6 +172,7 @@
|
||||
<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" />
|
||||
@ -183,6 +184,7 @@
|
||||
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';
|
||||
@ -214,6 +216,7 @@ 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();
|
||||
@ -403,6 +406,15 @@ const onLoadLog = (row: any) => {
|
||||
};
|
||||
|
||||
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) => {
|
||||
|
101
frontend/src/views/ai/model/terminal/index.vue
Normal file
101
frontend/src/views/ai/model/terminal/index.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="terminalVisible"
|
||||
@close="handleClose"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:size="globalStore.isFullScreen ? '100%' : '50%'"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.containerTerminal')" :resource="title" :back="handleClose">
|
||||
<template #extra v-if="!mobile">
|
||||
<el-tooltip :content="loadTooltip()" placement="top">
|
||||
<el-button @click="toggleFullscreen" class="fullScreen" icon="FullScreen" plain></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</DrawerHeader>
|
||||
</template>
|
||||
<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.disconnect') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
import Terminal from '@/components/terminal/index.vue';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { closeOllamaModel } from '@/api/modules/ai';
|
||||
import { GlobalStore } from '@/store';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
const mobile = computed(() => {
|
||||
return globalStore.isMobile();
|
||||
});
|
||||
|
||||
const title = ref();
|
||||
const terminalVisible = ref(false);
|
||||
const itemName = ref();
|
||||
const terminalRef = ref();
|
||||
|
||||
interface DialogProps {
|
||||
name: string;
|
||||
}
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
itemName.value = params.name;
|
||||
terminalVisible.value = true;
|
||||
initTerm();
|
||||
};
|
||||
|
||||
const loadTooltip = () => {
|
||||
return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));
|
||||
};
|
||||
|
||||
const initTerm = () => {
|
||||
nextTick(() => {
|
||||
terminalRef.value.acceptParams({
|
||||
endpoint: '/api/v1/ai/ollama/exec',
|
||||
args: `name=${itemName.value}`,
|
||||
error: '',
|
||||
initCmd: '',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function toggleFullscreen() {
|
||||
globalStore.isFullScreen = !globalStore.isFullScreen;
|
||||
}
|
||||
|
||||
const onClose = async () => {
|
||||
await closeOllamaModel(itemName.value)
|
||||
.then(() => {
|
||||
terminalRef.value?.onClose();
|
||||
})
|
||||
.catch(() => {
|
||||
terminalRef.value?.onClose();
|
||||
});
|
||||
};
|
||||
|
||||
function handleClose() {
|
||||
onClose();
|
||||
globalStore.isFullScreen = false;
|
||||
terminalVisible.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
@ -10,7 +10,12 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.containerTerminal')" :resource="title" :back="handleClose" />
|
||||
</template>
|
||||
<el-form ref="formRef" :model="form" label-position="top">
|
||||
<el-alert type="error" :closable="false">
|
||||
<template #title>
|
||||
<span>{{ $t('commons.msg.disConn', ['exit']) }}</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-form ref="formRef" class="mt-2" :model="form" label-position="top">
|
||||
<el-form-item :label="$t('commons.table.user')" prop="user">
|
||||
<el-input placeholder="root" clearable v-model="form.user" />
|
||||
</el-form-item>
|
||||
@ -46,7 +51,7 @@
|
||||
</el-button>
|
||||
<el-button v-else @click="onClose()">{{ $t('commons.button.disconnect') }}</el-button>
|
||||
<Terminal
|
||||
style="height: calc(100vh - 302px); margin-top: 18px"
|
||||
style="height: calc(100vh - 355px); margin-top: 18px"
|
||||
ref="terminalRef"
|
||||
v-if="terminalOpen"
|
||||
></Terminal>
|
||||
|
Loading…
x
Reference in New Issue
Block a user