mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Enable logical operators in metadata. #11387 #11376 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -14,6 +14,7 @@ type MetadataFilterProps = {
|
|||||||
export const MetadataFilterSchema = {
|
export const MetadataFilterSchema = {
|
||||||
meta_data_filter: z
|
meta_data_filter: z
|
||||||
.object({
|
.object({
|
||||||
|
logic: z.string().optional(),
|
||||||
method: z.string().optional(),
|
method: z.string().optional(),
|
||||||
manual: z
|
manual: z
|
||||||
.array(
|
.array(
|
||||||
|
|||||||
@ -15,14 +15,17 @@ import {
|
|||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { SwitchOperatorOptions } from '@/constants/agent';
|
import { SwitchLogicOperator, SwitchOperatorOptions } from '@/constants/agent';
|
||||||
import { useBuildSwitchOperatorOptions } from '@/hooks/logic-hooks/use-build-operator-options';
|
import { useBuildSwitchOperatorOptions } from '@/hooks/logic-hooks/use-build-operator-options';
|
||||||
|
import { useBuildSwitchLogicOperatorOptions } from '@/hooks/logic-hooks/use-build-options';
|
||||||
import { useFetchKnowledgeMetadata } from '@/hooks/use-knowledge-request';
|
import { useFetchKnowledgeMetadata } from '@/hooks/use-knowledge-request';
|
||||||
import { PromptEditor } from '@/pages/agent/form/components/prompt-editor';
|
import { PromptEditor } from '@/pages/agent/form/components/prompt-editor';
|
||||||
import { Plus, X } from 'lucide-react';
|
import { Plus, X } from 'lucide-react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { RAGFlowFormItem } from '../ragflow-form';
|
||||||
|
import { RAGFlowSelect } from '../ui/select';
|
||||||
|
|
||||||
export function MetadataFilterConditions({
|
export function MetadataFilterConditions({
|
||||||
kbIds,
|
kbIds,
|
||||||
@ -36,10 +39,13 @@ export function MetadataFilterConditions({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const name = prefix + 'meta_data_filter.manual';
|
const name = prefix + 'meta_data_filter.manual';
|
||||||
|
const logic = prefix + 'meta_data_filter.logic';
|
||||||
const metadata = useFetchKnowledgeMetadata(kbIds);
|
const metadata = useFetchKnowledgeMetadata(kbIds);
|
||||||
|
|
||||||
const switchOperatorOptions = useBuildSwitchOperatorOptions();
|
const switchOperatorOptions = useBuildSwitchOperatorOptions();
|
||||||
|
|
||||||
|
const switchLogicOperatorOptions = useBuildSwitchLogicOperatorOptions();
|
||||||
|
|
||||||
const { fields, remove, append } = useFieldArray({
|
const { fields, remove, append } = useFieldArray({
|
||||||
name,
|
name,
|
||||||
control: form.control,
|
control: form.control,
|
||||||
@ -47,13 +53,14 @@ export function MetadataFilterConditions({
|
|||||||
|
|
||||||
const add = useCallback(
|
const add = useCallback(
|
||||||
(key: string) => () => {
|
(key: string) => () => {
|
||||||
|
form.setValue(logic, SwitchLogicOperator.And);
|
||||||
append({
|
append({
|
||||||
key,
|
key,
|
||||||
value: '',
|
value: '',
|
||||||
op: SwitchOperatorOptions[0].value,
|
op: SwitchOperatorOptions[0].value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[append],
|
[append, form, logic],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -77,73 +84,92 @@ export function MetadataFilterConditions({
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-5">
|
<section className="flex">
|
||||||
{fields.map((field, index) => {
|
{fields.length > 1 && (
|
||||||
const typeField = `${name}.${index}.key`;
|
<div className="relative min-w-14">
|
||||||
return (
|
<RAGFlowFormItem
|
||||||
<div key={field.id} className="flex w-full items-center gap-2">
|
name={logic}
|
||||||
<FormField
|
className="absolute top-1/2 -translate-y-1/2 right-1 left-0 z-10 bg-bg-base"
|
||||||
control={form.control}
|
>
|
||||||
name={typeField}
|
<RAGFlowSelect
|
||||||
render={({ field }) => (
|
options={switchLogicOperatorOptions}
|
||||||
<FormItem className="flex-1 overflow-hidden">
|
triggerClassName="w-full text-xs px-1 py-0 h-6"
|
||||||
<FormControl>
|
></RAGFlowSelect>
|
||||||
<Input
|
</RAGFlowFormItem>
|
||||||
{...field}
|
<div className="absolute border-l border-y w-5 right-0 top-4 bottom-4 rounded-l-lg"></div>
|
||||||
placeholder={t('common.pleaseInput')}
|
</div>
|
||||||
></Input>
|
)}
|
||||||
</FormControl>
|
<div className="space-y-5 flex-1">
|
||||||
<FormMessage />
|
{fields.map((field, index) => {
|
||||||
</FormItem>
|
const typeField = `${name}.${index}.key`;
|
||||||
)}
|
return (
|
||||||
/>
|
<section key={field.id} className="flex gap-2">
|
||||||
<Separator className="w-3 text-text-secondary" />
|
<div className="w-full space-y-2">
|
||||||
<FormField
|
<div className="flex items-center gap-1">
|
||||||
control={form.control}
|
<FormField
|
||||||
name={`${name}.${index}.op`}
|
control={form.control}
|
||||||
render={({ field }) => (
|
name={typeField}
|
||||||
<FormItem className="flex-1 overflow-hidden">
|
render={({ field }) => (
|
||||||
<FormControl>
|
<FormItem className="flex-1 overflow-hidden">
|
||||||
<SelectWithSearch
|
<FormControl>
|
||||||
{...field}
|
<Input
|
||||||
options={switchOperatorOptions}
|
{...field}
|
||||||
></SelectWithSearch>
|
placeholder={t('common.pleaseInput')}
|
||||||
</FormControl>
|
></Input>
|
||||||
<FormMessage />
|
</FormControl>
|
||||||
</FormItem>
|
<FormMessage />
|
||||||
)}
|
</FormItem>
|
||||||
/>
|
|
||||||
<Separator className="w-3 text-text-secondary" />
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={`${name}.${index}.value`}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex-1 overflow-hidden">
|
|
||||||
<FormControl>
|
|
||||||
{canReference ? (
|
|
||||||
<PromptEditor
|
|
||||||
{...field}
|
|
||||||
multiLine={false}
|
|
||||||
showToolbar={false}
|
|
||||||
></PromptEditor>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
placeholder={t('common.pleaseInput')}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</FormControl>
|
/>
|
||||||
<FormMessage />
|
<Separator className="w-1 text-text-secondary" />
|
||||||
</FormItem>
|
<FormField
|
||||||
)}
|
control={form.control}
|
||||||
/>
|
name={`${name}.${index}.op`}
|
||||||
<Button variant={'ghost'} onClick={() => remove(index)}>
|
render={({ field }) => (
|
||||||
<X className="text-text-sub-title-invert " />
|
<FormItem className="flex-1 overflow-hidden">
|
||||||
</Button>
|
<FormControl>
|
||||||
</div>
|
<SelectWithSearch
|
||||||
);
|
{...field}
|
||||||
})}
|
options={switchOperatorOptions}
|
||||||
</div>
|
></SelectWithSearch>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name={`${name}.${index}.value`}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1 overflow-hidden">
|
||||||
|
<FormControl>
|
||||||
|
{canReference ? (
|
||||||
|
<PromptEditor
|
||||||
|
{...field}
|
||||||
|
multiLine={false}
|
||||||
|
showToolbar={false}
|
||||||
|
></PromptEditor>
|
||||||
|
) : (
|
||||||
|
<Input
|
||||||
|
placeholder={t('common.pleaseInput')}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button variant={'ghost'} onClick={() => remove(index)}>
|
||||||
|
<X className="text-text-sub-title-invert " />
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -179,3 +179,8 @@ export enum JsonSchemaDataType {
|
|||||||
Array = 'array',
|
Array = 'array',
|
||||||
Object = 'object',
|
Object = 'object',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum SwitchLogicOperator {
|
||||||
|
And = 'and',
|
||||||
|
Or = 'or',
|
||||||
|
}
|
||||||
|
|||||||
12
web/src/hooks/logic-hooks/use-build-options.ts
Normal file
12
web/src/hooks/logic-hooks/use-build-options.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { SwitchLogicOperator } from '@/constants/agent';
|
||||||
|
import { buildOptions } from '@/utils/form';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export function useBuildSwitchLogicOperatorOptions() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return buildOptions(
|
||||||
|
SwitchLogicOperator,
|
||||||
|
t,
|
||||||
|
'flow.switchLogicOperatorOptions',
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ import {
|
|||||||
JsonSchemaDataType,
|
JsonSchemaDataType,
|
||||||
Operator,
|
Operator,
|
||||||
ProgrammingLanguage,
|
ProgrammingLanguage,
|
||||||
|
SwitchLogicOperator,
|
||||||
SwitchOperatorOptions,
|
SwitchOperatorOptions,
|
||||||
initialLlmBaseValues,
|
initialLlmBaseValues,
|
||||||
} from '@/constants/agent';
|
} from '@/constants/agent';
|
||||||
@ -51,8 +52,6 @@ import {
|
|||||||
|
|
||||||
export const BeginId = 'begin';
|
export const BeginId = 'begin';
|
||||||
|
|
||||||
export const SwitchLogicOperatorOptions = ['and', 'or'];
|
|
||||||
|
|
||||||
export const CommonOperatorList = Object.values(Operator).filter(
|
export const CommonOperatorList = Object.values(Operator).filter(
|
||||||
(x) => x !== Operator.Note,
|
(x) => x !== Operator.Note,
|
||||||
);
|
);
|
||||||
@ -308,7 +307,7 @@ export const initialExeSqlValues = {
|
|||||||
export const initialSwitchValues = {
|
export const initialSwitchValues = {
|
||||||
conditions: [
|
conditions: [
|
||||||
{
|
{
|
||||||
logical_operator: SwitchLogicOperatorOptions[0],
|
logical_operator: SwitchLogicOperator.And,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
operator: SwitchOperatorOptions[0].value,
|
operator: SwitchOperatorOptions[0].value,
|
||||||
|
|||||||
@ -11,16 +11,17 @@ import {
|
|||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
import { RAGFlowSelect } from '@/components/ui/select';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
|
import { SwitchLogicOperator } from '@/constants/agent';
|
||||||
import { useBuildSwitchOperatorOptions } from '@/hooks/logic-hooks/use-build-operator-options';
|
import { useBuildSwitchOperatorOptions } from '@/hooks/logic-hooks/use-build-operator-options';
|
||||||
|
import { useBuildSwitchLogicOperatorOptions } from '@/hooks/logic-hooks/use-build-options';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useFieldArray, useForm, useFormContext } from 'react-hook-form';
|
import { useFieldArray, useForm, useFormContext } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { SwitchLogicOperatorOptions } from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
import { IOperatorForm } from '../../interface';
|
||||||
import { FormWrapper } from '../components/form-wrapper';
|
import { FormWrapper } from '../components/form-wrapper';
|
||||||
import { QueryVariable } from '../components/query-variable';
|
import { QueryVariable } from '../components/query-variable';
|
||||||
@ -185,12 +186,7 @@ function SwitchForm({ node }: IOperatorForm) {
|
|||||||
control: form.control,
|
control: form.control,
|
||||||
});
|
});
|
||||||
|
|
||||||
const switchLogicOperatorOptions = useMemo(() => {
|
const switchLogicOperatorOptions = useBuildSwitchLogicOperatorOptions();
|
||||||
return SwitchLogicOperatorOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`flow.switchLogicOperatorOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
useWatchFormChange(node?.id, form);
|
useWatchFormChange(node?.id, form);
|
||||||
|
|
||||||
@ -253,7 +249,7 @@ function SwitchForm({ node }: IOperatorForm) {
|
|||||||
<BlockButton
|
<BlockButton
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
append({
|
append({
|
||||||
logical_operator: SwitchLogicOperatorOptions[0],
|
logical_operator: SwitchLogicOperator.And,
|
||||||
[ItemKey]: [
|
[ItemKey]: [
|
||||||
{
|
{
|
||||||
operator: switchOperatorOptions[0].value,
|
operator: switchOperatorOptions[0].value,
|
||||||
|
|||||||
Reference in New Issue
Block a user