mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Fix: Fixed the issue where the drop-down box could not be displayed after selecting a large model #9869 (#10205)
### What problem does this PR solve? Fix: Fixed the issue where the drop-down box could not be displayed after selecting a large model #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -3,7 +3,7 @@ import { useTranslate } from '@/hooks/common-hooks';
|
|||||||
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { camelCase } from 'lodash';
|
import { camelCase } from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext } from 'react-hook-form';
|
||||||
import { SelectWithSearch } from './originui/select-with-search';
|
import { SelectWithSearch } from './originui/select-with-search';
|
||||||
import {
|
import {
|
||||||
@ -23,10 +23,12 @@ export function LayoutRecognizeFormField({
|
|||||||
name = 'parser_config.layout_recognize',
|
name = 'parser_config.layout_recognize',
|
||||||
horizontal = true,
|
horizontal = true,
|
||||||
optionsWithoutLLM,
|
optionsWithoutLLM,
|
||||||
|
label,
|
||||||
}: {
|
}: {
|
||||||
name?: string;
|
name?: string;
|
||||||
horizontal?: boolean;
|
horizontal?: boolean;
|
||||||
optionsWithoutLLM?: { value: string; label: string }[];
|
optionsWithoutLLM?: { value: string; label: string }[];
|
||||||
|
label?: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
|
|
||||||
@ -81,7 +83,7 @@ export function LayoutRecognizeFormField({
|
|||||||
['w-1/4']: horizontal,
|
['w-1/4']: horizontal,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{t('layoutRecognize')}
|
{label || t('layoutRecognize')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<div className={horizontal ? 'w-3/4' : 'w-full'}>
|
<div className={horizontal ? 'w-3/4' : 'w-full'}>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
|
|||||||
@ -49,6 +49,22 @@ export type SelectWithSearchFlagProps = {
|
|||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function findLabelWithoutOptions(
|
||||||
|
options: SelectWithSearchFlagOptionType[],
|
||||||
|
value: string,
|
||||||
|
) {
|
||||||
|
return options.find((opt) => opt.value === value)?.label || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function findLabelWithOptions(
|
||||||
|
options: SelectWithSearchFlagOptionType[],
|
||||||
|
value: string,
|
||||||
|
) {
|
||||||
|
return options
|
||||||
|
.map((group) => group?.options?.find((item) => item.value === value))
|
||||||
|
.filter(Boolean)[0]?.label;
|
||||||
|
}
|
||||||
|
|
||||||
export const SelectWithSearch = forwardRef<
|
export const SelectWithSearch = forwardRef<
|
||||||
React.ElementRef<typeof Button>,
|
React.ElementRef<typeof Button>,
|
||||||
SelectWithSearchFlagProps
|
SelectWithSearchFlagProps
|
||||||
@ -69,6 +85,28 @@ export const SelectWithSearch = forwardRef<
|
|||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
const [value, setValue] = useState<string>('');
|
const [value, setValue] = useState<string>('');
|
||||||
|
|
||||||
|
const selectLabel = useMemo(() => {
|
||||||
|
if (options.every((x) => x.options === undefined)) {
|
||||||
|
return findLabelWithoutOptions(options, value);
|
||||||
|
} else if (options.every((x) => Array.isArray(x.options))) {
|
||||||
|
return findLabelWithOptions(options, value);
|
||||||
|
} else {
|
||||||
|
// Some have options, some don't
|
||||||
|
const optionsWithOptions = options.filter((x) =>
|
||||||
|
Array.isArray(x.options),
|
||||||
|
);
|
||||||
|
const optionsWithoutOptions = options.filter(
|
||||||
|
(x) => x.options === undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const label = findLabelWithOptions(optionsWithOptions, value);
|
||||||
|
if (label) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
return findLabelWithoutOptions(optionsWithoutOptions, value);
|
||||||
|
}
|
||||||
|
}, [options, value]);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
(val: string) => {
|
(val: string) => {
|
||||||
setValue(val);
|
setValue(val);
|
||||||
@ -90,16 +128,7 @@ export const SelectWithSearch = forwardRef<
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(val);
|
setValue(val);
|
||||||
}, [val]);
|
}, [val]);
|
||||||
const selectLabel = useMemo(() => {
|
|
||||||
const optionTemp = options[0];
|
|
||||||
if (optionTemp?.options) {
|
|
||||||
return options
|
|
||||||
.map((group) => group?.options?.find((item) => item.value === value))
|
|
||||||
.filter(Boolean)[0]?.label;
|
|
||||||
} else {
|
|
||||||
return options.find((opt) => opt.value === value)?.label || '';
|
|
||||||
}
|
|
||||||
}, [options, value]);
|
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|||||||
@ -1694,6 +1694,7 @@ This delimiter is used to split the input text into several text pieces echo of
|
|||||||
searchMethod: 'Search method',
|
searchMethod: 'Search method',
|
||||||
filenameEmbdWeight: 'Filename embd weight',
|
filenameEmbdWeight: 'Filename embd weight',
|
||||||
begin: 'File',
|
begin: 'File',
|
||||||
|
parserMethod: 'Parser method',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1604,6 +1604,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
searchMethod: '搜索方法',
|
searchMethod: '搜索方法',
|
||||||
filenameEmbdWeight: '文件名嵌入权重',
|
filenameEmbdWeight: '文件名嵌入权重',
|
||||||
begin: '文件',
|
begin: '文件',
|
||||||
|
parserMethod: '解析方法',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import { useBeforeDelete } from '../hooks/use-before-delete';
|
|||||||
import { useMoveNote } from '../hooks/use-move-note';
|
import { useMoveNote } from '../hooks/use-move-note';
|
||||||
import { useDropdownManager } from './context';
|
import { useDropdownManager } from './context';
|
||||||
|
|
||||||
|
import { useRunDataflow } from '../hooks/use-run-dataflow';
|
||||||
import {
|
import {
|
||||||
useHideFormSheetOnNodeDeletion,
|
useHideFormSheetOnNodeDeletion,
|
||||||
useShowDrawer,
|
useShowDrawer,
|
||||||
@ -152,6 +153,8 @@ function DataFlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
hideModal: hideLogSheet,
|
hideModal: hideLogSheet,
|
||||||
} = useSetModalState();
|
} = useSetModalState();
|
||||||
|
|
||||||
|
const { run, loading: running, messageId } = useRunDataflow(showLogSheet!);
|
||||||
|
|
||||||
const onConnect = (connection: Connection) => {
|
const onConnect = (connection: Connection) => {
|
||||||
originalOnConnect(connection);
|
originalOnConnect(connection);
|
||||||
isConnectedRef.current = true;
|
isConnectedRef.current = true;
|
||||||
@ -300,10 +303,13 @@ function DataFlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
{runVisible && (
|
{runVisible && (
|
||||||
<RunSheet
|
<RunSheet
|
||||||
hideModal={hideRunOrChatDrawer}
|
hideModal={hideRunOrChatDrawer}
|
||||||
showModal={showLogSheet}
|
run={run}
|
||||||
|
loading={running}
|
||||||
></RunSheet>
|
></RunSheet>
|
||||||
)}
|
)}
|
||||||
{logSheetVisible && <LogSheet hideModal={hideLogSheet}></LogSheet>}
|
{logSheetVisible && (
|
||||||
|
<LogSheet hideModal={hideLogSheet} messageId={messageId}></LogSheet>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -227,7 +227,7 @@ export enum FileType {
|
|||||||
Email = 'email',
|
Email = 'email',
|
||||||
TextMarkdown = 'text&markdown',
|
TextMarkdown = 'text&markdown',
|
||||||
Docx = 'word',
|
Docx = 'word',
|
||||||
PowerPoint = 'ppt',
|
PowerPoint = 'slides',
|
||||||
Video = 'video',
|
Video = 'video',
|
||||||
Audio = 'audio',
|
Audio = 'audio',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { crossLanguageOptions } from '@/components/cross-language-form-field';
|
||||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
import { LLMFormField } from '@/components/llm-setting-items/llm-form-field';
|
import { LLMFormField } from '@/components/llm-setting-items/llm-form-field';
|
||||||
import {
|
import {
|
||||||
@ -46,11 +47,13 @@ export function ParserMethodFormField({
|
|||||||
prefix,
|
prefix,
|
||||||
optionsWithoutLLM,
|
optionsWithoutLLM,
|
||||||
}: CommonProps & { optionsWithoutLLM?: { value: string; label: string }[] }) {
|
}: CommonProps & { optionsWithoutLLM?: { value: string; label: string }[] }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<LayoutRecognizeFormField
|
<LayoutRecognizeFormField
|
||||||
name={buildFieldNameWithPrefix(`parse_method`, prefix)}
|
name={buildFieldNameWithPrefix(`parse_method`, prefix)}
|
||||||
horizontal={false}
|
horizontal={false}
|
||||||
optionsWithoutLLM={optionsWithoutLLM}
|
optionsWithoutLLM={optionsWithoutLLM}
|
||||||
|
label={t('dataflow.parserMethod')}
|
||||||
></LayoutRecognizeFormField>
|
></LayoutRecognizeFormField>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -62,3 +65,22 @@ export function LargeModelFormField({ prefix }: CommonProps) {
|
|||||||
></LLMFormField>
|
></LLMFormField>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function LanguageFormField({ prefix }: CommonProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={buildFieldNameWithPrefix(`lang`, prefix)}
|
||||||
|
label={t('dataflow.lang')}
|
||||||
|
>
|
||||||
|
{(field) => (
|
||||||
|
<SelectWithSearch
|
||||||
|
options={crossLanguageOptions}
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
></SelectWithSearch>
|
||||||
|
)}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
import { ImageParseMethod } from '../../constant';
|
import { ImageParseMethod } from '../../constant';
|
||||||
import { ParserMethodFormField } from './common-form-fields';
|
import { LanguageFormField, ParserMethodFormField } from './common-form-fields';
|
||||||
import { CommonProps } from './interface';
|
import { CommonProps } from './interface';
|
||||||
|
import { useSetInitialLanguage } from './use-set-initial-language';
|
||||||
import { buildFieldNameWithPrefix } from './utils';
|
import { buildFieldNameWithPrefix } from './utils';
|
||||||
|
|
||||||
const options = buildOptions(ImageParseMethod);
|
const options = buildOptions(ImageParseMethod);
|
||||||
@ -13,6 +14,14 @@ export function ImageFormFields({ prefix }: CommonProps) {
|
|||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix);
|
const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix);
|
||||||
|
|
||||||
|
const parseMethod = useWatch({
|
||||||
|
name: parseMethodName,
|
||||||
|
});
|
||||||
|
|
||||||
|
const languageShown = useMemo(() => {
|
||||||
|
return !isEmpty(parseMethod) && parseMethod !== ImageParseMethod.OCR;
|
||||||
|
}, [parseMethod]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEmpty(form.getValues(parseMethodName))) {
|
if (isEmpty(form.getValues(parseMethodName))) {
|
||||||
form.setValue(parseMethodName, ImageParseMethod.OCR, {
|
form.setValue(parseMethodName, ImageParseMethod.OCR, {
|
||||||
@ -22,12 +31,15 @@ export function ImageFormFields({ prefix }: CommonProps) {
|
|||||||
}
|
}
|
||||||
}, [form, parseMethodName]);
|
}, [form, parseMethodName]);
|
||||||
|
|
||||||
|
useSetInitialLanguage({ prefix, languageShown });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ParserMethodFormField
|
<ParserMethodFormField
|
||||||
prefix={prefix}
|
prefix={prefix}
|
||||||
optionsWithoutLLM={options}
|
optionsWithoutLLM={options}
|
||||||
></ParserMethodFormField>
|
></ParserMethodFormField>
|
||||||
|
{languageShown && <LanguageFormField prefix={prefix}></LanguageFormField>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,9 @@ import { VideoFormFields } from './video-form-fields';
|
|||||||
|
|
||||||
const outputList = buildOutputList(initialParserValues.outputs);
|
const outputList = buildOutputList(initialParserValues.outputs);
|
||||||
|
|
||||||
const FileFormatOptions = buildOptions(FileType);
|
const FileFormatOptions = buildOptions(FileType).filter(
|
||||||
|
(x) => x.value !== FileType.Video, // Temporarily hide the video option
|
||||||
|
);
|
||||||
|
|
||||||
const FileFormatWidgetMap = {
|
const FileFormatWidgetMap = {
|
||||||
[FileType.PDF]: PdfFormFields,
|
[FileType.PDF]: PdfFormFields,
|
||||||
@ -105,7 +107,7 @@ function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={cn('space-y-5 px-5 py-2.5 rounded-md', {
|
className={cn('space-y-5 py-2.5 rounded-md', {
|
||||||
'bg-state-error-5': isHovering,
|
'bg-state-error-5': isHovering,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,17 +1,13 @@
|
|||||||
import { crossLanguageOptions } from '@/components/cross-language-form-field';
|
|
||||||
import { ParseDocumentType } from '@/components/layout-recognize-form-field';
|
import { ParseDocumentType } from '@/components/layout-recognize-form-field';
|
||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
|
||||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { useFormContext, useWatch } from 'react-hook-form';
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { LanguageFormField, ParserMethodFormField } from './common-form-fields';
|
||||||
import { ParserMethodFormField } from './common-form-fields';
|
|
||||||
import { CommonProps } from './interface';
|
import { CommonProps } from './interface';
|
||||||
|
import { useSetInitialLanguage } from './use-set-initial-language';
|
||||||
import { buildFieldNameWithPrefix } from './utils';
|
import { buildFieldNameWithPrefix } from './utils';
|
||||||
|
|
||||||
export function PdfFormFields({ prefix }: CommonProps) {
|
export function PdfFormFields({ prefix }: CommonProps) {
|
||||||
const { t } = useTranslation();
|
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
|
|
||||||
const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix);
|
const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix);
|
||||||
@ -19,7 +15,6 @@ export function PdfFormFields({ prefix }: CommonProps) {
|
|||||||
const parseMethod = useWatch({
|
const parseMethod = useWatch({
|
||||||
name: parseMethodName,
|
name: parseMethodName,
|
||||||
});
|
});
|
||||||
const lang = form.getValues(buildFieldNameWithPrefix('lang', prefix));
|
|
||||||
|
|
||||||
const languageShown = useMemo(() => {
|
const languageShown = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
@ -29,18 +24,7 @@ export function PdfFormFields({ prefix }: CommonProps) {
|
|||||||
);
|
);
|
||||||
}, [parseMethod]);
|
}, [parseMethod]);
|
||||||
|
|
||||||
useEffect(() => {
|
useSetInitialLanguage({ prefix, languageShown });
|
||||||
if (languageShown && isEmpty(lang)) {
|
|
||||||
form.setValue(
|
|
||||||
buildFieldNameWithPrefix('lang', prefix),
|
|
||||||
crossLanguageOptions[0].value,
|
|
||||||
{
|
|
||||||
shouldValidate: true,
|
|
||||||
shouldDirty: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [form, lang, languageShown, prefix]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEmpty(form.getValues(parseMethodName))) {
|
if (isEmpty(form.getValues(parseMethodName))) {
|
||||||
@ -54,20 +38,7 @@ export function PdfFormFields({ prefix }: CommonProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
|
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
|
||||||
{languageShown && (
|
{languageShown && <LanguageFormField prefix={prefix}></LanguageFormField>}
|
||||||
<RAGFlowFormItem
|
|
||||||
name={buildFieldNameWithPrefix(`lang`, prefix)}
|
|
||||||
label={t('dataflow.lang')}
|
|
||||||
>
|
|
||||||
{(field) => (
|
|
||||||
<SelectWithSearch
|
|
||||||
options={crossLanguageOptions}
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
></SelectWithSearch>
|
|
||||||
)}
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { crossLanguageOptions } from '@/components/cross-language-form-field';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useFormContext } from 'react-hook-form';
|
||||||
|
import { buildFieldNameWithPrefix } from './utils';
|
||||||
|
|
||||||
|
export function useSetInitialLanguage({
|
||||||
|
prefix,
|
||||||
|
languageShown,
|
||||||
|
}: {
|
||||||
|
prefix: string;
|
||||||
|
languageShown: boolean;
|
||||||
|
}) {
|
||||||
|
const form = useFormContext();
|
||||||
|
const lang = form.getValues(buildFieldNameWithPrefix('lang', prefix));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (languageShown && isEmpty(lang)) {
|
||||||
|
form.setValue(
|
||||||
|
buildFieldNameWithPrefix('lang', prefix),
|
||||||
|
crossLanguageOptions[0].value,
|
||||||
|
{
|
||||||
|
shouldValidate: true,
|
||||||
|
shouldDirty: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [form, lang, languageShown, prefix]);
|
||||||
|
}
|
||||||
@ -9,7 +9,6 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Operator } from '../constant';
|
|
||||||
import useGraphStore from '../store';
|
import useGraphStore from '../store';
|
||||||
import { getAgentNodeTools } from '../utils';
|
import { getAgentNodeTools } from '../utils';
|
||||||
|
|
||||||
@ -77,13 +76,10 @@ export const useHandleNodeNameChange = ({
|
|||||||
data: any;
|
data: any;
|
||||||
}) => {
|
}) => {
|
||||||
const [name, setName] = useState<string>('');
|
const [name, setName] = useState<string>('');
|
||||||
const { updateNodeName, nodes, getOperatorTypeFromId } = useGraphStore(
|
const { updateNodeName, nodes } = useGraphStore((state) => state);
|
||||||
(state) => state,
|
|
||||||
);
|
|
||||||
const previousName = data?.name;
|
const previousName = data?.name;
|
||||||
const isToolNode = getOperatorTypeFromId(id) === Operator.Tool;
|
|
||||||
|
|
||||||
const { handleToolNameBlur, previousToolName } = useHandleTooNodeNameChange({
|
const { previousToolName } = useHandleTooNodeNameChange({
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
setName,
|
setName,
|
||||||
@ -109,12 +105,12 @@ export const useHandleNodeNameChange = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setName(isToolNode ? previousToolName : previousName);
|
setName(previousName);
|
||||||
}, [isToolNode, previousName, previousToolName]);
|
}, [previousName, previousToolName]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
handleNameBlur: isToolNode ? handleToolNameBlur : handleNameBlur,
|
handleNameBlur: handleNameBlur,
|
||||||
handleNameChange,
|
handleNameChange,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
import { useSendMessageBySSE } from '@/hooks/use-send-message';
|
import { useSendMessageBySSE } from '@/hooks/use-send-message';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import { useCallback } from 'react';
|
import { get } from 'lodash';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
import { useParams } from 'umi';
|
import { useParams } from 'umi';
|
||||||
import { useSaveGraphBeforeOpeningDebugDrawer } from './use-save-graph';
|
import { useSaveGraphBeforeOpeningDebugDrawer } from './use-save-graph';
|
||||||
|
|
||||||
export function useRunDataflow(showLogSheet: () => void) {
|
export function useRunDataflow(showLogSheet: () => void) {
|
||||||
const { send } = useSendMessageBySSE(api.runCanvas);
|
const { send } = useSendMessageBySSE(api.runCanvas);
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
const [messageId, setMessageId] = useState();
|
||||||
|
|
||||||
const { handleRun: saveGraph, loading } =
|
const { handleRun: saveGraph, loading } =
|
||||||
useSaveGraphBeforeOpeningDebugDrawer(showLogSheet!);
|
useSaveGraphBeforeOpeningDebugDrawer(showLogSheet!);
|
||||||
@ -22,12 +24,21 @@ export function useRunDataflow(showLogSheet: () => void) {
|
|||||||
files: [fileResponseData.file],
|
files: [fileResponseData.file],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res && res?.response.status === 200 && res?.data?.code === 0) {
|
if (res && res?.response.status === 200 && get(res, 'data.code') === 0) {
|
||||||
// fetch canvas
|
// fetch canvas
|
||||||
|
|
||||||
|
const msgId = get(res, 'data.data.message_id');
|
||||||
|
if (msgId) {
|
||||||
|
setMessageId(msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgId;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[id, saveGraph, send],
|
[id, saveGraph, send],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { run, loading: loading };
|
return { run, loading: loading, messageId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RunDataflowType = ReturnType<typeof useRunDataflow>;
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import {
|
|||||||
TimelineSeparator,
|
TimelineSeparator,
|
||||||
TimelineTitle,
|
TimelineTitle,
|
||||||
} from '@/components/originui/timeline';
|
} from '@/components/originui/timeline';
|
||||||
|
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
|
||||||
import { Aperture } from 'lucide-react';
|
import { Aperture } from 'lucide-react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@ -48,7 +50,24 @@ const items = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function DataflowTimeline() {
|
export type DataflowTimelineProps = { messageId: string };
|
||||||
|
|
||||||
|
interface DataflowTrace {
|
||||||
|
datetime: string;
|
||||||
|
elapsed_time: number;
|
||||||
|
message: string;
|
||||||
|
progress: number;
|
||||||
|
timestamp: number;
|
||||||
|
}
|
||||||
|
export function DataflowTimeline({ messageId }: DataflowTimelineProps) {
|
||||||
|
const { setMessageId, data } = useFetchMessageTrace(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (messageId) {
|
||||||
|
setMessageId(messageId);
|
||||||
|
}
|
||||||
|
}, [messageId, setMessageId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Timeline>
|
<Timeline>
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
|
|||||||
@ -8,18 +8,18 @@ import { IModalProps } from '@/interfaces/common';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { NotebookText } from 'lucide-react';
|
import { NotebookText } from 'lucide-react';
|
||||||
import 'react18-json-view/src/style.css';
|
import 'react18-json-view/src/style.css';
|
||||||
import { DataflowTimeline } from './dataflow-timeline';
|
import { DataflowTimeline, DataflowTimelineProps } from './dataflow-timeline';
|
||||||
|
|
||||||
type LogSheetProps = IModalProps<any>;
|
type LogSheetProps = IModalProps<any> & DataflowTimelineProps;
|
||||||
|
|
||||||
export function LogSheet({ hideModal }: LogSheetProps) {
|
export function LogSheet({ hideModal, messageId }: LogSheetProps) {
|
||||||
return (
|
return (
|
||||||
<Sheet open onOpenChange={hideModal} modal={false}>
|
<Sheet open onOpenChange={hideModal} modal={false}>
|
||||||
<SheetContent className={cn('top-20 right-[620px]')}>
|
<SheetContent className={cn('top-20 right-[620px]')}>
|
||||||
<SheetHeader>
|
<SheetHeader>
|
||||||
<SheetTitle className="flex items-center gap-1">
|
<SheetTitle className="flex items-center gap-1">
|
||||||
<NotebookText className="size-4" />
|
<NotebookText className="size-4" />
|
||||||
<DataflowTimeline></DataflowTimeline>
|
<DataflowTimeline messageId={messageId}></DataflowTimeline>
|
||||||
</SheetTitle>
|
</SheetTitle>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
<section className="max-h-[82vh] overflow-auto mt-6"></section>
|
<section className="max-h-[82vh] overflow-auto mt-6"></section>
|
||||||
|
|||||||
@ -7,13 +7,14 @@ import {
|
|||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRunDataflow } from '../hooks/use-run-dataflow';
|
import { RunDataflowType } from '../hooks/use-run-dataflow';
|
||||||
import { UploaderForm } from './uploader';
|
import { UploaderForm } from './uploader';
|
||||||
|
|
||||||
const RunSheet = ({ hideModal, showModal: showLogSheet }: IModalProps<any>) => {
|
type RunSheetProps = IModalProps<any> &
|
||||||
const { t } = useTranslation();
|
Pick<RunDataflowType, 'run' | 'loading'>;
|
||||||
|
|
||||||
const { run, loading } = useRunDataflow(showLogSheet!);
|
const RunSheet = ({ hideModal, run, loading }: RunSheetProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet onOpenChange={hideModal} open modal={false}>
|
<Sheet onOpenChange={hideModal} open modal={false}>
|
||||||
|
|||||||
@ -1,22 +1,10 @@
|
|||||||
import {
|
import { IAgentForm } from '@/interfaces/database/agent';
|
||||||
IAgentForm,
|
|
||||||
ICategorizeItem,
|
|
||||||
ICategorizeItemResult,
|
|
||||||
} from '@/interfaces/database/agent';
|
|
||||||
import { DSLComponents, RAGFlowNodeType } from '@/interfaces/database/flow';
|
import { DSLComponents, RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||||
import { removeUselessFieldsFromValues } from '@/utils/form';
|
import { removeUselessFieldsFromValues } from '@/utils/form';
|
||||||
import { Edge, Node, XYPosition } from '@xyflow/react';
|
import { Edge, Node, XYPosition } from '@xyflow/react';
|
||||||
import { FormInstance, FormListFieldData } from 'antd';
|
import { FormInstance, FormListFieldData } from 'antd';
|
||||||
import { humanId } from 'human-id';
|
import { humanId } from 'human-id';
|
||||||
import {
|
import { curry, get, intersectionWith, isEmpty, isEqual, sample } from 'lodash';
|
||||||
curry,
|
|
||||||
get,
|
|
||||||
intersectionWith,
|
|
||||||
isEmpty,
|
|
||||||
isEqual,
|
|
||||||
omit,
|
|
||||||
sample,
|
|
||||||
} from 'lodash';
|
|
||||||
import pipe from 'lodash/fp/pipe';
|
import pipe from 'lodash/fp/pipe';
|
||||||
import isObject from 'lodash/isObject';
|
import isObject from 'lodash/isObject';
|
||||||
import {
|
import {
|
||||||
@ -30,7 +18,7 @@ import {
|
|||||||
import { HierarchicalMergerFormSchemaType } from './form/hierarchical-merger-form';
|
import { HierarchicalMergerFormSchemaType } from './form/hierarchical-merger-form';
|
||||||
import { ParserFormSchemaType } from './form/parser-form';
|
import { ParserFormSchemaType } from './form/parser-form';
|
||||||
import { SplitterFormSchemaType } from './form/splitter-form';
|
import { SplitterFormSchemaType } from './form/splitter-form';
|
||||||
import { BeginQuery, IPosition } from './interface';
|
import { IPosition } from './interface';
|
||||||
|
|
||||||
const buildComponentDownstreamOrUpstream = (
|
const buildComponentDownstreamOrUpstream = (
|
||||||
edges: Edge[],
|
edges: Edge[],
|
||||||
@ -122,6 +110,7 @@ function transformParserParams(params: ParserFormSchemaType) {
|
|||||||
filteredSetup = {
|
filteredSetup = {
|
||||||
...filteredSetup,
|
...filteredSetup,
|
||||||
parse_method: cur.parse_method,
|
parse_method: cur.parse_method,
|
||||||
|
lang: cur.lang,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case FileType.Email:
|
case FileType.Email:
|
||||||
@ -308,10 +297,6 @@ export const getOtherFieldValues = (
|
|||||||
x !== form.getFieldValue([formListName, field.name, latestField]),
|
x !== form.getFieldValue([formListName, field.name, latestField]),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const generateSwitchHandleText = (idx: number) => {
|
|
||||||
return `Case ${idx + 1}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getNodeDragHandle = (nodeType?: string) => {
|
export const getNodeDragHandle = (nodeType?: string) => {
|
||||||
return nodeType === Operator.Note ? '.note-drag-handle' : undefined;
|
return nodeType === Operator.Note ? '.note-drag-handle' : undefined;
|
||||||
};
|
};
|
||||||
@ -400,40 +385,6 @@ export const needsSingleStepDebugging = (label: string) => {
|
|||||||
return !NoDebugOperatorsList.some((x) => (label as Operator) === x);
|
return !NoDebugOperatorsList.some((x) => (label as Operator) === x);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the coordinates of the node relative to the Iteration node
|
|
||||||
export function getRelativePositionToIterationNode(
|
|
||||||
nodes: RAGFlowNodeType[],
|
|
||||||
position?: XYPosition, // relative position
|
|
||||||
) {
|
|
||||||
if (!position) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iterationNodes = nodes.filter(
|
|
||||||
(node) => node.data.label === Operator.Iteration,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const iterationNode of iterationNodes) {
|
|
||||||
const {
|
|
||||||
position: { x, y },
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = iterationNode;
|
|
||||||
const halfWidth = (width || 0) / 2;
|
|
||||||
if (
|
|
||||||
position.x >= x - halfWidth &&
|
|
||||||
position.x <= x + halfWidth &&
|
|
||||||
position.y >= y &&
|
|
||||||
position.y <= y + (height || 0)
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
parentId: iterationNode.id,
|
|
||||||
position: { x: position.x - x + halfWidth, y: position.y - y },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateDuplicateNode = (
|
export const generateDuplicateNode = (
|
||||||
position?: XYPosition,
|
position?: XYPosition,
|
||||||
label?: string,
|
label?: string,
|
||||||
@ -468,71 +419,11 @@ export function convertToObjectArray(list: Array<string | number | boolean>) {
|
|||||||
return list.map((x) => ({ value: x }));
|
return list.map((x) => ({ value: x }));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* convert the following object into a list
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
"product_related": {
|
|
||||||
"description": "The question is about product usage, appearance and how it works.",
|
|
||||||
"examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?",
|
|
||||||
"to": "generate:0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
export const buildCategorizeListFromObject = (
|
|
||||||
categorizeItem: ICategorizeItemResult,
|
|
||||||
) => {
|
|
||||||
// Categorize's to field has two data sources, with edges as the data source.
|
|
||||||
// Changes in the edge or to field need to be synchronized to the form field.
|
|
||||||
return Object.keys(categorizeItem)
|
|
||||||
.reduce<Array<ICategorizeItem>>((pre, cur) => {
|
|
||||||
// synchronize edge data to the to field
|
|
||||||
|
|
||||||
pre.push({
|
|
||||||
name: cur,
|
|
||||||
...categorizeItem[cur],
|
|
||||||
examples: convertToObjectArray(categorizeItem[cur].examples),
|
|
||||||
});
|
|
||||||
return pre;
|
|
||||||
}, [])
|
|
||||||
.sort((a, b) => a.index - b.index);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the list in the following form into an object
|
|
||||||
* {
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"name": "Categorize 1",
|
|
||||||
"description": "111",
|
|
||||||
"examples": ["ddd"],
|
|
||||||
"to": "Retrieval:LazyEelsStick"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
export const buildCategorizeObjectFromList = (list: Array<ICategorizeItem>) => {
|
|
||||||
return list.reduce<ICategorizeItemResult>((pre, cur) => {
|
|
||||||
if (cur?.name) {
|
|
||||||
pre[cur.name] = {
|
|
||||||
...omit(cur, 'name', 'examples'),
|
|
||||||
examples: convertToStringArray(cur.examples) as string[],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return pre;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getAgentNodeTools(agentNode?: RAGFlowNodeType) {
|
export function getAgentNodeTools(agentNode?: RAGFlowNodeType) {
|
||||||
const tools: IAgentForm['tools'] = get(agentNode, 'data.form.tools', []);
|
const tools: IAgentForm['tools'] = get(agentNode, 'data.form.tools', []);
|
||||||
return tools;
|
return tools;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAgentNodeMCP(agentNode?: RAGFlowNodeType) {
|
|
||||||
const tools: IAgentForm['mcp'] = get(agentNode, 'data.form.mcp', []);
|
|
||||||
return tools;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mapEdgeMouseEvent(
|
export function mapEdgeMouseEvent(
|
||||||
edges: Edge[],
|
edges: Edge[],
|
||||||
edgeId: string,
|
edgeId: string,
|
||||||
@ -552,21 +443,3 @@ export function mapEdgeMouseEvent(
|
|||||||
|
|
||||||
return nextEdges;
|
return nextEdges;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildBeginQueryWithObject(
|
|
||||||
inputs: Record<string, BeginQuery>,
|
|
||||||
values: BeginQuery[],
|
|
||||||
) {
|
|
||||||
const nextInputs = Object.keys(inputs).reduce<Record<string, BeginQuery>>(
|
|
||||||
(pre, key) => {
|
|
||||||
const item = values.find((x) => x.key === key);
|
|
||||||
if (item) {
|
|
||||||
pre[key] = { ...item };
|
|
||||||
}
|
|
||||||
return pre;
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
return nextInputs;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user