mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-23 17:06:42 +08:00
Feat: Add page rank to ParserForm (#17267)
This commit is contained in:
@@ -25,6 +25,7 @@ type AsyncTreeSelectProps = {
|
||||
value?: TreeId;
|
||||
onChange?(value: TreeId): void;
|
||||
loadData?(node: TreeNodeType): Promise<any>;
|
||||
canSelect?(node: TreeNodeType): boolean;
|
||||
};
|
||||
|
||||
function getNodeText(node: ReactNode): string {
|
||||
@@ -39,6 +40,7 @@ export function AsyncTreeSelect({
|
||||
value,
|
||||
loadData,
|
||||
onChange,
|
||||
canSelect,
|
||||
}: AsyncTreeSelectProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
@@ -106,19 +108,8 @@ export function AsyncTreeSelect({
|
||||
[expandedKeys, searchText, treeData, visibleNodeIds],
|
||||
);
|
||||
|
||||
const handleNodeClick = useCallback(
|
||||
(id: TreeId) => (e: React.MouseEvent<HTMLLIElement>) => {
|
||||
e.stopPropagation();
|
||||
onChange?.(id);
|
||||
setOpen(false);
|
||||
setSearchText('');
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleArrowClick = useCallback(
|
||||
(node: TreeNodeType) => async (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
const toggleNode = useCallback(
|
||||
async (node: TreeNodeType) => {
|
||||
const { id } = node;
|
||||
if (isExpanded(id)) {
|
||||
setExpandedKeys((keys) => {
|
||||
@@ -140,6 +131,28 @@ export function AsyncTreeSelect({
|
||||
[isExpanded, loadData, treeData],
|
||||
);
|
||||
|
||||
const handleNodeClick = useCallback(
|
||||
(node: TreeNodeType) => async (e: React.MouseEvent<HTMLLIElement>) => {
|
||||
e.stopPropagation();
|
||||
if (canSelect && !canSelect(node)) {
|
||||
await toggleNode(node);
|
||||
return;
|
||||
}
|
||||
onChange?.(node.id);
|
||||
setOpen(false);
|
||||
setSearchText('');
|
||||
},
|
||||
[canSelect, onChange, toggleNode],
|
||||
);
|
||||
|
||||
const handleArrowClick = useCallback(
|
||||
(node: TreeNodeType) => async (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
await toggleNode(node);
|
||||
},
|
||||
[toggleNode],
|
||||
);
|
||||
|
||||
const handleOpenChange = useCallback((open: boolean) => {
|
||||
setOpen(open);
|
||||
if (!open) {
|
||||
@@ -166,7 +179,7 @@ export function AsyncTreeSelect({
|
||||
{currentLevelList.map((x) => (
|
||||
<li
|
||||
key={x.id}
|
||||
onClick={handleNodeClick(x.id)}
|
||||
onClick={handleNodeClick(x)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -631,6 +631,7 @@ Example: A 1 KB message with 1024-dim embedding uses ~9 KB. The 5 MB default lim
|
||||
autoKeywordsTip: `Automatically extract N keywords for each chunk to increase their ranking for queries containing those keywords. Be aware that extra tokens will be consumed by the indexing model specified in 'Configuration'. You can check or update the added keywords for a chunk from the chunk list. For details, see https://ragflow.io/docs/dev/autokeyword_autoquestion.`,
|
||||
autoQuestions: 'Auto-question',
|
||||
autoQuestionsTip: `Automatically extract N questions for each chunk to increase their ranking for queries containing those questions. You can check or update the added questions for a chunk from the chunk list. This feature will not disrupt the chunking process if an error occurs, except that it may add an empty result to the original chunk. Be aware that extra tokens will be consumed by the indexing model specified in 'Configuration'. For details, see https://ragflow.io/docs/dev/autokeyword_autoquestion.`,
|
||||
autoTags: 'Auto-tags',
|
||||
redo: 'Do you want to clear the existing {{chunkNum}} chunks?',
|
||||
setMetaData: 'Set metadata',
|
||||
pleaseInputJson: 'Please enter JSON',
|
||||
@@ -3015,6 +3016,7 @@ This delimiter is used to split the input text into several text pieces echo of
|
||||
systemPrompt: 'System prompt',
|
||||
userPrompt: 'User prompt',
|
||||
tocDataSource: 'Data source',
|
||||
tagFile: 'Tag file',
|
||||
addCategory: 'Add category',
|
||||
categoryName: 'Category name',
|
||||
nextStep: 'Next step',
|
||||
|
||||
@@ -574,6 +574,7 @@ export default {
|
||||
autoKeywordsTip: `自动为每个文本块中提取 N 个关键词,用以提升查询精度。请注意:该功能采用在“配置”中指定的索引模型提取关键词,因此也会产生更多 Token 消耗。另外,你也可以手动更新生成的关键词。详情请见 https://ragflow.io/docs/dev/autokeyword_autoquestion。`,
|
||||
autoQuestions: '自动问题提取',
|
||||
autoQuestionsTip: `利用在“配置”中指定的索引模型 对知识库的每个文本块提取 N 个问题以提高其排名得分。请注意,开启后将消耗额外的 token。您可以在块列表中查看、编辑结果。如果自动问题提取发生错误,不会妨碍整个分块过程,只会将空结果添加到原始文本块。详情请见 https://ragflow.io/docs/dev/autokeyword_autoquestion。`,
|
||||
autoTags: '自动标签提取',
|
||||
redo: '是否清空已有 {{chunkNum}}个 chunk?',
|
||||
setMetaData: '设置元数据',
|
||||
pleaseInputJson: '请输入JSON',
|
||||
@@ -2682,6 +2683,7 @@ NER:使用 spaCy NER 和基于规则的关键词提取来抽取实体和关系
|
||||
},
|
||||
systemPrompt: '系统提示词',
|
||||
userPrompt: '用户提示词',
|
||||
tagFile: '标签文件',
|
||||
prompt: '提示词',
|
||||
promptMessage: '提示词是必填项',
|
||||
promptTip:
|
||||
|
||||
@@ -197,6 +197,7 @@ export const initialParserValues = {
|
||||
preprocess: PreprocessValue.main_content,
|
||||
flatten_media_to_text: false,
|
||||
remove_header_footer: false,
|
||||
pages: [{ from: 1, to: 100000 }],
|
||||
},
|
||||
{
|
||||
fileFormat: FileType.Spreadsheet,
|
||||
@@ -353,6 +354,8 @@ export const initialExtractorValues = {
|
||||
field_name: ContextGeneratorFieldName.Summary,
|
||||
auto_keywords: 0,
|
||||
auto_questions: 0,
|
||||
auto_tags: 1,
|
||||
tag_file_id: '',
|
||||
outputs: {
|
||||
chunks: { type: 'Array<Object>', value: [] },
|
||||
},
|
||||
|
||||
@@ -7,8 +7,11 @@ import { LargeModelFormField } from '@/components/large-model-form-field';
|
||||
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { SliderInputFormField } from '@/components/slider-input-form-field';
|
||||
import { AsyncTreeSelect } from '@/components/ui/async-tree-select';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { PromptEditor } from '@/pages/agent/form/components/prompt-editor';
|
||||
import { isGoBackend } from '@/utils/backend-runtime';
|
||||
import { buildOptions } from '@/utils/form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
@@ -29,6 +32,8 @@ import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
import { useSwitchPrompt } from './use-switch-prompt';
|
||||
import { canSelectTagFile, useTagFileTree } from './use-tag-file-tree';
|
||||
import { FormLayout } from '@/constants/form';
|
||||
|
||||
export const FormSchema = z.object({
|
||||
field_name: z.string(),
|
||||
@@ -36,6 +41,8 @@ export const FormSchema = z.object({
|
||||
prompts: z.string().optional(),
|
||||
auto_keywords: z.number().optional(),
|
||||
auto_questions: z.number().optional(),
|
||||
auto_tags: z.number().optional(),
|
||||
tag_file_id: z.string().optional(),
|
||||
...LlmSettingSchema,
|
||||
});
|
||||
|
||||
@@ -75,6 +82,8 @@ const ExtractorForm = ({
|
||||
const ownerTenantId = useOwnerTenantId();
|
||||
const isToc = form.getValues('field_name') === 'toc';
|
||||
|
||||
const { treeData, loadData } = useTagFileTree();
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
@@ -83,6 +92,31 @@ const ExtractorForm = ({
|
||||
></LargeModelFormField>
|
||||
<AutoKeywordsFormField name="auto_keywords"></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField name="auto_questions"></AutoQuestionsFormField>
|
||||
{isGoBackend() && (
|
||||
<>
|
||||
<SliderInputFormField
|
||||
name="auto_tags"
|
||||
label={t('knowledgeDetails.autoTags')}
|
||||
min={1}
|
||||
max={10}
|
||||
defaultValue={1}
|
||||
layout={FormLayout.Vertical}
|
||||
></SliderInputFormField>
|
||||
|
||||
<RAGFlowFormItem label={t('flow.tagFile')} name="tag_file_id">
|
||||
{(field) => (
|
||||
<AsyncTreeSelect
|
||||
treeData={treeData}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
loadData={loadData}
|
||||
canSelect={canSelectTagFile}
|
||||
></AsyncTreeSelect>
|
||||
)}
|
||||
</RAGFlowFormItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
<RAGFlowFormItem label={t('flow.fieldName')} name="field_name">
|
||||
{(field) => (
|
||||
<SelectWithSearch
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const FormSchema = z.object({
|
||||
field_name: z.string(),
|
||||
sys_prompt: z.string(),
|
||||
prompts: z.string().optional(),
|
||||
...LlmSettingSchema,
|
||||
});
|
||||
type SwitchPromptField = 'field_name' | 'sys_prompt' | 'prompts';
|
||||
|
||||
export type ExtractorFormSchemaType = z.infer<typeof FormSchema>;
|
||||
type SwitchPromptForm = {
|
||||
getValues(name: 'field_name'): string;
|
||||
setValue(
|
||||
name: SwitchPromptField,
|
||||
value: string,
|
||||
options?: { shouldDirty?: boolean; shouldValidate?: boolean },
|
||||
): void;
|
||||
};
|
||||
|
||||
export function useSwitchPrompt(form: UseFormReturn<ExtractorFormSchemaType>) {
|
||||
export function useSwitchPrompt(form: SwitchPromptForm) {
|
||||
const { visible, showModal, hideModal } = useSetModalState();
|
||||
const { t } = useTranslation();
|
||||
const previousFieldNames = useRef<string[]>([form.getValues('field_name')]);
|
||||
|
||||
const setPromptValue = useCallback(
|
||||
(field: keyof ExtractorFormSchemaType, key: string, value: string) => {
|
||||
(field: SwitchPromptField, key: string, value: string) => {
|
||||
form.setValue(field, t(`flow.prompts.${key}.${value}`), {
|
||||
shouldDirty: true,
|
||||
shouldValidate: true,
|
||||
|
||||
52
web/src/pages/agent/form/extractor-form/use-tag-file-tree.ts
Normal file
52
web/src/pages/agent/form/extractor-form/use-tag-file-tree.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { TreeNodeType } from '@/components/ui/async-tree-select';
|
||||
import { useFetchPureFileList } from '@/hooks/use-file-request';
|
||||
import { IFile } from '@/interfaces/database/file-manager';
|
||||
import { isFolderType } from '@/pages/files/util';
|
||||
import { getExtension } from '@/utils/document-util';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
const AllowedExtensions = ['xlsx', 'xls', 'csv', 'txt'];
|
||||
|
||||
export function canSelectTagFile(node: TreeNodeType) {
|
||||
return Boolean(node.isLeaf);
|
||||
}
|
||||
|
||||
export function useTagFileTree() {
|
||||
const { fetchList } = useFetchPureFileList();
|
||||
|
||||
const [treeData, setTreeData] = useState<TreeNodeType[]>([]);
|
||||
|
||||
const loadData = useCallback(
|
||||
async ({ id }: TreeNodeType) => {
|
||||
const ret = await fetchList(id as string);
|
||||
if (ret.code === 0) {
|
||||
setTreeData((tree) =>
|
||||
uniqBy(
|
||||
tree.concat(
|
||||
ret.data.files
|
||||
.filter(
|
||||
(x: IFile) =>
|
||||
(isFolderType(x.type) &&
|
||||
x.name.toLowerCase() !== 'skills') ||
|
||||
AllowedExtensions.includes(getExtension(x.name)),
|
||||
)
|
||||
.map((x: IFile) => ({
|
||||
id: x.id,
|
||||
parentId: x.parent_id,
|
||||
title: x.name,
|
||||
// has_child_folder only counts child folders, so folders
|
||||
// must always be expandable to reach files inside them
|
||||
isLeaf: !isFolderType(x.type),
|
||||
})),
|
||||
),
|
||||
'id',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
[fetchList],
|
||||
);
|
||||
|
||||
return { treeData, loadData };
|
||||
}
|
||||
107
web/src/pages/agent/form/parser-form/dynamic-page-range.tsx
Normal file
107
web/src/pages/agent/form/parser-form/dynamic-page-range.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { LucidePlus, LucideTrash2 } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CommonProps } from './interface';
|
||||
import { buildFieldNameWithPrefix } from './utils';
|
||||
|
||||
export function DynamicPageRange({ prefix }: CommonProps) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
|
||||
const pagesName = buildFieldNameWithPrefix('pages', prefix);
|
||||
|
||||
const { fields, remove, append } = useFieldArray({
|
||||
name: pagesName,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
const handleAppend = useCallback(() => {
|
||||
append({ from: 1, to: 100000 });
|
||||
}, [append]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormLabel tooltip={t('knowledgeDetails.pageRangesTip')}>
|
||||
{t('knowledgeDetails.pageRanges')}
|
||||
</FormLabel>
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<div key={field.id} className="flex items-center gap-2 pt-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${pagesName}.${index}.from`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-2/5">
|
||||
<FormDescription />
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t('common.pleaseInput')}
|
||||
className="!m-0"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Separator className="w-3 "></Separator>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${pagesName}.${index}.to`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormDescription />
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t('common.pleaseInput')}
|
||||
className="!m-0"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="ml-4"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<LucideTrash2 />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<Button
|
||||
onClick={handleAppend}
|
||||
block
|
||||
className="mt-4"
|
||||
variant="dashed"
|
||||
type="button"
|
||||
>
|
||||
<LucidePlus />
|
||||
{t('knowledgeDetails.addPage')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -47,83 +47,6 @@ import { WordFormFields } from './word-form-fields';
|
||||
|
||||
const outputList = buildOutputList(initialParserValues.outputs);
|
||||
|
||||
// type PreprocessOptionConfig = {
|
||||
// value: PreprocessValue;
|
||||
// required?: boolean;
|
||||
// };
|
||||
|
||||
// const DefaultPreprocessOptionConfigs: PreprocessOptionConfig[] = [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// ];
|
||||
|
||||
// const PreprocessOptionConfigsMap: Partial<
|
||||
// Record<FileType, PreprocessOptionConfig[]>
|
||||
// > = {
|
||||
// [FileType.PDF]: [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// { value: PreprocessValue.abstract },
|
||||
// { value: PreprocessValue.author },
|
||||
// { value: PreprocessValue.section_title },
|
||||
// ],
|
||||
// [FileType.PowerPoint]: [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// ],
|
||||
// [FileType.Spreadsheet]: [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// ],
|
||||
// [FileType.TextMarkdown]: [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// { value: PreprocessValue.section_title },
|
||||
// ],
|
||||
// [FileType.Code]: [{ value: MAIN_CONTENT_PREPROCESS_VALUE, required: true }],
|
||||
// [FileType.Html]: [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// { value: PreprocessValue.section_title },
|
||||
// ],
|
||||
// [FileType.Doc]: [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// { value: PreprocessValue.section_title },
|
||||
// ],
|
||||
// [FileType.Docx]: [
|
||||
// { value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
// { value: PreprocessValue.section_title },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// function getPreprocessOptionConfigs(fileType?: FileType) {
|
||||
// if (!fileType) {
|
||||
// return DefaultPreprocessOptionConfigs;
|
||||
// }
|
||||
|
||||
// return PreprocessOptionConfigsMap[fileType] ?? DefaultPreprocessOptionConfigs;
|
||||
// }
|
||||
|
||||
// function normalizePreprocessValuesByFileType(
|
||||
// fileType: FileType | undefined,
|
||||
// values: string[] | undefined,
|
||||
// ) {
|
||||
// const optionConfigs = getPreprocessOptionConfigs(fileType);
|
||||
// const allowedValueSet = new Set(optionConfigs.map((x) => x.value));
|
||||
// const requiredValues = optionConfigs
|
||||
// .filter((x) => x.required)
|
||||
// .map((x) => x.value);
|
||||
// const normalizedOptionalValues = (Array.isArray(values) ? values : []).filter(
|
||||
// (value) => allowedValueSet.has(value as PreprocessValue),
|
||||
// ) as PreprocessValue[];
|
||||
|
||||
// return Array.from(
|
||||
// new Set<PreprocessValue>([...requiredValues, ...normalizedOptionalValues]),
|
||||
// );
|
||||
// }
|
||||
|
||||
// function isSameStringArray(a: string[] | undefined, b: string[]) {
|
||||
// if (!a || a.length !== b.length) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// return a.every((item, idx) => item === b[idx]);
|
||||
// }
|
||||
|
||||
const FileFormatWidgetMap = {
|
||||
[FileType.PDF]: PdfFormFields,
|
||||
[FileType.Spreadsheet]: SpreadsheetFormFields,
|
||||
@@ -163,6 +86,9 @@ export const FormSchema = z.object({
|
||||
enable_multi_column: z.boolean().optional(),
|
||||
remove_toc: z.boolean().optional(),
|
||||
remove_header_footer: z.boolean().optional(),
|
||||
pages: z
|
||||
.array(z.object({ from: z.coerce.number(), to: z.coerce.number() }))
|
||||
.optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -213,57 +139,6 @@ function ParserItem({
|
||||
[form, index],
|
||||
);
|
||||
|
||||
// const handlePreprocessChange = useCallback(
|
||||
// (value: PreprocessValue[]) => {
|
||||
// form.setValue(`setups.${index}.preprocess`, value, {
|
||||
// shouldDirty: true,
|
||||
// shouldValidate: true,
|
||||
// shouldTouch: true,
|
||||
// });
|
||||
// },
|
||||
// [form, index],
|
||||
// );
|
||||
|
||||
// const preprocessOptions = useMemo(() => {
|
||||
// const optionConfigs = getPreprocessOptionConfigs(fileFormat as FileType);
|
||||
|
||||
// return optionConfigs.map((optionConfig) => {
|
||||
// const labelMap: Record<string, string> = {
|
||||
// [MAIN_CONTENT_PREPROCESS_VALUE]: t('flow.preprocess.mainContent'),
|
||||
// [PreprocessValue.section_title]: t('flow.preprocess.sectionTitle'),
|
||||
// [PreprocessValue.abstract]: t('flow.preprocess.abstract'),
|
||||
// [PreprocessValue.author]: t('flow.preprocess.author'),
|
||||
// };
|
||||
|
||||
// const label = labelMap[optionConfig.value] || optionConfig.value;
|
||||
|
||||
// return {
|
||||
// value: optionConfig.value,
|
||||
// disabled: optionConfig.required,
|
||||
// label: label,
|
||||
// };
|
||||
// });
|
||||
// }, [fileFormat, t]);
|
||||
|
||||
// useEffect(() => {
|
||||
// const currentPreprocessValues = form.getValues(
|
||||
// `setups.${index}.preprocess`,
|
||||
// ) as string[] | undefined;
|
||||
// const normalizedPreprocessValues = normalizePreprocessValuesByFileType(
|
||||
// fileFormat as FileType,
|
||||
// currentPreprocessValues,
|
||||
// );
|
||||
|
||||
// if (
|
||||
// !isSameStringArray(currentPreprocessValues, normalizedPreprocessValues)
|
||||
// ) {
|
||||
// form.setValue(`setups.${index}.preprocess`, normalizedPreprocessValues, {
|
||||
// shouldDirty: false,
|
||||
// shouldValidate: true,
|
||||
// });
|
||||
// }
|
||||
// }, [fileFormat, form, index]);
|
||||
|
||||
return (
|
||||
<section
|
||||
className={cn('space-y-5 py-2.5 rounded-md', {
|
||||
@@ -302,26 +177,7 @@ function ParserItem({
|
||||
fileType={fileFormat as FileType}
|
||||
/>
|
||||
</div>
|
||||
{/* <RAGFlowFormItem
|
||||
name={buildFieldNameWithPrefix(`preprocess`, prefix)}
|
||||
label={t('flow.preprocess.preprocess')}
|
||||
>
|
||||
{(field) => (
|
||||
<MultiSelect
|
||||
value={field.value || []}
|
||||
onValueChange={(val) => {
|
||||
const nextValues = normalizePreprocessValuesByFileType(
|
||||
fileFormat as FileType,
|
||||
val,
|
||||
);
|
||||
field.onChange(nextValues);
|
||||
handlePreprocessChange(nextValues);
|
||||
}}
|
||||
showSelectAll={false}
|
||||
options={preprocessOptions}
|
||||
></MultiSelect>
|
||||
)}
|
||||
</RAGFlowFormItem> */}
|
||||
|
||||
{index < fieldLength - 1 && <Separator />}
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SelectWithSearchFlagOptionType,
|
||||
} from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { isGoBackend } from '@/utils/backend-runtime';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
TwoColumnCheckFormField,
|
||||
} from './common-form-fields';
|
||||
import { CommonProps } from './interface';
|
||||
import { DynamicPageRange } from './dynamic-page-range';
|
||||
import { useSetInitialLanguage } from './use-set-initial-language';
|
||||
import { buildFieldNameWithPrefix } from './utils';
|
||||
|
||||
@@ -107,6 +109,7 @@ export function PdfFormFields({ prefix }: CommonProps) {
|
||||
<RmdirFormField prefix={prefix} />
|
||||
<RemoveHeaderFooterFormField prefix={prefix} />
|
||||
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
|
||||
{isGoBackend() && <DynamicPageRange prefix={prefix} />}
|
||||
<FlattenMediaToTextFormField prefix={prefix} />
|
||||
{!flattenMediaToText && (
|
||||
<ModelTreeSelectFormField
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ICategorizeItemResult,
|
||||
RAGFlowNodeType,
|
||||
} from '@/interfaces/database/agent';
|
||||
import { getBackendLanguage } from '@/utils/backend-runtime';
|
||||
import { getBackendLanguage, isGoBackend } from '@/utils/backend-runtime';
|
||||
import { buildSelectOptions } from '@/utils/component-util';
|
||||
import { buildOptions, removeUselessFieldsFromValues } from '@/utils/form';
|
||||
import { Edge, Node, XYPosition } from '@xyflow/react';
|
||||
@@ -212,6 +212,7 @@ export function transformParserParams(params: ParserFormSchemaType) {
|
||||
ParserFormSchemaType['setups'][0] & { suffix: string[] } & {
|
||||
two_column_check: boolean;
|
||||
enable_multi_column: boolean;
|
||||
pages: number[][];
|
||||
}
|
||||
> = {
|
||||
output_format: cur.output_format,
|
||||
@@ -230,6 +231,11 @@ export function transformParserParams(params: ParserFormSchemaType) {
|
||||
enable_multi_column: cur.enable_multi_column,
|
||||
remove_toc: cur.remove_toc,
|
||||
remove_header_footer: cur.remove_header_footer || false,
|
||||
...(isGoBackend()
|
||||
? {
|
||||
pages: cur.pages?.map((x) => [x.from, x.to]) ?? [],
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
// Only include TCADP parameters if TCADP Parser is selected
|
||||
if (cur.parse_method?.toLowerCase() === 'tcadp parser') {
|
||||
|
||||
@@ -15,6 +15,7 @@ export const formSchema = z
|
||||
parser_id: z.string().optional(),
|
||||
avatar: z.any().nullish(),
|
||||
permission: z.string().optional(),
|
||||
language: z.string().optional(),
|
||||
embedding_model: z.string(),
|
||||
pagerank: z.number(),
|
||||
parser_config: z.record(z.string(), z.any()).optional(),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { AvatarUpload } from '@/components/avatar-upload';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
@@ -8,6 +10,9 @@ import {
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { LanguageTranslationMap } from '@/constants/common';
|
||||
import { isGoBackend } from '@/utils/backend-runtime';
|
||||
import { useMemo } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useKnowledgeBaseContext } from '../contexts/knowledge-base-context';
|
||||
@@ -18,6 +23,13 @@ export function GeneralForm() {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const languageOptions = useMemo(() => {
|
||||
return Object.keys(LanguageTranslationMap).map((x) => ({
|
||||
label: x,
|
||||
value: x,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
@@ -44,6 +56,21 @@ export function GeneralForm() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{isGoBackend() && (
|
||||
<div className="items-center">
|
||||
<RAGFlowFormItem
|
||||
name="language"
|
||||
label={t('common.language')}
|
||||
horizontal={true}
|
||||
>
|
||||
<SelectWithSearch
|
||||
options={languageOptions}
|
||||
triggerClassName="w-full"
|
||||
testId="ds-settings-basic-language-select"
|
||||
></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
</div>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="avatar"
|
||||
|
||||
@@ -98,6 +98,7 @@ export const useFetchDatasetSettingOnMount = (
|
||||
'description',
|
||||
'name',
|
||||
'permission',
|
||||
'language',
|
||||
'connectors',
|
||||
'pagerank',
|
||||
'avatar',
|
||||
|
||||
@@ -52,6 +52,7 @@ export default function DatasetSetting() {
|
||||
description: '',
|
||||
avatar: null,
|
||||
permission: '',
|
||||
language: 'English',
|
||||
embedding_model: '',
|
||||
pagerank: 0,
|
||||
connectors: [],
|
||||
|
||||
Reference in New Issue
Block a user