mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 08:00:53 +00:00
feat: support session delete for hermes-agent (#12533)
This commit is contained in:
@@ -275,6 +275,26 @@ func (b *BaseApi) RenameHermesChatSession(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Delete Hermes chat session
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentHermesChatSessionDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/hermes/chat/sessions/delete [post]
|
||||
func (b *BaseApi) DeleteHermesChatSession(c *gin.Context) {
|
||||
var req dto.AgentHermesChatSessionDeleteReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.DeleteHermesChatSession(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Providers
|
||||
// @Success 200 {array} dto.ProviderInfo
|
||||
|
||||
@@ -115,6 +115,11 @@ type AgentHermesChatSessionRenameReq struct {
|
||||
Title string `json:"title" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentHermesChatSessionDeleteReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
ID string `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentOverview struct {
|
||||
Snapshot AgentOverviewSnapshot `json:"snapshot"`
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ type IAgentService interface {
|
||||
UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
|
||||
GetHermesChatSessions(req dto.AgentIDReq) ([]dto.AgentHermesChatSessionItem, error)
|
||||
RenameHermesChatSession(req dto.AgentHermesChatSessionRenameReq) error
|
||||
DeleteHermesChatSession(req dto.AgentHermesChatSessionDeleteReq) error
|
||||
GetOverview(req dto.AgentOverviewReq) (*dto.AgentOverview, error)
|
||||
GetProviders() ([]dto.ProviderInfo, error)
|
||||
GetSecurityConfig(req dto.AgentIDReq) (*dto.AgentSecurityConfig, error)
|
||||
@@ -456,7 +457,7 @@ func (a AgentService) GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
model, err := resolveHermesConfiguredModelIDStrict(account, accountModels, cfg.Model.Default)
|
||||
model, err := resolveHermesConfiguredModelID(account, accountModels, cfg.Model.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -905,7 +906,13 @@ func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeHermesConfig(path.Dir(agent.ConfigPath), account, agent.Model, strings.TrimSpace(req.UserTimezone))
|
||||
if err := writeHermesConfig(path.Dir(agent.ConfigPath), account, agent.Model, strings.TrimSpace(req.UserTimezone)); err != nil {
|
||||
return err
|
||||
}
|
||||
return NewIAppInstalledService().Operate(request.AppInstalledOperate{
|
||||
InstallId: install.ID,
|
||||
Operate: constant.Restart,
|
||||
})
|
||||
}
|
||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||
return err
|
||||
@@ -921,7 +928,10 @@ func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error
|
||||
if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {
|
||||
return err
|
||||
}
|
||||
return setOpenclawNPMRegistry(install.ContainerName, req.NPMRegistry)
|
||||
if err := setOpenclawNPMRegistry(install.ContainerName, req.NPMRegistry); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) GetConfigFile(req dto.AgentConfigFileReq) (*dto.AgentConfigFile, error) {
|
||||
|
||||
@@ -54,17 +54,6 @@ func buildHermesDockerExecArgs(containerName string, hermesArgs ...string) []str
|
||||
return buildHermesDockerExecCommandArgs(containerName, "hermes", hermesArgs...)
|
||||
}
|
||||
|
||||
func buildHermesSkillUninstallArgs(containerName, skillName string) []string {
|
||||
return buildHermesDockerExecCommandArgs(
|
||||
containerName,
|
||||
"sh",
|
||||
"-lc",
|
||||
fmt.Sprintf(`printf 'y\n' | %s skills uninstall "$1"`, hermesExecutablePath),
|
||||
"sh",
|
||||
skillName,
|
||||
)
|
||||
}
|
||||
|
||||
func writeHermesConfig(confDir string, account *model.AgentAccount, modelName string, timezone string) error {
|
||||
if strings.TrimSpace(confDir) == "" {
|
||||
return fmt.Errorf("config dir is required")
|
||||
@@ -382,29 +371,21 @@ func resolveHermesModel(sourceProvider, targetProvider, modelName string) string
|
||||
return target
|
||||
}
|
||||
|
||||
func resolveHermesConfiguredModelID(account *model.AgentAccount, accountModels []dto.AgentAccountModel, configuredModel string) string {
|
||||
func resolveHermesConfiguredModelID(account *model.AgentAccount, accountModels []dto.AgentAccountModel, configuredModel string) (string, error) {
|
||||
if account == nil {
|
||||
return ""
|
||||
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||
}
|
||||
configuredModel = strings.TrimSpace(configuredModel)
|
||||
if configuredModel == "" {
|
||||
return ""
|
||||
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||
}
|
||||
provider := resolveHermesProvider(account.Provider)
|
||||
for _, item := range accountModels {
|
||||
if resolveHermesModel(account.Provider, provider, item.ID) == configuredModel {
|
||||
return item.ID
|
||||
return item.ID, nil
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func resolveHermesConfiguredModelIDStrict(account *model.AgentAccount, accountModels []dto.AgentAccountModel, configuredModel string) (string, error) {
|
||||
modelID := resolveHermesConfiguredModelID(account, accountModels, configuredModel)
|
||||
if modelID == "" {
|
||||
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||
}
|
||||
return modelID, nil
|
||||
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||
}
|
||||
|
||||
func resolveHermesEnvEntries(account *model.AgentAccount) []hermesEnvEntry {
|
||||
|
||||
@@ -43,6 +43,22 @@ func (a AgentService) RenameHermesChatSession(req dto.AgentHermesChatSessionRena
|
||||
return err
|
||||
}
|
||||
|
||||
func (a AgentService) DeleteHermesChatSession(req dto.AgentHermesChatSessionDeleteReq) 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", "delete", req.ID, "--yes")...,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func listHermesChatSessionsFromStateDB(stateDBPath string) ([]dto.AgentHermesChatSessionItem, error) {
|
||||
if !files.NewFileOp().Stat(stateDBPath) {
|
||||
return []dto.AgentHermesChatSessionItem{}, nil
|
||||
|
||||
@@ -166,7 +166,14 @@ func (a AgentService) UninstallSkill(req dto.AgentSkillUninstallReq) error {
|
||||
}
|
||||
return cmd.NewCommandMgr(cmd.WithTimeout(5*time.Minute)).Run(
|
||||
"docker",
|
||||
buildHermesSkillUninstallArgs(install.ContainerName, req.Name)...,
|
||||
buildHermesDockerExecCommandArgs(
|
||||
install.ContainerName,
|
||||
"sh",
|
||||
"-lc",
|
||||
fmt.Sprintf(`printf 'y\n' | %s skills uninstall "$1"`, hermesExecutablePath),
|
||||
"sh",
|
||||
req.Name,
|
||||
)...,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
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/hermes/chat/sessions/delete", baseApi.DeleteHermesChatSession)
|
||||
aiToolsRouter.POST("/agents/overview", baseApi.GetAgentOverview)
|
||||
aiToolsRouter.GET("/agents/providers", baseApi.GetAgentProviders)
|
||||
aiToolsRouter.POST("/agents/accounts", baseApi.CreateAgentAccount)
|
||||
|
||||
@@ -343,6 +343,11 @@ export namespace AI {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface AgentHermesChatSessionDeleteReq {
|
||||
agentId: number;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface AgentOverviewReq {
|
||||
agentId: number;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,10 @@ export const renameAgentHermesChatSession = (req: AI.AgentHermesChatSessionRenam
|
||||
return http.post(`/ai/agents/hermes/chat/sessions/rename`, req);
|
||||
};
|
||||
|
||||
export const deleteAgentHermesChatSession = (req: AI.AgentHermesChatSessionDeleteReq) => {
|
||||
return http.post(`/ai/agents/hermes/chat/sessions/delete`, req);
|
||||
};
|
||||
|
||||
export const getAgentOverview = (req: AI.AgentOverviewReq) => {
|
||||
return http.post<AI.AgentOverview>(`/ai/agents/overview`, req, TimeoutEnum.T_5M);
|
||||
};
|
||||
|
||||
@@ -783,6 +783,8 @@ const message = {
|
||||
hermesChatEmptyHint: 'Select a session or click New Chat',
|
||||
hermesChatTitlePlaceholder: 'Enter session title',
|
||||
hermesChatRenameSuccess: 'Session title updated',
|
||||
hermesChatDeleteConfirm: 'Delete session {0}?',
|
||||
hermesChatDeleteSuccess: 'Session deleted',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -793,6 +793,8 @@ const message = {
|
||||
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',
|
||||
hermesChatDeleteConfirm: '¿Eliminar la sesión {0}?',
|
||||
hermesChatDeleteSuccess: 'Sesión eliminada',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -786,6 +786,8 @@ const message = {
|
||||
hermesChatEmptyHint: '左側のセッションを選択するか、「新しい会話」をクリックしてください',
|
||||
hermesChatTitlePlaceholder: 'セッションタイトルを入力してください',
|
||||
hermesChatRenameSuccess: 'セッションタイトルを更新しました',
|
||||
hermesChatDeleteConfirm: 'セッション {0} を削除しますか?',
|
||||
hermesChatDeleteSuccess: 'セッションを削除しました',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -772,6 +772,8 @@ const message = {
|
||||
hermesChatEmptyHint: '왼쪽 세션을 선택하거나 새 대화를 클릭하세요',
|
||||
hermesChatTitlePlaceholder: '세션 제목을 입력하세요',
|
||||
hermesChatRenameSuccess: '세션 제목이 업데이트되었습니다',
|
||||
hermesChatDeleteConfirm: '세션 {0}을(를) 삭제하시겠습니까?',
|
||||
hermesChatDeleteSuccess: '세션이 삭제되었습니다',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -791,6 +791,8 @@ const message = {
|
||||
hermesChatEmptyHint: 'Pilih sesi di sebelah kiri atau klik Perbualan baharu',
|
||||
hermesChatTitlePlaceholder: 'Masukkan tajuk sesi',
|
||||
hermesChatRenameSuccess: 'Tajuk sesi telah dikemas kini',
|
||||
hermesChatDeleteConfirm: 'Padam sesi {0}?',
|
||||
hermesChatDeleteSuccess: 'Sesi berjaya dipadam',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -788,6 +788,8 @@ const message = {
|
||||
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',
|
||||
hermesChatDeleteConfirm: 'Excluir a sessão {0}?',
|
||||
hermesChatDeleteSuccess: 'Sessão excluída',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -783,6 +783,8 @@ const message = {
|
||||
hermesChatEmptyHint: 'Выберите сессию слева или нажмите «Новый диалог»',
|
||||
hermesChatTitlePlaceholder: 'Введите название сессии',
|
||||
hermesChatRenameSuccess: 'Название сессии обновлено',
|
||||
hermesChatDeleteConfirm: 'Удалить сессию {0}?',
|
||||
hermesChatDeleteSuccess: 'Сессия удалена',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -790,6 +790,8 @@ const message = {
|
||||
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',
|
||||
hermesChatDeleteConfirm: '{0} oturumu silinsin mi?',
|
||||
hermesChatDeleteSuccess: 'Oturum silindi',
|
||||
weixin: 'Weixin',
|
||||
wecom: 'WeCom',
|
||||
dingtalk: 'DingTalk',
|
||||
|
||||
@@ -737,6 +737,8 @@ const message = {
|
||||
hermesChatEmptyHint: '選擇左側會話或點擊新對話',
|
||||
hermesChatTitlePlaceholder: '請輸入會話標題',
|
||||
hermesChatRenameSuccess: '會話標題已更新',
|
||||
hermesChatDeleteConfirm: '確認刪除會話 {0}?',
|
||||
hermesChatDeleteSuccess: '會話已刪除',
|
||||
weixin: '微信',
|
||||
wecom: '企業微信',
|
||||
dingtalk: '釘釘',
|
||||
|
||||
@@ -732,6 +732,8 @@ const message = {
|
||||
hermesChatEmptyHint: '选择左侧会话或点击新对话',
|
||||
hermesChatTitlePlaceholder: '请输入会话标题',
|
||||
hermesChatRenameSuccess: '会话标题已更新',
|
||||
hermesChatDeleteConfirm: '确认删除会话 {0}?',
|
||||
hermesChatDeleteSuccess: '会话已删除',
|
||||
weixin: '微信',
|
||||
wecom: '企业微信',
|
||||
dingtalk: '钉钉',
|
||||
|
||||
@@ -43,7 +43,12 @@
|
||||
<div v-else class="hermes-chat-dialog__item-title">{{ item.title || item.id }}</div>
|
||||
<div class="hermes-chat-dialog__item-actions">
|
||||
<template v-if="editingSessionId === item.id">
|
||||
<el-button link type="primary" @click.stop="saveSessionTitle(item)">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
:disabled="!canSaveSessionTitle"
|
||||
@click.stop="saveSessionTitle(item)"
|
||||
>
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
<el-button link @click.stop="cancelEditSessionTitle">
|
||||
@@ -58,6 +63,16 @@
|
||||
@mousedown.stop
|
||||
@click.stop="startEditSessionTitle(item)"
|
||||
/>
|
||||
<el-button
|
||||
v-if="editingSessionId !== item.id"
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
:disabled="isDeleteDisabled(item)"
|
||||
:loading="deletingSessionId === item.id"
|
||||
@mousedown.stop
|
||||
@click.stop="deleteSession(item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hermes-chat-dialog__item-model">{{ item.model || '-' }}</div>
|
||||
@@ -80,11 +95,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, ref } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import DialogPro from '@/components/dialog-pro/index.vue';
|
||||
import Terminal from '@/components/terminal/index.vue';
|
||||
import { getAgentHermesChatSessions, renameAgentHermesChatSession } from '@/api/modules/ai';
|
||||
import {
|
||||
deleteAgentHermesChatSession,
|
||||
getAgentHermesChatSessions,
|
||||
renameAgentHermesChatSession,
|
||||
} from '@/api/modules/ai';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
@@ -113,6 +133,8 @@ const sessions = ref<AI.AgentHermesChatSessionItem[]>([]);
|
||||
const activeSessionId = ref('');
|
||||
const editingSessionId = ref('');
|
||||
const editingTitle = ref('');
|
||||
const deletingSessionId = ref('');
|
||||
const canSaveSessionTitle = computed(() => editingTitle.value.length > 0);
|
||||
|
||||
const formatTime = (value: string) => {
|
||||
if (!value) {
|
||||
@@ -192,6 +214,9 @@ const cancelEditSessionTitle = () => {
|
||||
};
|
||||
|
||||
const saveSessionTitle = async (item: AI.AgentHermesChatSessionItem) => {
|
||||
if (!canSaveSessionTitle.value) {
|
||||
return;
|
||||
}
|
||||
await renameAgentHermesChatSession({
|
||||
agentId: agentId.value,
|
||||
id: item.id,
|
||||
@@ -202,6 +227,44 @@ const saveSessionTitle = async (item: AI.AgentHermesChatSessionItem) => {
|
||||
await loadSessions();
|
||||
};
|
||||
|
||||
const isDeleteDisabled = (item: AI.AgentHermesChatSessionItem) => {
|
||||
return terminalOpen.value && activeSessionId.value === item.id;
|
||||
};
|
||||
|
||||
const deleteSession = async (item: AI.AgentHermesChatSessionItem) => {
|
||||
if (isDeleteDisabled(item)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
t('aiTools.agents.hermesChatDeleteConfirm', [item.title || item.id]),
|
||||
t('commons.button.delete'),
|
||||
{
|
||||
confirmButtonText: t('commons.button.delete'),
|
||||
cancelButtonText: t('commons.button.cancel'),
|
||||
type: 'warning',
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
deletingSessionId.value = item.id;
|
||||
try {
|
||||
await deleteAgentHermesChatSession({
|
||||
agentId: agentId.value,
|
||||
id: item.id,
|
||||
});
|
||||
MsgSuccess(t('aiTools.agents.hermesChatDeleteSuccess'));
|
||||
cancelEditSessionTitle();
|
||||
if (activeSessionId.value === item.id) {
|
||||
activeSessionId.value = '';
|
||||
}
|
||||
await loadSessions();
|
||||
} finally {
|
||||
deletingSessionId.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const disconnectTerminal = () => {
|
||||
terminalRef.value?.onClose();
|
||||
terminalOpen.value = false;
|
||||
|
||||
@@ -54,11 +54,6 @@
|
||||
/>
|
||||
<span class="input-help">{{ t('aiTools.agents.groupAllowFromHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-link type="primary" icon="Position" @click="toFeishuDoc">
|
||||
{{ t('container.mirrorsHelper2') }}
|
||||
</el-link>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<ChannelBots
|
||||
:bots="form.bots"
|
||||
@@ -250,10 +245,6 @@ const updateBots = (bots: FeishuBotForm[]) => {
|
||||
|
||||
const isApproveDisabled = (bot: FeishuBotForm) => bot.dmPolicy !== 'pairing';
|
||||
|
||||
const toFeishuDoc = () => {
|
||||
window.open('https://openclaw.club/guides/feishu-platform', '_blank');
|
||||
};
|
||||
|
||||
const load = async (id: number) => {
|
||||
await loadPlugin(id);
|
||||
const res = await getAgentFeishuConfig({ agentId: id });
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
{{ t('commons.button.save') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<span class="input-help">{{ t('aiTools.agents.channelAutoRestartHelper') }}</span>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user