mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 12:32:30 +08:00
Feat: If a query variable in a data manipulation operator is deleted, a warning message should be displayed to the user. #10427 #11255 (#11384)
### What problem does this PR solve? Feat: If a query variable in a data manipulation operator is deleted, a warning message should be displayed to the user. #10427 #11255 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -140,6 +140,16 @@
|
|||||||
<path d="M0 0h1024v1024H0z" opacity=".01"></path>
|
<path d="M0 0h1024v1024H0z" opacity=".01"></path>
|
||||||
<path d="M867.072 141.184H156.032a32 32 0 0 0 0 64h711.04a32 32 0 0 0 0-64z m0.832 226.368H403.2a32 32 0 0 0 0 64h464.704a32 32 0 0 0 0-64zM403.2 573.888h464.704a32 32 0 0 1 0 64H403.2a32 32 0 0 1 0-64z m464.704 226.368H156.864a32 32 0 0 0 0 64h711.04a32 32 0 0 0 0-64zM137.472 367.552v270.336l174.528-122.24-174.528-148.096z" ></path>
|
<path d="M867.072 141.184H156.032a32 32 0 0 0 0 64h711.04a32 32 0 0 0 0-64z m0.832 226.368H403.2a32 32 0 0 0 0 64h464.704a32 32 0 0 0 0-64zM403.2 573.888h464.704a32 32 0 0 1 0 64H403.2a32 32 0 0 1 0-64z m464.704 226.368H156.864a32 32 0 0 0 0 64h711.04a32 32 0 0 0 0-64zM137.472 367.552v270.336l174.528-122.24-174.528-148.096z" ></path>
|
||||||
</symbol>` +
|
</symbol>` +
|
||||||
|
` <symbol id="icon-a-listoperations" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M341.376 96a32 32 0 0 1 0 64h-128a10.688 10.688 0 0 0-10.688 10.688v682.624a10.752 10.752 0 0 0 10.688 10.688h128a32 32 0 0 1 0 64h-128a74.688 74.688 0 0 1-74.688-74.688V170.688A74.688 74.688 0 0 1 213.376 96h128z m469.312 0a74.688 74.688 0 0 1 74.688 74.688v682.624a74.752 74.752 0 0 1-74.688 74.688h-128a32 32 0 1 1 0-64h128a10.752 10.752 0 0 0 10.688-10.688V170.688a10.752 10.752 0 0 0-10.688-10.688h-128a32 32 0 1 1 0-64h128zM357.248 464.256a48 48 0 0 1 0 95.488l-4.928 0.256H352a48 48 0 0 1 0-96h0.32l4.928 0.256z m155.072-0.256a48 48 0 1 1 0 96H512a48 48 0 0 1 0-96h0.32z m160 0a48 48 0 0 1 0 96H672a48 48 0 0 1 0-96h0.32z" ></path>
|
||||||
|
</symbol>` +
|
||||||
|
`<symbol id="icon-aggregator" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M949.312 533.312a32 32 0 0 1-9.344 22.592l-170.688 170.688a32 32 0 0 1-45.248-45.248l116.032-116.032H478.208l-10.176-0.128a202.688 202.688 0 0 1-135.36-59.264L41.344 214.592a32 32 0 1 1 45.312-45.248l291.264 291.328 10.24 9.344a138.688 138.688 0 0 0 89.344 31.296h362.56L724.032 385.28a32 32 0 0 1 45.248-45.248l170.688 170.624a32 32 0 0 1 9.344 22.656zM299.968 638.656a32 32 0 0 1 0 45.248L86.656 897.28a32 32 0 0 1-45.312-45.248L254.72 638.72a32 32 0 0 1 45.312 0z" ></path>
|
||||||
|
</symbol>` +
|
||||||
|
`<symbol id="icon-a-ariableassigner" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M509.056 64c123.136 0 235.072 48.512 317.12 130.56l-41.024 37.312C714.24 161.024 617.216 119.936 509.056 119.936a391.808 391.808 0 1 0 0 783.552 392.448 392.448 0 0 0 294.784-134.272l41.024 37.312c-82.048 93.248-201.472 149.248-335.808 149.248-246.272 3.712-447.744-197.76-447.744-444.032S262.784 64 509.056 64z m-63.424 186.56a29.184 29.184 0 0 1 14.912 14.912l160.448 444.032c3.712 14.912-3.712 26.112-18.56 33.536-14.976 3.776-26.24-3.648-33.664-14.848l-48.512-149.248H341.12l-59.712 149.248a27.648 27.648 0 0 1-33.6 14.848c-14.912-3.712-18.56-18.624-14.848-33.536l179.008-444.032c3.776-11.136 22.4-18.624 33.6-14.912zM889.6 530.432c14.976 0 26.176 11.2 26.176 26.112a25.472 25.472 0 0 1-26.176 26.112h-212.608a25.472 25.472 0 0 1-26.112-26.112c0-14.912 11.2-26.112 26.112-26.112H889.6z m-529.792 0h141.824L434.432 351.36l-74.624 179.2zM889.6 411.008c14.912 0 26.176 11.2 26.176 26.112a25.536 25.536 0 0 1-26.176 26.112h-212.608a25.536 25.536 0 0 1-26.112-26.112c0-14.912 11.2-26.112 26.112-26.112H889.6z" ></path>
|
||||||
|
</symbol>
|
||||||
|
` +
|
||||||
'</svg>'),
|
'</svg>'),
|
||||||
((h) => {
|
((h) => {
|
||||||
var a = (l = (l = document.getElementsByTagName('script'))[
|
var a = (l = (l = document.getElementsByTagName('script'))[
|
||||||
|
|||||||
@ -48,7 +48,7 @@ const JsonSchemaVisualizer: FC<JsonSchemaVisualizerProps> = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const parsedJson = JSON.parse(value);
|
const parsedJson = JSON.parse(value);
|
||||||
if (onChange) {
|
if (onChange && typeof parsedJson !== 'number') {
|
||||||
onChange(parsedJson);
|
onChange(parsedJson);
|
||||||
}
|
}
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
|
|||||||
@ -1646,6 +1646,7 @@ The variable aggregation node (originally the variable assignment node) is a cru
|
|||||||
beginInputTip:
|
beginInputTip:
|
||||||
'By defining input parameters, this content can be accessed by other components in subsequent processes.',
|
'By defining input parameters, this content can be accessed by other components in subsequent processes.',
|
||||||
query: 'Query variables',
|
query: 'Query variables',
|
||||||
|
queryRequired: 'Query is required',
|
||||||
queryTip: 'Select the variable you want to use',
|
queryTip: 'Select the variable you want to use',
|
||||||
agent: 'Agent',
|
agent: 'Agent',
|
||||||
addAgent: 'Add Agent',
|
addAgent: 'Add Agent',
|
||||||
|
|||||||
@ -1549,6 +1549,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
task: '任务',
|
task: '任务',
|
||||||
beginInputTip: '通过定义输入参数,此内容可以被后续流程中的其他组件访问。',
|
beginInputTip: '通过定义输入参数,此内容可以被后续流程中的其他组件访问。',
|
||||||
query: '查询变量',
|
query: '查询变量',
|
||||||
|
queryRequired: '查询变量是必填项',
|
||||||
queryTip: '选择您想要使用的变量',
|
queryTip: '选择您想要使用的变量',
|
||||||
agent: '智能体',
|
agent: '智能体',
|
||||||
addAgent: '添加智能体',
|
addAgent: '添加智能体',
|
||||||
|
|||||||
@ -11,11 +11,12 @@ export function DataOperationsNode({
|
|||||||
}: NodeProps<BaseNode<DataOperationsFormSchemaType>>) {
|
}: NodeProps<BaseNode<DataOperationsFormSchemaType>>) {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const operations = data.form?.operations;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RagNode {...props}>
|
<RagNode {...props}>
|
||||||
<LabelCard>
|
<LabelCard>
|
||||||
{t(`flow.operationsOptions.${camelCase(data.form?.operations)}`)}
|
{operations && t(`flow.operationsOptions.${camelCase(operations)}`)}
|
||||||
</LabelCard>
|
</LabelCard>
|
||||||
</RagNode>
|
</RagNode>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -7,17 +7,24 @@ export type FormListHeaderProps = {
|
|||||||
label: ReactNode;
|
label: ReactNode;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function DynamicFormHeader({
|
export function DynamicFormHeader({
|
||||||
label,
|
label,
|
||||||
tooltip,
|
tooltip,
|
||||||
onClick,
|
onClick,
|
||||||
|
disabled = false,
|
||||||
}: FormListHeaderProps) {
|
}: FormListHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<FormLabel tooltip={tooltip}>{label}</FormLabel>
|
<FormLabel tooltip={tooltip}>{label}</FormLabel>
|
||||||
<Button variant={'ghost'} type="button" onClick={onClick}>
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
type="button"
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
<Plus />
|
<Plus />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,10 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
import { JsonSchemaDataType } from '../../constant';
|
import { JsonSchemaDataType } from '../../constant';
|
||||||
|
import {
|
||||||
|
flatOptions,
|
||||||
|
useFilterQueryVariableOptionsByTypes,
|
||||||
|
} from '../../hooks/use-get-begin-query';
|
||||||
import { DynamicFormHeader, FormListHeaderProps } from './dynamic-fom-header';
|
import { DynamicFormHeader, FormListHeaderProps } from './dynamic-fom-header';
|
||||||
import { QueryVariable } from './query-variable';
|
import { QueryVariable } from './query-variable';
|
||||||
|
|
||||||
@ -16,6 +20,10 @@ export function QueryVariableList({
|
|||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const name = 'query';
|
const name = 'query';
|
||||||
|
|
||||||
|
let options = useFilterQueryVariableOptionsByTypes(types);
|
||||||
|
|
||||||
|
const secondOptions = flatOptions(options);
|
||||||
|
|
||||||
const { fields, remove, append } = useFieldArray({
|
const { fields, remove, append } = useFieldArray({
|
||||||
name: name,
|
name: name,
|
||||||
control: form.control,
|
control: form.control,
|
||||||
@ -26,14 +34,15 @@ export function QueryVariableList({
|
|||||||
<DynamicFormHeader
|
<DynamicFormHeader
|
||||||
label={label}
|
label={label}
|
||||||
tooltip={tooltip}
|
tooltip={tooltip}
|
||||||
onClick={() => append({ input: '' })}
|
onClick={() => append({ input: secondOptions.at(0)?.value })}
|
||||||
|
disabled={!secondOptions.length}
|
||||||
></DynamicFormHeader>
|
></DynamicFormHeader>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
{fields.map((field, index) => {
|
{fields.map((field, index) => {
|
||||||
const nameField = `${name}.${index}.input`;
|
const nameField = `${name}.${index}.input`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={field.id} className="flex items-center gap-2">
|
<div key={field.id} className="flex gap-2">
|
||||||
<QueryVariable
|
<QueryVariable
|
||||||
name={nameField}
|
name={nameField}
|
||||||
hideLabel
|
hideLabel
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Form } from '@/components/ui/form';
|
|||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { t } from 'i18next';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -25,7 +26,11 @@ import { SelectKeys } from './select-keys';
|
|||||||
import { Updates } from './updates';
|
import { Updates } from './updates';
|
||||||
|
|
||||||
export const RetrievalPartialSchema = {
|
export const RetrievalPartialSchema = {
|
||||||
query: z.array(z.object({ input: z.string().optional() })),
|
query: z.array(
|
||||||
|
z.object({
|
||||||
|
input: z.string().min(1, { message: t('flow.queryRequired') }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
operations: z.string(),
|
operations: z.string(),
|
||||||
select_keys: z.array(z.object({ name: z.string().optional() })).optional(),
|
select_keys: z.array(z.object({ name: z.string().optional() })).optional(),
|
||||||
remove_keys: z.array(z.object({ name: z.string().optional() })).optional(),
|
remove_keys: z.array(z.object({ name: z.string().optional() })).optional(),
|
||||||
|
|||||||
@ -317,14 +317,18 @@ export const useGetComponentLabelByValue = (nodeId: string) => {
|
|||||||
return getLabel;
|
return getLabel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function flatOptions(options: DefaultOptionType[]) {
|
||||||
|
return options.reduce<DefaultOptionType[]>((pre, cur) => {
|
||||||
|
return [...pre, ...cur.options];
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
export function useFlattenQueryVariableOptions(nodeId?: string) {
|
export function useFlattenQueryVariableOptions(nodeId?: string) {
|
||||||
const { getNode } = useGraphStore((state) => state);
|
const { getNode } = useGraphStore((state) => state);
|
||||||
const nextOptions = useBuildQueryVariableOptions(getNode(nodeId));
|
const nextOptions = useBuildQueryVariableOptions(getNode(nodeId));
|
||||||
|
|
||||||
const flattenOptions = useMemo(() => {
|
const flattenOptions = useMemo(() => {
|
||||||
return nextOptions.reduce<DefaultOptionType[]>((pre, cur) => {
|
return flatOptions(nextOptions);
|
||||||
return [...pre, ...cur.options];
|
|
||||||
}, []);
|
|
||||||
}, [nextOptions]);
|
}, [nextOptions]);
|
||||||
|
|
||||||
return flattenOptions;
|
return flattenOptions;
|
||||||
|
|||||||
@ -12,9 +12,9 @@ import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg';
|
|||||||
import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg';
|
import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg';
|
||||||
import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg';
|
import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg';
|
||||||
|
|
||||||
import { IconFont } from '@/components/icon-font';
|
import { IconFontFill } from '@/components/icon-font';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Columns3, Equal, FileCode, HousePlus, Variable } from 'lucide-react';
|
import { FileCode, HousePlus } from 'lucide-react';
|
||||||
import { Operator } from './constant';
|
import { Operator } from './constant';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -37,6 +37,9 @@ export const OperatorIconMap = {
|
|||||||
[Operator.ExeSQL]: 'executesql-0',
|
[Operator.ExeSQL]: 'executesql-0',
|
||||||
[Operator.Invoke]: 'httprequest-0',
|
[Operator.Invoke]: 'httprequest-0',
|
||||||
[Operator.Email]: 'sendemail-0',
|
[Operator.Email]: 'sendemail-0',
|
||||||
|
[Operator.ListOperations]: 'a-listoperations',
|
||||||
|
[Operator.VariableAssigner]: 'a-ariableassigner',
|
||||||
|
[Operator.VariableAggregator]: 'aggregator',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SVGIconMap = {
|
export const SVGIconMap = {
|
||||||
@ -57,9 +60,6 @@ export const SVGIconMap = {
|
|||||||
};
|
};
|
||||||
export const LucideIconMap = {
|
export const LucideIconMap = {
|
||||||
[Operator.DataOperations]: FileCode,
|
[Operator.DataOperations]: FileCode,
|
||||||
[Operator.ListOperations]: Columns3,
|
|
||||||
[Operator.VariableAssigner]: Equal,
|
|
||||||
[Operator.VariableAggregator]: Variable,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Empty = () => {
|
const Empty = () => {
|
||||||
@ -86,7 +86,10 @@ const OperatorIcon = ({ name, className }: IProps) => {
|
|||||||
|
|
||||||
if (Icon) {
|
if (Icon) {
|
||||||
return (
|
return (
|
||||||
<IconFont name={Icon} className={cn('size-5 ', className)}></IconFont>
|
<IconFontFill
|
||||||
|
name={Icon}
|
||||||
|
className={cn('size-5 ', className)}
|
||||||
|
></IconFontFill>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user