From 96fceb56cd290a878c97606d95a4fc3dd2bf5b25 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Fri, 3 Apr 2026 15:02:17 +0800 Subject: [PATCH] feat: improve terminal ai runtime updates --- agent/app/service/agents.go | 6 ++++++ agent/i18n/lang/en.yaml | 1 + agent/i18n/lang/es-ES.yaml | 1 + agent/i18n/lang/ja.yaml | 1 + agent/i18n/lang/ko.yaml | 1 + agent/i18n/lang/ms.yaml | 1 + agent/i18n/lang/pt-BR.yaml | 1 + agent/i18n/lang/ru.yaml | 1 + agent/i18n/lang/tr.yaml | 1 + agent/i18n/lang/zh-Hant.yaml | 1 + agent/i18n/lang/zh.yaml | 1 + agent/utils/terminal/ws_local_session.go | 16 ++++++++++++++++ agent/utils/terminal/ws_session.go | 16 ++++++++++++++++ 13 files changed, 48 insertions(+) diff --git a/agent/app/service/agents.go b/agent/app/service/agents.go index ab5abd110ec8..aeb490fcc051 100644 --- a/agent/app/service/agents.go +++ b/agent/app/service/agents.go @@ -7,6 +7,7 @@ import ( "os" "path" "sort" + "strconv" "strings" "time" @@ -680,6 +681,11 @@ func (a AgentService) DeleteAccount(req dto.AgentAccountDeleteReq) error { if exists, _ := agentRepo.GetFirst(repo.WithByAccountID(req.ID)); exists != nil && exists.ID > 0 { return buserr.New("ErrAgentAccountBound") } + if aiStatus, _ := settingRepo.GetValueByKey("AIStatus"); strings.EqualFold(strings.TrimSpace(aiStatus), constant.StatusEnable) { + if aiAccountID, _ := settingRepo.GetValueByKey("AIAccountID"); strings.TrimSpace(aiAccountID) == strconv.FormatUint(uint64(req.ID), 10) { + return buserr.New("ErrTerminalAIAccountInUse") + } + } if err := agentAccountModelRepo.Delete(repo.WithByAccountID(req.ID)); err != nil { return err } diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 042183f144ed..091b26275093 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -42,6 +42,7 @@ Decrypt: "Decrypt" #agent ErrAgentAccountBound: 'Account is bound to an agent and cannot be deleted' +ErrTerminalAIAccountInUse: 'This model account is currently used by Terminal AI. Switch the account in Terminal AI settings or disable Terminal AI and try again.' ErrAgentAccountUnavailable: 'Account connection unavailable: {{ .err }}' ErrAgentProviderNotSupported: 'Unsupported agent provider' ErrAgentAccountRequired: 'Select an agent account first' diff --git a/agent/i18n/lang/es-ES.yaml b/agent/i18n/lang/es-ES.yaml index 5d57c1f73eb3..eb841f611035 100644 --- a/agent/i18n/lang/es-ES.yaml +++ b/agent/i18n/lang/es-ES.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: 'Grupo predeterminado, no se puede eliminar' ErrGroupIsInWebsiteUse: 'El grupo está siendo usado por otro sitio web y no se puede eliminar.' Decrypt: 'Descifrar' ErrAgentAccountBound: 'Cuenta vinculada a agente' +ErrTerminalAIAccountInUse: 'Esta cuenta de modelo esta siendo usada por Terminal AI. Cambia la cuenta en la configuracion de Terminal AI o desactiva Terminal AI y vuelve a intentarlo.' ErrAgentAccountUnavailable: 'Conexión de cuenta no disponible: {{ .err }}' ErrAgentProviderNotSupported: 'Proveedor de agente no soportado' ErrAgentAccountRequired: 'Elige una cuenta de agente primero' diff --git a/agent/i18n/lang/ja.yaml b/agent/i18n/lang/ja.yaml index 5160b71a8df6..bd9c18fd7026 100644 --- a/agent/i18n/lang/ja.yaml +++ b/agent/i18n/lang/ja.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: 'デフォルト グループ、削除できません' ErrGroupIsInWebsiteUse: 'グループは別の Web サイトで使用されているため、削除できません。' Decrypt: '復号化' ErrAgentAccountBound: 'アカウントはエージェントに紐づいています' +ErrTerminalAIAccountInUse: 'このモデルアカウントは Terminal AI で使用中です。Terminal AI の設定で別のアカウントへ切り替えるか、Terminal AI を無効にしてから再試行してください。' ErrAgentAccountUnavailable: 'アカウント接続不可: {{ .err }}' ErrAgentProviderNotSupported: 'エージェントプロバイダ非対応' ErrAgentAccountRequired: 'まずエージェントアカウントを選択' diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index 7233ca49b3a9..8bb439b74ed0 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: '기본 그룹, 삭제할 수 없습니다' ErrGroupIsInWebsiteUse: '그룹이 다른 웹사이트에서 사용 중이므로 삭제할 수 없습니다.' Decrypt: '복호화' ErrAgentAccountBound: '계정이 에이전트에 묶여 있습니다' +ErrTerminalAIAccountInUse: '이 모델 계정은 현재 터미널 AI에서 사용 중입니다. 터미널 AI 설정에서 다른 계정으로 변경하거나 터미널 AI를 비활성화한 뒤 다시 시도하세요.' ErrAgentAccountUnavailable: '계정 연결 불가: {{ .err }}' ErrAgentProviderNotSupported: '지원되지 않는 에이전트 공급자' ErrAgentAccountRequired: '먼저 에이전트 계정을 선택하세요' diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index 8533811e2485..f3ed47a8642c 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: 'Kumpulan lalai, tidak boleh dipadamkan' ErrGroupIsInWebsiteUse: 'Kumpulan sedang digunakan oleh tapak web lain dan tidak boleh dipadamkan.' Decrypt: 'Dekripsi' ErrAgentAccountBound: 'Akaun terikat kepada ejen' +ErrTerminalAIAccountInUse: 'Akaun model ini sedang digunakan oleh Terminal AI. Tukar akaun dalam tetapan Terminal AI atau nyahaktifkan Terminal AI, kemudian cuba lagi.' ErrAgentAccountUnavailable: 'Sambungan akaun tidak tersedia: {{ .err }}' ErrAgentProviderNotSupported: 'Penyedia ejen tidak disokong' ErrAgentAccountRequired: 'Pilih akaun ejen terlebih dahulu' diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index c66928cc0743..5f5f3ff05f5a 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: 'Grupo padrão, não pode ser excluído' ErrGroupIsInWebsiteUse: 'O grupo está sendo usado por outro site e não pode ser excluído.' Decrypt: 'Descriptografar' ErrAgentAccountBound: 'Conta vinculada a agente' +ErrTerminalAIAccountInUse: 'Esta conta de modelo esta sendo usada pelo Terminal AI. Troque a conta nas configuracoes do Terminal AI ou desative o Terminal AI e tente novamente.' ErrAgentAccountUnavailable: 'Conexão da conta indisponível: {{ .err }}' ErrAgentProviderNotSupported: 'Provedor de agente não suportado' ErrAgentAccountRequired: 'Selecione uma conta de agente primeiro' diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index b8551b33907b..bdfb416e440d 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: 'Группа по умолчанию, не может быт ErrGroupIsInWebsiteUse: 'Группа используется другим веб-сайтом и не может быть удалена.' Decrypt: 'Расшифровать' ErrAgentAccountBound: 'Акаунт привязан к агенту' +ErrTerminalAIAccountInUse: 'Эта учетная запись модели сейчас используется Terminal AI. Смените учетную запись в настройках Terminal AI или отключите Terminal AI и повторите попытку.' ErrAgentAccountUnavailable: 'Связь с аккаунтом недоступна: {{ .err }}' ErrAgentProviderNotSupported: 'Провайдер агента не поддерживается' ErrAgentAccountRequired: 'Выберите аккаунт агента' diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index 3321e413ebe8..f987a21bbef0 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: 'Varsayılan grup, silinemez' ErrGroupIsInWebsiteUse: 'Grup başka bir web sitesi tarafından kullanılıyor ve silinemez.' Decrypt: 'Şifre Çöz' ErrAgentAccountBound: 'Hesap bir ajana bağlı' +ErrTerminalAIAccountInUse: 'Bu model hesabi su anda Terminal AI tarafindan kullaniliyor. Terminal AI ayarlarinda baska bir hesaba gecin veya Terminal AI yi devre disi birakip tekrar deneyin.' ErrAgentAccountUnavailable: 'Hesap bağlantısı yok: {{ .err }}' ErrAgentProviderNotSupported: 'Ajans sağlayıcısı desteklenmiyor' ErrAgentAccountRequired: 'Önce bir ajans hesabı seçin' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index b879c8c0db09..7b724ed56a03 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -37,6 +37,7 @@ ErrGroupIsDefault: '預設分組,無法刪除' ErrGroupIsInWebsiteUse: '分組正在被其他網站使用,無法刪除' Decrypt: '解密' ErrAgentAccountBound: '該帳號已綁定到智能體,無法刪除,請重試。' +ErrTerminalAIAccountInUse: '該模型帳號正被終端 AI 使用,請在終端 AI 設定中更換帳號或關閉終端 AI 後再試。' ErrAgentAccountUnavailable: '帳號連線資訊不可用,錯誤:{{ .err }},請重試' ErrAgentProviderNotSupported: '暫不支援該智能體提供商,請重試' ErrAgentAccountRequired: '請選擇智能體帳號後重試' diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 07600acc813e..465d798a573a 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -42,6 +42,7 @@ Decrypt: "解密" #agent ErrAgentAccountBound: "该账号已绑定智能体,无法删除" +ErrTerminalAIAccountInUse: "该模型账号正被终端 AI 使用,请在终端 AI 设置中更换账号或关闭终端 AI 后重试" ErrAgentAccountUnavailable: "账号连接信息不可用: {{ .err }}" ErrAgentProviderNotSupported: "不支持该智能体提供商" ErrAgentAccountRequired: "请选择智能体账号后重试" diff --git a/agent/utils/terminal/ws_local_session.go b/agent/utils/terminal/ws_local_session.go index 3e40ad62bcdd..9643d245ffc3 100644 --- a/agent/utils/terminal/ws_local_session.go +++ b/agent/utils/terminal/ws_local_session.go @@ -8,6 +8,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/i18n" + terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai" "github.com/gorilla/websocket" "github.com/pkg/errors" ) @@ -20,6 +21,7 @@ type LocalWsSession struct { writeMutex sync.Mutex lang string aiInterceptor *aiInputInterceptor + aiVersion uint64 } func NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalCommand, allowCtrlC bool) (*LocalWsSession, error) { @@ -35,6 +37,7 @@ func NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalComma allowCtrlC: allowCtrlC, lang: lang, aiInterceptor: newAIInputInterceptor("", lang), + aiVersion: terminalai.CurrentTerminalRuntimeVersion(), }, nil } @@ -116,6 +119,7 @@ func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) { global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err) } if isEnterInput(decodeBytes) { + sws.ensureAIInterceptor() if sws.aiInterceptor != nil { sws.aiInterceptor.SetCurrentLine(msgObj.Line) } @@ -141,6 +145,18 @@ func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) { } } +func (sws *LocalWsSession) ensureAIInterceptor() { + if sws == nil || sws.aiInterceptor != nil { + return + } + currentVersion := terminalai.CurrentTerminalRuntimeVersion() + if sws.aiVersion == currentVersion { + return + } + sws.aiVersion = currentVersion + sws.aiInterceptor = newAIInputInterceptor("", sws.lang) +} + func (sws *LocalWsSession) notifyAIThinking() { if sws == nil { return diff --git a/agent/utils/terminal/ws_session.go b/agent/utils/terminal/ws_session.go index 357e8c37c192..2c5bd40b7a54 100644 --- a/agent/utils/terminal/ws_session.go +++ b/agent/utils/terminal/ws_session.go @@ -11,6 +11,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/i18n" + terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai" "github.com/gorilla/websocket" "golang.org/x/crypto/ssh" ) @@ -65,6 +66,7 @@ type LogicSshWsSession struct { isAdmin bool IsFlagged bool aiInterceptor *aiInputInterceptor + aiVersion uint64 } func NewLogicSshWsSession(cols, rows int, sshClient *ssh.Client, wsConn *websocket.Conn, initCmd string) (*LogicSshWsSession, error) { @@ -109,6 +111,7 @@ func NewLogicSshWsSession(cols, rows int, sshClient *ssh.Client, wsConn *websock isAdmin: true, IsFlagged: false, aiInterceptor: newAIInputInterceptor("", lang), + aiVersion: terminalai.CurrentTerminalRuntimeVersion(), }, nil } @@ -161,6 +164,7 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) { global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err) } if isEnterInput(decodeBytes) { + sws.ensureAIInterceptor() if sws.aiInterceptor != nil { sws.aiInterceptor.SetCurrentLine(msgObj.Line) } @@ -184,6 +188,18 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) { } } +func (sws *LogicSshWsSession) ensureAIInterceptor() { + if sws == nil || sws.aiInterceptor != nil { + return + } + currentVersion := terminalai.CurrentTerminalRuntimeVersion() + if sws.aiVersion == currentVersion { + return + } + sws.aiVersion = currentVersion + sws.aiInterceptor = newAIInputInterceptor("", sws.lang) +} + func (sws *LogicSshWsSession) notifyAIThinking() { if sws == nil { return