From 4e78841bf630be021e5943bc55904e0549dbecee Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:44:20 +0800 Subject: [PATCH] feat(ai): support Hermes Agent chat sessions in 1Panel (#12497) * feat(ai): support Hermes Agent chat sessions in 1Panel * feat(ai): support Hermes Agent chat sessions in 1Panel * feat(ai): change docker command --- agent/app/api/v2/agents.go | 41 ++ agent/app/dto/agents.go | 15 + agent/app/service/agents.go | 2 + agent/app/service/agents_channels.go | 5 +- agent/app/service/agents_hermes.go | 9 - agent/app/service/agents_hermes_chat.go | 115 ++++++ agent/app/service/app.go | 3 +- agent/router/ro_ai.go | 2 + frontend/src/api/interface/ai.ts | 15 + frontend/src/api/modules/ai.ts | 8 + frontend/src/components/terminal/index.vue | 4 +- frontend/src/lang/modules/en.ts | 9 + frontend/src/lang/modules/es-es.ts | 9 + frontend/src/lang/modules/ja.ts | 9 + frontend/src/lang/modules/ko.ts | 9 + frontend/src/lang/modules/ms.ts | 9 + frontend/src/lang/modules/pt-br.ts | 9 + frontend/src/lang/modules/ru.ts | 9 + frontend/src/lang/modules/tr.ts | 9 + frontend/src/lang/modules/zh-Hant.ts | 9 + frontend/src/lang/modules/zh.ts | 9 + .../src/views/ai/agents/agent/add/index.vue | 2 +- .../agents/agent/components/hermes-chat.vue | 381 ++++++++++++++++++ .../ai/agents/agent/config/tabs/channels.vue | 2 +- frontend/src/views/ai/agents/agent/index.vue | 33 +- frontend/src/views/home/index.vue | 8 +- 26 files changed, 710 insertions(+), 25 deletions(-) create mode 100644 agent/app/service/agents_hermes_chat.go create mode 100644 frontend/src/views/ai/agents/agent/components/hermes-chat.vue diff --git a/agent/app/api/v2/agents.go b/agent/app/api/v2/agents.go index 5418df1de..9488edbc9 100644 --- a/agent/app/api/v2/agents.go +++ b/agent/app/api/v2/agents.go @@ -214,6 +214,47 @@ func (b *BaseApi) GetAgentOverview(c *gin.Context) { helper.SuccessWithData(c, res) } +// @Tags AI +// @Summary Get Hermes chat sessions +// @Accept json +// @Param request body dto.AgentIDReq true "request" +// @Success 200 {array} dto.AgentHermesChatSessionItem +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/hermes/chat/sessions [post] +func (b *BaseApi) GetHermesChatSessions(c *gin.Context) { + var req dto.AgentIDReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + res, err := agentService.GetHermesChatSessions(req) + if err != nil { + helper.BadRequest(c, err) + return + } + helper.SuccessWithData(c, res) +} + +// @Tags AI +// @Summary Rename Hermes chat session +// @Accept json +// @Param request body dto.AgentHermesChatSessionRenameReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/hermes/chat/sessions/rename [post] +func (b *BaseApi) RenameHermesChatSession(c *gin.Context) { + var req dto.AgentHermesChatSessionRenameReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := agentService.RenameHermesChatSession(req); err != nil { + helper.BadRequest(c, err) + return + } + helper.Success(c) +} + // @Tags AI // @Summary Get Providers // @Success 200 {array} dto.ProviderInfo diff --git a/agent/app/dto/agents.go b/agent/app/dto/agents.go index ca7f5fdc8..b59fa3d0a 100644 --- a/agent/app/dto/agents.go +++ b/agent/app/dto/agents.go @@ -91,6 +91,15 @@ type AgentModelConfig struct { Fallbacks []string `json:"fallbacks"` } +type AgentHermesChatSessionItem struct { + ID string `json:"id"` + Title string `json:"title"` + Model string `json:"model"` + MessageCount int64 `json:"messageCount"` + StartedAt string `json:"startedAt"` + LastActive string `json:"lastActive"` +} + type AgentOverviewReq struct { AgentID uint `json:"agentId" validate:"required"` } @@ -99,6 +108,12 @@ type AgentIDReq struct { AgentID uint `json:"agentId" validate:"required"` } +type AgentHermesChatSessionRenameReq struct { + AgentID uint `json:"agentId" validate:"required"` + ID string `json:"id" validate:"required"` + Title string `json:"title" validate:"required"` +} + type AgentOverview struct { Snapshot AgentOverviewSnapshot `json:"snapshot"` } diff --git a/agent/app/service/agents.go b/agent/app/service/agents.go index cd002c701..f23652636 100644 --- a/agent/app/service/agents.go +++ b/agent/app/service/agents.go @@ -38,6 +38,8 @@ type IAgentService interface { BindWebsite(req dto.AgentWebsiteBindReq) error GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error + GetHermesChatSessions(req dto.AgentIDReq) ([]dto.AgentHermesChatSessionItem, error) + RenameHermesChatSession(req dto.AgentHermesChatSessionRenameReq) error GetOverview(req dto.AgentOverviewReq) (*dto.AgentOverview, error) GetProviders() ([]dto.ProviderInfo, error) GetSecurityConfig(req dto.AgentIDReq) (*dto.AgentSecurityConfig, error) diff --git a/agent/app/service/agents_channels.go b/agent/app/service/agents_channels.go index b968fbc90..58d562fff 100644 --- a/agent/app/service/agents_channels.go +++ b/agent/app/service/agents_channels.go @@ -505,7 +505,10 @@ func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveRe return err } if agent.AgentType == constant.AppHermesAgent { - output, err := cmd.RunDefaultWithStdoutBashC(buildHermesPairingApproveCommand(install.ContainerName, req.Type, req.PairingCode)) + output, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout( + "docker", + buildHermesDockerExecArgs(install.ContainerName, "pairing", "approve", req.Type, req.PairingCode)..., + ) if err != nil { return err } diff --git a/agent/app/service/agents_hermes.go b/agent/app/service/agents_hermes.go index c0d919581..8bf0acc30 100644 --- a/agent/app/service/agents_hermes.go +++ b/agent/app/service/agents_hermes.go @@ -555,15 +555,6 @@ func extractHermesEnvBool(envMap map[string]string, key string, defaultValue boo return strings.EqualFold(value, "true") } -func buildHermesPairingApproveCommand(containerName string, channel string, pairingCode string) string { - return fmt.Sprintf( - "docker exec -u hermes -e HOME=/opt/data/home -e HERMES_HOME=/opt/data %s /opt/hermes/.venv/bin/hermes pairing approve %s %q", - containerName, - channel, - pairingCode, - ) -} - func validateHermesPairingApproveOutput(output string) error { text := strings.TrimSpace(output) if text == "" { diff --git a/agent/app/service/agents_hermes_chat.go b/agent/app/service/agents_hermes_chat.go new file mode 100644 index 000000000..f9731c524 --- /dev/null +++ b/agent/app/service/agents_hermes_chat.go @@ -0,0 +1,115 @@ +package service + +import ( + "database/sql" + "fmt" + "math" + "path/filepath" + "strings" + "time" + + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/files" +) + +const hermesSessionListLimit = 100 + +func (a AgentService) GetHermesChatSessions(req dto.AgentIDReq) ([]dto.AgentHermesChatSessionItem, error) { + agent, install, err := a.loadAgentAndInstall(req.AgentID) + if err != nil { + return nil, err + } + if agent.AgentType != constant.AppHermesAgent { + return nil, fmt.Errorf("%s does not support", agent.AgentType) + } + return listHermesChatSessionsFromStateDB(filepath.Join(install.GetPath(), "data", "state.db")) +} + +func (a AgentService) RenameHermesChatSession(req dto.AgentHermesChatSessionRenameReq) error { + agent, install, err := a.loadAgentAndInstall(req.AgentID) + if err != nil { + return err + } + if agent.AgentType != constant.AppHermesAgent { + return fmt.Errorf("%s does not support", agent.AgentType) + } + + _, err = cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout( + "docker", + buildHermesDockerExecArgs(install.ContainerName, "sessions", "rename", req.ID, req.Title)..., + ) + return err +} + +func listHermesChatSessionsFromStateDB(stateDBPath string) ([]dto.AgentHermesChatSessionItem, error) { + if !files.NewFileOp().Stat(stateDBPath) { + return []dto.AgentHermesChatSessionItem{}, nil + } + + db, err := sql.Open("sqlite", stateDBPath) + if err != nil { + return nil, err + } + defer db.Close() + + rows, err := db.Query(` +SELECT + s.id, + COALESCE(NULLIF(TRIM(s.title), ''), s.id) AS title, + COALESCE(s.model, '') AS model, + COALESCE(s.message_count, 0) AS message_count, + s.started_at, + COALESCE(MAX(m.timestamp), s.started_at) AS last_active +FROM sessions s +LEFT JOIN messages m ON m.session_id = s.id +WHERE s.source = 'cli' +GROUP BY s.id, title, model, s.message_count, s.started_at +ORDER BY last_active DESC +LIMIT ? +`, hermesSessionListLimit) + if err != nil { + return nil, err + } + defer rows.Close() + + items := make([]dto.AgentHermesChatSessionItem, 0, 8) + for rows.Next() { + var item dto.AgentHermesChatSessionItem + var title sql.NullString + var model sql.NullString + var startedAt sql.NullFloat64 + var lastActive sql.NullFloat64 + if err := rows.Scan(&item.ID, &title, &model, &item.MessageCount, &startedAt, &lastActive); err != nil { + return nil, err + } + item.Title = strings.TrimSpace(title.String) + if item.Title == "" { + item.Title = item.ID + } + item.Model = strings.TrimSpace(model.String) + item.StartedAt = formatHermesSessionTimestamp(startedAt) + item.LastActive = formatHermesSessionTimestamp(lastActive) + items = append(items, item) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return items, nil +} + +func formatHermesSessionTimestamp(value sql.NullFloat64) string { + if !value.Valid || value.Float64 <= 0 { + return "" + } + + seconds, fraction := math.Modf(value.Float64) + return time.Unix(int64(seconds), int64(fraction*float64(time.Second))).UTC().Format(time.RFC3339) +} + +func buildHermesDockerExecArgs(containerName string, hermesArgs ...string) []string { + args := []string{"exec", "-u", "hermes", containerName, "hermes"} + return append(args, hermesArgs...) +} diff --git a/agent/app/service/app.go b/agent/app/service/app.go index cc8d77932..a97009fa4 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/gin-gonic/gin" @@ -576,7 +577,7 @@ func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript _ = appInstallRepo.Save(context.Background(), appInstall) } - installTask.AddSubTask(task.GetTaskName(appInstall.Name, task.TaskInstall, task.TaskScopeApp), installApp, handleAppStatus) + installTask.AddSubTaskWithOps(task.GetTaskName(appInstall.Name, task.TaskInstall, task.TaskScopeApp), installApp, handleAppStatus, 0, time.Hour) go func() { if taskErr := installTask.Execute(); taskErr != nil { diff --git a/agent/router/ro_ai.go b/agent/router/ro_ai.go index 1a9263fab..d6e10adeb 100644 --- a/agent/router/ro_ai.go +++ b/agent/router/ro_ai.go @@ -49,6 +49,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) { aiToolsRouter.POST("/agents/website/bind", baseApi.BindAgentWebsite) aiToolsRouter.POST("/agents/model/get", baseApi.GetAgentModelConfig) aiToolsRouter.POST("/agents/model/update", baseApi.UpdateAgentModelConfig) + aiToolsRouter.POST("/agents/hermes/chat/sessions", baseApi.GetHermesChatSessions) + aiToolsRouter.POST("/agents/hermes/chat/sessions/rename", baseApi.RenameHermesChatSession) aiToolsRouter.POST("/agents/overview", baseApi.GetAgentOverview) aiToolsRouter.GET("/agents/providers", baseApi.GetAgentProviders) aiToolsRouter.POST("/agents/accounts", baseApi.CreateAgentAccount) diff --git a/frontend/src/api/interface/ai.ts b/frontend/src/api/interface/ai.ts index b6f74c013..9f217f9b9 100644 --- a/frontend/src/api/interface/ai.ts +++ b/frontend/src/api/interface/ai.ts @@ -327,6 +327,21 @@ export namespace AI { fallbacks: string[]; } + export interface AgentHermesChatSessionItem { + id: string; + title: string; + model: string; + messageCount: number; + startedAt: string; + lastActive: string; + } + + export interface AgentHermesChatSessionRenameReq { + agentId: number; + id: string; + title: string; + } + export interface AgentOverviewReq { agentId: number; } diff --git a/frontend/src/api/modules/ai.ts b/frontend/src/api/modules/ai.ts index 8c36b1eec..766ff3ba2 100644 --- a/frontend/src/api/modules/ai.ts +++ b/frontend/src/api/modules/ai.ts @@ -130,6 +130,14 @@ export const updateAgentModelConfig = (req: AI.AgentModelConfigUpdateReq) => { return http.post(`/ai/agents/model/update`, req); }; +export const getAgentHermesChatSessions = (req: AI.AgentIDReq) => { + return http.post(`/ai/agents/hermes/chat/sessions`, req); +}; + +export const renameAgentHermesChatSession = (req: AI.AgentHermesChatSessionRenameReq) => { + return http.post(`/ai/agents/hermes/chat/sessions/rename`, req); +}; + export const getAgentOverview = (req: AI.AgentOverviewReq) => { return http.post(`/ai/agents/overview`, req, TimeoutEnum.T_5M); }; diff --git a/frontend/src/components/terminal/index.vue b/frontend/src/components/terminal/index.vue index b3f5cb690..4664239e9 100644 --- a/frontend/src/components/terminal/index.vue +++ b/frontend/src/components/terminal/index.vue @@ -555,7 +555,9 @@ onBeforeUnmount(() => { .ai-notice-fade-enter-active, .ai-notice-fade-leave-active { - transition: opacity 180ms ease, transform 180ms ease; + transition: + opacity 180ms ease, + transform 180ms ease; } .ai-mask-fade-enter-active, diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index a3cdca220..b40e159ac 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -773,6 +773,15 @@ const message = { skillCount: 'Skills', jobCount: 'Scheduled Jobs', sessionCount: 'Sessions', + hermesChatAction: 'Chat', + hermesChatTitle: 'Hermes Chat', + hermesChatDialogTitle: 'Hermes Chat - {0}', + hermesChatNewChat: 'New Chat', + hermesChatNoSessions: 'No sessions', + hermesChatMessageCount: '{0} msgs', + hermesChatEmptyHint: 'Select a session or click New Chat', + hermesChatTitlePlaceholder: 'Enter session title', + hermesChatRenameSuccess: 'Session title updated', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 9c19caeda..5d11a5e6a 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -783,6 +783,15 @@ const message = { skillCount: 'Habilidades', jobCount: 'Tareas programadas', sessionCount: 'Sesiones', + hermesChatAction: 'Conversación', + hermesChatTitle: 'Conversación de Hermes', + hermesChatDialogTitle: 'Conversación de Hermes - {0}', + hermesChatNewChat: 'Nueva conversación', + hermesChatNoSessions: 'No hay sesiones', + hermesChatMessageCount: '{0} mensajes', + hermesChatEmptyHint: 'Seleccione una sesión a la izquierda o haga clic en Nueva conversación', + hermesChatTitlePlaceholder: 'Introduzca el título de la sesión', + hermesChatRenameSuccess: 'El título de la sesión se ha actualizado', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 9fd5b5106..79839d4db 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -776,6 +776,15 @@ const message = { skillCount: '技能数', jobCount: '定期タスク数', sessionCount: 'セッション数', + hermesChatAction: '会話', + hermesChatTitle: 'Hermes 会話', + hermesChatDialogTitle: 'Hermes 会話 - {0}', + hermesChatNewChat: '新しい会話', + hermesChatNoSessions: 'セッションはありません', + hermesChatMessageCount: '{0}件', + hermesChatEmptyHint: '左側のセッションを選択するか、「新しい会話」をクリックしてください', + hermesChatTitlePlaceholder: 'セッションタイトルを入力してください', + hermesChatRenameSuccess: 'セッションタイトルを更新しました', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index ab2c9dd92..7c1697161 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -762,6 +762,15 @@ const message = { skillCount: '기술 수', jobCount: '예약 작업 수', sessionCount: '세션 수', + hermesChatAction: '대화', + hermesChatTitle: 'Hermes 대화', + hermesChatDialogTitle: 'Hermes 대화 - {0}', + hermesChatNewChat: '새 대화', + hermesChatNoSessions: '세션이 없습니다', + hermesChatMessageCount: '{0}개', + hermesChatEmptyHint: '왼쪽 세션을 선택하거나 새 대화를 클릭하세요', + hermesChatTitlePlaceholder: '세션 제목을 입력하세요', + hermesChatRenameSuccess: '세션 제목이 업데이트되었습니다', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 05dbf65c4..90187ef73 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -781,6 +781,15 @@ const message = { skillCount: 'Bilangan kemahiran', jobCount: 'Bilangan tugas berjadual', sessionCount: 'Bilangan sesi', + hermesChatAction: 'Perbualan', + hermesChatTitle: 'Perbualan Hermes', + hermesChatDialogTitle: 'Perbualan Hermes - {0}', + hermesChatNewChat: 'Perbualan baharu', + hermesChatNoSessions: 'Tiada sesi', + hermesChatMessageCount: '{0} mesej', + hermesChatEmptyHint: 'Pilih sesi di sebelah kiri atau klik Perbualan baharu', + hermesChatTitlePlaceholder: 'Masukkan tajuk sesi', + hermesChatRenameSuccess: 'Tajuk sesi telah dikemas kini', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 281d551fd..87059542e 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -778,6 +778,15 @@ const message = { skillCount: 'Habilidades', jobCount: 'Tarefas agendadas', sessionCount: 'Sessões', + hermesChatAction: 'Conversa', + hermesChatTitle: 'Conversa do Hermes', + hermesChatDialogTitle: 'Conversa do Hermes - {0}', + hermesChatNewChat: 'Nova conversa', + hermesChatNoSessions: 'Nenhuma sessão', + hermesChatMessageCount: '{0} mensagens', + hermesChatEmptyHint: 'Selecione uma sessão à esquerda ou clique em Nova conversa', + hermesChatTitlePlaceholder: 'Digite o título da sessão', + hermesChatRenameSuccess: 'O título da sessão foi atualizado', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index ec189ea1d..3a33a93b2 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -773,6 +773,15 @@ const message = { skillCount: 'Навыки', jobCount: 'Запланированные задачи', sessionCount: 'Сессии', + hermesChatAction: 'Диалог', + hermesChatTitle: 'Диалог Hermes', + hermesChatDialogTitle: 'Диалог Hermes - {0}', + hermesChatNewChat: 'Новый диалог', + hermesChatNoSessions: 'Нет сессий', + hermesChatMessageCount: '{0} сообщений', + hermesChatEmptyHint: 'Выберите сессию слева или нажмите «Новый диалог»', + hermesChatTitlePlaceholder: 'Введите название сессии', + hermesChatRenameSuccess: 'Название сессии обновлено', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 46436a74a..e026c86c0 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -780,6 +780,15 @@ const message = { skillCount: 'Yetenekler', jobCount: 'Zamanlanmış görevler', sessionCount: 'Oturumlar', + hermesChatAction: 'Sohbet', + hermesChatTitle: 'Hermes Sohbeti', + hermesChatDialogTitle: 'Hermes Sohbeti - {0}', + hermesChatNewChat: 'Yeni sohbet', + hermesChatNoSessions: 'Oturum yok', + hermesChatMessageCount: '{0} mesaj', + hermesChatEmptyHint: "Soldan bir oturum seçin veya Yeni sohbet'e tıklayın", + hermesChatTitlePlaceholder: 'Oturum başlığını girin', + hermesChatRenameSuccess: 'Oturum başlığı güncellendi', weixin: 'Weixin', wecom: 'WeCom', dingtalk: 'DingTalk', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index edde2bb4a..8652493b6 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -727,6 +727,15 @@ const message = { skillCount: '技能數量', jobCount: '定時任務數量', sessionCount: '會話數量', + hermesChatAction: '對話', + hermesChatTitle: 'Hermes 對話', + hermesChatDialogTitle: 'Hermes 對話 - {0}', + hermesChatNewChat: '新對話', + hermesChatNoSessions: '暫無會話', + hermesChatMessageCount: '{0} 條', + hermesChatEmptyHint: '選擇左側會話或點擊新對話', + hermesChatTitlePlaceholder: '請輸入會話標題', + hermesChatRenameSuccess: '會話標題已更新', weixin: '微信', wecom: '企業微信', dingtalk: '釘釘', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index f5417ad29..12d1cf821 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -722,6 +722,15 @@ const message = { skillCount: '技能数量', jobCount: '定时任务数量', sessionCount: '会话数量', + hermesChatAction: '对话', + hermesChatTitle: 'Hermes 对话', + hermesChatDialogTitle: 'Hermes 对话 - {0}', + hermesChatNewChat: '新对话', + hermesChatNoSessions: '暂无会话', + hermesChatMessageCount: '{0} 条', + hermesChatEmptyHint: '选择左侧会话或点击新对话', + hermesChatTitlePlaceholder: '请输入会话标题', + hermesChatRenameSuccess: '会话标题已更新', weixin: '微信', wecom: '企业微信', dingtalk: '钉钉', diff --git a/frontend/src/views/ai/agents/agent/add/index.vue b/frontend/src/views/ai/agents/agent/add/index.vue index 32c73daa0..489cdf8c2 100644 --- a/frontend/src/views/ai/agents/agent/add/index.vue +++ b/frontend/src/views/ai/agents/agent/add/index.vue @@ -11,8 +11,8 @@ - + diff --git a/frontend/src/views/ai/agents/agent/components/hermes-chat.vue b/frontend/src/views/ai/agents/agent/components/hermes-chat.vue new file mode 100644 index 000000000..e753a5c6c --- /dev/null +++ b/frontend/src/views/ai/agents/agent/components/hermes-chat.vue @@ -0,0 +1,381 @@ + + + + + diff --git a/frontend/src/views/ai/agents/agent/config/tabs/channels.vue b/frontend/src/views/ai/agents/agent/config/tabs/channels.vue index 7492d5b67..2a53e1a83 100644 --- a/frontend/src/views/ai/agents/agent/config/tabs/channels.vue +++ b/frontend/src/views/ai/agents/agent/config/tabs/channels.vue @@ -164,7 +164,7 @@ const openHermesWeixinTerminal = () => { title: `${t('aiTools.agents.agent')} ${props.agentName}`, users: ['hermes', 'root'], shell: '/bin/bash', - initCmd: 'source /opt/hermes/.venv/bin/activate\ncd /opt/data/workspace\nhermes gateway setup\n', + initCmd: 'hermes gateway setup\n', }); }; diff --git a/frontend/src/views/ai/agents/agent/index.vue b/frontend/src/views/ai/agents/agent/index.vue index dc064ae20..7c450419a 100644 --- a/frontend/src/views/ai/agents/agent/index.vue +++ b/frontend/src/views/ai/agents/agent/index.vue @@ -196,6 +196,7 @@ + @@ -223,6 +224,7 @@ import AppUpgrade from '@/views/app-store/installed/upgrade/index.vue'; import TaskLog from '@/components/log/task/index.vue'; import ComposeLogs from '@/components/log/compose/index.vue'; import AgentTerminalDialog from '@/views/ai/agents/agent/components/terminal.vue'; +import HermesChatDialog from '@/views/ai/agents/agent/components/hermes-chat.vue'; import i18n from '@/lang'; import PortJumpDialog from '@/components/port-jump/index.vue'; import DockerStatus from '@/views/container/docker-status/index.vue'; @@ -251,6 +253,7 @@ const bindWebsiteRef = ref(); const upgradeRef = ref(); const composeLogRef = ref(); const dialogTerminalRef = ref(); +const hermesChatRef = ref(); const dialogPortJumpRef = ref(); const route = useRoute(); const router = useRouter(); @@ -276,6 +279,11 @@ const buttons = [ show: (row: AI.AgentItem) => supportsAgentModelConfig(row.agentType), disabled: (row: AI.AgentItem) => row.status !== 'Running', }, + { + label: i18n.global.t('aiTools.agents.hermesChatAction'), + click: (row: AI.AgentItem) => openHermesChat(row), + show: (row: AI.AgentItem) => row.agentType === 'hermes-agent' && row.status === 'Running', + }, { label: i18n.global.t('menu.terminal'), click: (row: AI.AgentItem) => openTerminal(row), @@ -464,18 +472,29 @@ const openTerminal = (row: AI.AgentItem) => { title, users: row.agentType === 'hermes-agent' ? ['hermes', 'root'] : ['node', 'root'], shell: '/bin/bash', - initCmd: - row.agentType === 'hermes-agent' ? 'source /opt/hermes/.venv/bin/activate\ncd /opt/data/workspace\n' : '', + initCmd: '', + }); +}; + +const openHermesChat = (row: AI.AgentItem) => { + hermesChatRef.value?.acceptParams({ + agentId: row.id, + containerID: row.containerName, + title: i18n.global.t('aiTools.agents.hermesChatDialogTitle', [row.name]), }); }; const handleConfigRestartRequired = async (installId: number) => { try { - await ElMessageBox.confirm(t('aiTools.agents.configFileRestartHelper'), t('database.restartNow'), { - confirmButtonText: t('database.restartNow'), - cancelButtonText: t('commons.button.cancel'), - type: 'info', - }); + await ElMessageBox.confirm( + i18n.global.t('aiTools.agents.configFileRestartHelper'), + i18n.global.t('database.restartNow'), + { + confirmButtonText: i18n.global.t('database.restartNow'), + cancelButtonText: i18n.global.t('commons.button.cancel'), + type: 'info', + }, + ); await restartInstall(installId); } catch {} }; diff --git a/frontend/src/views/home/index.vue b/frontend/src/views/home/index.vue index 3d0363541..6e2f9c953 100644 --- a/frontend/src/views/home/index.vue +++ b/frontend/src/views/home/index.vue @@ -256,8 +256,8 @@ baseInfo.prettyDistro ? baseInfo.prettyDistro : baseInfo.platformVersion - ? baseInfo.platform + '-' + baseInfo.platformVersion - : baseInfo.platform + ? baseInfo.platform + '-' + baseInfo.platformVersion + : baseInfo.platform }} { (baseInfo.value.prettyDistro ? baseInfo.value.prettyDistro : baseInfo.value.platformVersion - ? baseInfo.value.platform + '-' + baseInfo.value.platformVersion - : baseInfo.value.platform) + + ? baseInfo.value.platform + '-' + baseInfo.value.platformVersion + : baseInfo.value.platform) + '\n' + i18n.global.t('home.kernelVersion') + ': ' +