Fix: The Context Generator(Transformer) node can only be followed by a Tokenizer(Indexer) and a Context Generator(Transformer). #9869 (#10515)

### What problem does this PR solve?

Fix: The Context Generator node can only be followed by a Tokenizer and
a Context Generator. #9869
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-10-13 14:37:30 +08:00
committed by GitHub
parent 24481f0332
commit 9c53b3336a
7 changed files with 34 additions and 13 deletions

View File

@ -98,7 +98,7 @@ export function FileUploadDialog({
return ( return (
<Dialog open onOpenChange={hideModal}> <Dialog open onOpenChange={hideModal}>
<DialogContent className="sm:max-w-[425px]"> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>{t('fileManager.uploadFile')}</DialogTitle> <DialogTitle>{t('fileManager.uploadFile')}</DialogTitle>
</DialogHeader> </DialogHeader>

View File

@ -1703,8 +1703,8 @@ This delimiter is used to split the input text into several text pieces echo of
parser: 'Parser', parser: 'Parser',
parserDescription: parserDescription:
'Extracts raw text and structure from files for downstream processing.', 'Extracts raw text and structure from files for downstream processing.',
tokenizer: 'Tokenizer', tokenizer: 'Indexer',
tokenizerRequired: 'Please add the Tokenizer node first', tokenizerRequired: 'Please add the Indexer node first',
tokenizerDescription: tokenizerDescription:
'Transforms text into the required data structure (e.g., vector embeddings for Embedding Search) depending on the chosen search method.', 'Transforms text into the required data structure (e.g., vector embeddings for Embedding Search) depending on the chosen search method.',
splitter: 'Token Splitter', splitter: 'Token Splitter',
@ -1713,7 +1713,7 @@ This delimiter is used to split the input text into several text pieces echo of
hierarchicalMergerDescription: hierarchicalMergerDescription:
'Split documents into sections by title hierarchy with regex rules for finer control.', 'Split documents into sections by title hierarchy with regex rules for finer control.',
hierarchicalMerger: 'Title Splitter', hierarchicalMerger: 'Title Splitter',
extractor: 'Context Generator', extractor: 'Transformer',
extractorDescription: extractorDescription:
'Use an LLM to extract structured insights from document chunks—such as summaries, classifications, etc.', 'Use an LLM to extract structured insights from document chunks—such as summaries, classifications, etc.',
outputFormat: 'Output format', outputFormat: 'Output format',

View File

@ -292,6 +292,7 @@ function DataFlowCanvas({ drawerVisible, hideDrawer, showLogSheet }: IProps) {
clearActiveDropdown(); clearActiveDropdown();
}} }}
position={dropdownPosition} position={dropdownPosition}
nodeId={connectionStartRef.current?.nodeId || ''}
> >
<span></span> <span></span>
</NextStepDropdown> </NextStepDropdown>

View File

@ -131,16 +131,24 @@ function useRestrictSingleOperatorOnCanvas() {
function AccordionOperators({ function AccordionOperators({
isCustomDropdown = false, isCustomDropdown = false,
mousePosition, mousePosition,
nodeId,
}: { }: {
isCustomDropdown?: boolean; isCustomDropdown?: boolean;
mousePosition?: { x: number; y: number }; mousePosition?: { x: number; y: number };
nodeId?: string;
}) { }) {
const singleOperators = useRestrictSingleOperatorOnCanvas(); const singleOperators = useRestrictSingleOperatorOnCanvas();
const { getOperatorTypeFromId } = useGraphStore((state) => state);
const operators = useMemo(() => { const operators = useMemo(() => {
const list = [...singleOperators]; let list = [...singleOperators];
if (getOperatorTypeFromId(nodeId) === Operator.Extractor) {
const Splitters = [Operator.HierarchicalMerger, Operator.Splitter];
list = list.filter((x) => !Splitters.includes(x)); // The Context Generator node can only be followed by a Tokenizer and a Context Generator.
}
list.push(Operator.Extractor); list.push(Operator.Extractor);
return list; return list;
}, [singleOperators]); }, [getOperatorTypeFromId, nodeId, singleOperators]);
return ( return (
<OperatorItemList <OperatorItemList
@ -151,16 +159,19 @@ function AccordionOperators({
); );
} }
type NextStepDropdownProps = PropsWithChildren &
IModalProps<any> & {
position?: { x: number; y: number };
onNodeCreated?: (newNodeId: string) => void;
nodeId?: string;
};
export function InnerNextStepDropdown({ export function InnerNextStepDropdown({
children, children,
hideModal, hideModal,
position, position,
onNodeCreated, onNodeCreated,
}: PropsWithChildren & nodeId,
IModalProps<any> & { }: NextStepDropdownProps) {
position?: { x: number; y: number };
onNodeCreated?: (newNodeId: string) => void;
}) {
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
@ -200,6 +211,7 @@ export function InnerNextStepDropdown({
<AccordionOperators <AccordionOperators
isCustomDropdown={true} isCustomDropdown={true}
mousePosition={position} mousePosition={position}
nodeId={nodeId}
></AccordionOperators> ></AccordionOperators>
</OnNodeCreatedContext.Provider> </OnNodeCreatedContext.Provider>
</HideModalContext.Provider> </HideModalContext.Provider>
@ -224,7 +236,7 @@ export function InnerNextStepDropdown({
> >
<DropdownMenuLabel>{t('flow.nextStep')}</DropdownMenuLabel> <DropdownMenuLabel>{t('flow.nextStep')}</DropdownMenuLabel>
<HideModalContext.Provider value={hideModal}> <HideModalContext.Provider value={hideModal}>
<AccordionOperators></AccordionOperators> <AccordionOperators nodeId={nodeId}></AccordionOperators>
</HideModalContext.Provider> </HideModalContext.Provider>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@ -61,6 +61,7 @@ export function CommonHandle({
hideModal(); hideModal();
clearActiveDropdown(); clearActiveDropdown();
}} }}
nodeId={nodeId}
> >
<span></span> <span></span>
</NextStepDropdown> </NextStepDropdown>

View File

@ -299,7 +299,9 @@ export const initialHierarchicalMergerValues = {
export const initialExtractorValues = { export const initialExtractorValues = {
...initialLlmBaseValues, ...initialLlmBaseValues,
field_name: ContextGeneratorFieldName.Summary, field_name: ContextGeneratorFieldName.Summary,
outputs: {}, outputs: {
chunks: { type: 'Array<Object>', value: [] },
},
}; };
export const CategorizeAnchorPointPositions = [ export const CategorizeAnchorPointPositions = [

View File

@ -19,7 +19,9 @@ import { useBuildNodeOutputOptions } from '../../hooks/use-build-options';
import { useFormValues } from '../../hooks/use-form-values'; import { useFormValues } from '../../hooks/use-form-values';
import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { useWatchFormChange } from '../../hooks/use-watch-form-change';
import { INextOperatorForm } from '../../interface'; import { INextOperatorForm } from '../../interface';
import { buildOutputList } from '../../utils/build-output-list';
import { FormWrapper } from '../components/form-wrapper'; import { FormWrapper } from '../components/form-wrapper';
import { Output } from '../components/output';
import { useSwitchPrompt } from './use-switch-prompt'; import { useSwitchPrompt } from './use-switch-prompt';
export const FormSchema = z.object({ export const FormSchema = z.object({
@ -31,6 +33,8 @@ export const FormSchema = z.object({
export type ExtractorFormSchemaType = z.infer<typeof FormSchema>; export type ExtractorFormSchemaType = z.infer<typeof FormSchema>;
const outputList = buildOutputList(initialExtractorValues.outputs);
const ExtractorForm = ({ node }: INextOperatorForm) => { const ExtractorForm = ({ node }: INextOperatorForm) => {
const defaultValues = useFormValues(initialExtractorValues, node); const defaultValues = useFormValues(initialExtractorValues, node);
const { t } = useTranslation(); const { t } = useTranslation();
@ -85,6 +89,7 @@ const ExtractorForm = ({ node }: INextOperatorForm) => {
baseOptions={promptOptions} baseOptions={promptOptions}
></PromptEditor> ></PromptEditor>
</RAGFlowFormItem> </RAGFlowFormItem>
<Output list={outputList}></Output>
</FormWrapper> </FormWrapper>
{visible && ( {visible && (
<ConfirmDeleteDialog <ConfirmDeleteDialog