diff --git a/backend/app/api/v1/ai.go b/backend/app/api/v1/ai.go index a4a1e4b72..2c7c6171a 100644 --- a/backend/app/api/v1/ai.go +++ b/backend/app/api/v1/ai.go @@ -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 { diff --git a/backend/app/api/v1/terminal.go b/backend/app/api/v1/terminal.go index f79b66a3c..1aaefc335 100644 --- a/backend/app/api/v1/terminal.go +++ b/backend/app/api/v1/terminal.go @@ -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 { diff --git a/backend/app/service/ai.go b/backend/app/service/ai.go index 68982725f..89be57ad6 100644 --- a/backend/app/service/ai.go +++ b/backend/app/service/ai.go @@ -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) diff --git a/backend/router/ro_ai.go b/backend/router/ro_ai.go index 7f1bf3eca..45f574a71 100644 --- a/backend/router/ro_ai.go +++ b/backend/router/ro_ai.go @@ -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) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 85e1663c6..c88f15d6e 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -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" }, diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 3430b0e73..5c8eb1311 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -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" }, diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 6a2f6880f..554bb6d9c 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -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: diff --git a/frontend/src/api/modules/ai.ts b/frontend/src/api/modules/ai.ts index 84d0568eb..464f553cd 100644 --- a/frontend/src/api/modules/ai.ts +++ b/frontend/src/api/modules/ai.ts @@ -20,6 +20,9 @@ export const loadOllamaModel = (name: string) => { export const syncOllamaModel = () => { return http.post>(`/ai/ollama/model/sync`); }; +export const closeOllamaModel = (name: string) => { + return http.post(`/ai/ollama/close`, { name: name }); +}; export const loadGPUInfo = () => { return http.get(`/ai/gpu/load`); diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index f38bb1880..1ac151419 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -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', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index cad41dc77..518b4dcd7 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -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: 'アプリ', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 13d30753e..5ff1d2b53 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -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: '애플리케이션', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 478494aaf..0abf039d8 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -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', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index cb34ce795..e38e6b8c8 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -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', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 61b8b9004..dbb91a23a 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -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: 'Приложение', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 380c79bcb..6474d95c7 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -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: '應用', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 75fad15af..4bcf01a83 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -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: '应用', diff --git a/frontend/src/views/ai/model/index.vue b/frontend/src/views/ai/model/index.vue index b84cfe5bd..e95d897db 100644 --- a/frontend/src/views/ai/model/index.vue +++ b/frontend/src/views/ai/model/index.vue @@ -172,6 +172,7 @@ + @@ -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) => { diff --git a/frontend/src/views/ai/model/terminal/index.vue b/frontend/src/views/ai/model/terminal/index.vue new file mode 100644 index 000000000..7d28c8b64 --- /dev/null +++ b/frontend/src/views/ai/model/terminal/index.vue @@ -0,0 +1,101 @@ + + + diff --git a/frontend/src/views/container/container/terminal/index.vue b/frontend/src/views/container/container/terminal/index.vue index c831c1565..9ce81f77e 100644 --- a/frontend/src/views/container/container/terminal/index.vue +++ b/frontend/src/views/container/container/terminal/index.vue @@ -10,7 +10,12 @@ - + + + + @@ -46,7 +51,7 @@ {{ $t('commons.button.disconnect') }}