diff --git a/document/content/docs/upgrading/4-14/4145.mdx b/document/content/docs/upgrading/4-14/4145.mdx index 4d953545a..19c06200c 100644 --- a/document/content/docs/upgrading/4-14/4145.mdx +++ b/document/content/docs/upgrading/4-14/4145.mdx @@ -6,7 +6,7 @@ description: 'FastGPT V4.14.5 更新说明' ## 🚀 新增内容 - +1. 对话记录使用侧改成软删除,增加从日志管理里删除对话记录。 ## ⚙️ 优化 diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index ea785c4ca..ff68ce8cb 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -102,7 +102,7 @@ "document/content/docs/protocol/terms.en.mdx": "2025-12-15T23:36:54+08:00", "document/content/docs/protocol/terms.mdx": "2025-12-15T23:36:54+08:00", "document/content/docs/toc.en.mdx": "2025-08-04T13:42:36+08:00", - "document/content/docs/toc.mdx": "2025-12-09T23:33:32+08:00", + "document/content/docs/toc.mdx": "2025-12-17T17:44:38+08:00", "document/content/docs/upgrading/4-10/4100.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-10/4101.mdx": "2025-09-08T20:07:20+08:00", "document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00", @@ -120,6 +120,7 @@ "document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00", "document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00", "document/content/docs/upgrading/4-14/4144.mdx": "2025-12-16T14:56:04+08:00", + "document/content/docs/upgrading/4-14/4145.mdx": "2025-12-17T17:44:38+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", diff --git a/packages/global/common/file/api.d.ts b/packages/global/common/file/api.d.ts index c22042e22..b13d2e78a 100644 --- a/packages/global/common/file/api.d.ts +++ b/packages/global/common/file/api.d.ts @@ -1,4 +1,4 @@ -import type { OutLinkChatAuthProps } from '../../support/permission/chat.d'; +import type { OutLinkChatAuthProps } from '../../support/permission/chat'; export type preUploadImgProps = OutLinkChatAuthProps & { // expiredTime?: Date; diff --git a/packages/global/core/app/logs/constants.ts b/packages/global/core/app/logs/constants.ts index e980b18a2..93a840941 100644 --- a/packages/global/core/app/logs/constants.ts +++ b/packages/global/core/app/logs/constants.ts @@ -37,7 +37,7 @@ export const AppLogKeysEnumMap = { }; export const DefaultAppLogKeys = [ - { key: AppLogKeysEnum.SOURCE, enable: true }, + { key: AppLogKeysEnum.SOURCE, enable: false }, { key: AppLogKeysEnum.USER, enable: true }, { key: AppLogKeysEnum.TITLE, enable: true }, { key: AppLogKeysEnum.SESSION_ID, enable: false }, diff --git a/packages/global/core/chat/type.d.ts b/packages/global/core/chat/type.d.ts index 7392d97b2..f95c33311 100644 --- a/packages/global/core/chat/type.d.ts +++ b/packages/global/core/chat/type.d.ts @@ -51,6 +51,8 @@ export type ChatSchemaType = { hasBadFeedback?: boolean; hasUnreadGoodFeedback?: boolean; hasUnreadBadFeedback?: boolean; + + deleteTime?: Date | null; }; export type ChatWithAppSchema = Omit & { @@ -197,7 +199,7 @@ export type HistoryItemType = { }; export type ChatHistoryItemType = HistoryItemType & { appId: string; - top: boolean; + top?: boolean; }; /* ------- response data ------------ */ diff --git a/packages/global/openapi/api.ts b/packages/global/openapi/api.ts index c8be011d3..169e16f75 100644 --- a/packages/global/openapi/api.ts +++ b/packages/global/openapi/api.ts @@ -1,7 +1,17 @@ import { z } from 'zod'; export const PaginationSchema = z.object({ - pageSize: z.union([z.number(), z.string()]), + pageSize: z.union([z.number(), z.string()]).optional(), offset: z.union([z.number(), z.string()]).optional(), pageNum: z.union([z.number(), z.string()]).optional() }); +export type PaginationType = z.infer; + +export const PaginationResponseSchema = (itemSchema: T) => + z.object({ + total: z.number().optional().default(0), + list: z.array(itemSchema).optional().default([]) + }); +export type PaginationResponseType = z.infer< + ReturnType> +>; diff --git a/packages/global/openapi/core/app/log/api.ts b/packages/global/openapi/core/app/log/api.ts index 22c1a7f72..5360a98c5 100644 --- a/packages/global/openapi/core/app/log/api.ts +++ b/packages/global/openapi/core/app/log/api.ts @@ -34,7 +34,7 @@ export const ChatLogItemSchema = z.object({ title: z.string().optional().meta({ example: '用户对话', description: '对话标题' }), customTitle: z.string().nullish().meta({ example: '自定义标题', description: '自定义对话标题' }), source: z.enum(ChatSourceEnum).meta({ example: ChatSourceEnum.api, description: '对话来源' }), - sourceName: z.string().optional().meta({ example: 'API调用', description: '来源名称' }), + sourceName: z.string().nullish().meta({ example: 'API调用', description: '来源名称' }), updateTime: z.date().meta({ example: '2024-01-01T00:30:00.000Z', description: '更新时间' }), createTime: z.date().meta({ example: '2024-01-01T00:00:00.000Z', description: '创建时间' }), messageCount: z.int().nullish().meta({ example: 10, description: '消息数量' }), @@ -50,7 +50,7 @@ export const ChatLogItemSchema = z.object({ totalPoints: z.number().nullish().meta({ example: 150.5, description: '总积分消耗' }), outLinkUid: z.string().nullish().meta({ example: 'outLink123', description: '外链用户 ID' }), tmbId: z.string().nullish().meta({ example: 'tmb123', description: '团队成员 ID' }), - sourceMember: SourceMemberSchema.optional().meta({ description: '来源成员信息' }), + sourceMember: SourceMemberSchema.nullish().meta({ description: '来源成员信息' }), versionName: z.string().nullish().meta({ example: 'v1.0.0', description: '版本名称' }), region: z.string().nullish().meta({ example: '中国', description: '区域' }) }); diff --git a/packages/global/openapi/core/chat/feedback/api.ts b/packages/global/openapi/core/chat/feedback/api.ts index 4268bba5e..aa2967c24 100644 --- a/packages/global/openapi/core/chat/feedback/api.ts +++ b/packages/global/openapi/core/chat/feedback/api.ts @@ -105,11 +105,11 @@ export const UpdateUserFeedbackBodySchema = z.object({ example: 'data123', description: '消息数据 ID' }), - userGoodFeedback: z.string().optional().nullable().meta({ + userGoodFeedback: z.string().nullish().meta({ example: '回答很好', description: '用户好评反馈内容' }), - userBadFeedback: z.string().optional().nullable().meta({ + userBadFeedback: z.string().nullish().meta({ example: '回答不准确', description: '用户差评反馈内容' }) diff --git a/packages/global/openapi/core/chat/history/api.ts b/packages/global/openapi/core/chat/history/api.ts new file mode 100644 index 000000000..70fa76d49 --- /dev/null +++ b/packages/global/openapi/core/chat/history/api.ts @@ -0,0 +1,73 @@ +import z from 'zod'; +import { ObjectIdSchema } from '../../../../common/type/mongo'; +import { OutLinkChatAuthSchema } from '../../../../support/permission/chat'; +import { ChatSourceEnum } from '../../../../core/chat/constants'; +import { PaginationSchema, PaginationResponseSchema } from '../../../api'; + +// Get chat histories schema +export const GetHistoriesBodySchema = PaginationSchema.and( + OutLinkChatAuthSchema.and( + z.object({ + appId: ObjectIdSchema.optional().describe('应用ID'), + source: z.enum(ChatSourceEnum).optional().describe('对话来源'), + startCreateTime: z.string().optional().describe('创建时间开始'), + endCreateTime: z.string().optional().describe('创建时间结束'), + startUpdateTime: z.string().optional().describe('更新时间开始'), + endUpdateTime: z.string().optional().describe('更新时间结束') + }) + ) +); +export type GetHistoriesBodyType = z.infer; +export const GetHistoriesResponseSchema = PaginationResponseSchema( + z.object({ + chatId: z.string(), + updateTime: z.date(), + appId: z.string(), + customTitle: z.string().optional(), + title: z.string(), + top: z.boolean().optional() + }) +); +export type GetHistoriesResponseType = z.infer; + +// Update chat history schema +export const UpdateHistoryBodySchema = OutLinkChatAuthSchema.and( + z.object({ + appId: ObjectIdSchema.describe('应用ID'), + chatId: z.string().min(1).describe('对话ID'), + title: z.string().optional().describe('标题'), + customTitle: z.string().optional().describe('自定义标题'), + top: z.boolean().optional().describe('是否置顶') + }) +); +export type UpdateHistoryBodyType = z.infer; + +// Delete single chat history schema +export const DelChatHistorySchema = OutLinkChatAuthSchema.and( + z.object({ + appId: ObjectIdSchema.describe('应用ID'), + chatId: z.string().min(1).describe('对话ID') + }) +); +export type DelChatHistoryType = z.infer; + +// Clear all chat histories schema +export const ClearChatHistoriesSchema = OutLinkChatAuthSchema.and( + z.object({ + appId: ObjectIdSchema.describe('应用ID') + }) +); +export type ClearChatHistoriesType = z.infer; + +// Batch delete chat histories schema (for log manager) +export const ChatBatchDeleteBodySchema = z.object({ + appId: ObjectIdSchema, + chatIds: z + .array(z.string().min(1)) + .min(1) + .meta({ + description: '对话ID列表', + example: ['chat_123456', 'chat_789012'] + }) +}); +export type ChatBatchDeleteBodyType = z.infer; diff --git a/packages/global/openapi/core/chat/history/index.ts b/packages/global/openapi/core/chat/history/index.ts new file mode 100644 index 000000000..f03fdd4ad --- /dev/null +++ b/packages/global/openapi/core/chat/history/index.ts @@ -0,0 +1,109 @@ +import type { OpenAPIPath } from '../../../type'; +import { TagsMap } from '../../../tag'; +import { + GetHistoriesBodySchema, + GetHistoriesResponseSchema, + UpdateHistoryBodySchema, + ChatBatchDeleteBodySchema, + DelChatHistorySchema, + ClearChatHistoriesSchema +} from './api'; + +export const ChatHistoryPath: OpenAPIPath = { + '/core/chat/history/getHistories': { + post: { + summary: '获取对话历史列表', + description: '分页获取指定应用的对话历史记录', + tags: [TagsMap.chatHistory], + requestBody: { + content: { + 'application/json': { + schema: GetHistoriesBodySchema + } + } + }, + responses: { + 200: { + description: '成功获取对话历史列表', + content: { + 'application/json': { + schema: GetHistoriesResponseSchema + } + } + } + } + } + }, + '/core/chat/history/updateHistory': { + put: { + summary: '修改对话历史', + description: '修改对话历史的标题、自定义标题或置顶状态', + tags: [TagsMap.chatHistory], + requestBody: { + content: { + 'application/json': { + schema: UpdateHistoryBodySchema + } + } + }, + responses: { + 200: { + description: '成功修改对话历史' + } + } + } + }, + '/core/chat/history/delHistory': { + delete: { + summary: '删除单个对话历史', + description: '软删除指定的单个对话记录', + tags: [TagsMap.chatHistory], + requestBody: { + content: { + 'application/json': { + schema: DelChatHistorySchema + } + } + }, + responses: { + 200: { + description: '成功删除对话' + } + } + } + }, + '/core/chat/history/clearHistories': { + delete: { + summary: '清空应用对话历史', + description: '清空指定应用的所有对话记录(软删除)', + tags: [TagsMap.chatHistory], + requestParams: { + query: ClearChatHistoriesSchema + }, + responses: { + 200: { + description: '成功清空对话历史' + } + } + } + }, + '/core/chat/history/batchDelete': { + post: { + summary: '批量删除对话历史', + description: '批量删除指定应用的多个对话记录(真实删除),需应用日志权限。', + tags: [TagsMap.chatHistory], + requestBody: { + content: { + 'application/json': { + schema: ChatBatchDeleteBodySchema + } + } + }, + responses: { + 200: { + description: '成功删除对话' + } + } + } + } +}; diff --git a/packages/global/openapi/core/chat/index.ts b/packages/global/openapi/core/chat/index.ts index 8bfb37ea2..5d44ce944 100644 --- a/packages/global/openapi/core/chat/index.ts +++ b/packages/global/openapi/core/chat/index.ts @@ -2,6 +2,7 @@ import type { OpenAPIPath } from '../../type'; import { ChatSettingPath } from './setting'; import { ChatFavouriteAppPath } from './favourite/index'; import { ChatFeedbackPath } from './feedback/index'; +import { ChatHistoryPath } from './history/index'; import { z } from 'zod'; import { CreatePostPresignedUrlResultSchema } from '../../../../service/common/s3/type'; import { PresignChatFileGetUrlSchema, PresignChatFilePostUrlSchema } from '../../../core/chat/api'; @@ -11,6 +12,7 @@ export const ChatPath: OpenAPIPath = { ...ChatSettingPath, ...ChatFavouriteAppPath, ...ChatFeedbackPath, + ...ChatHistoryPath, '/core/chat/presignChatFileGetUrl': { post: { diff --git a/packages/global/openapi/index.ts b/packages/global/openapi/index.ts index 1a96c8b46..b06c9d816 100644 --- a/packages/global/openapi/index.ts +++ b/packages/global/openapi/index.ts @@ -30,7 +30,7 @@ export const openAPIDocument = createDocument({ }, { name: '对话管理', - tags: [TagsMap.chatSetting, TagsMap.chatPage, TagsMap.chatFeedback] + tags: [TagsMap.chatHistory, TagsMap.chatPage, TagsMap.chatFeedback, TagsMap.chatSetting] }, { name: '插件系统', diff --git a/packages/global/openapi/tag.ts b/packages/global/openapi/tag.ts index d43a031ab..34806829e 100644 --- a/packages/global/openapi/tag.ts +++ b/packages/global/openapi/tag.ts @@ -4,7 +4,8 @@ export const TagsMap = { appLog: 'Agent 日志', // Chat - home - chatPage: '对话页', + chatPage: '对话页操作', + chatHistory: '对话历史管理', chatSetting: '门户页配置', chatFeedback: '对话反馈', diff --git a/packages/global/support/permission/chat.d.ts b/packages/global/support/permission/chat.d.ts deleted file mode 100644 index cb1e70709..000000000 --- a/packages/global/support/permission/chat.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -type ShareChatAuthProps = { - shareId?: string; - outLinkUid?: string; -}; -type TeamChatAuthProps = { - teamId?: string; - teamToken?: string; -}; -export type OutLinkChatAuthProps = ShareChatAuthProps & TeamChatAuthProps; diff --git a/packages/global/support/permission/chat.ts b/packages/global/support/permission/chat.ts new file mode 100644 index 000000000..6bc4499eb --- /dev/null +++ b/packages/global/support/permission/chat.ts @@ -0,0 +1,16 @@ +import z from 'zod'; + +export const ShareChatAuthSchema = z.object({ + shareId: z.string().optional().describe('分享链接ID'), + outLinkUid: z.string().optional().describe('外链用户ID') +}); +export type ShareChatAuthProps = z.infer; + +export const TeamChatAuthSchema = z.object({ + teamId: z.string().optional().describe('团队ID'), + teamToken: z.string().optional().describe('团队Token') +}); +export type TeamChatAuthProps = z.infer; + +export const OutLinkChatAuthSchema = ShareChatAuthSchema.and(TeamChatAuthSchema); +export type OutLinkChatAuthProps = z.infer; diff --git a/packages/global/support/user/type.ts b/packages/global/support/user/type.ts index c3949453f..ea62d6140 100644 --- a/packages/global/support/user/type.ts +++ b/packages/global/support/user/type.ts @@ -38,9 +38,7 @@ export type UserType = { export const SourceMemberSchema = z.object({ name: z.string().meta({ example: '张三', description: '成员名称' }), - avatar: z - .string() - .meta({ example: 'https://cloud.fastgpt.cn/avatar.png', description: '成员头像' }), + avatar: z.string().nullish().meta({ description: '成员头像' }), status: z .enum(TeamMemberStatusEnum) .meta({ example: TeamMemberStatusEnum.active, description: '成员状态' }) diff --git a/packages/service/common/middle/entry.ts b/packages/service/common/middle/entry.ts index 09a26342e..6c79dedf9 100644 --- a/packages/service/common/middle/entry.ts +++ b/packages/service/common/middle/entry.ts @@ -54,10 +54,8 @@ export const NextEntry = ({ if (error instanceof ZodError) { return jsonRes(res, { code: 400, - error: { - message: 'Validation error', - details: error.message - }, + message: 'Data validation error', + error, url: req.url }); } diff --git a/packages/service/common/response/index.ts b/packages/service/common/response/index.ts index 8ef4f294e..12b01800d 100644 --- a/packages/service/common/response/index.ts +++ b/packages/service/common/response/index.ts @@ -5,6 +5,7 @@ import { addLog } from '../system/log'; import { replaceSensitiveText } from '@fastgpt/global/common/string/tools'; import { UserError } from '@fastgpt/global/common/error/utils'; import { clearCookie } from '../../support/permission/auth/common'; +import { ZodError } from 'zod'; export interface ResponseType { code: number; @@ -65,6 +66,9 @@ export function processError(params: { // 3. 根据错误类型记录不同级别的日志 if (error instanceof UserError) { addLog.info(`Request error: ${url}, ${msg}`); + } else if (error instanceof ZodError) { + addLog.error(`[Zod] Error in ${url}`, error.message); + msg = error.message; } else { addLog.error(`System unexpected error: ${url}, ${msg}`, error); } diff --git a/packages/service/core/app/controller.ts b/packages/service/core/app/controller.ts index b427ed38b..e6e546cac 100644 --- a/packages/service/core/app/controller.ts +++ b/packages/service/core/app/controller.ts @@ -233,7 +233,6 @@ export const onDelOneApp = async ({ await MongoChat.deleteMany({ appId }); - await getS3ChatSource().deleteChatFilesByPrefix({ appId }); } for await (const app of apps) { diff --git a/packages/service/core/chat/chatSchema.ts b/packages/service/core/chat/chatSchema.ts index 7077be6dc..1979d3575 100644 --- a/packages/service/core/chat/chatSchema.ts +++ b/packages/service/core/chat/chatSchema.ts @@ -95,7 +95,13 @@ const ChatSchema = new Schema({ hasGoodFeedback: Boolean, hasBadFeedback: Boolean, hasUnreadGoodFeedback: Boolean, - hasUnreadBadFeedback: Boolean + hasUnreadBadFeedback: Boolean, + + deleteTime: { + type: Date, + default: null, + select: false + } }); try { @@ -103,13 +109,13 @@ try { ChatSchema.index({ chatId: 1 }); // get user history - ChatSchema.index({ tmbId: 1, appId: 1, top: -1, updateTime: -1 }); + ChatSchema.index({ tmbId: 1, appId: 1, deleteTime: 1, top: -1, updateTime: -1 }); // delete by appid; clear history; init chat; update chat; auth chat; get chat; ChatSchema.index({ appId: 1, chatId: 1 }); /* get chat logs */ // 1. No feedback filter - ChatSchema.index({ teamId: 1, appId: 1, source: 1, tmbId: 1, updateTime: -1 }); + ChatSchema.index({ teamId: 1, appId: 1, source: 1, tmbId: 1, deleteTime: 1, updateTime: -1 }); /* 反馈过滤的索引 */ // 2. Has good feedback filter @@ -120,11 +126,13 @@ try { source: 1, tmbId: 1, hasGoodFeedback: 1, + deleteTime: 1, updateTime: -1 }, { partialFilterExpression: { - hasGoodFeedback: true + hasGoodFeedback: true, + deleteTime: null } } ); @@ -136,11 +144,13 @@ try { source: 1, tmbId: 1, hasBadFeedback: 1, + deleteTime: 1, updateTime: -1 }, { partialFilterExpression: { - hasBadFeedback: true + hasBadFeedback: true, + deleteTime: null } } ); @@ -152,11 +162,13 @@ try { source: 1, tmbId: 1, hasUnreadGoodFeedback: 1, + deleteTime: 1, updateTime: -1 }, { partialFilterExpression: { - hasUnreadGoodFeedback: true + hasUnreadGoodFeedback: true, + deleteTime: null } } ); @@ -168,11 +180,13 @@ try { source: 1, tmbId: 1, hasUnreadBadFeedback: 1, + deleteTime: 1, updateTime: -1 }, { partialFilterExpression: { - hasUnreadBadFeedback: true + hasUnreadBadFeedback: true, + deleteTime: null } } ); diff --git a/packages/web/components/common/Avatar/index.tsx b/packages/web/components/common/Avatar/index.tsx index 8b7abb30f..6f4140693 100644 --- a/packages/web/components/common/Avatar/index.tsx +++ b/packages/web/components/common/Avatar/index.tsx @@ -6,7 +6,11 @@ import MyIcon from '../Icon'; import { iconPaths } from '../Icon/constants'; import MyImage from '../Image/MyImage'; -const Avatar = ({ w = '30px', src, ...props }: ImageProps) => { +const Avatar = ({ + w = '30px', + src, + ...props +}: Omit & { src?: string | null }) => { // @ts-ignore const isIcon = !!iconPaths[src as any]; diff --git a/packages/web/components/common/MyPopover/PopoverConfirm.tsx b/packages/web/components/common/MyPopover/PopoverConfirm.tsx index a4b9eea8d..cbcbf4626 100644 --- a/packages/web/components/common/MyPopover/PopoverConfirm.tsx +++ b/packages/web/components/common/MyPopover/PopoverConfirm.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React from 'react'; import { useTranslation } from 'next-i18next'; import MyIcon from '../Icon'; import { useRequest2 } from '../../../hooks/useRequest'; @@ -11,9 +11,9 @@ import { HStack, Box, Button, - PopoverArrow, - Portal + PopoverArrow } from '@chakra-ui/react'; +import { useMemoEnhance } from '../../../hooks/useMemoEnhance'; const PopoverConfirm = ({ content, @@ -32,13 +32,13 @@ const PopoverConfirm = ({ Trigger: React.ReactNode; placement?: PlacementWithLogical; offset?: [number, number]; - onConfirm: () => any; + onConfirm: () => Promise | any; confirmText?: string; cancelText?: string; }) => { const { t } = useTranslation(); - const map = useMemo(() => { + const map = useMemoEnhance(() => { const map = { info: { variant: 'primary', @@ -56,7 +56,7 @@ const PopoverConfirm = ({ const firstFieldRef = React.useRef(null); const { onOpen, onClose, isOpen } = useDisclosure(); - const { runAsync: onclickConfirm, loading } = useRequest2(onConfirm, { + const { runAsync: onclickConfirm, loading } = useRequest2(async () => onConfirm(), { onSuccess: onClose }); @@ -90,7 +90,14 @@ const PopoverConfirm = ({ {showCancel && ( - )} diff --git a/packages/web/hooks/useTableMultipleSelect.tsx b/packages/web/hooks/useTableMultipleSelect.tsx index 57166c9ae..25373d64a 100644 --- a/packages/web/hooks/useTableMultipleSelect.tsx +++ b/packages/web/hooks/useTableMultipleSelect.tsx @@ -64,15 +64,20 @@ export const useTableMultipleSelect = ({ children, Controler, activedStyles, + activeBg, ...props - }: { children?: ReactNode; activedStyles?: FlexProps; Controler: ReactNode } & FlexProps) => { + }: { + children?: ReactNode; + activeBg?: string; + activedStyles?: FlexProps; + Controler: ReactNode; + } & FlexProps) => { return hasSelections || !!children ? ( 0 ? activeBg : 'transparent'} px={6} - pt={4} - pb={2} + py={2} alignItems="center" {...props} {...activedStyles} diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index 29ca6468b..7989592cc 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -71,6 +71,8 @@ "confirm_copy_app_tip": "The system will create an app with the same configuration for you, but permissions will not be copied. Please confirm!", "confirm_del_app_tip": "Confirm to delete this Agent? \nDeleting an Agent will delete its associated conversation records as well.", "confirm_del_tool_tip": "Confirm to delete this tool? \nDeleting a tool will delete its associated conversation records, and the Agent that uses the tool may not function properly.", + "confirm_delete_chat_content": "Are you sure you want to delete this chat? This action cannot be undone!", + "confirm_delete_chats": "Are you sure you want to delete {{n}} conversation records? \nThe records will be permanently deleted!", "confirm_delete_folder_tip": "When you delete this folder, all applications and corresponding chat records under it will be deleted.", "confirm_delete_tool": "Confirm to delete this tool?", "copilot_config_message": "Current Node Configuration Information: \n Code Type: {{codeType}} \n Current Code: \\\\`\\\\`\\\\`{{codeType}} \n{{code}} \\\\`\\\\`\\\\` \n Input Parameters: {{inputs}} \n Output Parameters: {{outputs}}", diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index 2345bea85..7330c6d59 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -73,6 +73,8 @@ "confirm_copy_app_tip": "系统将为您创建一个相同配置应用,但权限不会进行复制,请确认!", "confirm_del_app_tip": "确认删除该 Agent?删除 Agent 会将其关联的对话记录一并删除。", "confirm_del_tool_tip": "确认删除该工具?删除工具会将其关联的对话记录一并删除,并且使用到该工具的 Agent 可能无法正常运行。", + "confirm_delete_chat_content": "确定要删除这个对话吗?此操作不可恢复!", + "confirm_delete_chats": "确认删除 {{n}} 组对话记录?记录将会永久删除!", "confirm_delete_folder_tip": "删除该文件夹时,将会删除它下面所有应用及对应的聊天记录。", "confirm_delete_tool": "确认删除该工具?", "copilot_config_message": "`当前节点配置信息: \n代码类型:{{codeType}} \n当前代码: \\`\\`\\`{{codeType}} \n{{code}} \\`\\`\\` \n输入参数: {{inputs}} \n输出参数: {{outputs}}`", diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index a444b3f08..dcf9eba8f 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -71,6 +71,8 @@ "confirm_copy_app_tip": "系統將為您建立一個相同設定的應用程式,但權限不會複製,請確認!", "confirm_del_app_tip": "確認刪除該 Agent?\n刪除 Agent 會將其關聯的對話記錄一併刪除。", "confirm_del_tool_tip": "確認刪除該工具?\n刪除工具會將其關聯的對話記錄一併刪除,並且使用到該工具的 Agent 可能無法正常運行。", + "confirm_delete_chat_content": "確定要刪除這個對話嗎?\n此操作不可恢復!", + "confirm_delete_chats": "確認刪除 {{n}} 組對話記錄?\n記錄將會永久刪除!", "confirm_delete_folder_tip": "刪除該文件夾時,將會刪除它下面所有應用及對應的聊天記錄。", "confirm_delete_tool": "確認刪除該工具?", "copilot_config_message": "當前節點配置信息: \n代碼類型:{{codeType}} \n當前代碼: \\\\`\\\\`\\\\`{{codeType}} \n{{code}} \\\\`\\\\`\\\\` \n輸入參數: {{inputs}} \n輸出參數: {{outputs}}", diff --git a/projects/app/src/global/core/chat/api.d.ts b/projects/app/src/global/core/chat/api.d.ts index 6c804f30b..8be1c1992 100644 --- a/projects/app/src/global/core/chat/api.d.ts +++ b/projects/app/src/global/core/chat/api.d.ts @@ -1,11 +1,12 @@ import type { AppChatConfigType, AppTTSConfigType } from '@fastgpt/global/core/app/type.d'; import type { AdminFbkType } from '@fastgpt/global/core/chat/type'; import { ChatItemType } from '@fastgpt/global/core/chat/type'; -import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat.d'; +import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat'; import type { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { RequestPaging } from '@/types'; import type { GetChatTypeEnum } from '@/global/core/chat/constants'; import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants'; + export type GetChatSpeechProps = OutLinkChatAuthProps & { appId: string; ttsConfig: AppTTSConfigType; @@ -56,33 +57,6 @@ export type InitChatResponse = { }; }; -/* ---------- history ----------- */ -export type GetHistoriesProps = OutLinkChatAuthProps & { - appId?: string; - source?: `${ChatSourceEnum}`; - - startCreateTime?: string; - endCreateTime?: string; - startUpdateTime?: string; - endUpdateTime?: string; -}; - -export type UpdateHistoryProps = OutLinkChatAuthProps & { - appId: string; - chatId: string; - title?: string; - customTitle?: string; - top?: boolean; -}; - -export type DelHistoryProps = OutLinkChatAuthProps & { - appId: string; - chatId: string; -}; -export type ClearHistoriesProps = OutLinkChatAuthProps & { - appId: string; -}; - /* -------- chat item ---------- */ export type DeleteChatItemProps = OutLinkChatAuthProps & { appId: string; @@ -90,16 +64,3 @@ export type DeleteChatItemProps = OutLinkChatAuthProps & { contentId?: string; delFile?: boolean; }; - -export type AdminUpdateFeedbackParams = AdminFbkType & { - appId: string; - chatId: string; - dataId: string; -}; - -export type CloseCustomFeedbackParams = { - appId: string; - chatId: string; - dataId: string; - index: number; -}; diff --git a/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx b/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx index 320973154..0398f2595 100644 --- a/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx +++ b/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx @@ -10,7 +10,8 @@ import { Td, Th, Thead, - Tr + Tr, + Checkbox } from '@chakra-ui/react'; import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants'; import { ChatSourceMap } from '@fastgpt/global/core/chat/constants'; @@ -53,6 +54,10 @@ import MyBox from '@fastgpt/web/components/common/MyBox'; import { useContextSelector } from 'use-context-selector'; import { AppContext } from '../context'; import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance'; +import { batchDeleteChatHistories } from '@/web/core/chat/history/api'; +import { useTableMultipleSelect } from '@fastgpt/web/hooks/useTableMultipleSelect'; +import MyIconButton from '@fastgpt/web/components/common/Icon/button'; +import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; const DetailLogsModal = dynamic(() => import('./DetailLogsModal')); @@ -213,7 +218,34 @@ const LogTable = ({ refreshDeps: [params] }); - const HeaderRenderMap = useMemo( + const { + selectedItems, + toggleSelect, + isSelected, + FloatingActionBar, + isSelecteAll, + selectAllTrigger + } = useTableMultipleSelect({ + list: logs, + getItemId: (item) => item._id + }); + const chatIds = useMemoEnhance(() => selectedItems.map((item) => item.chatId), [selectedItems]); + + const { openConfirm: openConfirmDelete, ConfirmModal: ConfirmDeleteModal } = useConfirm({ + type: 'delete' + }); + + const { runAsync: handleDelete } = useRequest2( + async (chatIds: string[]) => { + await batchDeleteChatHistories({ appId, chatIds }); + await getData(pageNum); + }, + { + successToast: t('common:delete_success') + } + ); + + const HeaderRenderMap = useMemoEnhance( () => ({ [AppLogKeysEnum.SOURCE]: {t('app:logs_keys_source')}, [AppLogKeysEnum.CREATED_TIME]: ( @@ -503,9 +535,13 @@ const LogTable = ({ + {logKeys .filter((logKey) => logKey.enable) .map((logKey) => HeaderRenderMap[logKey.key])} + @@ -516,12 +552,28 @@ const LogTable = ({ key={item._id} _hover={{ bg: 'myWhite.600' }} cursor={'pointer'} - title={t('common:core.view_chat_detail')} onClick={() => setDetailLogsId(item.chatId)} > + {logKeys .filter((logKey) => logKey.enable) .map((logKey) => cellRenderMap[logKey.key as AppLogKeysEnum])} + ); })} @@ -530,11 +582,32 @@ const LogTable = ({ {logs.length === 0 && !isLoading && } - {total >= pageSize && ( - - - - )} + + + + } + > + {total > pageSize && ( + + + + )} + {!!detailLogsId && ( )} + + ); }; diff --git a/projects/app/src/pageComponents/dataset/detail/CollectionCard/index.tsx b/projects/app/src/pageComponents/dataset/detail/CollectionCard/index.tsx index 157af6265..020679e94 100644 --- a/projects/app/src/pageComponents/dataset/detail/CollectionCard/index.tsx +++ b/projects/app/src/pageComponents/dataset/detail/CollectionCard/index.tsx @@ -450,6 +450,7 @@ const CollectionCard = () => {
+ + {t('common:Action')}
+ e.stopPropagation()}> + toggleSelect(item)} /> + + e.stopPropagation()}> + handleDelete([item.chatId])} + Trigger={ + + + + } + /> +