mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 12:32:30 +08:00
### What problem does this PR solve? Feat: Set the outputs type of list operation. #10427 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -6,6 +6,7 @@ interface NumberInputProps {
|
||||
value?: number;
|
||||
onChange?: (value: number) => void;
|
||||
height?: number | string;
|
||||
min?: number;
|
||||
}
|
||||
|
||||
const NumberInput: React.FC<NumberInputProps> = ({
|
||||
@ -13,6 +14,7 @@ const NumberInput: React.FC<NumberInputProps> = ({
|
||||
value: initialValue,
|
||||
onChange,
|
||||
height,
|
||||
min = 0,
|
||||
}) => {
|
||||
const [value, setValue] = useState<number>(() => {
|
||||
return initialValue ?? 0;
|
||||
@ -76,6 +78,7 @@ const NumberInput: React.FC<NumberInputProps> = ({
|
||||
onChange={handleChange}
|
||||
className="w-full flex-1 text-center bg-transparent focus:outline-none"
|
||||
style={style}
|
||||
min={min}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@ -1062,7 +1062,7 @@ Example: https://fsn1.your-objectstorage.com`,
|
||||
apiKeyPlaceholder:
|
||||
'YOUR_API_KEY (obtained from https://serpapi.com/manage-api-key)',
|
||||
flowStart: 'Start',
|
||||
flowNum: 'Num',
|
||||
flowNum: 'N',
|
||||
test: 'Test',
|
||||
extractDepth: 'Extract Depth',
|
||||
format: 'Format',
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
AgentStructuredOutputField,
|
||||
CodeTemplateStrMap,
|
||||
ComparisonOperator,
|
||||
JsonSchemaDataType,
|
||||
Operator,
|
||||
ProgrammingLanguage,
|
||||
SwitchOperatorOptions,
|
||||
@ -610,15 +611,15 @@ export const initialListOperationsValues = {
|
||||
query: '',
|
||||
operations: ListOperations.TopN,
|
||||
outputs: {
|
||||
result: {
|
||||
type: 'Array<?>',
|
||||
},
|
||||
first: {
|
||||
type: '?',
|
||||
},
|
||||
last: {
|
||||
type: '?',
|
||||
},
|
||||
// result: {
|
||||
// type: 'Array<?>',
|
||||
// },
|
||||
// first: {
|
||||
// type: '?',
|
||||
// },
|
||||
// last: {
|
||||
// type: '?',
|
||||
// },
|
||||
},
|
||||
};
|
||||
|
||||
@ -874,3 +875,22 @@ export enum ExportFileType {
|
||||
Markdown = 'md',
|
||||
DOCX = 'docx',
|
||||
}
|
||||
|
||||
export enum TypesWithArray {
|
||||
String = 'string',
|
||||
Number = 'number',
|
||||
Boolean = 'boolean',
|
||||
Object = 'object',
|
||||
ArrayString = 'array<string>',
|
||||
ArrayNumber = 'array<number>',
|
||||
ArrayBoolean = 'array<boolean>',
|
||||
ArrayObject = 'array<object>',
|
||||
}
|
||||
|
||||
export const ArrayFields = [
|
||||
JsonSchemaDataType.Array,
|
||||
TypesWithArray.ArrayBoolean,
|
||||
TypesWithArray.ArrayNumber,
|
||||
TypesWithArray.ArrayString,
|
||||
TypesWithArray.ArrayObject,
|
||||
];
|
||||
|
||||
@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { JsonSchemaDataType } from '../../constant';
|
||||
import { ArrayFields } from '../../constant';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
@ -44,7 +44,7 @@ function IterationForm({ node }: INextOperatorForm) {
|
||||
<FormContainer>
|
||||
<QueryVariable
|
||||
name="items_ref"
|
||||
types={[JsonSchemaDataType.Array]}
|
||||
types={ArrayFields as any[]}
|
||||
></QueryVariable>
|
||||
</FormContainer>
|
||||
<DynamicOutput node={node}></DynamicOutput>
|
||||
|
||||
@ -13,20 +13,22 @@ import { Separator } from '@/components/ui/separator';
|
||||
import { useBuildSwitchOperatorOptions } from '@/hooks/logic-hooks/use-build-operator-options';
|
||||
import { buildOptions } from '@/utils/form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
ArrayFields,
|
||||
DataOperationsOperatorOptions,
|
||||
JsonSchemaDataType,
|
||||
ListOperations,
|
||||
SortMethod,
|
||||
initialListOperationsValues,
|
||||
} from '../../constant';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useGetVariableLabelOrTypeByValue } from '../../hooks/use-get-begin-query';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { getArrayElementType } from '../../utils';
|
||||
import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output, OutputSchema } from '../components/output';
|
||||
@ -36,7 +38,7 @@ import { QueryVariable } from '../components/query-variable';
|
||||
export const RetrievalPartialSchema = {
|
||||
query: z.string(),
|
||||
operations: z.string(),
|
||||
n: z.number().int().min(0).optional(),
|
||||
n: z.number().int().min(1).optional(),
|
||||
sort_method: z.string().optional(),
|
||||
filter: z
|
||||
.object({
|
||||
@ -47,26 +49,68 @@ export const RetrievalPartialSchema = {
|
||||
...OutputSchema,
|
||||
};
|
||||
|
||||
const NumFields = [
|
||||
ListOperations.TopN,
|
||||
ListOperations.Head,
|
||||
ListOperations.Tail,
|
||||
];
|
||||
|
||||
function showField(operations: string) {
|
||||
const showNum = NumFields.includes(operations as ListOperations);
|
||||
const showSortMethod = [ListOperations.Sort].includes(
|
||||
operations as ListOperations,
|
||||
);
|
||||
const showFilter = [ListOperations.Filter].includes(
|
||||
operations as ListOperations,
|
||||
);
|
||||
|
||||
return {
|
||||
showNum,
|
||||
showSortMethod,
|
||||
showFilter,
|
||||
};
|
||||
}
|
||||
|
||||
export const FormSchema = z.object(RetrievalPartialSchema);
|
||||
|
||||
export type ListOperationsFormSchemaType = z.infer<typeof FormSchema>;
|
||||
|
||||
const outputList = buildOutputList(initialListOperationsValues.outputs);
|
||||
|
||||
function ListOperationsForm({ node }: INextOperatorForm) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { getType } = useGetVariableLabelOrTypeByValue();
|
||||
|
||||
const defaultValues = useFormValues(initialListOperationsValues, node);
|
||||
|
||||
const form = useForm<ListOperationsFormSchemaType>({
|
||||
defaultValues: defaultValues,
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(FormSchema),
|
||||
shouldUnregister: true,
|
||||
// shouldUnregister: true,
|
||||
});
|
||||
|
||||
const operations = useWatch({ control: form.control, name: 'operations' });
|
||||
|
||||
const query = useWatch({ control: form.control, name: 'query' });
|
||||
|
||||
const subType = getArrayElementType(getType(query));
|
||||
|
||||
const currentOutputs = useMemo(() => {
|
||||
return {
|
||||
result: {
|
||||
type: `Array<${subType}>`,
|
||||
},
|
||||
first: {
|
||||
type: subType,
|
||||
},
|
||||
last: {
|
||||
type: subType,
|
||||
},
|
||||
};
|
||||
}, [subType]);
|
||||
|
||||
const outputList = buildOutputList(currentOutputs);
|
||||
|
||||
const ListOperationsOptions = buildOptions(
|
||||
ListOperations,
|
||||
t,
|
||||
@ -79,9 +123,39 @@ function ListOperationsForm({ node }: INextOperatorForm) {
|
||||
`flow.SortMethodOptions`,
|
||||
true,
|
||||
);
|
||||
|
||||
const operatorOptions = useBuildSwitchOperatorOptions(
|
||||
DataOperationsOperatorOptions,
|
||||
);
|
||||
|
||||
const { showFilter, showNum, showSortMethod } = showField(operations);
|
||||
|
||||
const handleOperationsChange = useCallback(
|
||||
(operations: string) => {
|
||||
const { showFilter, showNum, showSortMethod } = showField(operations);
|
||||
|
||||
if (showNum) {
|
||||
form.setValue('n', 1, { shouldDirty: true });
|
||||
}
|
||||
|
||||
if (showSortMethod) {
|
||||
form.setValue('sort_method', SortMethodOptions.at(0)?.value, {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}
|
||||
if (showFilter) {
|
||||
form.setValue('filter.operator', operatorOptions.at(0)?.value, {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[SortMethodOptions, form, operatorOptions],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
form.setValue('outputs', currentOutputs, { shouldDirty: true });
|
||||
}, [currentOutputs, form]);
|
||||
|
||||
useWatchFormChange(node?.id, form, true);
|
||||
|
||||
return (
|
||||
@ -90,37 +164,46 @@ function ListOperationsForm({ node }: INextOperatorForm) {
|
||||
<QueryVariable
|
||||
name="query"
|
||||
className="flex-1"
|
||||
types={[JsonSchemaDataType.Array]}
|
||||
types={ArrayFields as any[]}
|
||||
></QueryVariable>
|
||||
<Separator />
|
||||
<RAGFlowFormItem name="operations" label={t('flow.operations')}>
|
||||
<SelectWithSearch options={ListOperationsOptions} />
|
||||
{(field) => (
|
||||
<SelectWithSearch
|
||||
options={ListOperationsOptions}
|
||||
value={field.value}
|
||||
onChange={(val) => {
|
||||
handleOperationsChange(val);
|
||||
field.onChange(val);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</RAGFlowFormItem>
|
||||
{[
|
||||
ListOperations.TopN,
|
||||
ListOperations.Head,
|
||||
ListOperations.Tail,
|
||||
].includes(operations as ListOperations) && (
|
||||
{showNum && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="n"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flowNum')}</FormLabel>
|
||||
<FormLabel>{t('flow.flowNum')}</FormLabel>
|
||||
<FormControl>
|
||||
<NumberInput {...field} className="w-full"></NumberInput>
|
||||
<NumberInput
|
||||
{...field}
|
||||
className="w-full"
|
||||
min={1}
|
||||
></NumberInput>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{[ListOperations.Sort].includes(operations as ListOperations) && (
|
||||
{showSortMethod && (
|
||||
<RAGFlowFormItem name="sort_method" label={t('flow.sortMethod')}>
|
||||
<SelectWithSearch options={SortMethodOptions} />
|
||||
</RAGFlowFormItem>
|
||||
)}
|
||||
{[ListOperations.Filter].includes(operations as ListOperations) && (
|
||||
{showFilter && (
|
||||
<div className="flex items-center gap-2">
|
||||
<RAGFlowFormItem name="filter.operator" className="flex-1">
|
||||
<SelectWithSearch options={operatorOptions}></SelectWithSearch>
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
VariableAssignerLogicalOperator,
|
||||
} from '../../constant';
|
||||
import { useGetVariableLabelOrTypeByValue } from '../../hooks/use-get-begin-query';
|
||||
import { getArrayElementType } from '../../utils';
|
||||
import { DynamicFormHeader } from '../components/dynamic-fom-header';
|
||||
import { QueryVariable } from '../components/query-variable';
|
||||
import { useBuildLogicalOptions } from './use-build-logical-options';
|
||||
@ -152,9 +153,13 @@ export function DynamicVariables({
|
||||
} else if (
|
||||
logicalOperator === VariableAssignerLogicalArrayOperator.Append
|
||||
) {
|
||||
const subType = type.match(/<([^>]+)>/).at(1);
|
||||
const subType = getArrayElementType(type);
|
||||
return (
|
||||
<QueryVariable types={[subType]} hideLabel pureQuery></QueryVariable>
|
||||
<QueryVariable
|
||||
types={[subType as JsonSchemaDataType]}
|
||||
hideLabel
|
||||
pureQuery
|
||||
></QueryVariable>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { FormFieldConfig, FormFieldType } from '@/components/dynamic-form';
|
||||
import { buildSelectOptions } from '@/utils/component-util';
|
||||
import { t } from 'i18next';
|
||||
import { TypesWithArray } from '../constant';
|
||||
export { TypesWithArray } from '../constant';
|
||||
// const TypesWithoutArray = Object.values(JsonSchemaDataType).filter(
|
||||
// (item) => item !== JsonSchemaDataType.Array,
|
||||
// );
|
||||
@ -9,17 +11,6 @@ import { t } from 'i18next';
|
||||
// ...TypesWithoutArray.map((item) => `array<${item}>`),
|
||||
// ];
|
||||
|
||||
export enum TypesWithArray {
|
||||
String = 'string',
|
||||
Number = 'number',
|
||||
Boolean = 'boolean',
|
||||
Object = 'object',
|
||||
ArrayString = 'array<string>',
|
||||
ArrayNumber = 'array<number>',
|
||||
ArrayBoolean = 'array<boolean>',
|
||||
ArrayObject = 'array<object>',
|
||||
}
|
||||
|
||||
export const GlobalFormFields = [
|
||||
{
|
||||
label: t('flow.name'),
|
||||
|
||||
@ -732,3 +732,7 @@ export function buildBeginQueryWithObject(
|
||||
|
||||
return nextInputs;
|
||||
}
|
||||
|
||||
export function getArrayElementType(type: string) {
|
||||
return typeof type === 'string' ? type.match(/<([^>]+)>/)?.at(1) ?? '' : '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user