mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Fix: Unified terminology to "Pipeline" and optimized related component logic. #9869 - Added logic to clear pipeline_id when parseType changes in the chunk method dialog. - Fixed an issue in the Tooltip form component that prevented clicks from triggering saves. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -240,6 +240,11 @@ export function ChunkMethodDialog({
|
||||
name: 'parseType',
|
||||
defaultValue: pipelineId ? 2 : 1,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (parseType === 1) {
|
||||
form.setValue('pipeline_id', '');
|
||||
}
|
||||
}, [parseType, form]);
|
||||
return (
|
||||
<Dialog open onOpenChange={hideModal}>
|
||||
<DialogContent className="max-w-[50vw]">
|
||||
|
||||
@ -75,7 +75,7 @@ export function DataFlowSelect(props: IProps) {
|
||||
tooltip={t('dataFlowTip')}
|
||||
className="text-sm text-text-primary whitespace-wrap "
|
||||
>
|
||||
{t('dataFlow')}
|
||||
{t('dataPipeline')}
|
||||
</FormLabel>
|
||||
{toDataPipeline && (
|
||||
<div
|
||||
|
||||
@ -33,7 +33,12 @@ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
||||
export const FormTooltip = ({ tooltip }: { tooltip: React.ReactNode }) => {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger tabIndex={-1}>
|
||||
<TooltipTrigger
|
||||
tabIndex={-1}
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent clicking the tooltip from triggering form save
|
||||
}}
|
||||
>
|
||||
<Info className="size-3 ml-2" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
@ -107,7 +112,7 @@ export const AntToolTip: React.FC<AntToolTipProps> = ({
|
||||
{visible && title && (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute z-50 px-2.5 py-2 text-xs text-text-primary bg-muted rounded-sm shadow-sm whitespace-wrap',
|
||||
'absolute z-50 px-2.5 py-2 text-xs text-text-primary bg-muted rounded-sm shadow-sm whitespace-wrap w-max',
|
||||
getPlacementClasses(),
|
||||
className,
|
||||
)}
|
||||
|
||||
@ -290,9 +290,9 @@ export default {
|
||||
linkDataPipeline: 'Link Data Pipeline',
|
||||
enableAutoGenerate: 'Enable Auto Generate',
|
||||
teamPlaceholder: 'Please select a team.',
|
||||
dataFlowPlaceholder: 'Please select a data flow.',
|
||||
dataFlowPlaceholder: 'Please select a pipeline.',
|
||||
buildItFromScratch: 'Build it from scratch',
|
||||
dataFlow: 'Data Flow',
|
||||
dataFlow: 'Pipeline',
|
||||
parseType: 'Parse Type',
|
||||
manualSetup: 'Manual Setup',
|
||||
builtIn: 'Built-in',
|
||||
@ -420,7 +420,7 @@ export default {
|
||||
<p>In a Tag column, <b>comma</b> is used to separate tags.</p>
|
||||
<i>Lines of texts that fail to follow the above rules will be ignored.</i>
|
||||
`,
|
||||
useRaptor: 'Use RAPTOR to enhance retrieval',
|
||||
useRaptor: 'RAPTOR',
|
||||
useRaptorTip:
|
||||
'Enable RAPTOR for multi-hop question-answering tasks. See https://ragflow.io/docs/dev/enable_raptor for details.',
|
||||
prompt: 'Prompt',
|
||||
@ -466,7 +466,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
||||
topnTags: 'Top-N Tags',
|
||||
tags: 'Tags',
|
||||
addTag: 'Add tag',
|
||||
useGraphRag: 'Extract knowledge graph',
|
||||
useGraphRag: 'Knowledge graph',
|
||||
useGraphRagTip:
|
||||
'Construct a knowledge graph over file chunks of the current knowledge base to enhance multi-hop question-answering involving nested logic. See https://ragflow.io/docs/dev/construct_knowledge_graph for details.',
|
||||
graphRagMethod: 'Method',
|
||||
@ -474,7 +474,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
||||
General: Use prompts provided by github.com/microsoft/graphrag to extract entities and relationships`,
|
||||
resolution: 'Entity resolution',
|
||||
resolutionTip: `An entity deduplication switch. When enabled, the LLM will combine similar entities - e.g., '2025' and 'the year of 2025', or 'IT' and 'Information Technology' - to construct a more accurate graph`,
|
||||
community: 'Community reports generation',
|
||||
community: 'Community reports',
|
||||
communityTip:
|
||||
'In a knowledge graph, a community is a cluster of entities linked by relationships. You can have the LLM generate an abstract for each community, known as a community report. See here for more information: https://www.microsoft.com/en-us/research/blog/graphrag-improving-global-search-via-dynamic-community-selection/',
|
||||
theDocumentBeingParsedCannotBeDeleted:
|
||||
@ -1791,5 +1791,12 @@ Important structured information may include: names, dates, locations, events, k
|
||||
summary: 'Augmented Context',
|
||||
},
|
||||
},
|
||||
datasetOverview: {
|
||||
downloadTip: 'Files being downloaded from data sources. ',
|
||||
processingTip: 'Files being processed by data flows.',
|
||||
totalFiles: 'Total Files',
|
||||
downloading: 'Downloading',
|
||||
processing: 'Processing',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -1696,5 +1696,12 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
filenameEmbeddingWeight: '文件名嵌入权重',
|
||||
switchPromptMessage: '提示词将发生变化,请确认是否放弃已有提示词?',
|
||||
},
|
||||
datasetOverview: {
|
||||
downloadTip: '正在从数据源下载文件。',
|
||||
processingTip: '正在由数据流处理文件。',
|
||||
totalFiles: '文件总数',
|
||||
downloading: '正在下载',
|
||||
processing: '正在处理',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -5,8 +5,8 @@ export const useParserInit = ({
|
||||
initialValue,
|
||||
}: {
|
||||
initialValue:
|
||||
| Pick<IJsonContainerProps, 'initialValue'>
|
||||
| Pick<IObjContainerProps, 'initialValue'>;
|
||||
| IJsonContainerProps['initialValue']
|
||||
| IObjContainerProps['initialValue'];
|
||||
}) => {
|
||||
const [content, setContent] = useState(initialValue);
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { IJsonContainerProps } from './interface';
|
||||
export const parserKeyMap = {
|
||||
json: 'text',
|
||||
chunks: 'text',
|
||||
};
|
||||
} as const;
|
||||
|
||||
export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
const {
|
||||
@ -33,24 +33,15 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
editDivRef,
|
||||
} = useParserInit({ initialValue });
|
||||
|
||||
const parserKey = parserKeyMap[content.key as keyof typeof parserKeyMap];
|
||||
|
||||
const handleEdit = useCallback(
|
||||
(e?: any, index?: number) => {
|
||||
setContent((pre) => ({
|
||||
...pre,
|
||||
value: pre.value.map((item, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...item,
|
||||
[parserKeyMap[content.key]]: unescapeNewlines(e.target.innerText),
|
||||
};
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
}));
|
||||
setActiveEditIndex(index);
|
||||
},
|
||||
[setContent, setActiveEditIndex],
|
||||
);
|
||||
|
||||
const handleSave = useCallback(
|
||||
(e: any) => {
|
||||
const saveData = {
|
||||
@ -59,7 +50,7 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
if (index === activeEditIndex) {
|
||||
return {
|
||||
...item,
|
||||
[parserKeyMap[content.key]]: e.target.innerText,
|
||||
[parserKey]: e.target.textContent || '',
|
||||
};
|
||||
} else {
|
||||
return item;
|
||||
@ -75,26 +66,28 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
useEffect(() => {
|
||||
if (activeEditIndex !== undefined && editDivRef.current) {
|
||||
editDivRef.current.focus();
|
||||
editDivRef.current.textContent = escapeNewlines(
|
||||
content.value[activeEditIndex][parserKeyMap[content.key]],
|
||||
);
|
||||
editDivRef.current.textContent =
|
||||
content.value[activeEditIndex][parserKey];
|
||||
}
|
||||
}, [activeEditIndex, content]);
|
||||
}, [editDivRef, activeEditIndex, content, parserKey]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{content.value?.map((item, index) => {
|
||||
if (item[parserKeyMap[content.key]] === '') {
|
||||
if (
|
||||
item[parserKeyMap[content.key as keyof typeof parserKeyMap]] === ''
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<section
|
||||
key={index}
|
||||
className={
|
||||
className={cn(
|
||||
isChunck
|
||||
? 'bg-bg-card my-2 p-2 rounded-lg flex gap-1 items-start'
|
||||
: ''
|
||||
}
|
||||
: '',
|
||||
activeEditIndex === index && isChunck ? 'bg-bg-title' : '',
|
||||
)}
|
||||
>
|
||||
{isChunck && !isReadonly && (
|
||||
<Checkbox
|
||||
@ -113,6 +106,7 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
onBlur={handleSave}
|
||||
className={cn(
|
||||
'w-full bg-transparent text-text-secondary border-none focus-visible:border-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none p-0',
|
||||
|
||||
className,
|
||||
)}
|
||||
></div>
|
||||
@ -120,7 +114,7 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
{activeEditIndex !== index && (
|
||||
<div
|
||||
className={cn(
|
||||
'text-text-secondary overflow-auto scrollbar-auto whitespace-pre-wrap w-full',
|
||||
'text-text-secondary overflow-auto scrollbar-auto w-full',
|
||||
{
|
||||
[styles.contentEllipsis]:
|
||||
textMode === ChunkTextMode.Ellipse,
|
||||
@ -134,7 +128,7 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{escapeNewlines(item[parserKeyMap[content.key]])}
|
||||
{item[parserKeyMap[content.key]]}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@ -25,22 +25,19 @@ export const ObjectContainer = (props: IObjContainerProps) => {
|
||||
editDivRef,
|
||||
} = useParserInit({ initialValue });
|
||||
|
||||
const handleEdit = useCallback(
|
||||
(e?: any) => {
|
||||
setContent((pre) => ({
|
||||
...pre,
|
||||
value: escapeNewlines(e.target.innerText),
|
||||
}));
|
||||
setActiveEditIndex(1);
|
||||
},
|
||||
[setContent, setActiveEditIndex],
|
||||
);
|
||||
const handleEdit = useCallback(() => {
|
||||
// setContent((pre) => ({
|
||||
// ...pre,
|
||||
// value: escapeNewlines(e.target.innerText),
|
||||
// }));
|
||||
setActiveEditIndex(1);
|
||||
}, [setContent, setActiveEditIndex]);
|
||||
|
||||
const handleSave = useCallback(
|
||||
(e: any) => {
|
||||
const saveData = {
|
||||
...content,
|
||||
value: e.target.innerText,
|
||||
value: e.target.textContent,
|
||||
};
|
||||
onSave(saveData);
|
||||
setActiveEditIndex(undefined);
|
||||
@ -51,9 +48,9 @@ export const ObjectContainer = (props: IObjContainerProps) => {
|
||||
useEffect(() => {
|
||||
if (activeEditIndex !== undefined && editDivRef.current) {
|
||||
editDivRef.current.focus();
|
||||
editDivRef.current.textContent = escapeNewlines(content.value);
|
||||
editDivRef.current.textContent = content.value;
|
||||
}
|
||||
}, [activeEditIndex, content, escapeNewlines]);
|
||||
}, [activeEditIndex, content]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -90,7 +87,7 @@ export const ObjectContainer = (props: IObjContainerProps) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{escapeNewlines(content.value)}
|
||||
{content.value}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { FilterCollection } from '@/components/list-filter-bar/interface';
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
import { useIsDarkTheme } from '@/components/theme-provider';
|
||||
import { AntToolTip } from '@/components/ui/tooltip';
|
||||
import { useFetchDocumentList } from '@/hooks/use-document-request';
|
||||
import { t } from 'i18next';
|
||||
import { CircleQuestionMark } from 'lucide-react';
|
||||
@ -17,19 +18,30 @@ interface StatCardProps {
|
||||
value: number;
|
||||
icon: JSX.Element;
|
||||
children?: JSX.Element;
|
||||
tooltip?: string;
|
||||
}
|
||||
interface CardFooterProcessProps {
|
||||
success: number;
|
||||
failed: number;
|
||||
}
|
||||
|
||||
const StatCard: FC<StatCardProps> = ({ title, value, children, icon }) => {
|
||||
const StatCard: FC<StatCardProps> = ({
|
||||
title,
|
||||
value,
|
||||
children,
|
||||
icon,
|
||||
tooltip,
|
||||
}) => {
|
||||
return (
|
||||
<div className="bg-bg-card p-4 rounded-lg border border-border flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="flex items-center gap-1 text-sm font-medium text-text-secondary">
|
||||
{title}
|
||||
<CircleQuestionMark size={12} />
|
||||
{tooltip && (
|
||||
<AntToolTip title={tooltip} trigger="hover">
|
||||
<CircleQuestionMark size={12} />
|
||||
</AntToolTip>
|
||||
)}
|
||||
</h3>
|
||||
{icon}
|
||||
</div>
|
||||
@ -51,15 +63,19 @@ const CardFooterProcess: FC<CardFooterProcessProps> = ({
|
||||
<div className="w-full flex justify-between gap-4 rounded-lg text-sm font-bold text-text-primary">
|
||||
<div className="flex items-center justify-between rounded-md w-1/2 p-2 bg-state-success-5">
|
||||
<div className="flex items-center rounded-lg gap-1">
|
||||
<div className="w-2 h-2 rounded-full bg-state-success"></div>
|
||||
<div>{t('knowledgeDetails.success')}</div>
|
||||
<div className="w-2 h-2 rounded-full bg-state-success "></div>
|
||||
<div className="font-normal text-text-secondary text-xs">
|
||||
{t('knowledgeDetails.success')}
|
||||
</div>
|
||||
</div>
|
||||
<div>{success || 0}</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded-md w-1/2 bg-state-error-5 p-2">
|
||||
<div className="flex items-center rounded-lg gap-1">
|
||||
<div className="w-2 h-2 rounded-full bg-state-error"></div>
|
||||
<div>{t('knowledgeDetails.failed')}</div>
|
||||
<div className="font-normal text-text-secondary text-xs">
|
||||
{t('knowledgeDetails.failed')}
|
||||
</div>
|
||||
</div>
|
||||
<div>{failed || 0}</div>
|
||||
</div>
|
||||
@ -189,7 +205,7 @@ const FileLogsPage: FC = () => {
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-3 md:grid-cols-3 gap-4 mb-6">
|
||||
<StatCard
|
||||
title="Total Files"
|
||||
title={t('datasetOverview.totalFiles')}
|
||||
value={topAllData.totalFiles.value}
|
||||
icon={
|
||||
isDark ? (
|
||||
@ -204,11 +220,13 @@ const FileLogsPage: FC = () => {
|
||||
{topAllData.totalFiles.precent > 0 ? '+' : ''}
|
||||
{topAllData.totalFiles.precent}%{' '}
|
||||
</span>
|
||||
from last week
|
||||
<span className="font-normal text-text-secondary text-xs">
|
||||
from last week
|
||||
</span>
|
||||
</div>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title="Downloading"
|
||||
title={t('datasetOverview.downloading')}
|
||||
value={topAllData.downloads.value}
|
||||
icon={
|
||||
isDark ? (
|
||||
@ -217,6 +235,7 @@ const FileLogsPage: FC = () => {
|
||||
<SvgIcon name="data-flow/data-icon-bri" width={40} />
|
||||
)
|
||||
}
|
||||
tooltip={t('datasetOverview.downloadTip')}
|
||||
>
|
||||
<CardFooterProcess
|
||||
success={topAllData.downloads.success}
|
||||
@ -224,7 +243,7 @@ const FileLogsPage: FC = () => {
|
||||
/>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title="Processing"
|
||||
title={t('datasetOverview.processing')}
|
||||
value={topAllData.processing.value}
|
||||
icon={
|
||||
isDark ? (
|
||||
@ -233,6 +252,7 @@ const FileLogsPage: FC = () => {
|
||||
<SvgIcon name="data-flow/processing-icon-bri" width={40} />
|
||||
)
|
||||
}
|
||||
tooltip={t('datasetOverview.processingTip')}
|
||||
>
|
||||
<CardFooterProcess
|
||||
success={topAllData.processing.success}
|
||||
|
||||
@ -42,7 +42,7 @@ export function ChunkMethodItem(props: IProps) {
|
||||
'w-1/4 whitespace-pre-wrap': line === 1,
|
||||
})}
|
||||
>
|
||||
{t('chunkMethod')}
|
||||
{t('dataPipeline')}
|
||||
</FormLabel>
|
||||
<div className={line === 1 ? 'w-3/4 ' : 'w-full'}>
|
||||
<FormControl>
|
||||
|
||||
@ -99,7 +99,7 @@ export function ParsingStatusCell({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={handleShowChangeParserModal}>
|
||||
{t('knowledgeDetails.chunkMethod')}
|
||||
{t('knowledgeDetails.dataPipeline')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleShowSetMetaModal}>
|
||||
{t('knowledgeDetails.setMetaData')}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { IconFontFill } from '@/components/icon-font';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useSecondPathName } from '@/hooks/route-hook';
|
||||
@ -9,13 +10,7 @@ import { cn, formatBytes } from '@/lib/utils';
|
||||
import { Routes } from '@/routes';
|
||||
import { formatPureDate } from '@/utils/date';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
Banknote,
|
||||
DatabaseZap,
|
||||
FileSearch2,
|
||||
FolderOpen,
|
||||
GitGraph,
|
||||
} from 'lucide-react';
|
||||
import { Banknote, DatabaseZap, FileSearch2, FolderOpen } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHandleMenuClick } from './hooks';
|
||||
@ -35,29 +30,29 @@ export function SideBar({ refreshCount }: PropType) {
|
||||
const items = useMemo(() => {
|
||||
const list = [
|
||||
{
|
||||
icon: DatabaseZap,
|
||||
icon: <DatabaseZap className="size-4" />,
|
||||
label: t(`knowledgeDetails.overview`),
|
||||
key: Routes.DataSetOverview,
|
||||
},
|
||||
{
|
||||
icon: FolderOpen,
|
||||
icon: <FolderOpen className="size-4" />,
|
||||
label: t(`knowledgeDetails.subbarFiles`),
|
||||
key: Routes.DatasetBase,
|
||||
},
|
||||
{
|
||||
icon: FileSearch2,
|
||||
icon: <FileSearch2 className="size-4" />,
|
||||
label: t(`knowledgeDetails.testing`),
|
||||
key: Routes.DatasetTesting,
|
||||
},
|
||||
{
|
||||
icon: Banknote,
|
||||
icon: <Banknote className="size-4" />,
|
||||
label: t(`knowledgeDetails.configuration`),
|
||||
key: Routes.DataSetSetting,
|
||||
},
|
||||
];
|
||||
if (!isEmpty(routerData?.graph)) {
|
||||
list.push({
|
||||
icon: GitGraph,
|
||||
icon: <IconFontFill name="knowledgegraph" className="size-4" />,
|
||||
label: t(`knowledgeDetails.knowledgeGraph`),
|
||||
key: Routes.KnowledgeGraph,
|
||||
});
|
||||
@ -105,7 +100,7 @@ export function SideBar({ refreshCount }: PropType) {
|
||||
)}
|
||||
onClick={handleMenuClick(item.key)}
|
||||
>
|
||||
<item.icon className="size-4" />
|
||||
{item.icon}
|
||||
<span>{item.label}</span>
|
||||
</Button>
|
||||
);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DataFlowSelect } from '@/components/data-pipeline-select';
|
||||
import { ButtonLoading } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
@ -18,11 +19,10 @@ import { Input } from '@/components/ui/input';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DataFlowSelect } from '@/components/data-pipeline-select';
|
||||
import {
|
||||
ChunkMethodItem,
|
||||
EmbeddingModelItem,
|
||||
@ -95,6 +95,13 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
||||
control: form.control,
|
||||
name: 'parseType',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log('parseType', parseType);
|
||||
if (parseType === 1) {
|
||||
form.setValue('pipeline_id', '');
|
||||
}
|
||||
}, [parseType, form]);
|
||||
const { navigateToAgents } = useNavigatePage();
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user