From 28e69719388966190e669dcd7c66eed470d664ac Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Thu, 20 Feb 2025 15:14:36 +0800 Subject: [PATCH] add metadata --- .../components/metadata/add-condition.tsx | 64 +++++++++ .../condition-list/condition-date.tsx | 65 ++++++++++ .../condition-list/condition-item.tsx | 121 ++++++++++++++++++ .../condition-list/condition-number.tsx | 33 +++++ .../condition-list/condition-operator.tsx | 98 ++++++++++++++ .../condition-list/condition-string.tsx | 33 +++++ .../condition-list/condition-value-method.tsx | 69 ++++++++++ .../condition-variable-selector.tsx | 67 ++++++++++ .../metadata/condition-list/hooks.ts | 11 ++ .../metadata/condition-list/index.tsx | 71 ++++++++++ .../metadata/condition-list/utils.ts | 61 +++++++++ .../metadata/metadata-filter/index.tsx | 60 +++++++++ .../metadata-filter-selector.tsx | 98 ++++++++++++++ .../components/metadata/metadata-panel.tsx | 41 ++++++ .../components/metadata/metadata-trigger.tsx | 48 +++++++ .../nodes/knowledge-retrieval/types.ts | 67 ++++++++++ .../nodes/knowledge-retrieval/use-config.ts | 58 ++++++++- 17 files changed, 1062 insertions(+), 3 deletions(-) create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/hooks.ts create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx create mode 100644 web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx new file mode 100644 index 0000000000..b514f8125a --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx @@ -0,0 +1,64 @@ +import { useState } from 'react' +import { + RiAddLine, +} from '@remixicon/react' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import Button from '@/app/components/base/button' +import Input from '@/app/components/base/input' +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +const AddCondition = ({ + handleAddCondition, +}: Pick) => { + const [open, setOpen] = useState(false) + + return ( + + setOpen(!open)}> + + + +
+
+ +
+
+
+
handleAddCondition?.('language')} + > + Language +
+
string
+
+
+
+
+
+ ) +} + +export default AddCondition diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx new file mode 100644 index 0000000000..244b236238 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx @@ -0,0 +1,65 @@ +import { useCallback } from 'react' +import dayjs from 'dayjs' +import { + RiCalendarLine, + RiCloseCircleFill, +} from '@remixicon/react' +import DatePicker from '@/app/components/base/date-and-time-picker/date-picker' +import cn from '@/utils/classnames' + +type ConditionDateProps = { + value: string + onChange: (date: string) => void +} +const ConditionDate = ({ + value, + onChange, +}: ConditionDateProps) => { + const handleDateChange = useCallback((date?: dayjs.Dayjs) => { + if (date) + onChange(date.format('YYYY-MM-DD')) + else + onChange('') + }, [onChange]) + + const renderTrigger = useCallback(() => { + return ( +
+
+ {value || 'Choose a time...'} +
+ handleDateChange()} + /> + +
+ ) + }, [value, handleDateChange]) + + return ( + + + + ) +} + +export default ConditionDate diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx new file mode 100644 index 0000000000..e9aca524ae --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx @@ -0,0 +1,121 @@ +import { + useCallback, + useMemo, + useState, +} from 'react' +import { RiDeleteBinLine } from '@remixicon/react' +import { comparisonOperatorNotRequireValue } from './utils' +import ConditionOperator from './condition-operator' +import ConditionValueMethod from './condition-value-method' +import ConditionString from './condition-string' +import ConditionNumber from './condition-number' +import ConditionDate from './condition-date' +import { useCondition } from './hooks' +import type { + HandleRemoveCondition, + HandleUpdateCondition, + MetadataFilteringCondition, +} from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { MetadataFilteringVariableType } from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +import type { + Node, + NodeOutPutVar, +} from '@/app/components/workflow/types' +import cn from '@/utils/classnames' + +type ConditionItemProps = { + index: number + className?: string + disabled?: boolean + condition: MetadataFilteringCondition // condition may the condition of case or condition of sub variable + onRemoveCondition?: HandleRemoveCondition + onUpdateCondition?: HandleUpdateCondition + nodesOutputVars: NodeOutPutVar[] + availableNodes: Node[] +} +const ConditionItem = ({ + index, + className, + disabled, + condition, + onRemoveCondition, +}: ConditionItemProps) => { + const [isHovered, setIsHovered] = useState(false) + const { getConditionVariableType } = useCondition() + + const canChooseOperator = useMemo(() => { + if (disabled) + return false + + return true + }, [disabled]) + + const doRemoveCondition = useCallback(() => { + onRemoveCondition?.(index) + }, [onRemoveCondition, index]) + + return ( +
+
+
+
+
+
Language
+
string
+
+
+
+ {}} + /> +
+
+ {}} + /> +
+
+ { + !comparisonOperatorNotRequireValue(condition.comparison_operator) && getConditionVariableType(condition.name) === MetadataFilteringVariableType.string && ( + {}} + /> + ) + } + { + !comparisonOperatorNotRequireValue(condition.comparison_operator) && getConditionVariableType(condition.name) === MetadataFilteringVariableType.number && ( + {}} + /> + ) + } + { + !comparisonOperatorNotRequireValue(condition.comparison_operator) && getConditionVariableType(condition.name) === MetadataFilteringVariableType.date && ( + {}} + /> + ) + } +
+
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + onClick={doRemoveCondition} + > + +
+
+ ) +} + +export default ConditionItem diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx new file mode 100644 index 0000000000..0f945e5649 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx @@ -0,0 +1,33 @@ +import ConditionValueMethod from './condition-value-method' +import type { ConditionValueMethodProps } from './condition-value-method' +import ConditionVariableSelector from './condition-variable-selector' + +type ConditionNumberProps = {} & ConditionValueMethodProps +const ConditionNumber = ({ + valueMethod, + onValueMethodChange, +}: ConditionNumberProps) => { + return ( +
+ +
+ { + valueMethod === 'variable' && ( + {}} + /> + ) + } + { + valueMethod === 'constant' && ( + + ) + } +
+ ) +} + +export default ConditionNumber diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx new file mode 100644 index 0000000000..6cbc489476 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx @@ -0,0 +1,98 @@ +import { + useMemo, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import { RiArrowDownSLine } from '@remixicon/react' +import { + getOperators, + isComparisonOperatorNeedTranslate, +} from './utils' +import Button from '@/app/components/base/button' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import cn from '@/utils/classnames' +import type { + ComparisonOperator, + MetadataFilteringVariableType, +} from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +const i18nPrefix = 'workflow.nodes.ifElse' + +type ConditionOperatorProps = { + className?: string + disabled?: boolean + variableType: MetadataFilteringVariableType + value?: string + onSelect: (value: ComparisonOperator) => void +} +const ConditionOperator = ({ + className, + disabled, + variableType, + value, + onSelect, +}: ConditionOperatorProps) => { + const { t } = useTranslation() + const [open, setOpen] = useState(false) + + const options = useMemo(() => { + return getOperators(variableType).map((o) => { + return { + label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`) : o, + value: o, + } + }) + }, [t, variableType]) + const selectedOption = options.find(o => Array.isArray(value) ? o.value === value[0] : o.value === value) + return ( + + setOpen(v => !v)}> + + + +
+ { + options.map(option => ( +
{ + onSelect(option.value) + setOpen(false) + }} + > + {option.label} +
+ )) + } +
+
+
+ ) +} + +export default ConditionOperator diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx new file mode 100644 index 0000000000..dfe8fa1992 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx @@ -0,0 +1,33 @@ +import ConditionValueMethod from './condition-value-method' +import type { ConditionValueMethodProps } from './condition-value-method' +import ConditionVariableSelector from './condition-variable-selector' + +type ConditionStringProps = {} & ConditionValueMethodProps +const ConditionString = ({ + valueMethod, + onValueMethodChange, +}: ConditionStringProps) => { + return ( +
+ +
+ { + valueMethod === 'variable' && ( + {}} + /> + ) + } + { + valueMethod === 'constant' && ( + + ) + } +
+ ) +} + +export default ConditionString diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx new file mode 100644 index 0000000000..d5c558ac10 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx @@ -0,0 +1,69 @@ +import { useState } from 'react' +import { capitalize } from 'lodash-es' +import { RiArrowDownSLine } from '@remixicon/react' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import Button from '@/app/components/base/button' +import cn from '@/utils/classnames' + +export type ConditionValueMethodProps = { + valueMethod?: string + onValueMethodChange: (v: string) => void +} +const options = [ + 'variable', + 'constant', +] +const ConditionValueMethod = ({ + valueMethod = 'variable', + onValueMethodChange, +}: ConditionValueMethodProps) => { + const [open, setOpen] = useState(false) + + return ( + + setOpen(v => !v)}> + + + +
+ { + options.map(option => ( +
{ + onValueMethodChange(option) + setOpen(false) + }} + > + {capitalize(option)} +
+ )) + } +
+
+
+ ) +} + +export default ConditionValueMethod diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx new file mode 100644 index 0000000000..6d0528d3d3 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx @@ -0,0 +1,67 @@ +import { useState } from 'react' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import VariableTag from '@/app/components/workflow/nodes/_base/components/variable-tag' +import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' +import type { + Node, + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' +import { VarType } from '@/app/components/workflow/types' + +type ConditionVariableSelectorProps = { + valueSelector?: ValueSelector + varType?: VarType + availableNodes?: Node[] + nodesOutputVars?: NodeOutPutVar[] + onChange: (valueSelector: ValueSelector, varItem: Var) => void +} + +const ConditionVariableSelector = ({ + valueSelector = [], + varType = VarType.string, + availableNodes = [], + nodesOutputVars = [], + onChange, +}: ConditionVariableSelectorProps) => { + const [open, setOpen] = useState(false) + + return ( + + setOpen(!open)}> +
+ +
+
+ +
+ +
+
+
+ ) +} + +export default ConditionVariableSelector diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/hooks.ts b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/hooks.ts new file mode 100644 index 0000000000..18a66c4d5a --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/hooks.ts @@ -0,0 +1,11 @@ +import { useCallback } from 'react' + +export const useCondition = () => { + const getConditionVariableType = useCallback((name: string) => { + return name + }, []) + + return { + getConditionVariableType, + } +} diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx new file mode 100644 index 0000000000..fe7777c82c --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx @@ -0,0 +1,71 @@ +import { RiLoopLeftLine } from '@remixicon/react' +import { useMemo } from 'react' +import ConditionItem from './condition-item' +import type { + Node, + NodeOutPutVar, +} from '@/app/components/workflow/types' +import cn from '@/utils/classnames' +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { LogicalOperator } from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +type ConditionListProps = { + disabled?: boolean + nodesOutputVars?: NodeOutPutVar[] + availableNodes?: Node[] +} & Omit +const ConditionList = ({ + disabled, + metadataFilteringConditions, + handleRemoveCondition, + handleToggleConditionLogicalOperator, + handleUpdateCondition, + nodesOutputVars = [], + availableNodes = [], +}: ConditionListProps) => { + const { conditions, logical_operator } = metadataFilteringConditions + + const conditionItemClassName = useMemo(() => { + if (conditions.length < 2) + return '' + return logical_operator === LogicalOperator.and ? 'pl-[51px]' : 'pl-[42px]' + }, [conditions.length, logical_operator]) + + return ( +
+ { + conditions.length > 1 && ( +
+
+
+
handleToggleConditionLogicalOperator()} + > + {logical_operator.toUpperCase()} + +
+
+ ) + } + { + conditions.map((condition, index) => ( + + )) + } +
+ ) +} + +export default ConditionList diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts new file mode 100644 index 0000000000..fbd3947c79 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts @@ -0,0 +1,61 @@ +import { + ComparisonOperator, + MetadataFilteringVariableType, +} from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +export const isEmptyRelatedOperator = (operator: ComparisonOperator) => { + return [ComparisonOperator.empty, ComparisonOperator.notEmpty, ComparisonOperator.isNull, ComparisonOperator.isNotNull, ComparisonOperator.exists, ComparisonOperator.notExists].includes(operator) +} + +const notTranslateKey = [ + ComparisonOperator.equal, ComparisonOperator.notEqual, + ComparisonOperator.largerThan, ComparisonOperator.largerThanOrEqual, + ComparisonOperator.lessThan, ComparisonOperator.lessThanOrEqual, +] + +export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => { + if (!operator) + return false + return !notTranslateKey.includes(operator) +} + +export const getOperators = (type?: MetadataFilteringVariableType) => { + switch (type) { + case MetadataFilteringVariableType.string: + return [ + ComparisonOperator.contains, + ComparisonOperator.notContains, + ComparisonOperator.startWith, + ComparisonOperator.endWith, + ComparisonOperator.is, + ComparisonOperator.isNot, + ComparisonOperator.empty, + ComparisonOperator.notEmpty, + ] + case MetadataFilteringVariableType.number: + return [ + ComparisonOperator.equal, + ComparisonOperator.notEqual, + ComparisonOperator.largerThan, + ComparisonOperator.lessThan, + ComparisonOperator.largerThanOrEqual, + ComparisonOperator.lessThanOrEqual, + ComparisonOperator.empty, + ComparisonOperator.notEmpty, + ] + default: + return [ + ComparisonOperator.is, + ComparisonOperator.isNot, + ComparisonOperator.empty, + ComparisonOperator.notEmpty, + ] + } +} + +export const comparisonOperatorNotRequireValue = (operator?: ComparisonOperator) => { + if (!operator) + return false + + return [ComparisonOperator.empty, ComparisonOperator.notEmpty, ComparisonOperator.isNull, ComparisonOperator.isNotNull, ComparisonOperator.exists, ComparisonOperator.notExists].includes(operator) +} diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx new file mode 100644 index 0000000000..96dadd57ce --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx @@ -0,0 +1,60 @@ +import { useState } from 'react' +import MetadataTrigger from '../metadata-trigger' +import MetadataFilterSelector from './metadata-filter-selector' +import Collapse from '@/app/components/workflow/nodes/_base/components/collapse' +import Tooltip from '@/app/components/base/tooltip' +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +type MetadataFilterProps = { + metadataFilterMode: MetadataFilteringModeEnum + handleMetadataFilterModeChange: (mode: MetadataFilteringModeEnum) => void +} & MetadataShape +const MetadataFilter = ({ + metadataFilterMode, + handleMetadataFilterModeChange, + ...restProps +}: MetadataFilterProps) => { + const [collapsed, setCollapsed] = useState(true) + + return ( + +
+
+ metadata filtering +
+ +
+
+ + { + metadataFilterMode === MetadataFilteringModeEnum.manual && ( + + ) + } +
+ + } + > + <> + { + metadataFilterMode === MetadataFilteringModeEnum.automatic && ( +
+ Automatically generate metadata filtering conditions based on Query Variable +
+ ) + } + +
+ ) +} + +export default MetadataFilter diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx new file mode 100644 index 0000000000..691ae25fa0 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx @@ -0,0 +1,98 @@ +import { useState } from 'react' +import { + RiArrowDownSLine, + RiCheckLine, +} from '@remixicon/react' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import Button from '@/app/components/base/button' +import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +type MetadataFilterSelectorProps = { + value: MetadataFilteringModeEnum + onSelect: (value: MetadataFilteringModeEnum) => void +} +const MetadataFilterSelector = ({ + value, + onSelect, +}: MetadataFilterSelectorProps) => { + const [open, setOpen] = useState(false) + const options = [ + { + key: MetadataFilteringModeEnum.disabled, + value: 'Disabled', + desc: 'Not enabling metadata filtering', + }, + { + key: MetadataFilteringModeEnum.automatic, + value: 'Automatic', + desc: 'Automatically generate metadata filtering conditions based on user query', + }, + { + key: MetadataFilteringModeEnum.manual, + value: 'Manual', + desc: 'Manually add metadata filtering conditions', + }, + ] + + const selectedOption = options.find(option => option.key === value)! + + return ( + + setOpen(!open)}> + + + +
+ { + options.map(option => ( +
{ + onSelect(option.key) + setOpen(false) + }} + > +
+ { + option.key === value && ( + + ) + } +
+
+
+ {option.value} +
+
+ {option.desc} +
+
+
+ )) + } +
+
+
+ ) +} + +export default MetadataFilterSelector diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx new file mode 100644 index 0000000000..a5858050db --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx @@ -0,0 +1,41 @@ +import { RiCloseLine } from '@remixicon/react' +import AddCondition from './add-condition' +import ConditionList from './condition-list' +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +type MetadataPanelProps = { + onCancel: () => void +} & MetadataShape +const MetadataPanel = ({ + metadataFilteringConditions, + onCancel, + handleAddCondition, + ...restProps +}: MetadataPanelProps) => { + return ( +
+
+
+ Metadata Filter Conditions +
+
+ +
+
+
+
+ + +
+
+
+ ) +} + +export default MetadataPanel diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx new file mode 100644 index 0000000000..dd1a3dac73 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx @@ -0,0 +1,48 @@ +import { useState } from 'react' +import { RiFilter3Line } from '@remixicon/react' +import MetadataPanel from './metadata-panel' +import Button from '@/app/components/base/button' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' + +const MetadataTrigger = ({ + metadataFilteringConditions, + ...restProps +}: MetadataShape) => { + const [open, setOpen] = useState(false) + + return ( + + setOpen(!open)}> + + + + setOpen(false)} + {...restProps} + /> + + + ) +} + +export default MetadataTrigger diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/types.ts b/web/app/components/workflow/nodes/knowledge-retrieval/types.ts index 1b85bfc0b5..ab705621dd 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/types.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/types.ts @@ -30,6 +30,58 @@ export type SingleRetrievalConfig = { model: ModelConfig } +export enum LogicalOperator { + and = 'and', + or = 'or', +} + +export enum ComparisonOperator { + contains = 'contains', + notContains = 'not contains', + startWith = 'start with', + endWith = 'end with', + is = 'is', + isNot = 'is not', + empty = 'empty', + notEmpty = 'not empty', + equal = '=', + notEqual = '≠', + largerThan = '>', + lessThan = '<', + largerThanOrEqual = '≥', + lessThanOrEqual = '≤', + isNull = 'is null', + isNotNull = 'is not null', + in = 'in', + notIn = 'not in', + allOf = 'all of', + exists = 'exists', + notExists = 'not exists', +} + +export enum MetadataFilteringModeEnum { + disabled = 'disabled', + automatic = 'automatic', + manual = 'manual', +} + +export enum MetadataFilteringVariableType { + string = 'string', + number = 'number', + date = 'date', +} + +export type MetadataFilteringCondition = { + name: string + comparison_operator: ComparisonOperator + value?: string +} + +export type MetadataFilteringConditions = { + logical_operator: LogicalOperator + conditions: MetadataFilteringCondition[] +} + export type KnowledgeRetrievalNodeType = CommonNodeType & { query_variable_selector: ValueSelector dataset_ids: string[] @@ -37,4 +89,19 @@ export type KnowledgeRetrievalNodeType = CommonNodeType & { multiple_retrieval_config?: MultipleRetrievalConfig single_retrieval_config?: SingleRetrievalConfig _datasets?: DataSet[] + metadata_filtering_mode?: MetadataFilteringModeEnum + metadata_filtering_conditions?: MetadataFilteringConditions +} + +export type HandleAddCondition = (name: string) => void +export type HandleRemoveCondition = (index: number) => void +export type HandleUpdateCondition = (index: number, newCondition: MetadataFilteringCondition) => void +export type HandleToggleConditionLogicalOperator = () => void + +export type MetadataShape = { + metadataFilteringConditions: MetadataFilteringConditions + handleAddCondition: HandleAddCondition + handleRemoveCondition: HandleRemoveCondition + handleToggleConditionLogicalOperator: HandleToggleConditionLogicalOperator + handleUpdateCondition: HandleUpdateCondition } diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts b/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts index 6b09c611f8..ee79f84950 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts @@ -9,10 +9,20 @@ import { isEqual } from 'lodash-es' import type { ValueSelector, Var } from '../../types' import { BlockEnum, VarType } from '../../types' import { - useIsChatMode, useNodesReadOnly, + useIsChatMode, + useNodesReadOnly, useWorkflow, } from '../../hooks' -import type { KnowledgeRetrievalNodeType, MultipleRetrievalConfig } from './types' +import type { + HandleAddCondition, + HandleRemoveCondition, + HandleToggleConditionLogicalOperator, + HandleUpdateCondition, + KnowledgeRetrievalNodeType, + MetadataFilteringModeEnum, + MultipleRetrievalConfig, +} from './types' +import { ComparisonOperator, LogicalOperator } from './types' import { getMultipleRetrievalConfig, getSelectedDatasetsMode, @@ -202,7 +212,7 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => { const inputs = inputRef.current const datasetIds = inputs.dataset_ids if (datasetIds?.length > 0) { - const { data: dataSetsWithDetail } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: datasetIds } }) + const { data: dataSetsWithDetail } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: datasetIds } as any }) setSelectedDatasets(dataSetsWithDetail) } const newInputs = produce(inputs, (draft) => { @@ -287,6 +297,43 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => { }) }, [runInputData, setRunInputData]) + const handleMetadataFilterModeChange = useCallback((newMode: MetadataFilteringModeEnum) => { + setInputs(produce(inputRef.current, (draft) => { + draft.metadata_filtering_mode = newMode + })) + }, [setInputs]) + + const handleAddCondition = useCallback((name) => { + const newInputs = produce(inputRef.current, (draft) => { + draft.metadata_filtering_conditions?.conditions.push({ + name, + comparison_operator: ComparisonOperator.is, + }) + }) + setInputs(newInputs) + }, [setInputs]) + + const handleRemoveCondition = useCallback((index) => { + const newInputs = produce(inputRef.current, (draft) => { + draft.metadata_filtering_conditions?.conditions.splice(index, 1) + }) + setInputs(newInputs) + }, [setInputs]) + + const handleUpdateCondition = useCallback((index, newCondition) => { + const newInputs = produce(inputRef.current, (draft) => { + draft.metadata_filtering_conditions!.conditions[index] = newCondition + }) + setInputs(newInputs) + }, [setInputs]) + + const handleToggleConditionLogicalOperator = useCallback(() => { + const newInputs = produce(inputRef.current, (draft) => { + draft.metadata_filtering_conditions!.logical_operator = LogicalOperator.and + }) + setInputs(newInputs) + }, [setInputs]) + return { readOnly, inputs, @@ -308,6 +355,11 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => { runResult, rerankModelOpen, setRerankModelOpen, + handleMetadataFilterModeChange, + handleUpdateCondition, + handleAddCondition, + handleRemoveCondition, + handleToggleConditionLogicalOperator, } }