mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-23 23:16:58 +08:00
### What problem does this PR solve? Fix: Interoperate with the pipeline rerun and unbindTask interfaces. #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -53,12 +53,15 @@ type GraphRagItemsProps = {
|
||||
marginBottom?: boolean;
|
||||
className?: string;
|
||||
data: IGenerateLogButtonProps;
|
||||
onDelete?: () => void;
|
||||
};
|
||||
|
||||
export function UseGraphRagFormField({
|
||||
data,
|
||||
onDelete,
|
||||
}: {
|
||||
data: IGenerateLogButtonProps;
|
||||
onDelete?: () => void;
|
||||
}) {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
@ -84,6 +87,7 @@ export function UseGraphRagFormField({
|
||||
></Switch> */}
|
||||
<GenerateLogButton
|
||||
{...data}
|
||||
onDelete={onDelete}
|
||||
className="w-full text-text-secondary"
|
||||
status={1}
|
||||
type={GenerateType.KnowledgeGraph}
|
||||
@ -106,6 +110,7 @@ const GraphRagItems = ({
|
||||
marginBottom = false,
|
||||
className = 'p-10',
|
||||
data,
|
||||
onDelete,
|
||||
}: GraphRagItemsProps) => {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
@ -131,7 +136,10 @@ const GraphRagItems = ({
|
||||
|
||||
return (
|
||||
<FormContainer className={cn({ 'mb-4': marginBottom }, className)}>
|
||||
<UseGraphRagFormField data={data}></UseGraphRagFormField>
|
||||
<UseGraphRagFormField
|
||||
data={data}
|
||||
onDelete={onDelete}
|
||||
></UseGraphRagFormField>
|
||||
{useRaptor && (
|
||||
<>
|
||||
<EntityTypesFormField name="parser_config.graphrag.entity_types"></EntityTypesFormField>
|
||||
|
||||
@ -56,7 +56,13 @@ const Prompt = 'parser_config.raptor.prompt';
|
||||
|
||||
// The three types "table", "resume" and "one" do not display this configuration.
|
||||
|
||||
const RaptorFormFields = ({ data }: { data: IGenerateLogButtonProps }) => {
|
||||
const RaptorFormFields = ({
|
||||
data,
|
||||
onDelete,
|
||||
}: {
|
||||
data: IGenerateLogButtonProps;
|
||||
onDelete: () => void;
|
||||
}) => {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const useRaptor = useWatch({ name: UseRaptorField });
|
||||
@ -106,6 +112,7 @@ const RaptorFormFields = ({ data }: { data: IGenerateLogButtonProps }) => {
|
||||
<FormControl>
|
||||
<GenerateLogButton
|
||||
{...data}
|
||||
onDelete={onDelete}
|
||||
className="w-full text-text-secondary"
|
||||
status={1}
|
||||
type={GenerateType.Raptor}
|
||||
|
||||
@ -5,6 +5,7 @@ export interface IDocumentInfo {
|
||||
create_date: string;
|
||||
create_time: number;
|
||||
created_by: string;
|
||||
nickname: string;
|
||||
id: string;
|
||||
kb_id: string;
|
||||
location: string;
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { CheckedState } from '@radix-ui/react-checkbox';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ChunkTextMode } from '../../constant';
|
||||
import styles from '../../index.less';
|
||||
import { ArrayContainer, parserKeyMap } from './json-parser';
|
||||
import { ObjectContainer } from './object-parser';
|
||||
interface FormatPreserveEditorProps {
|
||||
initialValue: {
|
||||
key: string;
|
||||
key: keyof typeof parserKeyMap | 'text' | 'html';
|
||||
type: string;
|
||||
value: Array<{ [key: string]: string }>;
|
||||
};
|
||||
@ -29,152 +26,47 @@ const FormatPreserveEditor = ({
|
||||
selectedChunkIds,
|
||||
textMode,
|
||||
}: FormatPreserveEditorProps) => {
|
||||
const [content, setContent] = useState(initialValue);
|
||||
// const [isEditing, setIsEditing] = useState(false);
|
||||
const [activeEditIndex, setActiveEditIndex] = useState<number | undefined>(
|
||||
undefined,
|
||||
);
|
||||
console.log('initialValue', initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
setContent(initialValue);
|
||||
}, [initialValue]);
|
||||
const handleEdit = (e?: any, index?: number) => {
|
||||
console.log(e, index, content);
|
||||
if (content.key === 'json') {
|
||||
console.log(e, e.target.innerText);
|
||||
setContent((pre) => ({
|
||||
...pre,
|
||||
value: pre.value.map((item, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...item,
|
||||
[Object.keys(item)[0]]: e.target.innerText,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
}));
|
||||
setActiveEditIndex(index);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e: any) => {
|
||||
if (content.key === 'json') {
|
||||
setContent((pre) => ({
|
||||
...pre,
|
||||
value: pre.value.map((item, i) => {
|
||||
if (i === activeEditIndex) {
|
||||
return {
|
||||
...item,
|
||||
[Object.keys(item)[0]]: e.target.value,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
}));
|
||||
} else {
|
||||
setContent(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
const escapeNewlines = (text: string) => {
|
||||
return text.replace(/\n/g, '\\n');
|
||||
};
|
||||
const unescapeNewlines = (text: string) => {
|
||||
return text.replace(/\\n/g, '\n');
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const saveData = {
|
||||
...content,
|
||||
value: content.value?.map((item) => {
|
||||
return { ...item, text: unescapeNewlines(item.text) };
|
||||
}),
|
||||
};
|
||||
onSave(saveData);
|
||||
setActiveEditIndex(undefined);
|
||||
};
|
||||
const handleCheck = (e: CheckedState, id: string | number) => {
|
||||
handleCheckboxClick?.(id, e === 'indeterminate' ? false : e);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="editor-container">
|
||||
{/* {isEditing && content.key === 'json' ? (
|
||||
<Textarea
|
||||
className={cn(
|
||||
'w-full h-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 min-h-6 p-0',
|
||||
className,
|
||||
)}
|
||||
value={content.value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleSave}
|
||||
autoSize={{ maxRows: 100 }}
|
||||
autoFocus
|
||||
{['json', 'chunks'].includes(initialValue.key) && (
|
||||
<ArrayContainer
|
||||
className={className}
|
||||
initialValue={initialValue}
|
||||
handleCheck={handleCheck}
|
||||
selectedChunkIds={selectedChunkIds}
|
||||
onSave={onSave}
|
||||
escapeNewlines={escapeNewlines}
|
||||
unescapeNewlines={unescapeNewlines}
|
||||
textMode={textMode}
|
||||
isChunck={isChunck}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{content.key === 'json' && */}
|
||||
{content.value?.map((item, index) => (
|
||||
<section
|
||||
key={index}
|
||||
className={
|
||||
isChunck
|
||||
? 'bg-bg-card my-2 p-2 rounded-lg flex gap-1 items-start'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{isChunck && (
|
||||
<Checkbox
|
||||
onCheckedChange={(e) => {
|
||||
handleCheck(e, index);
|
||||
}}
|
||||
checked={selectedChunkIds?.some(
|
||||
(id) => id.toString() === index.toString(),
|
||||
)}
|
||||
></Checkbox>
|
||||
)}
|
||||
{activeEditIndex === index && (
|
||||
<Textarea
|
||||
key={'t' + index}
|
||||
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 !h-6 min-h-6 p-0',
|
||||
className,
|
||||
)}
|
||||
value={escapeNewlines(content.value[index].text)}
|
||||
onChange={handleChange}
|
||||
onBlur={handleSave}
|
||||
autoSize={{ maxRows: 100, minRows: 1 }}
|
||||
autoFocus
|
||||
/>
|
||||
)}
|
||||
{activeEditIndex !== index && (
|
||||
<div
|
||||
className={cn(
|
||||
'text-text-secondary overflow-auto scrollbar-auto whitespace-pre-wrap w-full',
|
||||
{
|
||||
[styles.contentEllipsis]: textMode === ChunkTextMode.Ellipse,
|
||||
},
|
||||
)}
|
||||
key={index}
|
||||
onClick={(e) => {
|
||||
handleEdit(e, index);
|
||||
}}
|
||||
>
|
||||
{escapeNewlines(item.text)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
))}
|
||||
{/* {content.key !== 'json' && (
|
||||
<pre
|
||||
className="text-text-secondary overflow-auto scrollbar-auto"
|
||||
onClick={handleEdit}
|
||||
>
|
||||
</pre>
|
||||
)}
|
||||
</>
|
||||
)}*/}
|
||||
)}
|
||||
|
||||
{['text', 'html'].includes(initialValue.key) && (
|
||||
<ObjectContainer
|
||||
className={className}
|
||||
initialValue={initialValue}
|
||||
handleCheck={handleCheck}
|
||||
selectedChunkIds={selectedChunkIds}
|
||||
onSave={onSave}
|
||||
escapeNewlines={escapeNewlines}
|
||||
unescapeNewlines={unescapeNewlines}
|
||||
textMode={textMode}
|
||||
isChunck={isChunck}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,173 @@
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { CheckedState } from '@radix-ui/react-checkbox';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { ChunkTextMode } from '../../constant';
|
||||
import styles from '../../index.less';
|
||||
export const parserKeyMap = {
|
||||
json: 'text',
|
||||
chunks: 'content_with_weight',
|
||||
};
|
||||
type IProps = {
|
||||
initialValue: {
|
||||
key: keyof typeof parserKeyMap;
|
||||
type: string;
|
||||
value: {
|
||||
[key: string]: string;
|
||||
}[];
|
||||
};
|
||||
isChunck?: boolean;
|
||||
handleCheck: (e: CheckedState, index: number) => void;
|
||||
selectedChunkIds: string[] | undefined;
|
||||
unescapeNewlines: (text: string) => string;
|
||||
escapeNewlines: (text: string) => string;
|
||||
onSave: (data: {
|
||||
value: {
|
||||
text: string;
|
||||
}[];
|
||||
key: string;
|
||||
type: string;
|
||||
}) => void;
|
||||
className?: string;
|
||||
textMode?: ChunkTextMode;
|
||||
};
|
||||
export const ArrayContainer = (props: IProps) => {
|
||||
const {
|
||||
initialValue,
|
||||
isChunck,
|
||||
handleCheck,
|
||||
selectedChunkIds,
|
||||
unescapeNewlines,
|
||||
escapeNewlines,
|
||||
onSave,
|
||||
className,
|
||||
textMode,
|
||||
} = props;
|
||||
|
||||
const [content, setContent] = useState(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
setContent(initialValue);
|
||||
console.log('initialValue json parse', initialValue);
|
||||
}, [initialValue]);
|
||||
|
||||
const [activeEditIndex, setActiveEditIndex] = useState<number | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const editDivRef = useRef<HTMLDivElement>(null);
|
||||
const handleEdit = useCallback(
|
||||
(e?: any, index?: number) => {
|
||||
console.log(e, e.target.innerText);
|
||||
setContent((pre) => ({
|
||||
...pre,
|
||||
value: pre.value.map((item, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...item,
|
||||
[parserKeyMap[content.key]]: e.target.innerText,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
}));
|
||||
setActiveEditIndex(index);
|
||||
},
|
||||
[setContent, setActiveEditIndex],
|
||||
);
|
||||
const handleSave = useCallback(
|
||||
(e: any) => {
|
||||
console.log(e, e.target.innerText);
|
||||
const saveData = {
|
||||
...content,
|
||||
value: content.value?.map((item, index) => {
|
||||
if (index === activeEditIndex) {
|
||||
return {
|
||||
...item,
|
||||
[parserKeyMap[content.key]]: unescapeNewlines(e.target.innerText),
|
||||
};
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}),
|
||||
};
|
||||
onSave(saveData);
|
||||
setActiveEditIndex(undefined);
|
||||
},
|
||||
[content, onSave],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeEditIndex !== undefined && editDivRef.current) {
|
||||
editDivRef.current.focus();
|
||||
editDivRef.current.textContent =
|
||||
content.value[activeEditIndex][parserKeyMap[content.key]];
|
||||
}
|
||||
}, [activeEditIndex, content]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{content.value?.map((item, index) => {
|
||||
if (item[parserKeyMap[content.key]] === '') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<section
|
||||
key={index}
|
||||
className={
|
||||
isChunck
|
||||
? 'bg-bg-card my-2 p-2 rounded-lg flex gap-1 items-start'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{isChunck && (
|
||||
<Checkbox
|
||||
onCheckedChange={(e) => {
|
||||
handleCheck(e, index);
|
||||
}}
|
||||
checked={selectedChunkIds?.some(
|
||||
(id) => id.toString() === index.toString(),
|
||||
)}
|
||||
></Checkbox>
|
||||
)}
|
||||
{activeEditIndex === index && (
|
||||
<div
|
||||
ref={editDivRef}
|
||||
contentEditable={true}
|
||||
onBlur={handleSave}
|
||||
// onKeyUp={handleChange}
|
||||
// dangerouslySetInnerHTML={{
|
||||
// __html: DOMPurify.sanitize(
|
||||
// escapeNewlines(
|
||||
// content.value[index][parserKeyMap[content.key]],
|
||||
// ),
|
||||
// ),
|
||||
// }}
|
||||
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 whitespace-pre-wrap w-full',
|
||||
{
|
||||
[styles.contentEllipsis]:
|
||||
textMode === ChunkTextMode.Ellipse,
|
||||
},
|
||||
)}
|
||||
key={index}
|
||||
onClick={(e) => {
|
||||
handleEdit(e, index);
|
||||
}}
|
||||
>
|
||||
{escapeNewlines(item[parserKeyMap[content.key]])}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,112 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { CheckedState } from '@radix-ui/react-checkbox';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { ChunkTextMode } from '../../constant';
|
||||
import styles from '../../index.less';
|
||||
|
||||
type IProps = {
|
||||
initialValue: {
|
||||
key: string;
|
||||
type: string;
|
||||
value: string;
|
||||
};
|
||||
isChunck?: boolean;
|
||||
handleCheck: (e: CheckedState, index: number) => void;
|
||||
unescapeNewlines: (text: string) => string;
|
||||
escapeNewlines: (text: string) => string;
|
||||
onSave: (data: { value: string; key: string; type: string }) => void;
|
||||
className?: string;
|
||||
textMode?: ChunkTextMode;
|
||||
};
|
||||
export const ObjectContainer = (props: IProps) => {
|
||||
const {
|
||||
initialValue,
|
||||
isChunck,
|
||||
unescapeNewlines,
|
||||
escapeNewlines,
|
||||
onSave,
|
||||
className,
|
||||
textMode,
|
||||
} = props;
|
||||
|
||||
const [content, setContent] = useState(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
setContent(initialValue);
|
||||
console.log('initialValue object parse', initialValue);
|
||||
}, [initialValue]);
|
||||
|
||||
const [activeEditIndex, setActiveEditIndex] = useState<number | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const editDivRef = useRef<HTMLDivElement>(null);
|
||||
const handleEdit = useCallback(
|
||||
(e?: any) => {
|
||||
console.log(e, e.target.innerText);
|
||||
setContent((pre) => ({
|
||||
...pre,
|
||||
value: e.target.innerText,
|
||||
}));
|
||||
setActiveEditIndex(1);
|
||||
},
|
||||
[setContent, setActiveEditIndex],
|
||||
);
|
||||
const handleSave = useCallback(
|
||||
(e: any) => {
|
||||
console.log(e, e.target.innerText);
|
||||
const saveData = {
|
||||
...content,
|
||||
value: unescapeNewlines(e.target.innerText),
|
||||
};
|
||||
onSave(saveData);
|
||||
setActiveEditIndex(undefined);
|
||||
},
|
||||
[content, onSave],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeEditIndex !== undefined && editDivRef.current) {
|
||||
editDivRef.current.focus();
|
||||
editDivRef.current.textContent = escapeNewlines(content.value);
|
||||
}
|
||||
}, [activeEditIndex, content, escapeNewlines]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section
|
||||
className={
|
||||
isChunck
|
||||
? 'bg-bg-card my-2 p-2 rounded-lg flex gap-1 items-start'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{activeEditIndex && (
|
||||
<div
|
||||
ref={editDivRef}
|
||||
contentEditable={true}
|
||||
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,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{!activeEditIndex && (
|
||||
<div
|
||||
className={cn(
|
||||
'text-text-secondary overflow-auto scrollbar-auto whitespace-pre-wrap w-full',
|
||||
{
|
||||
[styles.contentEllipsis]: textMode === ChunkTextMode.Ellipse,
|
||||
},
|
||||
)}
|
||||
onClick={(e) => {
|
||||
handleEdit(e);
|
||||
}}
|
||||
>
|
||||
{escapeNewlines(content.value)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -38,10 +38,6 @@ export const TimelineNodeObj = {
|
||||
},
|
||||
[TimelineNodeType.characterSplitter]: {
|
||||
title: 'Character Splitter',
|
||||
icon: <Heading size={13} />,
|
||||
},
|
||||
[TimelineNodeType.splitter]: {
|
||||
title: 'Splitter',
|
||||
icon: <Blocks size={13} />,
|
||||
},
|
||||
[TimelineNodeType.tokenizer]: {
|
||||
|
||||
@ -6,10 +6,9 @@ export enum ChunkTextMode {
|
||||
export enum TimelineNodeType {
|
||||
begin = 'file',
|
||||
parser = 'parser',
|
||||
splitter = 'splitter',
|
||||
contextGenerator = 'contextGenerator',
|
||||
titleSplitter = 'titleSplitter',
|
||||
characterSplitter = 'characterSplitter',
|
||||
tokenizer = 'tokenizer',
|
||||
contextGenerator = 'extractor',
|
||||
titleSplitter = 'hierarchicalMerger',
|
||||
characterSplitter = 'splitter',
|
||||
tokenizer = 'indexer',
|
||||
end = 'end',
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { TimelineNode } from '@/components/originui/timeline';
|
||||
import message from '@/components/ui/message';
|
||||
import {
|
||||
useCreateChunk,
|
||||
useDeleteChunk,
|
||||
@ -10,7 +11,8 @@ import { IChunk } from '@/interfaces/database/knowledge';
|
||||
import kbService from '@/services/knowledge-service';
|
||||
import { formatSecondsToHumanReadable } from '@/utils/date';
|
||||
import { buildChunkHighlights } from '@/utils/document-util';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { t } from 'i18next';
|
||||
import { camelCase, upperFirst } from 'lodash';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { IHighlight } from 'react-pdf-highlighter';
|
||||
@ -169,22 +171,16 @@ export const useUpdateChunk = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useFetchParserList = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
return {
|
||||
loading,
|
||||
};
|
||||
};
|
||||
|
||||
export const useRerunDataflow = ({
|
||||
data,
|
||||
}: {
|
||||
data: IPipelineFileLogDetail;
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isChange, setIsChange] = useState(false);
|
||||
const handleReRunFunc = useCallback(
|
||||
(newData: { value: IDslComponent; key: string }) => {
|
||||
|
||||
const { mutateAsync: handleReRunFunc, isPending: loading } = useMutation({
|
||||
mutationKey: ['pipelineRerun', data],
|
||||
mutationFn: async (newData: { value: IDslComponent; key: string }) => {
|
||||
const newDsl = {
|
||||
...data.dsl,
|
||||
components: {
|
||||
@ -197,16 +193,21 @@ export const useRerunDataflow = ({
|
||||
const params = {
|
||||
id: data.id,
|
||||
dsl: newDsl,
|
||||
compenent_id: newData.key,
|
||||
component_id: newData.key,
|
||||
};
|
||||
console.log('newDsl', newDsl, params);
|
||||
const { data: result } = await kbService.pipelineRerun(params);
|
||||
if (result.code === 0) {
|
||||
message.success(t('message.operated'));
|
||||
// queryClient.invalidateQueries({
|
||||
// queryKey: [type],
|
||||
// });
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[data],
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
loading,
|
||||
setLoading,
|
||||
isChange,
|
||||
setIsChange,
|
||||
handleReRunFunc,
|
||||
@ -225,7 +226,6 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => {
|
||||
type:
|
||||
| TimelineNodeType.begin
|
||||
| TimelineNodeType.parser
|
||||
| TimelineNodeType.splitter
|
||||
| TimelineNodeType.tokenizer
|
||||
| TimelineNodeType.characterSplitter
|
||||
| TimelineNodeType.titleSplitter,
|
||||
@ -242,10 +242,9 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => {
|
||||
tempType = TimelineNodeType.tokenizer;
|
||||
} else if (
|
||||
name === TimelineNodeType.characterSplitter ||
|
||||
name === TimelineNodeType.titleSplitter ||
|
||||
name === TimelineNodeType.splitter
|
||||
name === TimelineNodeType.titleSplitter
|
||||
) {
|
||||
tempType = TimelineNodeType.splitter;
|
||||
tempType = TimelineNodeType.characterSplitter;
|
||||
}
|
||||
const timeNode = {
|
||||
...TimelineNodeObj[name],
|
||||
|
||||
@ -94,18 +94,18 @@ const Chunk = () => {
|
||||
></div>
|
||||
),
|
||||
onVisibleChange: () => {
|
||||
Modal.hide();
|
||||
Modal.destroy();
|
||||
},
|
||||
footer: (
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant={'outline'} onClick={() => Modal.hide()}>
|
||||
<Button variant={'outline'} onClick={() => Modal.destroy()}>
|
||||
{t('dataflowParser.changeStepModalCancelText')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
className="!bg-state-error text-text-primary"
|
||||
onClick={() => {
|
||||
Modal.hide();
|
||||
Modal.destroy();
|
||||
setActiveStepId(id);
|
||||
setIsChange(false);
|
||||
}}
|
||||
@ -193,7 +193,8 @@ const Chunk = () => {
|
||||
)} */}
|
||||
{/* {currentTimeNode?.type === TimelineNodeType.parser && ( */}
|
||||
{(currentTimeNode?.type === TimelineNodeType.parser ||
|
||||
currentTimeNode?.type === TimelineNodeType.splitter) && (
|
||||
currentTimeNode?.type === TimelineNodeType.characterSplitter ||
|
||||
currentTimeNode?.type === TimelineNodeType.titleSplitter) && (
|
||||
<ParserContainer
|
||||
isChange={isChange}
|
||||
reRunLoading={reRunLoading}
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import { TimelineNode } from '@/components/originui/timeline';
|
||||
import Spotlight from '@/components/spotlight';
|
||||
import { Spin } from '@/components/ui/spin';
|
||||
import { cn } from '@/lib/utils';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ChunkResultBar from './components/chunk-result-bar';
|
||||
import CheckboxSets from './components/chunk-result-bar/checkbox-sets';
|
||||
import FormatPreserEditor from './components/parse-editer';
|
||||
import RerunButton from './components/rerun-button';
|
||||
import { TimelineNodeType } from './constant';
|
||||
import { useChangeChunkTextMode, useFetchParserList } from './hooks';
|
||||
import { useChangeChunkTextMode } from './hooks';
|
||||
import { IDslComponent } from './interface';
|
||||
interface IProps {
|
||||
isChange: boolean;
|
||||
@ -23,15 +22,15 @@ interface IProps {
|
||||
const ParserContainer = (props: IProps) => {
|
||||
const { isChange, setIsChange, step, data, reRunFunc, reRunLoading } = props;
|
||||
const { t } = useTranslation();
|
||||
const { loading } = useFetchParserList();
|
||||
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
|
||||
const { changeChunkTextMode, textMode } = useChangeChunkTextMode();
|
||||
const initialValue = useMemo(() => {
|
||||
const outputs = data?.value?.obj?.params?.outputs;
|
||||
const key = outputs?.output_format?.value;
|
||||
if (!outputs || !key) return { key: '', type: '', value: [] };
|
||||
const value = outputs[key]?.value;
|
||||
const type = outputs[key]?.type;
|
||||
console.log('outputs-->', outputs);
|
||||
console.log('outputs-->', outputs, data, key, value);
|
||||
return {
|
||||
key,
|
||||
type,
|
||||
@ -40,6 +39,10 @@ const ParserContainer = (props: IProps) => {
|
||||
}, [data]);
|
||||
|
||||
const [initialText, setInitialText] = useState(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
setInitialText(initialValue);
|
||||
}, [initialValue]);
|
||||
const handleSave = (newContent: any) => {
|
||||
console.log('newContent-change-->', newContent, initialValue);
|
||||
if (JSON.stringify(newContent) !== JSON.stringify(initialValue)) {
|
||||
@ -109,8 +112,7 @@ const ParserContainer = (props: IProps) => {
|
||||
|
||||
const isChunck =
|
||||
step?.type === TimelineNodeType.characterSplitter ||
|
||||
step?.type === TimelineNodeType.titleSplitter ||
|
||||
step?.type === TimelineNodeType.splitter;
|
||||
step?.type === TimelineNodeType.titleSplitter;
|
||||
|
||||
const handleCreateChunk = useCallback(
|
||||
(text: string) => {
|
||||
@ -124,6 +126,7 @@ const ParserContainer = (props: IProps) => {
|
||||
},
|
||||
[initialText],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isChange && (
|
||||
@ -136,71 +139,72 @@ const ParserContainer = (props: IProps) => {
|
||||
</div>
|
||||
)}
|
||||
<div className={classNames('flex flex-col w-full')}>
|
||||
<Spin spinning={loading} className="" size="large">
|
||||
<div className="h-[50px] flex flex-col justify-end pb-[5px]">
|
||||
{!isChunck && (
|
||||
<div>
|
||||
<h2 className="text-[16px]">
|
||||
{t('dataflowParser.parseSummary')}
|
||||
</h2>
|
||||
<div className="text-[12px] text-text-secondary italic ">
|
||||
{t('dataflowParser.parseSummaryTip')}
|
||||
</div>
|
||||
{/* <Spin spinning={false} className="" size="large"> */}
|
||||
<div className="h-[50px] flex flex-col justify-end pb-[5px]">
|
||||
{!isChunck && (
|
||||
<div>
|
||||
<h2 className="text-[16px]">
|
||||
{t('dataflowParser.parseSummary')}
|
||||
</h2>
|
||||
<div className="text-[12px] text-text-secondary italic ">
|
||||
{t('dataflowParser.parseSummaryTip')}
|
||||
</div>
|
||||
)}
|
||||
{isChunck && (
|
||||
<div>
|
||||
<h2 className="text-[16px]">{t('chunk.chunkResult')}</h2>
|
||||
<div className="text-[12px] text-text-secondary italic">
|
||||
{t('chunk.chunkResultTip')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isChunck && (
|
||||
<div className="pt-[5px] pb-[5px] flex justify-between items-center">
|
||||
<CheckboxSets
|
||||
selectAllChunk={selectAllChunk}
|
||||
removeChunk={handleRemoveChunk}
|
||||
checked={selectedChunkIds.length === initialText.value.length}
|
||||
selectedChunkIds={selectedChunkIds}
|
||||
/>
|
||||
<ChunkResultBar
|
||||
changeChunkTextMode={changeChunkTextMode}
|
||||
createChunk={handleCreateChunk}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isChunck && (
|
||||
<div>
|
||||
<h2 className="text-[16px]">{t('chunk.chunkResult')}</h2>
|
||||
<div className="text-[12px] text-text-secondary italic">
|
||||
{t('chunk.chunkResultTip')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
' border rounded-lg p-[20px] box-border w-[calc(100%-20px)] overflow-auto scrollbar-none',
|
||||
{
|
||||
'h-[calc(100vh-240px)]': isChunck,
|
||||
'h-[calc(100vh-180px)]': !isChunck,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{isChunck && (
|
||||
<div className="pt-[5px] pb-[5px] flex justify-between items-center">
|
||||
<CheckboxSets
|
||||
selectAllChunk={selectAllChunk}
|
||||
removeChunk={handleRemoveChunk}
|
||||
checked={selectedChunkIds.length === initialText.value.length}
|
||||
selectedChunkIds={selectedChunkIds}
|
||||
/>
|
||||
<ChunkResultBar
|
||||
changeChunkTextMode={changeChunkTextMode}
|
||||
createChunk={handleCreateChunk}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
' border rounded-lg p-[20px] box-border w-[calc(100%-20px)] overflow-auto scrollbar-none',
|
||||
{
|
||||
'h-[calc(100vh-240px)]': isChunck,
|
||||
'h-[calc(100vh-180px)]': !isChunck,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{initialText && (
|
||||
<FormatPreserEditor
|
||||
initialValue={initialText}
|
||||
onSave={handleSave}
|
||||
className={
|
||||
initialText.key !== 'json' ? '!h-[calc(100vh-220px)]' : ''
|
||||
}
|
||||
// className={
|
||||
// initialText.key !== 'json' ? '!h-[calc(100vh-220px)]' : ''
|
||||
// }
|
||||
isChunck={isChunck}
|
||||
textMode={textMode}
|
||||
isDelete={
|
||||
step?.type === TimelineNodeType.characterSplitter ||
|
||||
step?.type === TimelineNodeType.titleSplitter ||
|
||||
step?.type === TimelineNodeType.splitter
|
||||
step?.type === TimelineNodeType.titleSplitter
|
||||
}
|
||||
handleCheckboxClick={handleCheckboxClick}
|
||||
selectedChunkIds={selectedChunkIds}
|
||||
/>
|
||||
<Spotlight opcity={0.6} coverage={60} />
|
||||
</div>
|
||||
</Spin>
|
||||
)}
|
||||
<Spotlight opcity={0.6} coverage={60} />
|
||||
</div>
|
||||
{/* </Spin> */}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -12,7 +12,10 @@ import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import { TopTitle } from '../dataset-title';
|
||||
import { IGenerateLogButtonProps } from '../dataset/generate-button/generate';
|
||||
import {
|
||||
GenerateType,
|
||||
IGenerateLogButtonProps,
|
||||
} from '../dataset/generate-button/generate';
|
||||
import LinkDataPipeline, {
|
||||
IDataPipelineNodeProps,
|
||||
} from './components/link-data-pipeline';
|
||||
@ -120,6 +123,20 @@ export default function DatasetSettings() {
|
||||
// form.setValue('pipeline_avatar', data.avatar || '');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeletePipelineTask = (type: GenerateType) => {
|
||||
if (type === GenerateType.KnowledgeGraph) {
|
||||
setGraphRagGenerateData({
|
||||
finish_at: '',
|
||||
task_id: '',
|
||||
} as IGenerateLogButtonProps);
|
||||
} else if (type === GenerateType.Raptor) {
|
||||
setRaptorGenerateData({
|
||||
finish_at: '',
|
||||
task_id: '',
|
||||
} as IGenerateLogButtonProps);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<section className="p-5 h-full flex flex-col">
|
||||
<TopTitle
|
||||
@ -140,10 +157,14 @@ export default function DatasetSettings() {
|
||||
<GraphRagItems
|
||||
className="border-none p-0"
|
||||
data={graphRagGenerateData as IGenerateLogButtonProps}
|
||||
onDelete={() =>
|
||||
handleDeletePipelineTask(GenerateType.KnowledgeGraph)
|
||||
}
|
||||
></GraphRagItems>
|
||||
<Divider />
|
||||
<RaptorFormFields
|
||||
data={raptorGenerateData as IGenerateLogButtonProps}
|
||||
onDelete={() => handleDeletePipelineTask(GenerateType.Raptor)}
|
||||
></RaptorFormFields>
|
||||
<Divider />
|
||||
<LinkDataPipeline
|
||||
|
||||
@ -16,17 +16,23 @@ import { lowerFirst } from 'lodash';
|
||||
import { CirclePause, Trash2, WandSparkles } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ProcessingType } from '../../dataset-overview/dataset-common';
|
||||
import { replaceText } from '../../process-log-modal';
|
||||
import {
|
||||
ITraceInfo,
|
||||
generateStatus,
|
||||
useDatasetGenerate,
|
||||
useTraceGenerate,
|
||||
useUnBindTask,
|
||||
} from './hook';
|
||||
export enum GenerateType {
|
||||
KnowledgeGraph = 'KnowledgeGraph',
|
||||
Raptor = 'Raptor',
|
||||
}
|
||||
export const GenerateTypeMap = {
|
||||
[GenerateType.KnowledgeGraph]: ProcessingType.knowledgeGraph,
|
||||
[GenerateType.Raptor]: ProcessingType.raptor,
|
||||
};
|
||||
const MenuItem: React.FC<{
|
||||
name: GenerateType;
|
||||
data: ITraceInfo;
|
||||
@ -78,9 +84,11 @@ const MenuItem: React.FC<{
|
||||
'border cursor-pointer p-2 rounded-md focus:bg-transparent',
|
||||
{
|
||||
'hover:border-accent-primary hover:bg-[rgba(59,160,92,0.1)]':
|
||||
status === generateStatus.start,
|
||||
status === generateStatus.start ||
|
||||
status === generateStatus.completed,
|
||||
'hover:border-border hover:bg-[rgba(59,160,92,0)]':
|
||||
status !== generateStatus.start,
|
||||
status !== generateStatus.start &&
|
||||
status !== generateStatus.completed,
|
||||
},
|
||||
)}
|
||||
onSelect={(e) => {
|
||||
@ -93,7 +101,10 @@ const MenuItem: React.FC<{
|
||||
<div
|
||||
className="flex items-start gap-2 flex-col w-full"
|
||||
onClick={() => {
|
||||
if (status === generateStatus.start) {
|
||||
if (
|
||||
status === generateStatus.start ||
|
||||
status === generateStatus.completed
|
||||
) {
|
||||
runGenerate({ type });
|
||||
}
|
||||
}}
|
||||
@ -234,7 +245,21 @@ export type IGenerateLogProps = IGenerateLogButtonProps & {
|
||||
};
|
||||
export const GenerateLogButton = (props: IGenerateLogProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { task_id, message, finish_at, type, onDelete } = props;
|
||||
const { message, finish_at, type, onDelete } = props;
|
||||
|
||||
const { handleUnbindTask } = useUnBindTask();
|
||||
|
||||
const handleDeleteFunc = async () => {
|
||||
const data = await handleUnbindTask({
|
||||
type: GenerateTypeMap[type as GenerateType],
|
||||
});
|
||||
Modal.destroy();
|
||||
console.log('handleUnbindTask', data);
|
||||
if (data.code === 0) {
|
||||
onDelete?.();
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
Modal.show({
|
||||
visible: true,
|
||||
@ -259,14 +284,14 @@ export const GenerateLogButton = (props: IGenerateLogProps) => {
|
||||
></div>
|
||||
),
|
||||
onVisibleChange: () => {
|
||||
Modal.hide();
|
||||
Modal.destroy();
|
||||
},
|
||||
footer: (
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant={'outline'}
|
||||
onClick={() => Modal.hide()}
|
||||
onClick={() => Modal.destroy()}
|
||||
>
|
||||
{t('dataflowParser.changeStepModalCancelText')}
|
||||
</Button>
|
||||
@ -275,7 +300,7 @@ export const GenerateLogButton = (props: IGenerateLogProps) => {
|
||||
variant={'secondary'}
|
||||
className="!bg-state-error text-text-primary"
|
||||
onClick={() => {
|
||||
Modal.hide();
|
||||
handleDeleteFunc();
|
||||
}}
|
||||
>
|
||||
{t('common.delete')}
|
||||
@ -284,6 +309,7 @@ export const GenerateLogButton = (props: IGenerateLogProps) => {
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex bg-bg-card rounded-md py-1 px-3', props.className)}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import message from '@/components/ui/message';
|
||||
import agentService from '@/services/agent-service';
|
||||
import kbService from '@/services/knowledge-service';
|
||||
import kbService, { deletePipelineTask } from '@/services/knowledge-service';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { t } from 'i18next';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'umi';
|
||||
import { ProcessingType } from '../../dataset-overview/dataset-common';
|
||||
import { GenerateType } from './generate';
|
||||
export const generateStatus = {
|
||||
running: 'running',
|
||||
@ -153,3 +154,21 @@ export const useDatasetGenerate = () => {
|
||||
});
|
||||
return { runGenerate: mutateAsync, pauseGenerate, data, loading };
|
||||
};
|
||||
|
||||
export const useUnBindTask = () => {
|
||||
const { id } = useParams();
|
||||
const { mutateAsync: handleUnbindTask } = useMutation({
|
||||
mutationKey: [DatasetKey.pauseGenerate],
|
||||
mutationFn: async ({ type }: { type: ProcessingType }) => {
|
||||
const { data } = await deletePipelineTask({ kb_id: id as string, type });
|
||||
if (data.code === 0) {
|
||||
message.success(t('message.operated'));
|
||||
// queryClient.invalidateQueries({
|
||||
// queryKey: [type],
|
||||
// });
|
||||
}
|
||||
return data;
|
||||
},
|
||||
});
|
||||
return { handleUnbindTask };
|
||||
};
|
||||
|
||||
@ -79,7 +79,7 @@ export const useShowLog = (documents: IDocumentInfo[]) => {
|
||||
if (findRecord) {
|
||||
log = {
|
||||
fileType: findRecord?.suffix,
|
||||
uploadedBy: findRecord?.created_by,
|
||||
uploadedBy: findRecord?.nickname,
|
||||
fileName: findRecord?.name,
|
||||
uploadDate: formatDate(findRecord.create_date),
|
||||
fileSize: formatBytes(findRecord.size || 0),
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
IFetchKnowledgeListRequestBody,
|
||||
IFetchKnowledgeListRequestParams,
|
||||
} from '@/interfaces/request/knowledge';
|
||||
import { ProcessingType } from '@/pages/dataset/dataset-overview/dataset-common';
|
||||
import api from '@/utils/api';
|
||||
import registerServer from '@/utils/register-server';
|
||||
import request, { post } from '@/utils/request';
|
||||
@ -254,4 +255,14 @@ export const listPipelineDatasetLogs = (
|
||||
body?: IFetchDocumentListRequestBody,
|
||||
) => request.post(api.fetchPipelineDatasetLogs, { data: body || {}, params });
|
||||
|
||||
export function deletePipelineTask({
|
||||
kb_id,
|
||||
type,
|
||||
}: {
|
||||
kb_id: string;
|
||||
type: ProcessingType;
|
||||
}) {
|
||||
return request.delete(api.unbindPipelineTask({ kb_id, type }));
|
||||
}
|
||||
|
||||
export default kbService;
|
||||
|
||||
@ -54,6 +54,9 @@ export default {
|
||||
traceGraphRag: `${api_host}/kb/trace_graphrag`,
|
||||
runRaptor: `${api_host}/kb/run_raptor`,
|
||||
traceRaptor: `${api_host}/kb/trace_raptor`,
|
||||
unbindPipelineTask: ({ kb_id, type }: { kb_id: string; type: string }) =>
|
||||
`${api_host}/kb/unbind_task?kb_id=${kb_id}&pipeline_task_type=${type}`,
|
||||
pipelineRerun: `${api_host}/canvas/rerun`,
|
||||
|
||||
// tags
|
||||
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,
|
||||
|
||||
Reference in New Issue
Block a user