mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Fix: Optimize code and fix ts type errors #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -16,7 +16,7 @@ interface EditTagsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
|
const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
|
||||||
({ value = [], onChange }: EditTagsProps, ref) => {
|
({ value = [], onChange }: EditTagsProps) => {
|
||||||
const [inputVisible, setInputVisible] = useState(false);
|
const [inputVisible, setInputVisible] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|||||||
@ -1,22 +1,32 @@
|
|||||||
import Image from '@/components/image';
|
import Image from '@/components/image';
|
||||||
import SvgIcon from '@/components/svg-icon';
|
import SvgIcon from '@/components/svg-icon';
|
||||||
import { useFetchDocumentThumbnailsByIds, useGetDocumentUrl } from '@/hooks/document-hooks';
|
import {
|
||||||
|
useFetchDocumentThumbnailsByIds,
|
||||||
|
useGetDocumentUrl,
|
||||||
|
} from '@/hooks/document-hooks';
|
||||||
import { IReference, IReferenceChunk } from '@/interfaces/database/chat';
|
import { IReference, IReferenceChunk } from '@/interfaces/database/chat';
|
||||||
import { preprocessLaTeX, replaceThinkToSection, showImage } from '@/utils/chat';
|
import {
|
||||||
|
preprocessLaTeX,
|
||||||
|
replaceThinkToSection,
|
||||||
|
showImage,
|
||||||
|
} from '@/utils/chat';
|
||||||
import { getExtension } from '@/utils/document-util';
|
import { getExtension } from '@/utils/document-util';
|
||||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
import { Button, Flex, Popover, Tooltip } from 'antd';
|
import { Button, Flex, Popover, Tooltip } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
import 'katex/dist/katex.min.css';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { pipe } from 'lodash/fp';
|
import { pipe } from 'lodash/fp';
|
||||||
import 'katex/dist/katex.min.css';
|
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Markdown from 'react-markdown';
|
import Markdown from 'react-markdown';
|
||||||
import reactStringReplace from 'react-string-replace';
|
import reactStringReplace from 'react-string-replace';
|
||||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||||
import { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
import {
|
||||||
|
oneDark,
|
||||||
|
oneLight,
|
||||||
|
} from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||||
import rehypeKatex from 'rehype-katex';
|
import rehypeKatex from 'rehype-katex';
|
||||||
import rehypeRaw from 'rehype-raw';
|
import rehypeRaw from 'rehype-raw';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
@ -39,7 +49,8 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
clickDocumentButton?: (documentId: string, chunk: IReferenceChunk) => void;
|
clickDocumentButton?: (documentId: string, chunk: IReferenceChunk) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setDocumentIds, data: fileThumbnails } = useFetchDocumentThumbnailsByIds();
|
const { setDocumentIds, data: fileThumbnails } =
|
||||||
|
useFetchDocumentThumbnailsByIds();
|
||||||
const getDocumentUrl = useGetDocumentUrl();
|
const getDocumentUrl = useGetDocumentUrl();
|
||||||
const isDarkTheme = useIsDarkTheme();
|
const isDarkTheme = useIsDarkTheme();
|
||||||
|
|
||||||
@ -51,23 +62,37 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const docAggs = reference?.doc_aggs;
|
const docAggs = reference?.doc_aggs;
|
||||||
const docList = Array.isArray(docAggs) ? docAggs : Object.values(docAggs ?? {});
|
const docList = Array.isArray(docAggs)
|
||||||
|
? docAggs
|
||||||
|
: Object.values(docAggs ?? {});
|
||||||
setDocumentIds(docList.map((x: any) => x.doc_id).filter(Boolean));
|
setDocumentIds(docList.map((x: any) => x.doc_id).filter(Boolean));
|
||||||
}, [reference, setDocumentIds]);
|
}, [reference, setDocumentIds]);
|
||||||
|
|
||||||
const handleDocumentButtonClick = useCallback((documentId: string, chunk: IReferenceChunk, isPdf: boolean, documentUrl?: string) => () => {
|
const handleDocumentButtonClick = useCallback(
|
||||||
|
(
|
||||||
|
documentId: string,
|
||||||
|
chunk: IReferenceChunk,
|
||||||
|
isPdf: boolean,
|
||||||
|
documentUrl?: string,
|
||||||
|
) =>
|
||||||
|
() => {
|
||||||
if (!documentId) return;
|
if (!documentId) return;
|
||||||
if (!isPdf && documentUrl) {
|
if (!isPdf && documentUrl) {
|
||||||
window.open(documentUrl, '_blank');
|
window.open(documentUrl, '_blank');
|
||||||
} else if (clickDocumentButton) {
|
} else if (clickDocumentButton) {
|
||||||
clickDocumentButton(documentId, chunk);
|
clickDocumentButton(documentId, chunk);
|
||||||
}
|
}
|
||||||
}, [clickDocumentButton]);
|
},
|
||||||
|
[clickDocumentButton],
|
||||||
|
);
|
||||||
|
|
||||||
const rehypeWrapReference = () => (tree: any) => {
|
const rehypeWrapReference = () => (tree: any) => {
|
||||||
visitParents(tree, 'text', (node, ancestors) => {
|
visitParents(tree, 'text', (node, ancestors) => {
|
||||||
const latestAncestor = ancestors[ancestors.length - 1];
|
const latestAncestor = ancestors[ancestors.length - 1];
|
||||||
if (latestAncestor.tagName !== 'custom-typography' && latestAncestor.tagName !== 'code') {
|
if (
|
||||||
|
latestAncestor.tagName !== 'custom-typography' &&
|
||||||
|
latestAncestor.tagName !== 'code'
|
||||||
|
) {
|
||||||
node.type = 'element';
|
node.type = 'element';
|
||||||
node.tagName = 'custom-typography';
|
node.tagName = 'custom-typography';
|
||||||
node.properties = {};
|
node.properties = {};
|
||||||
@ -76,56 +101,120 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getReferenceInfo = useCallback((chunkIndex: number) => {
|
const getReferenceInfo = useCallback(
|
||||||
|
(chunkIndex: number) => {
|
||||||
const chunkItem = reference?.chunks?.[chunkIndex];
|
const chunkItem = reference?.chunks?.[chunkIndex];
|
||||||
if (!chunkItem) return null;
|
if (!chunkItem) return null;
|
||||||
const docAggsArray = Array.isArray(reference?.doc_aggs) ? reference.doc_aggs : Object.values(reference?.doc_aggs ?? {});
|
const docAggsArray = Array.isArray(reference?.doc_aggs)
|
||||||
const document = docAggsArray.find((x: any) => x?.doc_id === chunkItem?.document_id) as any;
|
? reference.doc_aggs
|
||||||
|
: Object.values(reference?.doc_aggs ?? {});
|
||||||
|
const document = docAggsArray.find(
|
||||||
|
(x: any) => x?.doc_id === chunkItem?.document_id,
|
||||||
|
) as any;
|
||||||
const documentId = document?.doc_id;
|
const documentId = document?.doc_id;
|
||||||
const documentUrl = document?.url ?? (documentId ? getDocumentUrl(documentId) : undefined);
|
const documentUrl =
|
||||||
|
document?.url ?? (documentId ? getDocumentUrl(documentId) : undefined);
|
||||||
const fileThumbnail = documentId ? fileThumbnails[documentId] : '';
|
const fileThumbnail = documentId ? fileThumbnails[documentId] : '';
|
||||||
const fileExtension = documentId ? getExtension(document?.doc_name ?? '') : '';
|
const fileExtension = documentId
|
||||||
return { documentUrl, fileThumbnail, fileExtension, imageId: chunkItem.image_id, chunkItem, documentId, document };
|
? getExtension(document?.doc_name ?? '')
|
||||||
}, [fileThumbnails, reference, getDocumentUrl]);
|
: '';
|
||||||
|
return {
|
||||||
|
documentUrl,
|
||||||
|
fileThumbnail,
|
||||||
|
fileExtension,
|
||||||
|
imageId: chunkItem.image_id,
|
||||||
|
chunkItem,
|
||||||
|
documentId,
|
||||||
|
document,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[fileThumbnails, reference, getDocumentUrl],
|
||||||
|
);
|
||||||
|
|
||||||
const getPopoverContent = useCallback((chunkIndex: number) => {
|
const getPopoverContent = useCallback(
|
||||||
|
(chunkIndex: number) => {
|
||||||
const info = getReferenceInfo(chunkIndex);
|
const info = getReferenceInfo(chunkIndex);
|
||||||
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return <div className="p-2 text-xs text-red-500">Error: Missing document information.</div>;
|
return (
|
||||||
|
<div className="p-2 text-xs text-red-500">
|
||||||
|
Error: Missing document information.
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { documentUrl, fileThumbnail, fileExtension, imageId, chunkItem, documentId, document } = info;
|
const {
|
||||||
|
documentUrl,
|
||||||
|
fileThumbnail,
|
||||||
|
fileExtension,
|
||||||
|
imageId,
|
||||||
|
chunkItem,
|
||||||
|
documentId,
|
||||||
|
document,
|
||||||
|
} = info;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={`popover-content-${chunkItem.id}`} className="flex gap-2 widget-citation-content">
|
<div
|
||||||
|
key={`popover-content-${chunkItem.id}`}
|
||||||
|
className="flex gap-2 widget-citation-content"
|
||||||
|
>
|
||||||
{imageId && (
|
{imageId && (
|
||||||
<Popover placement="left" content={<Image id={imageId} className="max-w-[80vw] max-h-[60vh] rounded" />}>
|
<Popover
|
||||||
<Image id={imageId} className="w-24 h-24 object-contain rounded m-1 cursor-pointer" />
|
placement="left"
|
||||||
|
content={
|
||||||
|
<Image
|
||||||
|
id={imageId}
|
||||||
|
className="max-w-[80vw] max-h-[60vh] rounded"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
id={imageId}
|
||||||
|
className="w-24 h-24 object-contain rounded m-1 cursor-pointer"
|
||||||
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
)}
|
)}
|
||||||
<div className="space-y-2 flex-1 min-w-0">
|
<div className="space-y-2 flex-1 min-w-0">
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(chunkItem?.content ?? '') }}
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: DOMPurify.sanitize(chunkItem?.content ?? ''),
|
||||||
|
}}
|
||||||
className="max-h-[250px] overflow-y-auto text-xs leading-relaxed p-2 bg-gray-50 dark:bg-gray-800 rounded prose-sm"
|
className="max-h-[250px] overflow-y-auto text-xs leading-relaxed p-2 bg-gray-50 dark:bg-gray-800 rounded prose-sm"
|
||||||
></div>
|
></div>
|
||||||
{documentId && (
|
{documentId && (
|
||||||
<Flex gap={'small'} align="center">
|
<Flex gap={'small'} align="center">
|
||||||
{fileThumbnail ? (
|
{fileThumbnail ? (
|
||||||
<img src={fileThumbnail} alt={document?.doc_name} className="w-6 h-6 rounded" />
|
<img
|
||||||
|
src={fileThumbnail}
|
||||||
|
alt={document?.doc_name}
|
||||||
|
className="w-6 h-6 rounded"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SvgIcon name={`file-icon/${fileExtension}`} width={20} />
|
<SvgIcon name={`file-icon/${fileExtension}`} width={20} />
|
||||||
)}
|
)}
|
||||||
<Tooltip title={!documentUrl && fileExtension !== 'pdf' ? 'Document link unavailable' : document.doc_name}>
|
<Tooltip
|
||||||
|
title={
|
||||||
|
!documentUrl && fileExtension !== 'pdf'
|
||||||
|
? 'Document link unavailable'
|
||||||
|
: document.doc_name
|
||||||
|
}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
className="p-0 text-xs break-words h-auto text-left flex-1"
|
className="p-0 text-xs break-words h-auto text-left flex-1"
|
||||||
onClick={handleDocumentButtonClick(documentId, chunkItem, fileExtension === 'pdf', documentUrl)}
|
onClick={handleDocumentButtonClick(
|
||||||
|
documentId,
|
||||||
|
chunkItem,
|
||||||
|
fileExtension === 'pdf',
|
||||||
|
documentUrl,
|
||||||
|
)}
|
||||||
disabled={!documentUrl && fileExtension !== 'pdf'}
|
disabled={!documentUrl && fileExtension !== 'pdf'}
|
||||||
style={{ whiteSpace: 'normal' }}
|
style={{ whiteSpace: 'normal' }}
|
||||||
>
|
>
|
||||||
<span className="truncate">{document?.doc_name ?? 'Unnamed Document'}</span>
|
<span className="truncate">
|
||||||
|
{document?.doc_name ?? 'Unnamed Document'}
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -133,33 +222,52 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [getReferenceInfo, handleDocumentButtonClick]);
|
},
|
||||||
|
[getReferenceInfo, handleDocumentButtonClick],
|
||||||
|
);
|
||||||
|
|
||||||
const renderReference = useCallback((text: string) => {
|
const renderReference = useCallback(
|
||||||
|
(text: string) => {
|
||||||
return reactStringReplace(text, currentReg, (match, i) => {
|
return reactStringReplace(text, currentReg, (match, i) => {
|
||||||
const chunkIndex = getChunkIndex(match);
|
const chunkIndex = getChunkIndex(match);
|
||||||
const info = getReferenceInfo(chunkIndex);
|
const info = getReferenceInfo(chunkIndex);
|
||||||
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return <Tooltip key={`err-tooltip-${i}`} title="Reference unavailable"><InfoCircleOutlined className={styles.referenceIcon} /></Tooltip>;
|
return (
|
||||||
|
<Tooltip key={`err-tooltip-${i}`} title="Reference unavailable">
|
||||||
|
<InfoCircleOutlined className={styles.referenceIcon} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { imageId, chunkItem, documentId, fileExtension, documentUrl } = info;
|
const { imageId, chunkItem, documentId, fileExtension, documentUrl } =
|
||||||
|
info;
|
||||||
|
|
||||||
if (showImage(chunkItem?.doc_type)) {
|
if (showImage(chunkItem?.doc_type)) {
|
||||||
return <Image key={`img-${i}`} id={imageId} className="block object-contain max-w-full max-h-48 rounded my-2 cursor-pointer" onClick={handleDocumentButtonClick(documentId, chunkItem, fileExtension === 'pdf', documentUrl)} />;
|
return (
|
||||||
|
<Image
|
||||||
|
key={`img-${i}`}
|
||||||
|
id={imageId}
|
||||||
|
className="block object-contain max-w-full max-h-48 rounded my-2 cursor-pointer"
|
||||||
|
onClick={handleDocumentButtonClick(
|
||||||
|
documentId,
|
||||||
|
chunkItem,
|
||||||
|
fileExtension === 'pdf',
|
||||||
|
documentUrl,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover content={getPopoverContent(chunkIndex)} key={`popover-${i}`}>
|
||||||
content={getPopoverContent(chunkIndex)}
|
|
||||||
key={`popover-${i}`}
|
|
||||||
>
|
|
||||||
<InfoCircleOutlined className={styles.referenceIcon} />
|
<InfoCircleOutlined className={styles.referenceIcon} />
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, [getPopoverContent, getReferenceInfo, handleDocumentButtonClick]);
|
},
|
||||||
|
[getPopoverContent, getReferenceInfo, handleDocumentButtonClick],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="floating-chat-widget">
|
<div className="floating-chat-widget">
|
||||||
@ -167,9 +275,12 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
rehypePlugins={[rehypeWrapReference, rehypeKatex, rehypeRaw]}
|
rehypePlugins={[rehypeWrapReference, rehypeKatex, rehypeRaw]}
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
className="text-sm leading-relaxed space-y-2 prose-sm max-w-full"
|
className="text-sm leading-relaxed space-y-2 prose-sm max-w-full"
|
||||||
components={{
|
components={
|
||||||
'custom-typography': ({ children }: { children: string }) => renderReference(children),
|
{
|
||||||
|
'custom-typography': ({ children }: { children: string }) =>
|
||||||
|
renderReference(children),
|
||||||
code(props: any) {
|
code(props: any) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { children, className, node, ...rest } = props;
|
const { children, className, node, ...rest } = props;
|
||||||
const match = /language-(\w+)/.exec(className || '');
|
const match = /language-(\w+)/.exec(className || '');
|
||||||
return match ? (
|
return match ? (
|
||||||
@ -183,12 +294,19 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
{String(children).replace(/\n$/, '')}
|
{String(children).replace(/\n$/, '')}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
) : (
|
) : (
|
||||||
<code {...rest} className={classNames(className, 'text-wrap text-xs bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded')}>
|
<code
|
||||||
|
{...rest}
|
||||||
|
className={classNames(
|
||||||
|
className,
|
||||||
|
'text-wrap text-xs bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded',
|
||||||
|
)}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</code>
|
</code>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
} as any}
|
} as any
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{contentWithCursor}
|
{contentWithCursor}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
|
|||||||
@ -1,18 +1,10 @@
|
|||||||
import PdfDrawer from '@/components/pdf-drawer';
|
import PdfDrawer from '@/components/pdf-drawer';
|
||||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||||
import { MessageType, SharedFrom } from '@/constants/chat';
|
import { MessageType } from '@/constants/chat';
|
||||||
import { useFetchNextConversationSSE } from '@/hooks/chat-hooks';
|
|
||||||
import { useFetchFlowSSE } from '@/hooks/flow-hooks';
|
|
||||||
import { useFetchExternalChatInfo } from '@/hooks/use-chat-request';
|
import { useFetchExternalChatInfo } from '@/hooks/use-chat-request';
|
||||||
import i18n from '@/locales/config';
|
import i18n from '@/locales/config';
|
||||||
import { MessageCircle, Minimize2, Send, X } from 'lucide-react';
|
import { MessageCircle, Minimize2, Send, X } from 'lucide-react';
|
||||||
import React, {
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import {
|
import {
|
||||||
useGetSharedChatSearchParams,
|
useGetSharedChatSearchParams,
|
||||||
useSendSharedMessage,
|
useSendSharedMessage,
|
||||||
@ -28,12 +20,7 @@ const FloatingChatWidget = () => {
|
|||||||
const [isLoaded, setIsLoaded] = useState(false);
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const {
|
const { sharedId: conversationId, locale } = useGetSharedChatSearchParams();
|
||||||
sharedId: conversationId,
|
|
||||||
from,
|
|
||||||
locale,
|
|
||||||
visibleAvatar,
|
|
||||||
} = useGetSharedChatSearchParams();
|
|
||||||
|
|
||||||
// Check if we're in button-only mode or window-only mode
|
// Check if we're in button-only mode or window-only mode
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
@ -58,14 +45,6 @@ const FloatingChatWidget = () => {
|
|||||||
|
|
||||||
const { data: chatInfo } = useFetchExternalChatInfo();
|
const { data: chatInfo } = useFetchExternalChatInfo();
|
||||||
|
|
||||||
const useFetchAvatar = useMemo(() => {
|
|
||||||
return from === SharedFrom.Agent
|
|
||||||
? useFetchFlowSSE
|
|
||||||
: useFetchNextConversationSSE;
|
|
||||||
}, [from]);
|
|
||||||
|
|
||||||
const { data: avatarData } = useFetchAvatar();
|
|
||||||
|
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
|
|
||||||
@ -181,6 +160,40 @@ const FloatingChatWidget = () => {
|
|||||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
}, [displayMessages]);
|
}, [displayMessages]);
|
||||||
|
|
||||||
|
// Render different content based on mode
|
||||||
|
// Master mode - handles everything and creates second iframe dynamically
|
||||||
|
useEffect(() => {
|
||||||
|
if (mode !== 'master') return;
|
||||||
|
// Create the chat window iframe dynamically when needed
|
||||||
|
const createChatWindow = () => {
|
||||||
|
// Check if iframe already exists in parent document
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
type: 'CREATE_CHAT_WINDOW',
|
||||||
|
src: window.location.href.replace('mode=master', 'mode=window'),
|
||||||
|
},
|
||||||
|
'*',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
createChatWindow();
|
||||||
|
|
||||||
|
// Listen for our own toggle events to show/hide the dynamic iframe
|
||||||
|
const handleToggle = (e: MessageEvent) => {
|
||||||
|
if (e.source === window) return; // Ignore our own messages
|
||||||
|
|
||||||
|
const chatWindow = document.getElementById(
|
||||||
|
'dynamic-chat-window',
|
||||||
|
) as HTMLIFrameElement;
|
||||||
|
if (chatWindow && e.data.type === 'TOGGLE_CHAT') {
|
||||||
|
chatWindow.style.display = e.data.isOpen ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('message', handleToggle);
|
||||||
|
return () => window.removeEventListener('message', handleToggle);
|
||||||
|
}, [mode]);
|
||||||
|
|
||||||
// Play sound only when AI response is complete (not streaming chunks)
|
// Play sound only when AI response is complete (not streaming chunks)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (derivedMessages && derivedMessages.length > 0 && !sendLoading) {
|
if (derivedMessages && derivedMessages.length > 0 && !sendLoading) {
|
||||||
@ -271,41 +284,8 @@ const FloatingChatWidget = () => {
|
|||||||
|
|
||||||
const messageCount = displayMessages?.length || 0;
|
const messageCount = displayMessages?.length || 0;
|
||||||
|
|
||||||
// Render different content based on mode
|
|
||||||
if (mode === 'master') {
|
|
||||||
// Master mode - handles everything and creates second iframe dynamically
|
|
||||||
useEffect(() => {
|
|
||||||
// Create the chat window iframe dynamically when needed
|
|
||||||
const createChatWindow = () => {
|
|
||||||
// Check if iframe already exists in parent document
|
|
||||||
window.parent.postMessage(
|
|
||||||
{
|
|
||||||
type: 'CREATE_CHAT_WINDOW',
|
|
||||||
src: window.location.href.replace('mode=master', 'mode=window'),
|
|
||||||
},
|
|
||||||
'*',
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
createChatWindow();
|
|
||||||
|
|
||||||
// Listen for our own toggle events to show/hide the dynamic iframe
|
|
||||||
const handleToggle = (e: MessageEvent) => {
|
|
||||||
if (e.source === window) return; // Ignore our own messages
|
|
||||||
|
|
||||||
const chatWindow = document.getElementById(
|
|
||||||
'dynamic-chat-window',
|
|
||||||
) as HTMLIFrameElement;
|
|
||||||
if (chatWindow && e.data.type === 'TOGGLE_CHAT') {
|
|
||||||
chatWindow.style.display = e.data.isOpen ? 'block' : 'none';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', handleToggle);
|
|
||||||
return () => window.removeEventListener('message', handleToggle);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Show just the button in master mode
|
// Show just the button in master mode
|
||||||
|
if (mode === 'master') {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed bottom-6 right-6 z-50 transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
className={`fixed bottom-6 right-6 z-50 transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
||||||
@ -678,6 +658,7 @@ const FloatingChatWidget = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleSendMessage}
|
onClick={handleSendMessage}
|
||||||
disabled={!inputValue.trim() || sendLoading}
|
disabled={!inputValue.trim() || sendLoading}
|
||||||
className="p-3 bg-blue-600 text-white rounded-full hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
className="p-3 bg-blue-600 text-white rounded-full hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
|
|||||||
@ -70,7 +70,7 @@ export function UseGraphRagFormField({
|
|||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="parser_config.graphrag.use_graphrag"
|
name="parser_config.graphrag.use_graphrag"
|
||||||
render={({ field }) => (
|
render={() => (
|
||||||
<FormItem defaultChecked={false} className=" items-center space-y-0 ">
|
<FormItem defaultChecked={false} className=" items-center space-y-0 ">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<FormLabel
|
<FormLabel
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { Plus } from 'lucide-react';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ChunkTextMode } from '../../constant';
|
import { ChunkTextMode } from '../../constant';
|
||||||
interface ChunkResultBarProps {
|
interface ChunkResultBarProps {
|
||||||
changeChunkTextMode: React.Dispatch<React.SetStateAction<string | number>>;
|
changeChunkTextMode: (mode: ChunkTextMode) => void;
|
||||||
createChunk: (text: string) => void;
|
createChunk: (text: string) => void;
|
||||||
isReadonly: boolean;
|
isReadonly: boolean;
|
||||||
}
|
}
|
||||||
@ -15,7 +15,7 @@ export default ({
|
|||||||
isReadonly,
|
isReadonly,
|
||||||
}: ChunkResultBarProps) => {
|
}: ChunkResultBarProps) => {
|
||||||
const { t } = useTranslate('chunk');
|
const { t } = useTranslate('chunk');
|
||||||
const [textSelectValue, setTextSelectValue] = useState<string | number>(
|
const [textSelectValue, setTextSelectValue] = useState<ChunkTextMode>(
|
||||||
ChunkTextMode.Full,
|
ChunkTextMode.Full,
|
||||||
);
|
);
|
||||||
const textSelectOptions = [
|
const textSelectOptions = [
|
||||||
@ -23,7 +23,7 @@ export default ({
|
|||||||
{ label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse },
|
{ label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse },
|
||||||
];
|
];
|
||||||
|
|
||||||
const changeTextSelectValue = (value: string | number) => {
|
const changeTextSelectValue = (value: ChunkTextMode) => {
|
||||||
setTextSelectValue(value);
|
setTextSelectValue(value);
|
||||||
changeChunkTextMode(value);
|
changeChunkTextMode(value);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export interface FormatPreserveEditorProps {
|
|||||||
key: keyof typeof parserKeyMap | 'text' | 'html';
|
key: keyof typeof parserKeyMap | 'text' | 'html';
|
||||||
type: string;
|
type: string;
|
||||||
value: Array<{ [key: string]: string }>;
|
value: Array<{ [key: string]: string }>;
|
||||||
|
params: ComponentParams;
|
||||||
};
|
};
|
||||||
onSave: (value: any) => void;
|
onSave: (value: any) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { isArray } from 'lodash';
|
|||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { ChunkTextMode } from '../../constant';
|
import { ChunkTextMode } from '../../constant';
|
||||||
import styles from '../../index.less';
|
import styles from '../../index.less';
|
||||||
|
import { IChunk } from '../../interface';
|
||||||
import { useParserInit } from './hook';
|
import { useParserInit } from './hook';
|
||||||
import { IJsonContainerProps } from './interface';
|
import { IJsonContainerProps } from './interface';
|
||||||
export const parserKeyMap = {
|
export const parserKeyMap = {
|
||||||
@ -17,8 +18,6 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
|||||||
isChunck,
|
isChunck,
|
||||||
handleCheck,
|
handleCheck,
|
||||||
selectedChunkIds,
|
selectedChunkIds,
|
||||||
unescapeNewlines,
|
|
||||||
escapeNewlines,
|
|
||||||
onSave,
|
onSave,
|
||||||
className,
|
className,
|
||||||
textMode,
|
textMode,
|
||||||
@ -26,13 +25,8 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
|||||||
isReadonly,
|
isReadonly,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const { content, activeEditIndex, setActiveEditIndex, editDivRef } =
|
||||||
content,
|
useParserInit({ initialValue });
|
||||||
setContent,
|
|
||||||
activeEditIndex,
|
|
||||||
setActiveEditIndex,
|
|
||||||
editDivRef,
|
|
||||||
} = useParserInit({ initialValue });
|
|
||||||
|
|
||||||
const parserKey = useMemo(() => {
|
const parserKey = useMemo(() => {
|
||||||
const key =
|
const key =
|
||||||
@ -46,11 +40,12 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
|||||||
(e?: any, index?: number) => {
|
(e?: any, index?: number) => {
|
||||||
setActiveEditIndex(index);
|
setActiveEditIndex(index);
|
||||||
},
|
},
|
||||||
[setContent, setActiveEditIndex],
|
[setActiveEditIndex],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSave = useCallback(
|
const handleSave = useCallback(
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
|
if (Array.isArray(content.value)) {
|
||||||
const saveData = {
|
const saveData = {
|
||||||
...content,
|
...content,
|
||||||
value: content.value?.map((item, index) => {
|
value: content.value?.map((item, index) => {
|
||||||
@ -64,18 +59,21 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
onSave(saveData);
|
onSave(saveData as any);
|
||||||
|
}
|
||||||
setActiveEditIndex(undefined);
|
setActiveEditIndex(undefined);
|
||||||
},
|
},
|
||||||
[content, onSave],
|
[content, onSave, activeEditIndex, parserKey, setActiveEditIndex],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeEditIndex !== undefined && editDivRef.current) {
|
if (activeEditIndex !== undefined && editDivRef.current) {
|
||||||
editDivRef.current.focus();
|
editDivRef.current.focus();
|
||||||
|
if (typeof content.value !== 'string') {
|
||||||
editDivRef.current.textContent =
|
editDivRef.current.textContent =
|
||||||
content.value[activeEditIndex][parserKey];
|
content.value[activeEditIndex][parserKey];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}, [editDivRef, activeEditIndex, content, parserKey]);
|
}, [editDivRef, activeEditIndex, content, parserKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -122,7 +120,7 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
|||||||
{activeEditIndex !== index && (
|
{activeEditIndex !== index && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-text-secondary overflow-auto scrollbar-auto w-full',
|
'text-text-secondary overflow-auto scrollbar-auto w-full min-h-3',
|
||||||
{
|
{
|
||||||
[styles.contentEllipsis]:
|
[styles.contentEllipsis]:
|
||||||
textMode === ChunkTextMode.Ellipse,
|
textMode === ChunkTextMode.Ellipse,
|
||||||
@ -130,7 +128,8 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
|||||||
)}
|
)}
|
||||||
key={index}
|
key={index}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
clickChunk(item);
|
clickChunk(item as unknown as IChunk);
|
||||||
|
console.log('clickChunk', item, index);
|
||||||
if (!isReadonly) {
|
if (!isReadonly) {
|
||||||
handleEdit(e, index);
|
handleEdit(e, index);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,13 @@ import { cn } from '@/lib/utils';
|
|||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { ChunkTextMode } from '../../constant';
|
import { ChunkTextMode } from '../../constant';
|
||||||
import styles from '../../index.less';
|
import styles from '../../index.less';
|
||||||
|
import { IChunk } from '../../interface';
|
||||||
import { useParserInit } from './hook';
|
import { useParserInit } from './hook';
|
||||||
import { IObjContainerProps } from './interface';
|
import { IObjContainerProps } from './interface';
|
||||||
export const ObjectContainer = (props: IObjContainerProps) => {
|
export const ObjectContainer = (props: IObjContainerProps) => {
|
||||||
const {
|
const {
|
||||||
initialValue,
|
initialValue,
|
||||||
isChunck,
|
isChunck,
|
||||||
unescapeNewlines,
|
|
||||||
escapeNewlines,
|
|
||||||
onSave,
|
onSave,
|
||||||
className,
|
className,
|
||||||
textMode,
|
textMode,
|
||||||
@ -19,7 +18,7 @@ export const ObjectContainer = (props: IObjContainerProps) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
content,
|
content,
|
||||||
setContent,
|
// setContent,
|
||||||
activeEditIndex,
|
activeEditIndex,
|
||||||
setActiveEditIndex,
|
setActiveEditIndex,
|
||||||
editDivRef,
|
editDivRef,
|
||||||
@ -31,7 +30,7 @@ export const ObjectContainer = (props: IObjContainerProps) => {
|
|||||||
// value: escapeNewlines(e.target.innerText),
|
// value: escapeNewlines(e.target.innerText),
|
||||||
// }));
|
// }));
|
||||||
setActiveEditIndex(1);
|
setActiveEditIndex(1);
|
||||||
}, [setContent, setActiveEditIndex]);
|
}, [setActiveEditIndex]);
|
||||||
|
|
||||||
const handleSave = useCallback(
|
const handleSave = useCallback(
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
@ -42,7 +41,7 @@ export const ObjectContainer = (props: IObjContainerProps) => {
|
|||||||
onSave(saveData);
|
onSave(saveData);
|
||||||
setActiveEditIndex(undefined);
|
setActiveEditIndex(undefined);
|
||||||
},
|
},
|
||||||
[content, onSave],
|
[content, onSave, setActiveEditIndex],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -81,14 +80,14 @@ export const ObjectContainer = (props: IObjContainerProps) => {
|
|||||||
[styles.contentEllipsis]: textMode === ChunkTextMode.Ellipse,
|
[styles.contentEllipsis]: textMode === ChunkTextMode.Ellipse,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
onClick={() => {
|
||||||
clickChunk(content);
|
clickChunk(content as unknown as IChunk);
|
||||||
if (!isReadonly) {
|
if (!isReadonly) {
|
||||||
handleEdit(e);
|
handleEdit();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content.value}
|
{content.value as string}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { useCreateChunk, useDeleteChunk } from '@/hooks/chunk-hooks';
|
|||||||
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
|
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
|
||||||
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
||||||
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
|
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
|
||||||
import { IChunk } from '@/interfaces/database/knowledge';
|
|
||||||
import kbService from '@/services/knowledge-service';
|
import kbService from '@/services/knowledge-service';
|
||||||
import { formatSecondsToHumanReadable } from '@/utils/date';
|
import { formatSecondsToHumanReadable } from '@/utils/date';
|
||||||
import { buildChunkHighlights } from '@/utils/document-util';
|
import { buildChunkHighlights } from '@/utils/document-util';
|
||||||
@ -20,7 +19,7 @@ import {
|
|||||||
PipelineResultSearchParams,
|
PipelineResultSearchParams,
|
||||||
TimelineNodeType,
|
TimelineNodeType,
|
||||||
} from './constant';
|
} from './constant';
|
||||||
import { IDslComponent, IPipelineFileLogDetail } from './interface';
|
import { IChunk, IDslComponent, IPipelineFileLogDetail } from './interface';
|
||||||
|
|
||||||
export const useFetchPipelineFileLogDetail = ({
|
export const useFetchPipelineFileLogDetail = ({
|
||||||
isAgent = false,
|
isAgent = false,
|
||||||
@ -64,7 +63,6 @@ export const useHandleChunkCardClick = () => {
|
|||||||
const [selectedChunk, setSelectedChunk] = useState<IChunk>();
|
const [selectedChunk, setSelectedChunk] = useState<IChunk>();
|
||||||
|
|
||||||
const handleChunkCardClick = useCallback((chunk: IChunk) => {
|
const handleChunkCardClick = useCallback((chunk: IChunk) => {
|
||||||
console.log('click-chunk-->', chunk);
|
|
||||||
setSelectedChunk(chunk);
|
setSelectedChunk(chunk);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -75,7 +73,9 @@ export const useGetChunkHighlights = (selectedChunk?: IChunk) => {
|
|||||||
const [size, setSize] = useState({ width: 849, height: 1200 });
|
const [size, setSize] = useState({ width: 849, height: 1200 });
|
||||||
|
|
||||||
const highlights: IHighlight[] = useMemo(() => {
|
const highlights: IHighlight[] = useMemo(() => {
|
||||||
return selectedChunk ? buildChunkHighlights(selectedChunk, size) : [];
|
return selectedChunk
|
||||||
|
? buildChunkHighlights(selectedChunk as any, size)
|
||||||
|
: [];
|
||||||
}, [selectedChunk, size]);
|
}, [selectedChunk, size]);
|
||||||
|
|
||||||
const setWidthAndHeight = useCallback((width: number, height: number) => {
|
const setWidthAndHeight = useCallback((width: number, height: number) => {
|
||||||
|
|||||||
@ -126,8 +126,17 @@ export interface IPipelineFileLogDetail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IChunk {
|
export interface IChunk {
|
||||||
|
available_int?: number; // Whether to enable, 0: not enabled, 1: enabled
|
||||||
|
chunk_id?: string;
|
||||||
|
content_with_weight?: string;
|
||||||
|
doc_id?: string;
|
||||||
|
doc_name?: string;
|
||||||
|
image_id?: string;
|
||||||
|
important_kwd?: string[];
|
||||||
|
question_kwd?: string[]; // keywords
|
||||||
|
tag_kwd?: string[];
|
||||||
positions: number[][];
|
positions: number[][];
|
||||||
image_id: string;
|
tag_feas?: Record<string, number>;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,12 +40,17 @@ const ParserContainer = (props: IProps) => {
|
|||||||
const initialValue = useMemo(() => {
|
const initialValue = useMemo(() => {
|
||||||
const outputs = data?.value?.obj?.params?.outputs;
|
const outputs = data?.value?.obj?.params?.outputs;
|
||||||
const key = outputs?.output_format?.value;
|
const key = outputs?.output_format?.value;
|
||||||
if (!outputs || !key) return { key: '', type: '', value: [] };
|
if (!outputs || !key)
|
||||||
const value = outputs[key]?.value;
|
return {
|
||||||
const type = outputs[key]?.type;
|
key: '' as 'text' | 'html' | 'json' | 'chunks',
|
||||||
|
type: '',
|
||||||
|
value: [],
|
||||||
|
};
|
||||||
|
const value = outputs[key as keyof typeof outputs]?.value;
|
||||||
|
const type = outputs[key as keyof typeof outputs]?.type;
|
||||||
console.log('outputs-->', outputs, data, key, value);
|
console.log('outputs-->', outputs, data, key, value);
|
||||||
return {
|
return {
|
||||||
key,
|
key: key as 'text' | 'html' | 'json' | 'chunks',
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
params: data?.value?.obj?.params,
|
params: data?.value?.obj?.params,
|
||||||
@ -95,7 +100,7 @@ const ParserContainer = (props: IProps) => {
|
|||||||
const handleRemoveChunk = useCallback(async () => {
|
const handleRemoveChunk = useCallback(async () => {
|
||||||
if (selectedChunkIds.length > 0) {
|
if (selectedChunkIds.length > 0) {
|
||||||
initialText.value = initialText.value.filter(
|
initialText.value = initialText.value.filter(
|
||||||
(item: any, index: number) => !selectedChunkIds.includes(index + ''),
|
(_item: any, index: number) => !selectedChunkIds.includes(index + ''),
|
||||||
);
|
);
|
||||||
setIsChange(true);
|
setIsChange(true);
|
||||||
setSelectedChunkIds([]);
|
setSelectedChunkIds([]);
|
||||||
@ -118,7 +123,7 @@ const ParserContainer = (props: IProps) => {
|
|||||||
const selectAllChunk = useCallback(
|
const selectAllChunk = useCallback(
|
||||||
(checked: boolean) => {
|
(checked: boolean) => {
|
||||||
setSelectedChunkIds(
|
setSelectedChunkIds(
|
||||||
checked ? initialText.value.map((x, index: number) => index) : [],
|
checked ? initialText.value.map((_x: any, index: number) => index) : [],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[initialText.value],
|
[initialText.value],
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { RunningStatus } from '../dataset/constant';
|
|||||||
import { LogTabs } from './dataset-common';
|
import { LogTabs } from './dataset-common';
|
||||||
import { DatasetFilter } from './dataset-filter';
|
import { DatasetFilter } from './dataset-filter';
|
||||||
import { useFetchFileLogList, useFetchOverviewTital } from './hook';
|
import { useFetchFileLogList, useFetchOverviewTital } from './hook';
|
||||||
|
import { DocumentLog, IFileLogItem } from './interface';
|
||||||
import FileLogsTable from './overview-table';
|
import FileLogsTable from './overview-table';
|
||||||
|
|
||||||
interface StatCardProps {
|
interface StatCardProps {
|
||||||
@ -212,18 +213,25 @@ const FileLogsPage: FC = () => {
|
|||||||
return tableOriginData.logs.map((item) => {
|
return tableOriginData.logs.map((item) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
fileName: item.document_name,
|
status: item.operation_status as RunningStatus,
|
||||||
statusName: item.operation_status,
|
statusName: RunningStatusMap[item.operation_status as RunningStatus],
|
||||||
};
|
} as unknown as IFileLogItem & DocumentLog;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
}, [tableOriginData]);
|
}, [tableOriginData]);
|
||||||
|
|
||||||
const changeActiveLogs = (active: (typeof LogTabs)[keyof typeof LogTabs]) => {
|
const changeActiveLogs = (active: (typeof LogTabs)[keyof typeof LogTabs]) => {
|
||||||
setFilterValue({});
|
setFilterValue({});
|
||||||
setActive(active);
|
setActive(active);
|
||||||
};
|
};
|
||||||
const handlePaginationChange = (page: number, pageSize: number) => {
|
const handlePaginationChange = ({
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
}: {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
}) => {
|
||||||
console.log('Pagination changed:', { page, pageSize });
|
console.log('Pagination changed:', { page, pageSize });
|
||||||
setPagination({
|
setPagination({
|
||||||
...pagination,
|
...pagination,
|
||||||
|
|||||||
@ -57,6 +57,6 @@ export interface IFileLogItem {
|
|||||||
update_time: number;
|
update_time: number;
|
||||||
}
|
}
|
||||||
export interface IFileLogList {
|
export interface IFileLogList {
|
||||||
logs: IFileLogItem[];
|
logs: Array<IFileLogItem & DocumentLog>;
|
||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
import {
|
import { DataFlowSelect } from '@/components/data-pipeline-select';
|
||||||
DataFlowSelect,
|
|
||||||
IDataPipelineSelectNode,
|
|
||||||
} from '@/components/data-pipeline-select';
|
|
||||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@ -22,7 +19,6 @@ import {
|
|||||||
} from '../dataset/generate-button/generate';
|
} from '../dataset/generate-button/generate';
|
||||||
import { ChunkMethodForm } from './chunk-method-form';
|
import { ChunkMethodForm } from './chunk-method-form';
|
||||||
import ChunkMethodLearnMore from './chunk-method-learn-more';
|
import ChunkMethodLearnMore from './chunk-method-learn-more';
|
||||||
import { IDataPipelineNodeProps } from './components/link-data-pipeline';
|
|
||||||
import { MainContainer } from './configuration-form-container';
|
import { MainContainer } from './configuration-form-container';
|
||||||
import { ChunkMethodItem, ParseTypeItem } from './configuration/common-item';
|
import { ChunkMethodItem, ParseTypeItem } from './configuration/common-item';
|
||||||
import { formSchema } from './form-schema';
|
import { formSchema } from './form-schema';
|
||||||
@ -85,7 +81,7 @@ export default function DatasetSettings() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form);
|
const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form);
|
||||||
const [pipelineData, setPipelineData] = useState<IDataPipelineNodeProps>();
|
// const [pipelineData, setPipelineData] = useState<IDataPipelineNodeProps>();
|
||||||
const [graphRagGenerateData, setGraphRagGenerateData] =
|
const [graphRagGenerateData, setGraphRagGenerateData] =
|
||||||
useState<IGenerateLogButtonProps>();
|
useState<IGenerateLogButtonProps>();
|
||||||
const [raptorGenerateData, setRaptorGenerateData] =
|
const [raptorGenerateData, setRaptorGenerateData] =
|
||||||
@ -94,13 +90,13 @@ export default function DatasetSettings() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('🚀 ~ DatasetSettings ~ knowledgeDetails:', knowledgeDetails);
|
console.log('🚀 ~ DatasetSettings ~ knowledgeDetails:', knowledgeDetails);
|
||||||
if (knowledgeDetails) {
|
if (knowledgeDetails) {
|
||||||
const data: IDataPipelineNodeProps = {
|
// const data: IDataPipelineNodeProps = {
|
||||||
id: knowledgeDetails.pipeline_id,
|
// id: knowledgeDetails.pipeline_id,
|
||||||
name: knowledgeDetails.pipeline_name,
|
// name: knowledgeDetails.pipeline_name,
|
||||||
avatar: knowledgeDetails.pipeline_avatar,
|
// avatar: knowledgeDetails.pipeline_avatar,
|
||||||
linked: true,
|
// linked: true,
|
||||||
};
|
// };
|
||||||
setPipelineData(data);
|
// setPipelineData(data);
|
||||||
setGraphRagGenerateData({
|
setGraphRagGenerateData({
|
||||||
finish_at: knowledgeDetails.graphrag_task_finish_at,
|
finish_at: knowledgeDetails.graphrag_task_finish_at,
|
||||||
task_id: knowledgeDetails.graphrag_task_id,
|
task_id: knowledgeDetails.graphrag_task_id,
|
||||||
@ -121,17 +117,17 @@ export default function DatasetSettings() {
|
|||||||
console.error('An error occurred during submission:', error);
|
console.error('An error occurred during submission:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleLinkOrEditSubmit = (
|
// const handleLinkOrEditSubmit = (
|
||||||
data: IDataPipelineSelectNode | undefined,
|
// data: IDataPipelineSelectNode | undefined,
|
||||||
) => {
|
// ) => {
|
||||||
console.log('🚀 ~ DatasetSettings ~ data:', data);
|
// console.log('🚀 ~ DatasetSettings ~ data:', data);
|
||||||
if (data) {
|
// if (data) {
|
||||||
setPipelineData(data);
|
// setPipelineData(data);
|
||||||
form.setValue('pipeline_id', data.id || '');
|
// form.setValue('pipeline_id', data.id || '');
|
||||||
// form.setValue('pipeline_name', data.name || '');
|
// // form.setValue('pipeline_name', data.name || '');
|
||||||
// form.setValue('pipeline_avatar', data.avatar || '');
|
// // form.setValue('pipeline_avatar', data.avatar || '');
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handleDeletePipelineTask = (type: GenerateType) => {
|
const handleDeletePipelineTask = (type: GenerateType) => {
|
||||||
if (type === GenerateType.KnowledgeGraph) {
|
if (type === GenerateType.KnowledgeGraph) {
|
||||||
@ -203,11 +199,7 @@ export default function DatasetSettings() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
{parseType === 1 && (
|
{parseType === 1 && <ChunkMethodForm />}
|
||||||
<ChunkMethodForm
|
|
||||||
selectedTag={selectedTag as DocumentParserType}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* <LinkDataPipeline
|
{/* <LinkDataPipeline
|
||||||
data={pipelineData}
|
data={pipelineData}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ interface IProps extends IModalProps<any> {
|
|||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MindMapDrawer = ({ data, hideModal, visible, loading }: IProps) => {
|
const MindMapDrawer = ({ data, hideModal, loading }: IProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const percent = usePendingMindMap();
|
const percent = usePendingMindMap();
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -14,15 +14,14 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/ui/popover';
|
} from '@/components/ui/popover';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
import {
|
import {
|
||||||
useAllTestingResult,
|
useAllTestingResult,
|
||||||
useSelectTestingResult,
|
useSelectTestingResult,
|
||||||
} from '@/hooks/knowledge-hooks';
|
} from '@/hooks/knowledge-hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Separator } from '@radix-ui/react-select';
|
|
||||||
import { CheckIcon, ChevronDown, Files, XIcon } from 'lucide-react';
|
import { CheckIcon, ChevronDown, Files, XIcon } from 'lucide-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onTesting(documentIds: string[]): void;
|
onTesting(documentIds: string[]): void;
|
||||||
@ -35,11 +34,9 @@ const RetrievalDocuments = ({
|
|||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
setSelectedDocumentIds,
|
setSelectedDocumentIds,
|
||||||
}: IProps) => {
|
}: IProps) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const { documents: documentsAll } = useAllTestingResult();
|
const { documents: documentsAll } = useAllTestingResult();
|
||||||
const { documents } = useSelectTestingResult();
|
const { documents } = useSelectTestingResult();
|
||||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||||
const maxCount = 3;
|
|
||||||
const { documents: useDocuments } = {
|
const { documents: useDocuments } = {
|
||||||
documents:
|
documents:
|
||||||
documentsAll?.length > documents?.length ? documentsAll : documents,
|
documentsAll?.length > documents?.length ? documentsAll : documents,
|
||||||
|
|||||||
@ -337,6 +337,8 @@ export const useRenameSearch = () => {
|
|||||||
});
|
});
|
||||||
const detail = reponse.data?.data;
|
const detail = reponse.data?.data;
|
||||||
console.log('detail-->', detail);
|
console.log('detail-->', detail);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { id, created_by, update_time, ...searchDataTemp } = detail;
|
const { id, created_by, update_time, ...searchDataTemp } = detail;
|
||||||
res = await updateSearch({
|
res = await updateSearch({
|
||||||
...searchDataTemp,
|
...searchDataTemp,
|
||||||
|
|||||||
@ -5,14 +5,13 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
|
||||||
import { useFetchSearchList, useRenameSearch } from './hooks';
|
import { useFetchSearchList, useRenameSearch } from './hooks';
|
||||||
import { SearchCard } from './search-card';
|
import { SearchCard } from './search-card';
|
||||||
|
|
||||||
export default function SearchList() {
|
export default function SearchList() {
|
||||||
// const { data } = useFetchFlowList();
|
// const { data } = useFetchFlowList();
|
||||||
const { t } = useTranslate('search');
|
const { t } = useTranslate('search');
|
||||||
const [isEdit, setIsEdit] = useState(false);
|
// const [isEdit, setIsEdit] = useState(false);
|
||||||
const {
|
const {
|
||||||
data: list,
|
data: list,
|
||||||
searchParams,
|
searchParams,
|
||||||
@ -36,11 +35,11 @@ export default function SearchList() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const openCreateModalFun = () => {
|
const openCreateModalFun = () => {
|
||||||
setIsEdit(false);
|
// setIsEdit(false);
|
||||||
showSearchRenameModal();
|
showSearchRenameModal();
|
||||||
};
|
};
|
||||||
const handlePageChange = (page: number, pageSize: number) => {
|
const handlePageChange = (page: number, pageSize: number) => {
|
||||||
setIsEdit(false);
|
// setIsEdit(false);
|
||||||
setSearchListParams({ ...searchParams, page, page_size: pageSize });
|
setSearchListParams({ ...searchParams, page, page_size: pageSize });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user