Compare commits

...

2 Commits

Author SHA1 Message Date
d8ef22db68 Fix(dataset): Optimized the dataset configuration page UI #9869 (#10066)
### What problem does this PR solve?
fix(dataset): Optimized the dataset configuration page UI

- Added the DataPipelineSelect component for selecting data pipelines
- Restructured the layout and style of the dataset settings page
- Removed unnecessary components and code
- Optimized data pipeline configuration
- Adjusted the Create Dataset dialog box
- Updated the processing log modal style

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-09-12 16:01:37 +08:00
592f3b1555 Feat: Bind options to the parser operator form. #9869 (#10069)
### What problem does this PR solve?

Feat: Bind options to the parser operator form. #9869

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
2025-09-12 16:01:24 +08:00
25 changed files with 470 additions and 326 deletions

View File

@ -30,11 +30,13 @@ const options = Languages.map((x) => ({
type CrossLanguageItemProps = {
name?: string;
vertical?: boolean;
label?: string;
};
export const CrossLanguageFormField = ({
name = 'prompt_config.cross_languages',
vertical = true,
label,
}: CrossLanguageItemProps) => {
const { t } = useTranslation();
const form = useFormContext();
@ -53,7 +55,7 @@ export const CrossLanguageFormField = ({
})}
>
<FormLabel tooltip={t('chat.crossLanguageTip')}>
{t('chat.crossLanguage')}
{label || t('chat.crossLanguage')}
</FormLabel>
<FormControl>
<MultiSelect

View File

@ -0,0 +1,76 @@
import { useTranslate } from '@/hooks/common-hooks';
import { buildSelectOptions } from '@/utils/component-util';
import { ArrowUpRight } from 'lucide-react';
import { useFormContext } from 'react-hook-form';
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '../ui/form';
import { RAGFlowSelect } from '../ui/select';
interface IProps {
toDataPipeline?: () => void;
formFieldName: string;
}
const data = [
{ id: '1', name: 'data-pipeline-1' },
{ id: '2', name: 'data-pipeline-2' },
{ id: '3', name: 'data-pipeline-3' },
{ id: '4', name: 'data-pipeline-4' },
];
export function DataFlowItem(props: IProps) {
const { toDataPipeline, formFieldName } = props;
const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext();
const toDataPipLine = () => {
// window.open('/data-pipeline');
toDataPipeline?.();
};
const options = buildSelectOptions(data, 'id', 'name');
return (
<FormField
control={form.control}
name={formFieldName}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className="flex flex-col gap-1">
<div className="flex gap-2 justify-between ">
<FormLabel
tooltip={t('dataFlowTip')}
className="text-sm text-text-primary whitespace-wrap "
>
{t('dataFlow')}
</FormLabel>
<div
className="text-sm flex text-text-primary cursor-pointer"
onClick={toDataPipLine}
>
{t('buildItFromScratch')}
<ArrowUpRight size={14} />
</div>
</div>
<div className="text-muted-foreground">
<FormControl>
<RAGFlowSelect
{...field}
placeholder={t('dataFlowPlaceholder')}
options={options}
/>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
);
}

View File

@ -1,6 +1,7 @@
import { LlmModelType } from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks';
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
import { cn } from '@/lib/utils';
import { camelCase } from 'lodash';
import { useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
@ -18,7 +19,13 @@ export const enum DocumentType {
PlainText = 'Plain Text',
}
export function LayoutRecognizeFormField() {
export function LayoutRecognizeFormField({
name = 'parser_config.layout_recognize',
horizontal = true,
}: {
name?: string;
horizontal?: boolean;
}) {
const form = useFormContext();
const { t } = useTranslate('knowledgeDetails');
@ -53,33 +60,32 @@ export function LayoutRecognizeFormField() {
return (
<FormField
control={form.control}
name="parser_config.layout_recognize"
name={name}
render={({ field }) => {
if (typeof field.value === 'undefined') {
// default value set
form.setValue(
'parser_config.layout_recognize',
form.formState.defaultValues?.parser_config?.layout_recognize ??
'DeepDOC',
);
}
return (
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormItem className={'items-center space-y-0 '}>
<div
className={cn('flex', {
'flex-col ': !horizontal,
'items-center': horizontal,
})}
>
<FormLabel
tooltip={t('layoutRecognizeTip')}
className="text-sm text-muted-foreground whitespace-wrap w-1/4"
className={cn('text-sm text-muted-foreground whitespace-wrap', {
['w-1/4']: horizontal,
})}
>
{t('layoutRecognize')}
</FormLabel>
<div className="w-3/4">
<div className={horizontal ? 'w-3/4' : 'w-full'}>
<FormControl>
<RAGFlowSelect {...field} options={options}></RAGFlowSelect>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<div className={horizontal ? 'w-1/4' : 'w-full'}></div>
<FormMessage />
</div>
</FormItem>

View File

@ -16,6 +16,7 @@ type RAGFlowFormItemProps = {
children: ReactNode | ((field: ControllerRenderProps) => ReactNode);
horizontal?: boolean;
required?: boolean;
labelClassName?: string;
};
export function RAGFlowFormItem({
@ -25,6 +26,7 @@ export function RAGFlowFormItem({
children,
horizontal = false,
required = false,
labelClassName,
}: RAGFlowFormItemProps) {
const form = useFormContext();
return (
@ -40,7 +42,7 @@ export function RAGFlowFormItem({
<FormLabel
required={required}
tooltip={tooltip}
className={cn({ 'w-1/4': horizontal })}
className={cn({ 'w-1/4': horizontal }, labelClassName)}
>
{label}
</FormLabel>

View File

@ -261,12 +261,13 @@ export default {
reRankModelWaring: 'Re-rank model is very time consuming.',
},
knowledgeConfiguration: {
default: 'Default',
dataPipeline: 'Data Pipeline',
linkDataPipeline: 'Link Data Pipeline',
enableAutoGenerate: 'Enable Auto Generate',
teamPlaceholder: 'Please select a team.',
dataFlowPlaceholder: 'Please select a data flow.',
buildItFromScratch: 'Build it from scratch',
useRAPTORToEnhanceRetrieval: 'Use RAPTOR to Enhance Retrieval',
extractKnowledgeGraph: 'Extract Knowledge Graph',
dataFlow: 'Data Flow',
parseType: 'Parse Type',
manualSetup: 'Manual Setup',

View File

@ -246,12 +246,13 @@ export default {
theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除',
},
knowledgeConfiguration: {
default: '默认',
dataPipeline: '数据流',
linkDataPipeline: '关联数据流',
enableAutoGenerate: '是否启用自动生成',
teamPlaceholder: '请选择团队',
dataFlowPlaceholder: '请选择数据流',
buildItFromScratch: '去Scratch构建',
useRAPTORToEnhanceRetrieval: '使用 RAPTOR 提升检索效果',
extractKnowledgeGraph: '知识图谱提取',
dataFlow: '数据流',
parseType: '切片方法',
manualSetup: '手动设置',

View File

@ -1,32 +1,53 @@
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { LLMFormField } from '@/components/llm-setting-items/llm-form-field';
import { SelectWithSearch } from '@/components/originui/select-with-search';
import {
SelectWithSearch,
SelectWithSearchFlagOptionType,
} from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { buildOptions } from '@/utils/form';
import { FileType } from '../../constant';
import { OutputFormatMap } from './constant';
import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
export function LanguageFormField({ prefix }: CommonProps) {
return (
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`lang`, prefix)}
label="lang"
>
<SelectWithSearch options={[]}></SelectWithSearch>
</RAGFlowFormItem>
);
function buildOutputOptionsFormatMap() {
return Object.entries(OutputFormatMap).reduce<
Record<string, SelectWithSearchFlagOptionType[]>
>((pre, [key, value]) => {
pre[key] = buildOptions(value);
return pre;
}, {});
}
export function OutputFormatFormField({ prefix }: CommonProps) {
export type OutputFormatFormFieldProps = CommonProps & {
fileType: FileType;
};
export function OutputFormatFormField({
prefix,
fileType,
}: OutputFormatFormFieldProps) {
return (
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`output_format`, prefix)}
label="output_format"
>
<SelectWithSearch options={[]}></SelectWithSearch>
<SelectWithSearch
options={buildOutputOptionsFormatMap()[fileType]}
></SelectWithSearch>
</RAGFlowFormItem>
);
}
export function ParserMethodFormField({ prefix }: CommonProps) {
return (
<LayoutRecognizeFormField
name={buildFieldNameWithPrefix(`parse_method`, prefix)}
horizontal={false}
></LayoutRecognizeFormField>
);
return (
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`parse_method`, prefix)}

View File

@ -0,0 +1,52 @@
import { FileType } from '../../constant';
export enum PdfOutputFormat {
Json = 'json',
Markdown = 'markdown',
}
export enum SpreadsheetOutputFormat {
Json = 'json',
Html = 'html',
}
export enum ImageOutputFormat {
Text = 'text',
}
export enum EmailOutputFormat {
Json = 'json',
Text = 'text',
}
export enum TextMarkdownOutputFormat {
Text = 'text',
}
export enum DocxOutputFormat {
Markdown = 'markdown',
}
export enum PptOutputFormat {
Json = 'json',
}
export enum VideoOutputFormat {
Json = 'json',
}
export enum AudioOutputFormat {
Text = 'text',
}
export const OutputFormatMap = {
[FileType.PDF]: PdfOutputFormat,
[FileType.Spreadsheet]: SpreadsheetOutputFormat,
[FileType.Image]: ImageOutputFormat,
[FileType.Email]: EmailOutputFormat,
[FileType.TextMarkdown]: TextMarkdownOutputFormat,
[FileType.Docx]: DocxOutputFormat,
[FileType.PowerPoint]: PptOutputFormat,
[FileType.Video]: VideoOutputFormat,
[FileType.Audio]: AudioOutputFormat,
};

View File

@ -1,6 +1,7 @@
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { buildOptions } from '@/utils/form';
import { FileType } from '../../constant';
import { OutputFormatFormField } from './common-form-fields';
import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
@ -25,7 +26,10 @@ export function EmailFormFields({ prefix }: CommonProps) {
>
<SelectWithSearch options={options}></SelectWithSearch>
</RAGFlowFormItem>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<OutputFormatFormField
prefix={prefix}
fileType={FileType.Email}
></OutputFormatFormField>
</>
);
}

View File

@ -1,3 +1,4 @@
import { FileType } from '../../constant';
import {
LargeModelFormField,
OutputFormatFormField,
@ -11,7 +12,10 @@ export function ImageFormFields({ prefix }: CommonProps) {
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
{/* Multimodal Model */}
<LargeModelFormField prefix={prefix}></LargeModelFormField>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<OutputFormatFormField
prefix={prefix}
fileType={FileType.Image}
></OutputFormatFormField>
</>
);
}

View File

@ -1,13 +1,20 @@
import { FormContainer } from '@/components/form-container';
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { BlockButton, Button } from '@/components/ui/button';
import { Form } from '@/components/ui/form';
import { Separator } from '@/components/ui/separator';
import { cn } from '@/lib/utils';
import { buildOptions } from '@/utils/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useHover } from 'ahooks';
import { Trash2 } from 'lucide-react';
import { memo, useCallback } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { memo, useCallback, useRef } from 'react';
import {
UseFieldArrayRemove,
useFieldArray,
useForm,
useFormContext,
} from 'react-hook-form';
import { z } from 'zod';
import { FileType, initialParserValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
@ -34,6 +41,51 @@ const FileFormatWidgetMap = {
[FileType.Image]: ImageFormFields,
};
type ParserItemProps = {
name: string;
index: number;
fieldLength: number;
remove: UseFieldArrayRemove;
};
function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
const form = useFormContext();
const ref = useRef(null);
const isHovering = useHover(ref);
const prefix = `${name}.${index}`;
const fileFormat = form.getValues(`${name}.${index}.fileFormat`);
const Widget =
fileFormat && fileFormat in FileFormatWidgetMap
? FileFormatWidgetMap[fileFormat as keyof typeof FileFormatWidgetMap]
: OutputFormatFormField;
return (
<section
className={cn('space-y-5 p-5 rounded-md', {
'bg-state-error-5': isHovering,
})}
>
<div className="flex justify-between items-center">
<span className="text-text-primary text-sm">Parser {index}</span>
{index > 0 && (
<Button variant={'ghost'} onClick={() => remove(index)} ref={ref}>
<Trash2 />
</Button>
)}
</div>
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`fileFormat`, prefix)}
label="File Formats"
>
<SelectWithSearch options={FileFormatOptions}></SelectWithSearch>
</RAGFlowFormItem>
<Widget prefix={prefix} fileType={fileFormat as FileType}></Widget>
{index < fieldLength - 1 && <Separator />}
</section>
);
}
export const FormSchema = z.object({
parser: z.array(
z.object({
@ -67,35 +119,22 @@ const ParserForm = ({ node }: INextOperatorForm) => {
return (
<Form {...form}>
{fields.map((field, index) => {
const prefix = `${name}.${index}`;
const fileFormat = form.getValues(`${name}.${index}.fileFormat`);
const Widget =
fileFormat && fileFormat in FileFormatWidgetMap
? FileFormatWidgetMap[
fileFormat as keyof typeof FileFormatWidgetMap
]
: OutputFormatFormField;
return (
<FormContainer key={field.id}>
<div className="flex justify-between items-center">
<span className="text-text-primary text-sm">Parser {index}</span>
<Button variant={'ghost'} onClick={() => remove(index)}>
<Trash2 />
</Button>
</div>
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`fileFormat`, prefix)}
label="File Formats"
>
<SelectWithSearch options={FileFormatOptions}></SelectWithSearch>
</RAGFlowFormItem>
<Widget prefix={prefix}></Widget>
</FormContainer>
);
})}
<BlockButton onClick={add}>Add Parser</BlockButton>
<form className="px-5">
{fields.map((field, index) => {
return (
<ParserItem
key={field.id}
name={name}
index={index}
fieldLength={fields.length}
remove={remove}
></ParserItem>
);
})}
<BlockButton onClick={add} type="button">
Add Parser
</BlockButton>
</form>
<div className="p-5">
<Output list={outputList}></Output>
</div>

View File

@ -1,10 +1,12 @@
import { CrossLanguageFormField } from '@/components/cross-language-form-field';
import { FileType } from '../../constant';
import {
LanguageFormField,
LargeModelFormField,
OutputFormatFormField,
ParserMethodFormField,
} from './common-form-fields';
import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
export function PdfFormFields({ prefix }: CommonProps) {
return (
@ -12,8 +14,14 @@ export function PdfFormFields({ prefix }: CommonProps) {
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
{/* Multimodal Model */}
<LargeModelFormField prefix={prefix}></LargeModelFormField>
<LanguageFormField prefix={prefix}></LanguageFormField>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<CrossLanguageFormField
name={buildFieldNameWithPrefix(`lang`, prefix)}
label="lang"
></CrossLanguageFormField>
<OutputFormatFormField
prefix={prefix}
fileType={FileType.Image}
></OutputFormatFormField>
</>
);
}

View File

@ -1,15 +1,21 @@
import {
LargeModelFormField,
OutputFormatFormField,
OutputFormatFormFieldProps,
} from './common-form-fields';
import { CommonProps } from './interface';
export function VideoFormFields({ prefix }: CommonProps) {
export function VideoFormFields({
prefix,
fileType,
}: OutputFormatFormFieldProps) {
return (
<>
{/* Multimodal Model */}
<LargeModelFormField prefix={prefix}></LargeModelFormField>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<OutputFormatFormField
prefix={prefix}
fileType={fileType}
></OutputFormatFormField>
</>
);
}

View File

@ -317,7 +317,8 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
state: 'Running',
startTime: '14/03/2025 14:53:39',
duration: '800',
details: 'PRD for DealBees 1.2 (1).text',
details:
'\n17:43:21 Task has been received.\n17:43:25 Page(1~100000001): Start to parse.\n17:43:25 Page(1~100000001): Start to tag for every chunk ...\n17:43:45 Page(1~100000001): Tagging 2 chunks completed in 18.99s\n17:43:45 Page(1~100000001): Generate 2 chunks\n17:43:55 Page(1~100000001): Embedding chunks (10.60s)\n17:43:55 Page(1~100000001): Indexing done (0.07s). Task done (33.97s)\n17:43:58 created task raptor\n17:43:58 Task has been received.\n17:44:36 Cluster one layer: 2 -> 1\n17:44:36 Indexing done (0.05s). Task done (37.88s)\n17:44:40 created task graphrag\n17:44:41 Task has been received.\n17:50:57 Entities extraction of chunk 0 1/3 done, 25 nodes, 26 edges, 14893 tokens.\n17:56:01 [ERROR][Exception]: Operation timed out after 7200 seconds and 1 attempts.',
};
return (
<div className="w-full h-[calc(100vh-350px)]">

View File

@ -1,31 +0,0 @@
import { Button } from '@/components/ui/button';
import { useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { NaiveConfiguration } from './naive';
import { SavingButton } from './saving-button';
export function ChunkMethodForm() {
const form = useFormContext();
const { t } = useTranslation();
return (
<section className="h-full flex flex-col">
<div className="overflow-auto flex-1 min-h-0">
<NaiveConfiguration></NaiveConfiguration>
</div>
<div className="text-right pt-4 flex justify-end gap-3">
<Button
type="reset"
className="bg-transparent text-color-white hover:bg-transparent border-gray-500 border-[1px]"
onClick={() => {
form.reset();
}}
>
{t('knowledgeConfiguration.cancel')}
</Button>
<SavingButton></SavingButton>
</div>
</section>
);
}

View File

@ -0,0 +1,79 @@
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import { Button } from '@/components/ui/button';
import { Link, Route, Settings2, Unlink } from 'lucide-react';
import { useTranslation } from 'react-i18next';
interface DataPipelineItemProps {
name: string;
avatar?: string;
isDefault?: boolean;
linked?: boolean;
}
const DataPipelineItem = (props: DataPipelineItemProps) => {
const { t } = useTranslation();
const { name, avatar, isDefault, linked } = props;
return (
<div className="flex items-center justify-between gap-1 px-2 rounded-lg border">
<div className="flex items-center gap-1">
<RAGFlowAvatar avatar={avatar} name={name} className="size-4" />
<div>{name}</div>
{isDefault && (
<div className="text-xs bg-text-secondary text-bg-base px-2 py-1 rounded-md">
{t('knowledgeConfiguration.default')}
</div>
)}
</div>
<div className="flex gap-1 items-center">
<Button variant={'transparent'} className="border-none">
<Settings2 />
</Button>
<Button variant={'transparent'} className="border-none">
{linked ? <Link /> : <Unlink />}
</Button>
</div>
</div>
);
};
const LinkDataPipeline = () => {
const { t } = useTranslation();
const testNode = [
{
name: 'Data Pipeline 1',
avatar: 'https://avatars.githubusercontent.com/u/10656201?v=4',
isDefault: true,
linked: true,
},
{
name: 'Data Pipeline 2',
avatar: 'https://avatars.githubusercontent.com/u/10656201?v=4',
linked: false,
},
];
return (
<div className="flex flex-col gap-2">
<section className="flex flex-col">
<div className="flex items-center gap-1 text-text-primary text-sm">
<Route className="size-4" />
{t('knowledgeConfiguration.dataPipeline')}
</div>
<div className="flex justify-between items-center">
<div className="text-center text-xs text-text-secondary">
Manage data pipeline linkage with this dataset
</div>
<Button variant={'transparent'}>
<Link />
<span className="text-xs text-text-primary">
{t('knowledgeConfiguration.linkDataPipeline')}
</span>
</Button>
</div>
</section>
<section className="flex flex-col gap-2">
{testNode.map((item) => (
<DataPipelineItem key={item.name} {...item} />
))}
</section>
</div>
);
};
export default LinkDataPipeline;

View File

@ -10,7 +10,6 @@ import { RAGFlowSelect } from '@/components/ui/select';
import { Switch } from '@/components/ui/switch';
import { useTranslate } from '@/hooks/common-hooks';
import { cn } from '@/lib/utils';
import { ArrowUpRight } from 'lucide-react';
import { useFormContext } from 'react-hook-form';
import {
useHasParsedDocument,
@ -70,8 +69,13 @@ export function EmbeddingModelItem({ line = 1 }: { line?: 1 | 2 }) {
control={form.control}
name={'embd_id'}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className={cn({ 'flex items-center': line === 1 })}>
<FormItem className={cn(' items-center space-y-0 ')}>
<div
className={cn('flex', {
' items-center': line === 1,
'flex-col gap-1': line === 2,
})}
>
<FormLabel
required
tooltip={t('embeddingModelTip')}
@ -142,155 +146,6 @@ export function ParseTypeItem() {
);
}
export function DataFlowItem() {
const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext();
return (
<FormField
control={form.control}
name={'data_flow'}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className="">
<div className="flex gap-2 justify-between ">
<FormLabel
tooltip={t('dataFlowTip')}
className="text-sm text-text-primary whitespace-wrap "
>
{t('dataFlow')}
</FormLabel>
<div className="text-sm flex text-text-primary">
{t('buildItFromScratch')}
<ArrowUpRight size={14} />
</div>
</div>
<div className="text-muted-foreground">
<FormControl>
<RAGFlowSelect
{...field}
placeholder={t('dataFlowPlaceholder')}
options={[{ value: '0', label: t('dataFlowDefault') }]}
/>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
);
}
export function DataExtractKnowledgeItem() {
const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext();
return (
<>
{' '}
<FormField
control={form.control}
name={'extractKnowledgeGraph'}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className="">
<FormLabel
tooltip={t('extractKnowledgeGraphTip')}
className="text-sm whitespace-wrap "
>
{t('extractKnowledgeGraph')}
</FormLabel>
<div className="text-muted-foreground">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>{' '}
<FormField
control={form.control}
name={'useRAPTORToEnhanceRetrieval'}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className="">
<FormLabel
tooltip={t('useRAPTORToEnhanceRetrievalTip')}
className="text-sm whitespace-wrap "
>
{t('useRAPTORToEnhanceRetrieval')}
</FormLabel>
<div className="text-muted-foreground">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
</>
);
}
export function TeamItem() {
const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext();
return (
<FormField
control={form.control}
name={'team'}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className="">
<FormLabel
tooltip={t('teamTip')}
className="text-sm whitespace-wrap "
>
<span className="text-destructive mr-1"> *</span>
{t('team')}
</FormLabel>
<div className="text-muted-foreground">
<FormControl>
<RAGFlowSelect
{...field}
placeholder={t('teamPlaceholder')}
options={[{ value: '0', label: t('teamDefault') }]}
/>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
);
}
export function EnableAutoGenerateItem() {
const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext();

View File

@ -17,7 +17,7 @@ export function GeneralForm() {
const { t } = useTranslation();
return (
<section className="space-y-4">
<>
<FormField
control={form.control}
name="name"
@ -87,6 +87,6 @@ export function GeneralForm() {
/>
<PermissionFormField></PermissionFormField>
<EmbeddingModelItem></EmbeddingModelItem>
</section>
</>
);
}

View File

@ -1,3 +1,6 @@
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
import { Button } from '@/components/ui/button';
import Divider from '@/components/ui/divider';
import { Form } from '@/components/ui/form';
import { DocumentParserType } from '@/constants/knowledge';
@ -7,11 +10,12 @@ import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { TopTitle } from '../dataset-title';
import { ChunkMethodForm } from './chunk-method-form';
import LinkDataPipeline from './components/link-data-pipeline';
import { MainContainer } from './configuration-form-container';
import { formSchema } from './form-schema';
import { GeneralForm } from './general-form';
import { useFetchKnowledgeConfigurationOnMount } from './hooks';
import { SavingButton } from './saving-button';
const enum DocumentType {
DeepDOC = 'DeepDOC',
PlainText = 'Plain Text',
@ -77,10 +81,29 @@ export default function DatasetSettings() {
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 flex-1"
>
<div className="w-[768px] h-[calc(100vh-220px)] overflow-y-auto scrollbar-thin">
<GeneralForm></GeneralForm>
<Divider />
<ChunkMethodForm></ChunkMethodForm>
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto">
<MainContainer>
<GeneralForm></GeneralForm>
<Divider />
<GraphRagItems className="border-none p-0"></GraphRagItems>
<Divider />
<RaptorFormFields></RaptorFormFields>
<Divider />
<LinkDataPipeline />
</MainContainer>
</div>
<div className="text-right items-center flex justify-end gap-3 w-[768px]">
<Button
type="reset"
className="bg-transparent text-color-white hover:bg-transparent border-gray-500 border-[1px]"
onClick={() => {
form.reset();
}}
>
{t('knowledgeConfiguration.cancel')}
</Button>
<SavingButton></SavingButton>
</div>
</form>
</Form>

View File

@ -1,20 +0,0 @@
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
import Divider from '@/components/ui/divider';
import {
ConfigurationFormContainer,
MainContainer,
} from './configuration-form-container';
export function NaiveConfiguration() {
return (
<MainContainer>
<GraphRagItems className="border-none p-0"></GraphRagItems>
<Divider />
<ConfigurationFormContainer>
<RaptorFormFields></RaptorFormFields>
</ConfigurationFormContainer>
<Divider />
</MainContainer>
);
}

View File

@ -1,3 +1,4 @@
import { DataFlowItem } from '@/components/data-pipeline-select';
import { ButtonLoading } from '@/components/ui/button';
import {
Dialog,
@ -15,17 +16,15 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IModalProps } from '@/interfaces/common';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import {
DataExtractKnowledgeItem,
DataFlowItem,
EmbeddingModelItem,
ParseTypeItem,
TeamItem,
} from '../dataset/dataset-setting/configuration/common-item';
const FormId = 'dataset-creating-form';
@ -58,6 +57,7 @@ export function InputForm({ onOk }: IModalProps<any>) {
control: form.control,
name: 'parseType',
});
const { navigateToAgents } = useNavigatePage();
return (
<Form {...form}>
<form
@ -88,9 +88,10 @@ export function InputForm({ onOk }: IModalProps<any>) {
<ParseTypeItem />
{parseType === 2 && (
<>
<DataFlowItem />
<DataExtractKnowledgeItem />
<TeamItem />
<DataFlowItem
toDataPipeline={navigateToAgents}
formFieldName="data_flow"
/>
</>
)}
</form>

View File

@ -1,7 +1,9 @@
import FileStatusBadge from '@/components/file-status-badge';
import { Button } from '@/components/ui/button';
import { Modal } from '@/components/ui/modal/modal';
import { useTranslate } from '@/hooks/common-hooks';
import React from 'react';
import reactStringReplace from 'react-string-replace';
interface ProcessLogModalProps {
visible: boolean;
@ -12,7 +14,7 @@ interface ProcessLogModalProps {
fileSize: string;
source: string;
task: string;
state: 'Running' | 'Completed' | 'Failed' | 'Pending';
state: 'Running' | 'Success' | 'Failed' | 'Pending';
startTime: string;
endTime?: string;
duration?: string;
@ -20,32 +22,6 @@ interface ProcessLogModalProps {
};
}
const StatusTag: React.FC<{ state: string }> = ({ state }) => {
const getTagStyle = () => {
switch (state) {
case 'Running':
return 'bg-green-500 text-green-100';
case 'Completed':
return 'bg-blue-500 text-blue-100';
case 'Failed':
return 'bg-red-500 text-red-100';
case 'Pending':
return 'bg-yellow-500 text-yellow-100';
default:
return 'bg-gray-500 text-gray-100';
}
};
return (
<span
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getTagStyle()}`}
>
<span className="w-1.5 h-1.5 rounded-full mr-1 bg-current"></span>
{state}
</span>
);
};
const InfoItem: React.FC<{
label: string;
value: string | React.ReactNode;
@ -65,6 +41,24 @@ const ProcessLogModal: React.FC<ProcessLogModalProps> = ({
taskInfo,
}) => {
const { t } = useTranslate('knowledgeDetails');
const replaceText = (text: string) => {
// Remove duplicate \n
const nextText = text.replace(/(\n)\1+/g, '$1');
const replacedText = reactStringReplace(
nextText,
/(\[ERROR\].+\s)/g,
(match, i) => {
return (
<span key={i} className={'text-red-600'}>
{match}
</span>
);
},
);
return replacedText;
};
return (
<Modal
title={t('processLog')}
@ -86,15 +80,14 @@ const ProcessLogModal: React.FC<ProcessLogModalProps> = ({
<InfoItem label="File Size" value={taskInfo.fileSize} />
<InfoItem label="Source" value={taskInfo.source} />
<InfoItem label="Task" value={taskInfo.task} />
<InfoItem label="Details" value={taskInfo.details} />
</div>
{/* Right Column */}
<div className="space-y-4">
<div className="flex flex-col">
<span className="text-text-secondary text-sm">States</span>
<span className="text-text-secondary text-sm">Status</span>
<div className="mt-1">
<StatusTag state={taskInfo.state} />
<FileStatusBadge status={taskInfo.state} />
</div>
</div>
@ -108,6 +101,17 @@ const ProcessLogModal: React.FC<ProcessLogModalProps> = ({
/>
</div>
</div>
{/* <InfoItem label="Details" value={taskInfo.details} /> */}
<div>
<div>Details</div>
<div>
<ul className="space-y-2">
<div className={'w-full whitespace-pre-line text-wrap '}>
{replaceText(taskInfo.details)}
</div>
</ul>
</div>
</div>
</div>
</Modal>
);

View File

@ -1,3 +1,10 @@
export function buildSelectOptions(list: Array<string>) {
export function buildSelectOptions(
list: Array<any>,
keyName?: string,
valueName?: string,
) {
if (keyName && valueName) {
return list.map((x) => ({ label: x[valueName], value: x[keyName] }));
}
return list.map((x) => ({ label: x, value: x }));
}

View File

@ -80,7 +80,10 @@ module.exports = {
'bg-accent': 'var(--bg-accent)',
'state-success': 'var(--state-success)',
'state-warning': 'var(--state-warning)',
'state-error': 'var(--state-error)',
'state-error': {
DEFAULT: 'rgb(var(--state-error) / <alpha-value>)',
5: 'rgba(var(--state-error) / 0.05)', // 5%
},
'team-group': 'var(--team-group)',
'team-member': 'var(--team-member)',
'team-department': 'var(--team-department)',

View File

@ -119,7 +119,7 @@
--state-success: #3ba05c;
--state-warning: #faad14;
--state-error: #d8494b;
--state-error: 216 73 75;
--team-group: #5ab77e;
--team-member: #5c96c8;
@ -291,8 +291,8 @@
@layer utilities {
.scrollbar-auto {
/* hide scrollbar */
scrollbar-width: none;
scrollbar-color: transparent transparent;
scrollbar-width: thin;
scrollbar-color: var(--border-default) var(--bg-card);
}
.scrollbar-auto::-webkit-scrollbar {