mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-11-30 17:06:33 +00:00
### What problem does this PR solve? Fix: Update the parsing editor to support dynamic field names and optimize UI styles #9869 -Modify the default background color of UI components such as Input and Select to 'bg bg base'` -Remove TagItems component reference from naive configuration page ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
parent
781d49cd0e
commit
66c69d10fe
@ -1,3 +1,4 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { forwardRef } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -36,6 +37,7 @@ export const DelimiterInput = forwardRef<HTMLInputElement, InputProps & IProps>(
|
||||
maxLength={maxLength}
|
||||
defaultValue={defaultValue}
|
||||
ref={ref}
|
||||
className={cn('bg-bg-base', props.className)}
|
||||
{...props}
|
||||
></Input>
|
||||
);
|
||||
|
||||
@ -31,7 +31,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'flex h-8 w-full rounded-md border border-input bg-bg-card px-2 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-8 w-full rounded-md border border-input bg-bg-base px-2 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
@ -65,7 +65,11 @@ const ExpandedInput = ({
|
||||
{prefix}
|
||||
</span>
|
||||
<Input
|
||||
className={cn({ 'pr-8': !!suffix, 'pl-8': !!prefix }, className)}
|
||||
className={cn(
|
||||
{ 'pr-8': !!suffix, 'pl-8': !!prefix },
|
||||
'bg-bg-base',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
></Input>
|
||||
<span
|
||||
|
||||
@ -291,7 +291,7 @@ export const RAGFlowSelect = forwardRef<
|
||||
onReset={handleReset}
|
||||
allowClear={allowClear}
|
||||
ref={ref}
|
||||
className={triggerClassName}
|
||||
className={cn(triggerClassName, 'bg-bg-base')}
|
||||
>
|
||||
<SelectValue placeholder={placeholder}>{label}</SelectValue>
|
||||
</SelectTrigger>
|
||||
|
||||
@ -24,6 +24,12 @@ export const useNavigatePage = () => {
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
const navigateToDatasetOverview = useCallback(
|
||||
(id: string) => () => {
|
||||
navigate(`${Routes.DatasetBase}${Routes.DataSetOverview}/${id}`);
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
const navigateToDataFile = useCallback(
|
||||
(id: string) => () => {
|
||||
@ -160,6 +166,7 @@ export const useNavigatePage = () => {
|
||||
return {
|
||||
navigateToDatasetList,
|
||||
navigateToDataset,
|
||||
navigateToDatasetOverview,
|
||||
navigateToHome,
|
||||
navigateToProfile,
|
||||
navigateToChatList,
|
||||
|
||||
@ -1672,6 +1672,7 @@ This delimiter is used to split the input text into several text pieces echo of
|
||||
page: '{{page}} /Page',
|
||||
},
|
||||
dataflowParser: {
|
||||
result: 'Result',
|
||||
parseSummary: 'Parse Summary',
|
||||
parseSummaryTip: 'Parser:deepdoc',
|
||||
rerunFromCurrentStep: 'Rerun From Current Step',
|
||||
|
||||
@ -1588,6 +1588,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
page: '{{page}}条/页',
|
||||
},
|
||||
dataflowParser: {
|
||||
result: '结果',
|
||||
parseSummary: '解析摘要',
|
||||
parseSummaryTip: '解析器: deepdoc',
|
||||
rerunFromCurrentStep: '从当前步骤重新运行',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { CheckedState } from '@radix-ui/react-checkbox';
|
||||
import { ChunkTextMode } from '../../constant';
|
||||
import { IChunk } from '../../interface';
|
||||
import { ComponentParams, IChunk } from '../../interface';
|
||||
import { parserKeyMap } from './json-parser';
|
||||
|
||||
export interface FormatPreserveEditorProps {
|
||||
@ -28,6 +28,7 @@ export type IJsonContainerProps = {
|
||||
value: {
|
||||
[key: string]: string;
|
||||
}[];
|
||||
params: ComponentParams;
|
||||
};
|
||||
isChunck?: boolean;
|
||||
handleCheck: (e: CheckedState, index: number) => void;
|
||||
@ -52,6 +53,7 @@ export type IObjContainerProps = {
|
||||
key: string;
|
||||
type: string;
|
||||
value: string;
|
||||
params: ComponentParams;
|
||||
};
|
||||
isChunck?: boolean;
|
||||
handleCheck: (e: CheckedState, index: number) => void;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { isArray } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { ChunkTextMode } from '../../constant';
|
||||
import styles from '../../index.less';
|
||||
import { useParserInit } from './hook';
|
||||
@ -33,7 +34,13 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
editDivRef,
|
||||
} = useParserInit({ initialValue });
|
||||
|
||||
const parserKey = parserKeyMap[content.key as keyof typeof parserKeyMap];
|
||||
const parserKey = useMemo(() => {
|
||||
const key =
|
||||
content.key === 'chunks' && content.params.field_name
|
||||
? content.params.field_name
|
||||
: parserKeyMap[content.key as keyof typeof parserKeyMap];
|
||||
return key;
|
||||
}, [content]);
|
||||
|
||||
const handleEdit = useCallback(
|
||||
(e?: any, index?: number) => {
|
||||
@ -73,67 +80,68 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{content.value?.map((item, index) => {
|
||||
if (
|
||||
item[parserKeyMap[content.key as keyof typeof parserKeyMap]] === ''
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<section
|
||||
key={index}
|
||||
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
|
||||
onCheckedChange={(e) => {
|
||||
handleCheck(e, index);
|
||||
}}
|
||||
checked={selectedChunkIds?.some(
|
||||
(id) => id.toString() === index.toString(),
|
||||
)}
|
||||
></Checkbox>
|
||||
)}
|
||||
{activeEditIndex === index && (
|
||||
<div
|
||||
ref={editDivRef}
|
||||
contentEditable={!isReadonly}
|
||||
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',
|
||||
{isArray(content.value) &&
|
||||
content.value?.map((item, index) => {
|
||||
if (
|
||||
item[parserKeyMap[content.key as keyof typeof parserKeyMap]] === ''
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<section
|
||||
key={index}
|
||||
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
|
||||
onCheckedChange={(e) => {
|
||||
handleCheck(e, index);
|
||||
}}
|
||||
checked={selectedChunkIds?.some(
|
||||
(id) => id.toString() === index.toString(),
|
||||
)}
|
||||
></Checkbox>
|
||||
)}
|
||||
{activeEditIndex === index && (
|
||||
<div
|
||||
ref={editDivRef}
|
||||
contentEditable={!isReadonly}
|
||||
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>
|
||||
)}
|
||||
{activeEditIndex !== index && (
|
||||
<div
|
||||
className={cn(
|
||||
'text-text-secondary overflow-auto scrollbar-auto w-full',
|
||||
{
|
||||
[styles.contentEllipsis]:
|
||||
textMode === ChunkTextMode.Ellipse,
|
||||
},
|
||||
)}
|
||||
key={index}
|
||||
onClick={(e) => {
|
||||
clickChunk(item);
|
||||
if (!isReadonly) {
|
||||
handleEdit(e, index);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item[parserKeyMap[content.key]]}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
className,
|
||||
)}
|
||||
></div>
|
||||
)}
|
||||
{activeEditIndex !== index && (
|
||||
<div
|
||||
className={cn(
|
||||
'text-text-secondary overflow-auto scrollbar-auto w-full',
|
||||
{
|
||||
[styles.contentEllipsis]:
|
||||
textMode === ChunkTextMode.Ellipse,
|
||||
},
|
||||
)}
|
||||
key={index}
|
||||
onClick={(e) => {
|
||||
clickChunk(item);
|
||||
if (!isReadonly) {
|
||||
handleEdit(e, index);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item[parserKey]}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -218,18 +218,11 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => {
|
||||
const nodes: Array<ITimelineNodeObj & { id: number | string }> = [];
|
||||
console.log('time-->', data);
|
||||
const times = data?.dsl?.components;
|
||||
const graphNodes = data?.dsl?.graph?.nodes;
|
||||
if (times) {
|
||||
const getNode = (
|
||||
key: string,
|
||||
index: number,
|
||||
type:
|
||||
| TimelineNodeType.begin
|
||||
| TimelineNodeType.parser
|
||||
| TimelineNodeType.tokenizer
|
||||
| TimelineNodeType.characterSplitter
|
||||
| TimelineNodeType.titleSplitter,
|
||||
) => {
|
||||
const getNode = (key: string, index: number, type: TimelineNodeType) => {
|
||||
const node = times[key].obj;
|
||||
const graphNode = graphNodes?.find((item) => item.id === key);
|
||||
const name = camelCase(
|
||||
node.component_name,
|
||||
) as keyof typeof TimelineNodeObj;
|
||||
@ -247,6 +240,7 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => {
|
||||
}
|
||||
const timeNode = {
|
||||
...TimelineNodeObj[name],
|
||||
title: graphNode?.data?.name,
|
||||
id: index,
|
||||
className: 'w-32',
|
||||
completed: false,
|
||||
@ -255,6 +249,13 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => {
|
||||
),
|
||||
type: tempType,
|
||||
detail: { value: times[key], key: key },
|
||||
} as ITimelineNodeObj & {
|
||||
id: number | string;
|
||||
className: string;
|
||||
completed: boolean;
|
||||
date: string;
|
||||
type: TimelineNodeType;
|
||||
detail: { value: IDslComponent; key: string };
|
||||
};
|
||||
console.log('timeNodetype-->', type);
|
||||
nodes.push(timeNode);
|
||||
@ -329,3 +330,30 @@ export function useFetchPipelineResult({
|
||||
|
||||
return { pipelineResult };
|
||||
}
|
||||
|
||||
export const useSummaryInfo = (
|
||||
data: IPipelineFileLogDetail,
|
||||
currentTimeNode: TimelineNode,
|
||||
) => {
|
||||
const summaryInfo = useMemo(() => {
|
||||
if (currentTimeNode.type === TimelineNodeType.parser) {
|
||||
const setups =
|
||||
currentTimeNode.detail?.value?.obj?.params?.setups?.[
|
||||
data.document_suffix
|
||||
];
|
||||
if (setups) {
|
||||
const { output_format, parse_method } = setups;
|
||||
const res = [];
|
||||
if (parse_method) {
|
||||
res.push(`${t('dataflow.parserMethod')}: ${parse_method}`);
|
||||
}
|
||||
if (output_format) {
|
||||
res.push(`${t('dataflow.outputFormat')}: ${output_format}`);
|
||||
}
|
||||
return res.join(' ');
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}, [data, currentTimeNode]);
|
||||
return { summaryInfo };
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
useGetPipelineResultSearchParams,
|
||||
useHandleChunkCardClick,
|
||||
useRerunDataflow,
|
||||
useSummaryInfo,
|
||||
useTimelineDataFlow,
|
||||
} from './hooks';
|
||||
|
||||
@ -61,7 +62,7 @@ const Chunk = () => {
|
||||
);
|
||||
|
||||
const {
|
||||
navigateToDataset,
|
||||
navigateToDatasetOverview,
|
||||
navigateToDatasetList,
|
||||
navigateToAgents,
|
||||
navigateToDataflow,
|
||||
@ -150,7 +151,7 @@ const Chunk = () => {
|
||||
({} as TimelineNode)
|
||||
);
|
||||
}, [activeStepId, timelineNodes]);
|
||||
|
||||
const { summaryInfo } = useSummaryInfo(dataset, currentTimeNode);
|
||||
return (
|
||||
<>
|
||||
<PageHeader>
|
||||
@ -175,7 +176,7 @@ const Chunk = () => {
|
||||
<BreadcrumbLink
|
||||
onClick={() => {
|
||||
if (knowledgeId) {
|
||||
navigateToDataset(knowledgeId)();
|
||||
navigateToDatasetOverview(knowledgeId)();
|
||||
}
|
||||
if (agentId) {
|
||||
navigateToDataflow(agentId)();
|
||||
@ -220,7 +221,7 @@ const Chunk = () => {
|
||||
></DocumentPreview>
|
||||
</section>
|
||||
</div>
|
||||
<div className="h-dvh border-r -mt-3"></div>
|
||||
<div className="h-[calc(100vh-100px)] border-r -mt-3"></div>
|
||||
<div className="w-3/5 h-full">
|
||||
{/* {currentTimeNode?.type === TimelineNodeType.splitter && (
|
||||
<ChunkerContainer
|
||||
@ -246,6 +247,7 @@ const Chunk = () => {
|
||||
key: string;
|
||||
}
|
||||
}
|
||||
summaryInfo={summaryInfo}
|
||||
clickChunk={handleChunkCardClick}
|
||||
reRunFunc={handleReRunFunc}
|
||||
/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { PipelineResultSearchParams } from './constant';
|
||||
|
||||
interface ComponentParams {
|
||||
export interface ComponentParams {
|
||||
debug_inputs: Record<string, any>;
|
||||
delay_after_error: number;
|
||||
description: string;
|
||||
@ -8,6 +8,7 @@ interface ComponentParams {
|
||||
exception_goto: any;
|
||||
exception_method: any;
|
||||
inputs: Record<string, any>;
|
||||
field_name: string;
|
||||
max_retries: number;
|
||||
message_history_window_size: number;
|
||||
outputs: {
|
||||
@ -30,6 +31,66 @@ export interface IDslComponent {
|
||||
obj: ComponentObject;
|
||||
upstream: Array<string>;
|
||||
}
|
||||
|
||||
interface NodeData {
|
||||
label: string;
|
||||
name: string;
|
||||
form?: {
|
||||
outputs?: Record<
|
||||
string,
|
||||
{
|
||||
type: string;
|
||||
value: string | Array<Record<string, any>> | number;
|
||||
}
|
||||
>;
|
||||
setups?: Array<Record<string, any>>;
|
||||
chunk_token_size?: number;
|
||||
delimiters?: Array<{
|
||||
value: string;
|
||||
}>;
|
||||
overlapped_percent?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface EdgeData {
|
||||
isHovered: boolean;
|
||||
}
|
||||
|
||||
interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface Measured {
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
interface Node {
|
||||
data: NodeData;
|
||||
dragging: boolean;
|
||||
id: string;
|
||||
measured: Measured;
|
||||
position: Position;
|
||||
selected: boolean;
|
||||
sourcePosition: string;
|
||||
targetPosition: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface Edge {
|
||||
data: EdgeData;
|
||||
id: string;
|
||||
source: string;
|
||||
sourceHandle: string;
|
||||
target: string;
|
||||
targetHandle: string;
|
||||
}
|
||||
interface GraphData {
|
||||
edges: Edge[];
|
||||
nodes: Node[];
|
||||
}
|
||||
|
||||
export interface IPipelineFileLogDetail {
|
||||
avatar: string;
|
||||
create_date: string;
|
||||
@ -42,6 +103,7 @@ export interface IPipelineFileLogDetail {
|
||||
components: {
|
||||
[key: string]: IDslComponent;
|
||||
};
|
||||
graph: GraphData;
|
||||
task_id: string;
|
||||
path: Array<string>;
|
||||
};
|
||||
|
||||
@ -19,6 +19,7 @@ interface IProps {
|
||||
data: { value: IDslComponent; key: string };
|
||||
reRunLoading: boolean;
|
||||
clickChunk: (chunk: IChunk) => void;
|
||||
summaryInfo: string;
|
||||
reRunFunc: (data: { value: IDslComponent; key: string }) => void;
|
||||
}
|
||||
const ParserContainer = (props: IProps) => {
|
||||
@ -31,6 +32,7 @@ const ParserContainer = (props: IProps) => {
|
||||
reRunLoading,
|
||||
clickChunk,
|
||||
isReadonly,
|
||||
summaryInfo,
|
||||
} = props;
|
||||
const { t } = useTranslation();
|
||||
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
|
||||
@ -46,6 +48,7 @@ const ParserContainer = (props: IProps) => {
|
||||
key,
|
||||
type,
|
||||
value,
|
||||
params: data?.value?.obj?.params,
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
@ -130,7 +133,7 @@ const ParserContainer = (props: IProps) => {
|
||||
const newText = [...initialText.value, { text: text || ' ' }];
|
||||
setInitialText({
|
||||
...initialText,
|
||||
value: newText,
|
||||
value: newText as any,
|
||||
});
|
||||
},
|
||||
[initialText],
|
||||
@ -156,15 +159,16 @@ const ParserContainer = (props: IProps) => {
|
||||
{t('dataflowParser.parseSummary')}
|
||||
</h2>
|
||||
<div className="text-[12px] text-text-secondary italic ">
|
||||
{t('dataflowParser.parseSummaryTip')}
|
||||
{/* {t('dataflowParser.parseSummaryTip')} */}
|
||||
{summaryInfo}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isChunck && (
|
||||
<div>
|
||||
<h2 className="text-[16px]">{t('chunk.chunkResult')}</h2>
|
||||
<h2 className="text-[16px]">{t('dataflowParser.result')}</h2>
|
||||
<div className="text-[12px] text-text-secondary italic">
|
||||
{t('chunk.chunkResultTip')}
|
||||
{/* {t('chunk.chunkResultTip')} */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -6,7 +6,6 @@ import { DelimiterFormField } from '@/components/delimiter-form-field';
|
||||
import { ExcelToHtmlFormField } from '@/components/excel-to-html-form-field';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field';
|
||||
import { TagItems } from '../components/tag-item';
|
||||
import {
|
||||
ConfigurationFormContainer,
|
||||
MainContainer,
|
||||
@ -26,7 +25,7 @@ export function NaiveConfiguration() {
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
<ExcelToHtmlFormField></ExcelToHtmlFormField>
|
||||
<TagItems></TagItems>
|
||||
{/* <TagItems></TagItems> */}
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user