mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-23 08:56:42 +08:00
Fix: The form related to switching the dataset pipeline configuration was not rendered. (#17124)
This commit is contained in:
@@ -33,6 +33,7 @@ export interface IDataset {
|
||||
nickname: string;
|
||||
pagerank: number;
|
||||
parser_config: Parserconfig;
|
||||
parser_id?: string;
|
||||
permission: string;
|
||||
pipeline_id: string;
|
||||
raptor_task_finish_at: string;
|
||||
|
||||
@@ -54,6 +54,7 @@ export default function Compilation() {
|
||||
>
|
||||
{t('knowledgeDetails.llmWiki')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant={viewMode === ViewMode.Skills ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
@@ -22,6 +23,7 @@ import { useParams, useSearchParams } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
import { formSchema } from './form-schema';
|
||||
import {
|
||||
buildParserConfigFromNodes,
|
||||
buildPipelineOperatorNodes,
|
||||
getOperatorType,
|
||||
transformApiConfigToForm,
|
||||
@@ -140,6 +142,55 @@ export const usePipelineOperatorNodes = (
|
||||
return { operatorNodes, loading };
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets parser_config when the selected pipeline changes, so stale configs
|
||||
* from the previous pipeline are never submitted. Once the new pipeline's
|
||||
* DSL has loaded, parser_config is seeded with the DSL defaults (i.e. what
|
||||
* the operator tabs display).
|
||||
*
|
||||
* The very first pipeline seen is treated as the initial load: the form was
|
||||
* already reset with the saved parser_config in useFetchDatasetSettingOnMount,
|
||||
* so it is left untouched.
|
||||
*/
|
||||
export const useResetParserConfigOnPipelineChange = (
|
||||
form: UseFormReturn<z.infer<typeof formSchema>>,
|
||||
pipelineId: string | undefined,
|
||||
savedPipelineId: string | undefined,
|
||||
operatorNodes: RAGFlowNodeType[],
|
||||
) => {
|
||||
const previousPipelineIdRef = useRef<string>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!pipelineId) {
|
||||
// Selection cleared (e.g. parse type switched): drop configs that
|
||||
// belonged to the previously selected pipeline.
|
||||
if (previousPipelineIdRef.current) {
|
||||
previousPipelineIdRef.current = '';
|
||||
form.setValue('parser_config', {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const isInitialLoad =
|
||||
previousPipelineIdRef.current === undefined &&
|
||||
pipelineId === savedPipelineId;
|
||||
if (isInitialLoad || previousPipelineIdRef.current === pipelineId) {
|
||||
previousPipelineIdRef.current = pipelineId;
|
||||
return;
|
||||
}
|
||||
|
||||
if (operatorNodes.length === 0) {
|
||||
// The new pipeline's DSL is still loading — clear the previous
|
||||
// pipeline's configs right away; defaults are seeded once it arrives.
|
||||
form.setValue('parser_config', {});
|
||||
return;
|
||||
}
|
||||
|
||||
previousPipelineIdRef.current = pipelineId;
|
||||
form.setValue('parser_config', buildParserConfigFromNodes(operatorNodes));
|
||||
}, [form, pipelineId, savedPipelineId, operatorNodes]);
|
||||
};
|
||||
|
||||
export const useSaveDatasetSetting = () => {
|
||||
const { saveKnowledgeConfiguration, loading } = useUpdateKnowledge();
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
useFetchDatasetSettingOnMount,
|
||||
usePipelineDataList,
|
||||
usePipelineOperatorNodes,
|
||||
useResetParserConfigOnPipelineChange,
|
||||
useSaveDatasetSetting,
|
||||
} from './hooks';
|
||||
import PipelineOperatorTabs from './pipeline-operator-tabs';
|
||||
@@ -92,10 +93,35 @@ export default function DatasetSetting() {
|
||||
| Record<string, any>
|
||||
| undefined;
|
||||
|
||||
const { operatorNodes } = usePipelineOperatorNodes(
|
||||
parseType === ParseType.Pipeline ? pipelineId : builtinPipelineId,
|
||||
pipelineParserConfig,
|
||||
parseType === ParseType.BuiltIn,
|
||||
const isPipelineMode = parseType === ParseType.Pipeline;
|
||||
const selectedPipelineId = isPipelineMode ? pipelineId : builtinPipelineId;
|
||||
// The saved parser_config only belongs to the pipeline (and parse type) it
|
||||
// was saved with — expose its id only while that exact pipeline is selected,
|
||||
// so that after switching, defaults come purely from the new pipeline's DSL.
|
||||
const savedParseType = knowledgeDetails?.pipeline_id
|
||||
? ParseType.Pipeline
|
||||
: ParseType.BuiltIn;
|
||||
const savedPipelineId =
|
||||
parseType === savedParseType
|
||||
? isPipelineMode
|
||||
? knowledgeDetails?.pipeline_id
|
||||
: knowledgeDetails?.parser_id
|
||||
: undefined;
|
||||
|
||||
const { operatorNodes, loading: operatorNodesLoading } =
|
||||
usePipelineOperatorNodes(
|
||||
selectedPipelineId,
|
||||
selectedPipelineId && selectedPipelineId === savedPipelineId
|
||||
? pipelineParserConfig
|
||||
: undefined,
|
||||
!isPipelineMode,
|
||||
);
|
||||
|
||||
useResetParserConfigOnPipelineChange(
|
||||
form,
|
||||
selectedPipelineId,
|
||||
savedPipelineId,
|
||||
operatorNodes,
|
||||
);
|
||||
|
||||
const { activeTab, setActiveTab } = useActiveTab(operatorNodes);
|
||||
@@ -211,7 +237,11 @@ export default function DatasetSetting() {
|
||||
|
||||
<ButtonLoading
|
||||
type="submit"
|
||||
loading={datasetSettingLoading || saveLoading}
|
||||
loading={
|
||||
datasetSettingLoading ||
|
||||
saveLoading ||
|
||||
(operatorNodesLoading && operatorNodes.length === 0)
|
||||
}
|
||||
>
|
||||
{t('knowledgeConfiguration.save')}
|
||||
</ButtonLoading>
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
transformTitleChunkerParams,
|
||||
transformTokenChunkerParams,
|
||||
} from '@/pages/agent/utils';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { cloneDeep, isEmpty } from 'lodash';
|
||||
|
||||
export const FileNodeId = 'File';
|
||||
|
||||
@@ -278,24 +278,21 @@ export function buildOperatorNode(
|
||||
const operatorId = dslNode.id;
|
||||
const operatorType = getOperatorType(operatorId);
|
||||
|
||||
// DSL-format config from API's parser_config
|
||||
const configFromApi = operatorId
|
||||
? pipelineParserConfig[operatorId]
|
||||
: undefined;
|
||||
|
||||
// Form-format config from DSL graph
|
||||
const configFromDsl = dslNode.data?.form;
|
||||
|
||||
// Convert API config to form format, then merge (DSL template is baseline, API overrides)
|
||||
const convertedApiConfig = transformApiConfigToForm(
|
||||
operatorType,
|
||||
configFromApi,
|
||||
);
|
||||
const rawForm = {
|
||||
...configFromDsl, // template defaults from DSL (form format)
|
||||
...convertedApiConfig, // user overrides from API (now also form format)
|
||||
};
|
||||
|
||||
if (!isEmpty(pipelineParserConfig)) {
|
||||
// user overrides from API (now also form format)
|
||||
Object.assign(
|
||||
rawForm,
|
||||
transformApiConfigToForm(operatorType, pipelineParserConfig[operatorId]), // Convert API config to form format, then merge (DSL template is baseline, API overrides)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...dslNode,
|
||||
id: '',
|
||||
@@ -343,3 +340,19 @@ export function buildPipelineOperatorNodes(
|
||||
.filter((node): node is RAGFlowNodeType => node !== undefined)
|
||||
.map((node) => buildOperatorNode(node, pipelineParserConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a parser_config record from operator nodes, keyed by operatorId.
|
||||
* Each entry is the node's merged form (DSL template defaults + overrides),
|
||||
* i.e. exactly what the operator tabs display.
|
||||
*/
|
||||
export function buildParserConfigFromNodes(
|
||||
operatorNodes: RAGFlowNodeType[],
|
||||
): Record<string, any> {
|
||||
return Object.fromEntries(
|
||||
operatorNodes.map((node) => [
|
||||
(node.data as Record<string, any>)?.operatorId || node.data?.label || '',
|
||||
node.data?.form ?? {},
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,17 +29,18 @@ import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
BuiltinPipelineItem,
|
||||
ChunkMethodItem,
|
||||
EmbeddingModelItem,
|
||||
ParseTypeItem,
|
||||
} from '../dataset/dataset-setting/configuration/common-item';
|
||||
import { isGoBackend } from '@/utils/backend-runtime';
|
||||
|
||||
const FormId = 'dataset-creating-form';
|
||||
|
||||
const ChunkMethodName = 'parser_id';
|
||||
|
||||
export function InputForm({ onOk }: IModalProps<any>) {
|
||||
const { t } = useTranslation();
|
||||
const defaultModelDictionary = useFetchDefaultModelDictionary();
|
||||
const ChunkMethodName = isGoBackend() ? 'parser_id' : 'chunk_method';
|
||||
|
||||
const FormSchema = z
|
||||
.object({
|
||||
@@ -141,9 +142,12 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
||||
|
||||
<EmbeddingModelItem line={2} isEdit={false} />
|
||||
<ParseTypeItem />
|
||||
{parseType === ParseType.BuiltIn && (
|
||||
<BuiltinPipelineItem name={ChunkMethodName} />
|
||||
)}
|
||||
{parseType === ParseType.BuiltIn &&
|
||||
(isGoBackend() ? (
|
||||
<BuiltinPipelineItem name={ChunkMethodName} />
|
||||
) : (
|
||||
<ChunkMethodItem name={ChunkMethodName}></ChunkMethodItem>
|
||||
))}
|
||||
{parseType === ParseType.Pipeline && (
|
||||
<DataFlowSelect
|
||||
isMult={false}
|
||||
|
||||
@@ -32,6 +32,8 @@ export const fetchBackendLanguage = (): Promise<string> => promise;
|
||||
|
||||
export const getBackendLanguage = (): string | null => backendLanguage;
|
||||
|
||||
export const isGoBackend = (): boolean => backendLanguage === 'go';
|
||||
|
||||
export const subscribeBackendLanguage = (listener: Listener): (() => void) => {
|
||||
listeners.add(listener);
|
||||
return () => {
|
||||
|
||||
Reference in New Issue
Block a user