mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-26 17:16:52 +08:00
### What problem does this PR solve? Feat: Add a form with data operations operators #10427 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
46
web/src/pages/agent/form/components/query-variable-list.tsx
Normal file
46
web/src/pages/agent/form/components/query-variable-list.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { BlockButton, Button } from '@/components/ui/button';
|
||||
import { X } from 'lucide-react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { JsonSchemaDataType } from '../../constant';
|
||||
import { QueryVariable } from './query-variable';
|
||||
|
||||
type QueryVariableListProps = {
|
||||
types?: JsonSchemaDataType[];
|
||||
};
|
||||
export function QueryVariableList({ types }: QueryVariableListProps) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
const name = 'inputs';
|
||||
|
||||
const { fields, remove, append } = useFieldArray({
|
||||
name: name,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
{fields.map((field, index) => {
|
||||
const nameField = `${name}.${index}.input`;
|
||||
|
||||
return (
|
||||
<div key={field.id} className="flex items-center gap-2">
|
||||
<QueryVariable
|
||||
name={nameField}
|
||||
hideLabel
|
||||
className="flex-1"
|
||||
types={types}
|
||||
></QueryVariable>
|
||||
<Button variant={'ghost'} onClick={() => remove(index)}>
|
||||
<X className="text-text-sub-title-invert " />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<BlockButton onClick={() => append({ input: '' })}>
|
||||
{t('common.add')}
|
||||
</BlockButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -5,24 +5,28 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { toLower } from 'lodash';
|
||||
import { isEmpty, toLower } from 'lodash';
|
||||
import { ReactNode, useMemo } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { VariableType } from '../../constant';
|
||||
import { JsonSchemaDataType } from '../../constant';
|
||||
import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query';
|
||||
import { GroupedSelectWithSecondaryMenu } from './select-with-secondary-menu';
|
||||
|
||||
type QueryVariableProps = {
|
||||
name?: string;
|
||||
type?: VariableType;
|
||||
types?: JsonSchemaDataType[];
|
||||
label?: ReactNode;
|
||||
hideLabel?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function QueryVariable({
|
||||
name = 'query',
|
||||
type,
|
||||
types = [],
|
||||
label,
|
||||
hideLabel = false,
|
||||
className,
|
||||
}: QueryVariableProps) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
@ -30,23 +34,25 @@ export function QueryVariable({
|
||||
const nextOptions = useBuildQueryVariableOptions();
|
||||
|
||||
const finalOptions = useMemo(() => {
|
||||
return type
|
||||
return !isEmpty(types)
|
||||
? nextOptions.map((x) => {
|
||||
return {
|
||||
...x,
|
||||
options: x.options.filter((y) => toLower(y.type).includes(type)),
|
||||
options: x.options.filter((y) =>
|
||||
types?.some((x) => toLower(y.type).includes(x)),
|
||||
),
|
||||
};
|
||||
})
|
||||
: nextOptions;
|
||||
}, [nextOptions, type]);
|
||||
}, [nextOptions, types]);
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
{label || (
|
||||
<FormItem className={className}>
|
||||
{hideLabel || label || (
|
||||
<FormLabel tooltip={t('flow.queryTip')}>
|
||||
{t('flow.query')}
|
||||
</FormLabel>
|
||||
@ -56,7 +62,7 @@ export function QueryVariable({
|
||||
options={finalOptions}
|
||||
{...field}
|
||||
// allowClear
|
||||
type={type}
|
||||
types={types}
|
||||
></GroupedSelectWithSecondaryMenu>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
|
||||
@ -23,7 +23,7 @@ import { ChevronDownIcon, XIcon } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { VariableType } from '../../constant';
|
||||
import { JsonSchemaDataType } from '../../constant';
|
||||
import {
|
||||
useFindAgentStructuredOutputLabel,
|
||||
useShowSecondaryMenu,
|
||||
@ -52,7 +52,7 @@ interface GroupedSelectWithSecondaryMenuProps {
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
placeholder?: string;
|
||||
type?: VariableType;
|
||||
types?: JsonSchemaDataType[];
|
||||
}
|
||||
|
||||
export function GroupedSelectWithSecondaryMenu({
|
||||
@ -60,7 +60,7 @@ export function GroupedSelectWithSecondaryMenu({
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
type,
|
||||
types,
|
||||
}: GroupedSelectWithSecondaryMenuProps) {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
@ -157,7 +157,7 @@ export function GroupedSelectWithSecondaryMenu({
|
||||
key={option.value}
|
||||
data={option}
|
||||
click={handleSecondaryMenuClick}
|
||||
type={type}
|
||||
types={types}
|
||||
></StructuredOutputSecondaryMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import { get, isEmpty, isPlainObject } from 'lodash';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { PropsWithChildren, ReactNode, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { JsonSchemaDataType, VariableType } from '../../constant';
|
||||
import { JsonSchemaDataType } from '../../constant';
|
||||
import { useGetStructuredOutputByValue } from '../../hooks/use-build-structured-output';
|
||||
import {
|
||||
hasJsonSchemaChild,
|
||||
@ -20,13 +20,13 @@ type DataItem = { label: ReactNode; value: string; parentLabel?: ReactNode };
|
||||
type StructuredOutputSecondaryMenuProps = {
|
||||
data: DataItem;
|
||||
click(option: { label: ReactNode; value: string }): void;
|
||||
type?: VariableType | JsonSchemaDataType;
|
||||
types?: JsonSchemaDataType[];
|
||||
} & PropsWithChildren;
|
||||
|
||||
export function StructuredOutputSecondaryMenu({
|
||||
data,
|
||||
click,
|
||||
type,
|
||||
types = [],
|
||||
}: StructuredOutputSecondaryMenuProps) {
|
||||
const { t } = useTranslation();
|
||||
const filterStructuredOutput = useGetStructuredOutputByValue();
|
||||
@ -35,18 +35,21 @@ export function StructuredOutputSecondaryMenu({
|
||||
const handleSubMenuClick = useCallback(
|
||||
(option: { label: ReactNode; value: string }, dataType?: string) => () => {
|
||||
// The query variable of the iteration operator can only select array type data.
|
||||
if ((type && type === dataType) || !type) {
|
||||
if (
|
||||
(!isEmpty(types) && types?.some((x) => x === dataType)) ||
|
||||
isEmpty(types)
|
||||
) {
|
||||
click(option);
|
||||
}
|
||||
},
|
||||
[click, type],
|
||||
[click, types],
|
||||
);
|
||||
|
||||
const handleMenuClick = useCallback(() => {
|
||||
if (isEmpty(type) || type === JsonSchemaDataType.Object) {
|
||||
if (isEmpty(types) || types?.some((x) => x === JsonSchemaDataType.Object)) {
|
||||
click(data);
|
||||
}
|
||||
}, [click, data, type]);
|
||||
}, [click, data, types]);
|
||||
|
||||
const renderAgentStructuredOutput = useCallback(
|
||||
(values: any, option: { label: ReactNode; value: string }) => {
|
||||
@ -62,10 +65,10 @@ export function StructuredOutputSecondaryMenu({
|
||||
const dataType = get(value, 'type');
|
||||
|
||||
if (
|
||||
!type ||
|
||||
(type &&
|
||||
(dataType === type ||
|
||||
hasSpecificTypeChild(value ?? {}, type)))
|
||||
isEmpty(types) ||
|
||||
(!isEmpty(types) &&
|
||||
(types?.some((x) => x === dataType) ||
|
||||
hasSpecificTypeChild(value ?? {}, types)))
|
||||
) {
|
||||
return (
|
||||
<li key={key} className="pl-1">
|
||||
@ -90,10 +93,13 @@ export function StructuredOutputSecondaryMenu({
|
||||
|
||||
return <div></div>;
|
||||
},
|
||||
[handleSubMenuClick, type],
|
||||
[handleSubMenuClick, types],
|
||||
);
|
||||
|
||||
if (!hasJsonSchemaChild(structuredOutput)) {
|
||||
if (
|
||||
!hasJsonSchemaChild(structuredOutput) ||
|
||||
(!isEmpty(types) && !hasSpecificTypeChild(structuredOutput, types))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user