From 1f2683eb01003a2d2e2fb074955bfde06251d5b8 Mon Sep 17 00:00:00 2001 From: Binx Date: Sat, 20 Jun 2026 13:34:55 +0800 Subject: [PATCH 1/3] feat: add a batch clean function to batch set agent log clean strategy --- apps/application/api/application_api.py | 7 ++- apps/application/serializers/application.py | 33 ++++++++++++++ apps/application/urls.py | 1 + apps/application/views/application.py | 43 +++++++++++++++++++ ui/src/api/application/application.ts | 17 ++++++++ .../permission/application/system-manage.ts | 1 + ui/src/permission/application/workspace.ts | 10 +++++ ui/src/views/application/index.vue | 23 +++++++++- 8 files changed, 132 insertions(+), 3 deletions(-) diff --git a/apps/application/api/application_api.py b/apps/application/api/application_api.py index 5b6b4171628..7cbbb24c0a0 100644 --- a/apps/application/api/application_api.py +++ b/apps/application/api/application_api.py @@ -12,7 +12,8 @@ from rest_framework import serializers from application.serializers.application import ApplicationCreateSerializer, ApplicationListResponse, \ - ApplicationImportRequest, ApplicationEditSerializer, TextToSpeechRequest, SpeechToTextRequest, PlayDemoTextRequest + ApplicationImportRequest, ApplicationEditSerializer, TextToSpeechRequest, SpeechToTextRequest, PlayDemoTextRequest, \ + BatchCleanTimeSerializer from common.mixins.api_mixin import APIMixin from common.result import ResultSerializer, ResultPageSerializer, DefaultResultSerializer from knowledge.serializers.common import BatchSerializer, BatchMoveSerializer @@ -181,6 +182,10 @@ def get_request(): def get_move_request(): return BatchMoveSerializer + @staticmethod + def get_clean_time_request(): + return BatchCleanTimeSerializer + class ApplicationExportAPI(APIMixin): @staticmethod diff --git a/apps/application/serializers/application.py b/apps/application/serializers/application.py index 73371ebd1bd..5c74570911b 100644 --- a/apps/application/serializers/application.py +++ b/apps/application/serializers/application.py @@ -1634,3 +1634,36 @@ def batch_move(self, instance: Dict, with_valid=True): QuerySet(Application).filter(id__in=id_list, workspace_id=workspace_id).update(folder_id=folder_id) return True + + @transaction.atomic + def batch_clean_time(self, instance: Dict, with_valid=True): + if with_valid: + BatchCleanTimeSerializer(data=instance).is_valid(model=Application, raise_exception=True) + self.is_valid(raise_exception=True) + id_list = instance.get("id_list") + workspace_id = self.data.get("workspace_id") + clean_time = instance.get("clean_time") + file_clean_time = instance.get("file_clean_time") + application_count = QuerySet(Application).filter(id__in=id_list, workspace_id=workspace_id).count() + if application_count != len(id_list): + raise AppApiException(500, _("Application does not exist")) + + QuerySet(Application).filter(id__in=id_list, workspace_id=workspace_id).update( + clean_time=clean_time, + file_clean_time=file_clean_time, + ) + return True + + +class BatchCleanTimeSerializer(BatchSerializer): + clean_time = serializers.IntegerField(required=True, min_value=1, max_value=100000, label=_("Clean time")) + file_clean_time = serializers.IntegerField( + required=True, min_value=1, max_value=100000, label=_("File clean time") + ) + + def is_valid(self, *, model=None, raise_exception=False): + super().is_valid(model=model, raise_exception=True) + if not self.data.get("id_list"): + raise AppApiException(500, _("id list cannot be empty")) + if self.data.get("file_clean_time") > self.data.get("clean_time"): + raise AppApiException(500, _("File clean time cannot exceed clean time")) diff --git a/apps/application/urls.py b/apps/application/urls.py index 449289d418b..1c473b63935 100644 --- a/apps/application/urls.py +++ b/apps/application/urls.py @@ -12,6 +12,7 @@ path('workspace//application//', views.ApplicationAPI.Page.as_view(), name='application_page'), path('workspace//application/batch_delete', views.ApplicationAPI.BatchDelete.as_view()), path('workspace//application/batch_move', views.ApplicationAPI.BatchMove.as_view()), + path('workspace//application/batch_clean_time', views.ApplicationAPI.BatchCleanTime.as_view()), path('workspace//application/', views.ApplicationAPI.Operate.as_view()), path('workspace//application//publish', views.ApplicationAPI.Publish.as_view()), path('workspace//application//move/', views.ApplicationAPI.Move.as_view()), diff --git a/apps/application/views/application.py b/apps/application/views/application.py index c7dc29956d9..5f5058bca4d 100644 --- a/apps/application/views/application.py +++ b/apps/application/views/application.py @@ -384,6 +384,49 @@ def inner(view,r, **kwargs): return result.success(inner(self,request, workspace_id=workspace_id)) + class BatchCleanTime(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['PUT'], + description=_("Batch update application chat log clear policy"), + summary=_("Batch update application chat log clear policy"), + operation_id=_("Batch update application chat log clear policy"), + parameters=ApplicationBatchOperateAPI.get_parameters(), + request=ApplicationBatchOperateAPI.get_clean_time_request(), + responses=result.DefaultResultSerializer, + tags=[_('Application')] + ) + @has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_permission(), + RoleConstants.USER.get_workspace_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role() + ) + def put(self, request: Request, workspace_id: str): + id_list = request.data.get('id_list', []) + permitted_ids = check_batch_permissions( + request, id_list, 'application_id', + (PermissionConstants.APPLICATION_CHAT_LOG_CLEAR_POLICY.get_workspace_application_permission(), + PermissionConstants.APPLICATION_CHAT_LOG_CLEAR_POLICY.get_workspace_permission_workspace_manage_role(), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [PermissionConstants.APPLICATION.get_workspace_application_permission()], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()), + workspace_id=workspace_id + ) + + @log(menu='Application', operate='Batch update application chat log clear policy', + get_operation_object=lambda r, k: get_application_operation_object_batch(permitted_ids)) + def inner(view,r, **kwargs): + return ApplicationBatchOperateSerializer( + data={'workspace_id': workspace_id, 'user_id': request.user.id} + ).batch_clean_time({ + 'id_list': permitted_ids, + 'clean_time': request.data.get('clean_time'), + 'file_clean_time': request.data.get('file_clean_time') + }) + + return result.success(inner(self,request, workspace_id=workspace_id)) + class McpServers(APIView): authentication_classes = [TokenAuth] diff --git a/ui/src/api/application/application.ts b/ui/src/api/application/application.ts index 3359846d1f6..c7e911c7a65 100644 --- a/ui/src/api/application/application.ts +++ b/ui/src/api/application/application.ts @@ -452,6 +452,22 @@ const putMulMoveApplication: (data: any, loading?: Ref) => Promise) => Promise> = ( + data, + loading, +) => { + return put(`${prefix.value}/batch_clean_time`, data, undefined, loading) +} + export default { getAllApplication, getApplication, @@ -486,4 +502,5 @@ export default { moveApplication, delMulApplication, putMulMoveApplication, + putMulCleanTime, } diff --git a/ui/src/permission/application/system-manage.ts b/ui/src/permission/application/system-manage.ts index 607c996bed3..7cb8c9b666f 100644 --- a/ui/src/permission/application/system-manage.ts +++ b/ui/src/permission/application/system-manage.ts @@ -6,6 +6,7 @@ const systemManage = { copy: () => hasPermission([RoleConst.ADMIN, PermissionConst.RESOURCE_APPLICATION_COPY], 'OR'), batchDelete: () => false, batchMove: () => false, + batchCleanStrategy: () => false, folderCreate: () => false, edit: () => hasPermission([RoleConst.ADMIN, PermissionConst.RESOURCE_APPLICATION_EDIT], 'OR'), publish: () => diff --git a/ui/src/permission/application/workspace.ts b/ui/src/permission/application/workspace.ts index a528345d1be..f413e52fccd 100644 --- a/ui/src/permission/application/workspace.ts +++ b/ui/src/permission/application/workspace.ts @@ -33,6 +33,16 @@ const workspace = { ], 'OR', ), + batchCleanStrategy: () => + hasPermission( + [ + RoleConst.USER.getWorkspaceRole, + RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, + PermissionConst.APPLICATION_CHAT_LOG_CLEAR_POLICY.getWorkspacePermission, + PermissionConst.APPLICATION_CHAT_LOG_CLEAR_POLICY.getWorkspacePermissionWorkspaceManageRole, + ], + 'OR', + ), copy: (source_id: string) => hasPermission( [ diff --git a/ui/src/views/application/index.vue b/ui/src/views/application/index.vue index 6fa79c94081..85c0013c97d 100644 --- a/ui/src/views/application/index.vue +++ b/ui/src/views/application/index.vue @@ -65,7 +65,11 @@ @@ -363,6 +367,14 @@ {{ $t('common.moveTo') }} + + {{ $t('views.chatLog.buttons.clearStrategy') }} + + + ([]) const applicationList = ref([]) const CopyApplicationDialogRef = ref() +const BatchClearStrategyDialogRef = ref>() // 批量操作 const isBatch = ref(false) @@ -523,6 +538,10 @@ function deleteMulApplication() { .catch(() => {}) } +function openBatchClearStrategyDialog() { + BatchClearStrategyDialogRef.value?.open([...multipleSelection.value]) +} + const resourceTriggerDrawerRef = ref>() const openTriggerDrawer = (data: any) => { resourceTriggerDrawerRef.value?.open(data) @@ -558,7 +577,7 @@ function openMoveToDialog(data?: any) { MoveToDialogRef.value?.open(obj) } -function refreshApplicationList(row: any) { +function refreshApplicationList(row?: any) { if (row) { // 不是根目录才会移除 if (folder.currentFolder?.parent_id) { From 9f3394fbc0c4b7390fdc5948e6313ec73e4f818d Mon Sep 17 00:00:00 2001 From: Binx Date: Sat, 20 Jun 2026 13:49:31 +0800 Subject: [PATCH 2/3] feat: add a batch clean function to batch set agent log clean strategy --- ui/src/locales/lang/en-US/views/application.ts | 2 ++ ui/src/locales/lang/zh-CN/views/application.ts | 1 + ui/src/locales/lang/zh-Hant/views/application.ts | 1 + 3 files changed, 4 insertions(+) diff --git a/ui/src/locales/lang/en-US/views/application.ts b/ui/src/locales/lang/en-US/views/application.ts index a2821ab266f..278aa5faf81 100644 --- a/ui/src/locales/lang/en-US/views/application.ts +++ b/ui/src/locales/lang/en-US/views/application.ts @@ -4,6 +4,8 @@ export default { createWorkFlowApplication: 'Create Workflow Agent', importApplication: 'Import Agent', copyApplication: 'Copy Agent', + batchClearStrategyTip: + 'This operation will batch update the chat log clear policy for the selected agents.', simple: 'SIMPLE', senior: 'WORKFLOW', simpleAgent: 'Simple Agent', diff --git a/ui/src/locales/lang/zh-CN/views/application.ts b/ui/src/locales/lang/zh-CN/views/application.ts index ec6daff87cf..0dc2d2088e4 100644 --- a/ui/src/locales/lang/zh-CN/views/application.ts +++ b/ui/src/locales/lang/zh-CN/views/application.ts @@ -4,6 +4,7 @@ export default { createWorkFlowApplication: '创建高级智能体', importApplication: '导入智能体', copyApplication: '复制智能体', + batchClearStrategyTip: '该操作会对选中的智能体批量调整对话日志清除策略。', simple: '简易', senior: '高级', simpleAgent: '简易智能体', diff --git a/ui/src/locales/lang/zh-Hant/views/application.ts b/ui/src/locales/lang/zh-Hant/views/application.ts index 988d0958593..e1105b28fb1 100644 --- a/ui/src/locales/lang/zh-Hant/views/application.ts +++ b/ui/src/locales/lang/zh-Hant/views/application.ts @@ -4,6 +4,7 @@ export default { createWorkFlowApplication: '建立進階智能體', importApplication: '匯入智能體', copyApplication: '複製智能體', + batchClearStrategyTip: '該操作會對選中的智能體批量調整對話日誌清除策略。', AdvancedAgent: '進階編智能體', simpleAgent: '簡易智能體', simple: '簡易', From e178a32a6902ff43829f469eba958e27836b7070 Mon Sep 17 00:00:00 2001 From: Binx Date: Sat, 20 Jun 2026 14:26:56 +0800 Subject: [PATCH 3/3] feat: batch clear strategy dialog page --- .../component/BatchClearStrategyDialog.vue | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 ui/src/views/application/component/BatchClearStrategyDialog.vue diff --git a/ui/src/views/application/component/BatchClearStrategyDialog.vue b/ui/src/views/application/component/BatchClearStrategyDialog.vue new file mode 100644 index 00000000000..90655988fcb --- /dev/null +++ b/ui/src/views/application/component/BatchClearStrategyDialog.vue @@ -0,0 +1,109 @@ + + + + +