mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-05 10:05:05 +08:00
Feature: Memory interface integration testing (#11833)
### What problem does this PR solve? Feature: Memory interface integration testing ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
128
web/src/pages/memory/memory-message/hook.ts
Normal file
128
web/src/pages/memory/memory-message/hook.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import message from '@/components/ui/message';
|
||||
import { useHandleSearchChange } from '@/hooks/logic-hooks';
|
||||
import memoryService, { getMemoryDetailById } from '@/services/memory-service';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { t } from 'i18next';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useParams, useSearchParams } from 'umi';
|
||||
import { MemoryApiAction } from '../constant';
|
||||
import {
|
||||
IMessageContentProps,
|
||||
IMessageTableProps,
|
||||
} from '../memory-message/interface';
|
||||
import { IMessageInfo } from './interface';
|
||||
|
||||
export const useFetchMemoryMessageList = () => {
|
||||
const { id } = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const memoryBaseId = searchParams.get('id') || id;
|
||||
const { handleInputChange, searchString, pagination, setPagination } =
|
||||
useHandleSearchChange();
|
||||
|
||||
let queryKey: (MemoryApiAction | number)[] = [
|
||||
MemoryApiAction.FetchMemoryMessage,
|
||||
];
|
||||
|
||||
const { data, isFetching: loading } = useQuery<IMessageTableProps>({
|
||||
queryKey: [...queryKey, searchString, pagination],
|
||||
initialData: {} as IMessageTableProps,
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
if (memoryBaseId) {
|
||||
const { data } = await getMemoryDetailById(memoryBaseId as string, {
|
||||
keyword: searchString,
|
||||
page: pagination.current,
|
||||
page_size: pagination.pageSize,
|
||||
});
|
||||
return data?.data ?? {};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
handleInputChange,
|
||||
searchString,
|
||||
pagination,
|
||||
setPagination,
|
||||
};
|
||||
};
|
||||
|
||||
export const useMessageAction = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const [selectedMessage, setSelectedMessage] = useState<IMessageInfo>(
|
||||
{} as IMessageInfo,
|
||||
);
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
const handleClickDeleteMessage = useCallback((message: IMessageInfo) => {
|
||||
console.log('handleClickDeleteMessage', message);
|
||||
setSelectedMessage(message);
|
||||
setShowDeleteDialog(true);
|
||||
}, []);
|
||||
|
||||
const handleDeleteMessage = useCallback(() => {
|
||||
// delete message
|
||||
memoryService.deleteMemoryMessage(selectedMessage.message_id).then(() => {
|
||||
message.success(t('message.deleted'));
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [MemoryApiAction.FetchMemoryMessage],
|
||||
});
|
||||
});
|
||||
setShowDeleteDialog(false);
|
||||
}, [selectedMessage.message_id, queryClient]);
|
||||
|
||||
const [showMessageContentDialog, setShowMessageContentDialog] =
|
||||
useState(false);
|
||||
const [selectedMessageContent, setSelectedMessageContent] =
|
||||
useState<IMessageContentProps>({} as IMessageContentProps);
|
||||
|
||||
const {
|
||||
data: messageContent,
|
||||
isPending: fetchMessageContentLoading,
|
||||
mutateAsync: fetchMessageContent,
|
||||
} = useMutation<IMessageContentProps>({
|
||||
mutationKey: [
|
||||
MemoryApiAction.FetchMessageContent,
|
||||
selectedMessage.message_id,
|
||||
],
|
||||
mutationFn: async () => {
|
||||
setShowMessageContentDialog(true);
|
||||
const res = await memoryService.getMessageContent(
|
||||
selectedMessage.message_id,
|
||||
);
|
||||
if (res.data.code === 0) {
|
||||
setSelectedMessageContent(res.data.data);
|
||||
} else {
|
||||
message.error(res.data.message);
|
||||
}
|
||||
return res.data.data;
|
||||
},
|
||||
});
|
||||
|
||||
const handleClickMessageContentDialog = useCallback(
|
||||
(message: IMessageInfo) => {
|
||||
setSelectedMessage(message);
|
||||
fetchMessageContent();
|
||||
},
|
||||
[fetchMessageContent],
|
||||
);
|
||||
|
||||
return {
|
||||
selectedMessage,
|
||||
setSelectedMessage,
|
||||
showDeleteDialog,
|
||||
setShowDeleteDialog,
|
||||
handleClickDeleteMessage,
|
||||
handleDeleteMessage,
|
||||
messageContent,
|
||||
fetchMessageContentLoading,
|
||||
fetchMessageContent,
|
||||
selectedMessageContent,
|
||||
showMessageContentDialog,
|
||||
setShowMessageContentDialog,
|
||||
handleClickMessageContentDialog,
|
||||
};
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
import ListFilterBar from '@/components/list-filter-bar';
|
||||
import { t } from 'i18next';
|
||||
import { useFetchMemoryMessageList } from '../hooks/use-memory-messages';
|
||||
import { useFetchMemoryMessageList } from './hook';
|
||||
import { MemoryTable } from './message-table';
|
||||
|
||||
export default function MemoryMessage() {
|
||||
|
||||
@ -17,3 +17,8 @@ export interface IMessageTableProps {
|
||||
messages: { message_list: Array<IMessageInfo>; total: number };
|
||||
storage_type: string;
|
||||
}
|
||||
|
||||
export interface IMessageContentProps {
|
||||
content: string;
|
||||
content_embed: string;
|
||||
}
|
||||
|
||||
@ -1,3 +1,23 @@
|
||||
import {
|
||||
ConfirmDeleteDialog,
|
||||
ConfirmDeleteDialogNode,
|
||||
} from '@/components/confirm-delete-dialog';
|
||||
import { EmptyType } from '@/components/empty/constant';
|
||||
import Empty from '@/components/empty/empty';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Modal } from '@/components/ui/modal/modal';
|
||||
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { Pagination } from '@/interfaces/common';
|
||||
import { replaceText } from '@/pages/dataset/process-log-modal';
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
@ -10,26 +30,13 @@ import {
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import * as React from 'react';
|
||||
|
||||
import { EmptyType } from '@/components/empty/constant';
|
||||
import Empty from '@/components/empty/empty';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { Pagination } from '@/interfaces/common';
|
||||
import { t } from 'i18next';
|
||||
import { pick } from 'lodash';
|
||||
import { Eraser, TextSelect } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { Copy, Eraser, TextSelect } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { useMessageAction } from './hook';
|
||||
import { IMessageInfo } from './interface';
|
||||
|
||||
export type MemoryTableProps = {
|
||||
@ -51,13 +58,27 @@ export function MemoryTable({
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [copied, setCopied] = useState(false);
|
||||
const {
|
||||
showDeleteDialog,
|
||||
setShowDeleteDialog,
|
||||
handleClickDeleteMessage,
|
||||
selectedMessage,
|
||||
handleDeleteMessage,
|
||||
|
||||
fetchMessageContent,
|
||||
selectedMessageContent,
|
||||
showMessageContentDialog,
|
||||
setShowMessageContentDialog,
|
||||
handleClickMessageContentDialog,
|
||||
} = useMessageAction();
|
||||
|
||||
// Define columns for the memory table
|
||||
const columns: ColumnDef<IMessageInfo>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
accessorKey: 'session_id',
|
||||
header: () => <span>{t('memoryDetail.messages.sessionId')}</span>,
|
||||
header: () => <span>{t('memory.messages.sessionId')}</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-sm font-medium ">
|
||||
{row.getValue('session_id')}
|
||||
@ -66,7 +87,7 @@ export function MemoryTable({
|
||||
},
|
||||
{
|
||||
accessorKey: 'agent_name',
|
||||
header: () => <span>{t('memoryDetail.messages.agent')}</span>,
|
||||
header: () => <span>{t('memory.messages.agent')}</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-sm font-medium ">
|
||||
{row.getValue('agent_name')}
|
||||
@ -75,7 +96,7 @@ export function MemoryTable({
|
||||
},
|
||||
{
|
||||
accessorKey: 'message_type',
|
||||
header: () => <span>{t('memoryDetail.messages.type')}</span>,
|
||||
header: () => <span>{t('memory.messages.type')}</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-sm font-medium capitalize">
|
||||
{row.getValue('message_type')}
|
||||
@ -84,28 +105,28 @@ export function MemoryTable({
|
||||
},
|
||||
{
|
||||
accessorKey: 'valid_at',
|
||||
header: () => <span>{t('memoryDetail.messages.validDate')}</span>,
|
||||
header: () => <span>{t('memory.messages.validDate')}</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-sm ">{row.getValue('valid_at')}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'forget_at',
|
||||
header: () => <span>{t('memoryDetail.messages.forgetAt')}</span>,
|
||||
header: () => <span>{t('memory.messages.forgetAt')}</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-sm ">{row.getValue('forget_at')}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'source_id',
|
||||
header: () => <span>{t('memoryDetail.messages.source')}</span>,
|
||||
header: () => <span>{t('memory.messages.source')}</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-sm ">{row.getValue('source_id')}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: () => <span>{t('memoryDetail.messages.enable')}</span>,
|
||||
header: () => <span>{t('memory.messages.enable')}</span>,
|
||||
cell: ({ row }) => {
|
||||
const isEnabled = row.getValue('status') as boolean;
|
||||
return (
|
||||
@ -117,19 +138,28 @@ export function MemoryTable({
|
||||
},
|
||||
{
|
||||
accessorKey: 'action',
|
||||
header: () => <span>{t('memoryDetail.messages.action')}</span>,
|
||||
header: () => <span>{t('memory.messages.action')}</span>,
|
||||
meta: {
|
||||
cellClassName: 'w-12',
|
||||
},
|
||||
cell: () => (
|
||||
cell: ({ row }) => (
|
||||
<div className=" flex opacity-0 group-hover:opacity-100">
|
||||
<Button variant={'ghost'} className="bg-transparent">
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="bg-transparent"
|
||||
onClick={() => {
|
||||
handleClickMessageContentDialog(row.original);
|
||||
}}
|
||||
>
|
||||
<TextSelect />
|
||||
</Button>
|
||||
<Button
|
||||
variant={'delete'}
|
||||
className="bg-transparent"
|
||||
aria-label="Edit"
|
||||
onClick={() => {
|
||||
handleClickDeleteMessage(row.original);
|
||||
}}
|
||||
>
|
||||
<Eraser />
|
||||
</Button>
|
||||
@ -137,7 +167,7 @@ export function MemoryTable({
|
||||
),
|
||||
},
|
||||
],
|
||||
[],
|
||||
[handleClickDeleteMessage],
|
||||
);
|
||||
|
||||
const currentPagination = useMemo(() => {
|
||||
@ -210,6 +240,85 @@ export function MemoryTable({
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{showDeleteDialog && (
|
||||
<ConfirmDeleteDialog
|
||||
onOk={handleDeleteMessage}
|
||||
title={t('memory.messages.forgetMessage')}
|
||||
open={showDeleteDialog}
|
||||
onOpenChange={setShowDeleteDialog}
|
||||
content={{
|
||||
node: (
|
||||
<ConfirmDeleteDialogNode
|
||||
// avatar={{ avatar: selectedMessage.avatar, name: selectedMessage.name }}
|
||||
name={
|
||||
t('memory.messages.sessionId') +
|
||||
': ' +
|
||||
selectedMessage.session_id
|
||||
}
|
||||
warnText={t('memory.messages.delMessageWarn')}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showMessageContentDialog && (
|
||||
<Modal
|
||||
title={t('memory.messages.content')}
|
||||
open={showMessageContentDialog}
|
||||
onOpenChange={setShowMessageContentDialog}
|
||||
className="!w-[640px]"
|
||||
footer={
|
||||
<div className="flex justify-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowMessageContentDialog(false)}
|
||||
className={
|
||||
'px-2 py-1 border border-border-button rounded-md hover:bg-bg-card hover:text-text-primary '
|
||||
}
|
||||
>
|
||||
{t('common.close')}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<div className="text-text-secondary text-sm">
|
||||
{t('memory.messages.sessionId')}:
|
||||
{selectedMessage.session_id}
|
||||
</div>
|
||||
{selectedMessageContent?.content && (
|
||||
<div className="w-full bg-accent-primary-5 whitespace-pre-line text-wrap rounded-lg h-fit max-h-[350px] overflow-y-auto scrollbar-auto px-2.5 py-1">
|
||||
{replaceText(selectedMessageContent?.content || '')}
|
||||
</div>
|
||||
)}
|
||||
{selectedMessageContent?.content_embed && (
|
||||
<div className="flex gap-2 items-center">
|
||||
<CopyToClipboard
|
||||
text={selectedMessageContent?.content_embed}
|
||||
onCopy={() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1000);
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="border border-border-button "
|
||||
>
|
||||
{t('memory.messages.contentEmbed')}
|
||||
<Copy />
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
{copied && (
|
||||
<span className="text-xs text-text-secondary">
|
||||
{t('memory.messages.copied')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-end py-4 absolute bottom-3 right-3">
|
||||
<RAGFlowPagination
|
||||
|
||||
Reference in New Issue
Block a user