diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py index 4be006de11..07a47f2d32 100644 --- a/api/core/workflow/nodes/agent/agent_node.py +++ b/api/core/workflow/nodes/agent/agent_node.py @@ -1,6 +1,6 @@ import json from collections.abc import Generator, Mapping, Sequence -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, Literal, cast from packaging.version import Version from pydantic import ValidationError @@ -42,6 +42,8 @@ from core.workflow.nodes.agent.entities import AgentNodeData, AgentOldVersionMod from core.workflow.nodes.base.node import Node from core.workflow.nodes.base.variable_template_parser import VariableTemplateParser from core.workflow.runtime import VariablePool +from core.workflow.utils.condition.entities import Condition +from core.workflow.utils.condition.processor import ConditionProcessor from extensions.ext_database import db from factories import file_factory from factories.agent_factory import get_plugin_agent_strategy @@ -182,6 +184,7 @@ class AgentNode(Node[AgentNodeData]): """ agent_parameters_dictionary = {parameter.name: parameter for parameter in agent_parameters} + condition_processor = ConditionProcessor() result: dict[str, Any] = {} for parameter_name in node_data.agent_parameters: @@ -219,7 +222,32 @@ class AgentNode(Node[AgentNodeData]): value = parameter_value if parameter.type == "array[tools]": value = cast(list[dict[str, Any]], value) - value = [tool for tool in value if tool.get("enabled", False)] + filtered_tools: list[dict[str, Any]] = [] + for tool in value: + activation_condition = tool.get("activation_condition") + include_tool = True + if activation_condition and activation_condition.get("enabled"): + logical_operator = activation_condition.get("logical_operator", "and") + if logical_operator not in {"and", "or"}: + logical_operator = "and" + try: + conditions_raw = activation_condition.get("conditions", []) or [] + conditions = [Condition.model_validate(condition) for condition in conditions_raw] + if conditions: + _, _, include_tool = condition_processor.process_conditions( + variable_pool=variable_pool, + conditions=conditions, + operator=cast(Literal["and", "or"], logical_operator), + ) + else: + include_tool = False + except (ValidationError, ValueError): + include_tool = False + tool.pop("activation_condition", None) + if include_tool: + filtered_tools.append(tool) + value = [tool for tool in filtered_tools if tool.get("enabled", False)] + value = self._filter_mcp_type_tool(strategy, value) for tool in value: if "schemas" in tool: diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx index 62c6fa3cd3..8f8df87e66 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx @@ -30,6 +30,7 @@ import ToolTrigger from '@/app/components/plugins/plugin-detail-panel/tool-selec import { CollectionType } from '@/app/components/tools/types' import { generateFormValue, getPlainValue, getStructureValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import ToolPicker from '@/app/components/workflow/block-selector/tool-picker' +import { AgentToolConditionEditor } from '@/app/components/workflow/nodes/agent/components/tool-condition' import ToolForm from '@/app/components/workflow/nodes/tool/components/tool-form' import { MARKETPLACE_API_PREFIX } from '@/config' import { useInvalidateInstalledPluginList } from '@/service/use-plugins' @@ -131,6 +132,7 @@ const ToolSelector: FC = ({ description: tool.tool_description, }, schemas: tool.paramSchemas, + activation_condition: value?.activation_condition, } } const handleSelectTool = (tool: ToolDefaultValue) => { @@ -153,6 +155,15 @@ const ToolSelector: FC = ({ } as any) } + const handleActivationConditionChange = (condition?: ToolValue['activation_condition']) => { + if (!value) + return + onSelect({ + ...value, + activation_condition: condition, + } as any) + } + // tool settings & params const currentToolSettings = useMemo(() => { if (!currentProvider) @@ -272,7 +283,7 @@ const ToolSelector: FC = ({ )} -
+
<>
{t(`detailPanel.toolSelector.${isEdit ? 'toolSetting' : 'title'}`, { ns: 'plugin' })}
{/* base form */} @@ -401,6 +412,20 @@ const ToolSelector: FC = ({ )} )} + {value?.provider_name && nodeId && ( + <> + +
+ +
+ + )}
diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts index 07efb0d02f..127cc149e9 100644 --- a/web/app/components/workflow/block-selector/types.ts +++ b/web/app/components/workflow/block-selector/types.ts @@ -1,6 +1,7 @@ -import type { ParametersSchema, PluginMeta, PluginTriggerSubscriptionConstructor, SupportedCreationMethods, TriggerEvent } from '../../plugins/types' +import type { ParametersSchema, PluginMeta, PluginTriggerSubscriptionConstructor, SupportedCreationMethods, TriggerEvent, PluginMeta as WorkflowPluginMeta } from '../../plugins/types' import type { Collection, Event } from '../../tools/types' import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { AgentToolActivationCondition } from '@/app/components/workflow/nodes/agent/types' export enum TabsEnum { Start = 'start', @@ -56,7 +57,7 @@ export type ToolDefaultValue = PluginCommonDefaultValue & { paramSchemas: Record[] output_schema?: Record credential_id?: string - meta?: PluginMeta + meta?: WorkflowPluginMeta plugin_id?: string provider_icon?: Collection['icon'] provider_icon_dark?: Collection['icon'] @@ -87,6 +88,7 @@ export type ToolValue = { enabled?: boolean extra?: { description?: string } & Record credential_id?: string + activation_condition?: AgentToolActivationCondition } export type DataSourceItem = { diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/condition-add.tsx b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-add.tsx new file mode 100644 index 0000000000..546602423c --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-add.tsx @@ -0,0 +1,68 @@ +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { RiAddLine } from '@remixicon/react' +import type { + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' +import Button from '@/app/components/base/button' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' + +type Props = { + variables: NodeOutPutVar[] + onSelect: (valueSelector: ValueSelector, varItem: Var) => void + disabled?: boolean +} + +const ConditionAdd = ({ + variables, + onSelect, + disabled, +}: Props) => { + const { t } = useTranslation('workflow') + const [open, setOpen] = useState(false) + + const handleSelect = useCallback((valueSelector: ValueSelector, varItem: Var) => { + onSelect(valueSelector, varItem) + setOpen(false) + }, [onSelect]) + + return ( + + !disabled && setOpen(!open)}> + + + +
+ +
+
+
+ ) +} + +export default ConditionAdd diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/condition-input.tsx b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-input.tsx new file mode 100644 index 0000000000..4f9d7fc929 --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-input.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from 'react-i18next' +import { useStore } from '@/app/components/workflow/store' +import PromptEditor from '@/app/components/base/prompt-editor' +import { BlockEnum } from '@/app/components/workflow/types' +import type { + Node, +} from '@/app/components/workflow/types' + +type ConditionInputProps = { + disabled?: boolean + value: string + onChange: (value: string) => void + availableNodes: Node[] +} +const ConditionInput = ({ + value, + onChange, + disabled, + availableNodes, +}: ConditionInputProps) => { + const { t } = useTranslation('workflow') + const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey) + const pipelineId = useStore(s => s.pipelineId) + const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel) + + return ( + { + acc[node.id] = { + title: node.data.title, + type: node.data.type, + } + if (node.data.type === BlockEnum.Start) { + acc.sys = { + title: t('blocks.start'), + type: BlockEnum.Start, + } + } + return acc + }, {} as any), + showManageInputField: !!pipelineId, + onManageInputField: () => setShowInputFieldPanel?.(true), + }} + onChange={onChange} + editable={!disabled} + /> + ) +} + +export default ConditionInput diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/condition-item.tsx b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-item.tsx new file mode 100644 index 0000000000..1241715337 --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-item.tsx @@ -0,0 +1,193 @@ +import { memo, useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { RiDeleteBinLine } from '@remixicon/react' +import { produce } from 'immer' +import ConditionVarSelector from './condition-var-selector' +import ConditionOperator from './condition-operator' +import { + getConditionOperators, + getDefaultValueByType, + operatorNeedsValue, +} from '../../utils' +import type { + AgentToolCondition, +} from '../../types' +import type { + Node, + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' +import { VarType } from '@/app/components/workflow/types' +import Input from '@/app/components/base/input' +import ConditionInput from './condition-input' +import { cn } from '@/utils/classnames' +import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/components/bool-value' + +type Props = { + className?: string + condition: AgentToolCondition + availableVars: NodeOutPutVar[] + availableNodes: Node[] + disabled?: boolean + onChange: (condition: AgentToolCondition) => void + onRemove: () => void +} + +const ConditionItem = ({ + className, + condition, + availableVars, + availableNodes, + disabled, + onChange, + onRemove, +}: Props) => { + const { t } = useTranslation('workflow') + const [open, setOpen] = useState(false) + + const needsValue = operatorNeedsValue(condition.comparison_operator) + + const handleSelectVar = useCallback((valueSelector: ValueSelector, varItem: Var) => { + const operators = getConditionOperators(varItem.type) + const defaultOperator = operators[0] + const nextCondition = produce(condition, (draft) => { + draft.variable_selector = valueSelector + draft.varType = varItem.type + draft.comparison_operator = defaultOperator + draft.value = operatorNeedsValue(defaultOperator) ? getDefaultValueByType(varItem.type) : undefined + }) + onChange(nextCondition) + }, [condition, onChange]) + + const handleOperatorChange = useCallback((operator: string) => { + const nextCondition = produce(condition, (draft) => { + draft.comparison_operator = operator + if (operatorNeedsValue(operator)) + draft.value = draft.varType ? getDefaultValueByType(draft.varType) : '' + else + draft.value = undefined + }) + onChange(nextCondition) + }, [condition, onChange]) + + const handleValueChange = useCallback((value: string | boolean) => { + const nextCondition = produce(condition, (draft) => { + draft.value = value + }) + onChange(nextCondition) + }, [condition, onChange]) + + const handleTextValueChange = useCallback((value: string) => { + handleValueChange(value) + }, [handleValueChange]) + + const handleBooleanValueChange = useCallback((value: boolean) => { + if (disabled) + return + handleValueChange(value) + }, [disabled, handleValueChange]) + + const renderValueInput = () => { + if (!needsValue) + return
{t('nodes.agent.toolCondition.noValueNeeded')}
+ + if (condition.varType === VarType.boolean) { + if (typeof condition.value === 'string' && condition.value !== 'true' && condition.value !== 'false') { + return ( + + ) + } + + const booleanValue = (() => { + if (typeof condition.value === 'boolean') + return condition.value + if (condition.value === 'false') + return false + if (condition.value === 'true') + return true + return true + })() + + return ( +
+ +
+ ) + } + + const normalizedValue = typeof condition.value === 'string' || typeof condition.value === 'number' + ? String(condition.value) + : '' + + if (condition.varType === VarType.number) { + return ( + handleTextValueChange(event.target.value)} + disabled={disabled} + /> + ) + } + + const textValue = typeof condition.value === 'string' ? condition.value : '' + + return ( + + ) + } + + return ( +
+
+
+
+ +
+
+ +
+
+ {renderValueInput()} +
+
+ +
+ ) +} + +export default memo(ConditionItem) diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/condition-list.tsx b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-list.tsx new file mode 100644 index 0000000000..424597729e --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-list.tsx @@ -0,0 +1,72 @@ +import { RiLoopLeftLine } from '@remixicon/react' +import { memo, useMemo } from 'react' +import ConditionItem from './condition-item' +import type { + AgentToolCondition, + AgentToolConditionLogicalOperator, +} from '../../types' +import type { + Node, + NodeOutPutVar, +} from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' + +type Props = { + conditions: AgentToolCondition[] + logicalOperator: AgentToolConditionLogicalOperator + availableVars: NodeOutPutVar[] + availableNodes: Node[] + disabled?: boolean + onChange: (condition: AgentToolCondition) => void + onRemove: (conditionId: string) => void + onToggleLogicalOperator: () => void +} + +const ConditionList = ({ + conditions, + logicalOperator, + availableVars, + availableNodes, + disabled, + onChange, + onRemove, + onToggleLogicalOperator, +}: Props) => { + const hasMultiple = conditions.length > 1 + + const containerClassName = useMemo(() => cn('relative', hasMultiple && 'pl-[60px]'), [hasMultiple]) + + return ( +
+ {hasMultiple && ( +
+
+
+ +
+ )} + {conditions.map(condition => ( + onRemove(condition.id)} + /> + ))} +
+ ) +} + +export default memo(ConditionList) diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/condition-operator.tsx b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-operator.tsx new file mode 100644 index 0000000000..415ab35ef4 --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-operator.tsx @@ -0,0 +1,93 @@ +import { + useMemo, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import { RiArrowDownSLine } from '@remixicon/react' +import type { VarType } from '@/app/components/workflow/types' +import { getConditionOperators } 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' + +type Props = { + varType?: VarType + value?: string + onSelect: (operator: string) => void + disabled?: boolean +} + +const ConditionOperator = ({ + varType, + value, + onSelect, + disabled, +}: Props) => { + const { t } = useTranslation('workflow') + const [open, setOpen] = useState(false) + const placeholder = t('nodes.agent.toolCondition.operatorPlaceholder') as string + + const options = useMemo(() => { + return getConditionOperators(varType).map((option) => { + const key = `nodes.ifElse.comparisonOperator.${option}` + const translated = t(key as any) as string + return { + value: option, + label: translated === key ? option : translated, + } + }) + }, [t, varType]) + + const selectedOption = options.find(option => option.value === value) + + return ( + + { + if (!disabled) + setOpen(v => !v) + }} + > + + + +
+ {options.map(option => ( +
{ + onSelect(option.value) + setOpen(false) + }} + > + {option.label} +
+ ))} +
+
+
+ ) +} + +export default ConditionOperator diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/condition-var-selector.tsx b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-var-selector.tsx new file mode 100644 index 0000000000..412902c9ab --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/condition-var-selector.tsx @@ -0,0 +1,95 @@ +import { memo, useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' +import VariableTag from '@/app/components/workflow/nodes/_base/components/variable-tag' +import type { + Node, + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' +import { VarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' + +type Props = { + open: boolean + onOpenChange: (open: boolean) => void + valueSelector?: ValueSelector + varType?: VarType + availableVars: NodeOutPutVar[] + availableNodes: Node[] + onSelect: (valueSelector: ValueSelector, varItem: Var) => void + disabled?: boolean +} + +const ConditionVarSelector = ({ + open, + onOpenChange, + valueSelector, + varType, + availableVars, + availableNodes, + onSelect, + disabled, +}: Props) => { + const { t } = useTranslation('workflow') + + const handleTriggerClick = useCallback(() => { + if (disabled) + return + onOpenChange(!open) + }, [disabled, onOpenChange, open]) + + const handleSelect = useCallback((selector: ValueSelector, varItem: Var) => { + if (disabled) + return + onSelect(selector, varItem) + onOpenChange(false) + }, [disabled, onOpenChange, onSelect]) + + return ( + { + if (!disabled) + onOpenChange(state) + }} + placement='bottom-start' + offset={{ + mainAxis: 4, + crossAxis: 0, + }} + > + +
+ {valueSelector && valueSelector.length > 0 ? ( + + ) : ( +
{t('nodes.agent.toolCondition.selectVariable')}
+ )} +
+
+ +
+ +
+
+
+ ) +} + +export default memo(ConditionVarSelector) diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/editor.tsx b/web/app/components/workflow/nodes/agent/components/tool-condition/editor.tsx new file mode 100644 index 0000000000..66c918af33 --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/editor.tsx @@ -0,0 +1,158 @@ +'use client' + +import { memo, useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { v4 as uuid4 } from 'uuid' +import { produce } from 'immer' +import Switch from '@/app/components/base/switch' +import ConditionAdd from './condition-add' +import ConditionList from './condition-list' +import type { + AgentToolActivationCondition, + AgentToolCondition, +} from '../../types' +import { AgentToolConditionLogicalOperator } from '../../types' +import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' +import { VarType } from '@/app/components/workflow/types' +import { + getConditionOperators, + getDefaultValueByType, + operatorNeedsValue, +} from '../../utils' +import { cn } from '@/utils/classnames' + +type Props = { + value?: AgentToolActivationCondition + onChange: (value: AgentToolActivationCondition | undefined) => void + availableVars: NodeOutPutVar[] + availableNodes: Node[] + disabled?: boolean +} + +const AgentToolConditionEditor = ({ + value, + onChange, + availableVars, + availableNodes, + disabled, +}: Props) => { + const { t } = useTranslation('workflow') + + const currentValue = useMemo(() => value ?? ({ + enabled: false, + logical_operator: AgentToolConditionLogicalOperator.And, + conditions: [], + }), [value]) + + const handleToggle = useCallback((state: boolean) => { + const next = produce(currentValue, (draft) => { + draft.enabled = state + }) + onChange(next) + }, [currentValue, onChange]) + + const handleAddCondition = useCallback((valueSelector: ValueSelector, varItem: Var) => { + const operators = getConditionOperators(varItem.type) + const defaultOperator = operators[0] + const newCondition: AgentToolCondition = { + id: uuid4(), + varType: varItem.type ?? VarType.string, + variable_selector: valueSelector, + comparison_operator: defaultOperator, + value: operatorNeedsValue(defaultOperator) ? getDefaultValueByType(varItem.type ?? VarType.string) : undefined, + } + const next = produce(currentValue, (draft) => { + draft.enabled = true + draft.conditions.push(newCondition) + }) + onChange(next) + }, [currentValue, onChange]) + + const handleConditionChange = useCallback((updated: AgentToolCondition) => { + const next = produce(currentValue, (draft) => { + const targetIndex = draft.conditions.findIndex(item => item.id === updated.id) + if (targetIndex !== -1) + draft.conditions[targetIndex] = updated + }) + onChange(next) + }, [currentValue, onChange]) + + const handleRemoveCondition = useCallback((conditionId: string) => { + const next = produce(currentValue, (draft) => { + draft.conditions = draft.conditions.filter(item => item.id !== conditionId) + }) + onChange(next) + }, [currentValue, onChange]) + + const handleToggleLogicalOperator = useCallback(() => { + const next = produce(currentValue, (draft) => { + draft.logical_operator = draft.logical_operator === AgentToolConditionLogicalOperator.And + ? AgentToolConditionLogicalOperator.Or + : AgentToolConditionLogicalOperator.And + }) + onChange(next) + }, [currentValue, onChange]) + + const isEnabled = currentValue.enabled + const hasConditions = currentValue.conditions.length > 0 + + return ( +
+
+
+
{t('nodes.agent.toolCondition.title')}
+
{t('nodes.agent.toolCondition.description')}
+
+ +
+ + {isEnabled && ( +
+
+ {hasConditions && ( +
+ +
+ )} +
1 && 'ml-[60px]', + !hasConditions && 'mt-1', + )}> + +
+
+ {!hasConditions && ( +
+ {t('nodes.agent.toolCondition.addFirstCondition')} +
+ )} + {hasConditions && currentValue.conditions.length <= 1 && ( +
+ {t('nodes.agent.toolCondition.singleConditionTip')} +
+ )} +
+ )} +
+ ) +} + +export default memo(AgentToolConditionEditor) diff --git a/web/app/components/workflow/nodes/agent/components/tool-condition/index.ts b/web/app/components/workflow/nodes/agent/components/tool-condition/index.ts new file mode 100644 index 0000000000..290befe038 --- /dev/null +++ b/web/app/components/workflow/nodes/agent/components/tool-condition/index.ts @@ -0,0 +1 @@ +export { default as AgentToolConditionEditor } from './editor' diff --git a/web/app/components/workflow/nodes/agent/types.ts b/web/app/components/workflow/nodes/agent/types.ts index efc7c0cd9a..a260bdc913 100644 --- a/web/app/components/workflow/nodes/agent/types.ts +++ b/web/app/components/workflow/nodes/agent/types.ts @@ -1,6 +1,6 @@ import type { ToolVarInputs } from '../tool/types' import type { PluginMeta } from '@/app/components/plugins/types' -import type { CommonNodeType, Memory } from '@/app/components/workflow/types' +import type { CommonNodeType, Memory, ValueSelector, VarType } from '@/app/components/workflow/types' export type AgentNodeType = CommonNodeType & { agent_strategy_provider_name?: string @@ -18,3 +18,22 @@ export type AgentNodeType = CommonNodeType & { export enum AgentFeature { HISTORY_MESSAGES = 'history-messages', } + +export enum AgentToolConditionLogicalOperator { + And = 'and', + Or = 'or', +} + +export type AgentToolCondition = { + id: string + varType: VarType + variable_selector?: ValueSelector + comparison_operator?: string + value?: string | string[] | boolean +} + +export type AgentToolActivationCondition = { + enabled: boolean + logical_operator: AgentToolConditionLogicalOperator + conditions: AgentToolCondition[] +} diff --git a/web/app/components/workflow/nodes/agent/use-config.ts b/web/app/components/workflow/nodes/agent/use-config.ts index 49af451f4c..b258fcd9c5 100644 --- a/web/app/components/workflow/nodes/agent/use-config.ts +++ b/web/app/components/workflow/nodes/agent/use-config.ts @@ -163,9 +163,11 @@ const useConfig = (id: string, payload: AgentNodeType) => { VarKindType.array, VarKindType.number, VarKindType.string, + VarKindType.boolean, VarKindType.secret, VarKindType.arrayString, VarKindType.arrayNumber, + VarKindType.arrayBoolean, VarKindType.file, VarKindType.arrayFile, ].includes(varPayload.type) diff --git a/web/app/components/workflow/nodes/agent/utils.ts b/web/app/components/workflow/nodes/agent/utils.ts new file mode 100644 index 0000000000..6945216853 --- /dev/null +++ b/web/app/components/workflow/nodes/agent/utils.ts @@ -0,0 +1,50 @@ +import { VarType } from '@/app/components/workflow/types' + +const COMPARISON_OPERATOR_WITHOUT_VALUE = new Set([ + 'empty', + 'not empty', + 'null', + 'not null', + 'exists', + 'not exists', +]) + +export const getConditionOperators = (varType?: VarType): string[] => { + switch (varType) { + case VarType.number: + case VarType.integer: + return ['=', '≠', '>', '<', '≥', '≤'] + case VarType.boolean: + return ['is', 'is not'] + case VarType.arrayString: + case VarType.arrayNumber: + case VarType.arrayBoolean: + case VarType.array: + case VarType.arrayAny: + return ['contains', 'not contains', 'empty', 'not empty'] + case VarType.arrayFile: + return ['contains', 'not contains', 'empty', 'not empty'] + case VarType.file: + return ['exists', 'not exists'] + case VarType.object: + return ['empty', 'not empty'] + case VarType.any: + return ['is', 'is not', 'empty', 'not empty'] + default: + return ['contains', 'not contains', 'start with', 'end with', 'is', 'is not', 'empty', 'not empty'] + } +} + +export const operatorNeedsValue = (operator?: string): boolean => { + if (!operator) + return false + + return !COMPARISON_OPERATOR_WITHOUT_VALUE.has(operator) +} + +export const getDefaultValueByType = (varType: VarType): string | boolean => { + if (varType === VarType.boolean) + return true + + return '' +} diff --git a/web/app/components/workflow/nodes/trigger-schedule/utils/cron-parser.ts b/web/app/components/workflow/nodes/trigger-schedule/utils/cron-parser.ts index 90f65db0aa..bf7512d6ed 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/utils/cron-parser.ts +++ b/web/app/components/workflow/nodes/trigger-schedule/utils/cron-parser.ts @@ -1,5 +1,10 @@ import { CronExpressionParser } from 'cron-parser' +// Minimal representation of the CronDate object returned by cron-parser. +type CronDateLike = { + toDate: () => Date +} + // Convert a UTC date from cron-parser to user timezone representation // This ensures consistency with other execution time calculations const convertToUserTimezoneRepresentation = (utcDate: Date, timezone: string): Date => { @@ -42,7 +47,7 @@ export const parseCronExpression = (cronExpression: string, timezone: string = ' }) // Get the next 5 execution times using the take() method - const nextCronDates = interval.take(5) + const nextCronDates: CronDateLike[] = interval.take(5) // Convert CronDate objects to Date objects and ensure they represent // the time in user timezone (consistent with execution-time-calculator.ts) diff --git a/web/i18n/ar-TN/billing.json b/web/i18n/ar-TN/billing.json index ab57a9576b..a67f8216a3 100644 --- a/web/i18n/ar-TN/billing.json +++ b/web/i18n/ar-TN/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "ميزات مجانية:", "plans.community.name": "مجتمع", "plans.community.price": "مجاني", + "plans.community.priceTip": "", "plans.enterprise.btnText": "اتصل بالمبيعات", "plans.enterprise.description": "للمؤسسات التي تتطلب أمانًا وامتثالًا وقابلية للتوسع وتحكمًا وحلولًا مخصصة على مستوى المؤسسة", "plans.enterprise.features": [ diff --git a/web/i18n/de-DE/billing.json b/web/i18n/de-DE/billing.json index 37c18a6d85..095b28c9de 100644 --- a/web/i18n/de-DE/billing.json +++ b/web/i18n/de-DE/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Kostenlose Funktionen:", "plans.community.name": "Gemeinschaft", "plans.community.price": "Kostenlos", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Vertrieb kontaktieren", "plans.enterprise.description": "Erhalten Sie volle Fähigkeiten und Unterstützung für großangelegte, missionskritische Systeme.", "plans.enterprise.features": [ diff --git a/web/i18n/de-DE/dataset-documents.json b/web/i18n/de-DE/dataset-documents.json index 89d64eff9c..f6f6d6de7c 100644 --- a/web/i18n/de-DE/dataset-documents.json +++ b/web/i18n/de-DE/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Norwegisch", "metadata.languageMap.pl": "Polnisch", "metadata.languageMap.pt": "Portugiesisch", + "metadata.languageMap.ro": "Rumänisch", "metadata.languageMap.ru": "Russisch", "metadata.languageMap.sv": "Schwedisch", "metadata.languageMap.th": "Thai", diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json index 107dad5b28..df5aef8544 100644 --- a/web/i18n/en-US/workflow.json +++ b/web/i18n/en-US/workflow.json @@ -363,6 +363,15 @@ "nodes.agent.strategyNotFoundDescAndSwitchVersion": "The installed plugin version does not provide this strategy. Click to switch version.", "nodes.agent.strategyNotInstallTooltip": "{{strategy}} is not installed", "nodes.agent.strategyNotSet": "Agentic strategy Not Set", + "nodes.agent.toolCondition.addCondition": "Add Condition", + "nodes.agent.toolCondition.addFirstCondition": "No conditions yet. Add your first condition.", + "nodes.agent.toolCondition.description": "When enabled, load this tool only when the conditions are met.", + "nodes.agent.toolCondition.logicalOperator": "Logical operator: {{value}}", + "nodes.agent.toolCondition.noValueNeeded": "No value is required for this operator.", + "nodes.agent.toolCondition.operatorPlaceholder": "Select operator", + "nodes.agent.toolCondition.selectVariable": "Select variable...", + "nodes.agent.toolCondition.singleConditionTip": "Add at least one condition to start.", + "nodes.agent.toolCondition.title": "Activation Condition", "nodes.agent.toolNotAuthorizedTooltip": "{{tool}} Not Authorized", "nodes.agent.toolNotInstallTooltip": "{{tool}} is not installed", "nodes.agent.toolbox": "toolbox", diff --git a/web/i18n/es-ES/billing.json b/web/i18n/es-ES/billing.json index 4b75e60a4a..ef4294ad8e 100644 --- a/web/i18n/es-ES/billing.json +++ b/web/i18n/es-ES/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Características gratuitas:", "plans.community.name": "Comunidad", "plans.community.price": "Gratis", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Contactar ventas", "plans.enterprise.description": "Obtén capacidades completas y soporte para sistemas críticos a gran escala.", "plans.enterprise.features": [ diff --git a/web/i18n/es-ES/dataset-documents.json b/web/i18n/es-ES/dataset-documents.json index d57c2a8caf..3ab3f6c6b6 100644 --- a/web/i18n/es-ES/dataset-documents.json +++ b/web/i18n/es-ES/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Noruego", "metadata.languageMap.pl": "Polaco", "metadata.languageMap.pt": "Portugués", + "metadata.languageMap.ro": "Rumano", "metadata.languageMap.ru": "Ruso", "metadata.languageMap.sv": "Sueco", "metadata.languageMap.th": "Tailandés", diff --git a/web/i18n/fa-IR/billing.json b/web/i18n/fa-IR/billing.json index 47b75b810b..ba666bbe09 100644 --- a/web/i18n/fa-IR/billing.json +++ b/web/i18n/fa-IR/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "ویژگی‌های رایگان:", "plans.community.name": "جامعه", "plans.community.price": "رایگان", + "plans.community.priceTip": "", "plans.enterprise.btnText": "تماس با فروش", "plans.enterprise.description": "دریافت کامل‌ترین قابلیت‌ها و پشتیبانی برای سیستم‌های بزرگ و بحرانی.", "plans.enterprise.features": [ diff --git a/web/i18n/fr-FR/billing.json b/web/i18n/fr-FR/billing.json index c789118f0e..6d5b53afa9 100644 --- a/web/i18n/fr-FR/billing.json +++ b/web/i18n/fr-FR/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Fonctionnalités gratuites :", "plans.community.name": "Communauté", "plans.community.price": "Gratuit", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Contacter les ventes", "plans.enterprise.description": "Obtenez toutes les capacités et le support pour les systèmes à grande échelle et critiques pour la mission.", "plans.enterprise.features": [ diff --git a/web/i18n/fr-FR/dataset-documents.json b/web/i18n/fr-FR/dataset-documents.json index 7b5ffd40c0..b333e156b1 100644 --- a/web/i18n/fr-FR/dataset-documents.json +++ b/web/i18n/fr-FR/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Norvégien", "metadata.languageMap.pl": "Polonais", "metadata.languageMap.pt": "Portugais", + "metadata.languageMap.ro": "Roumain", "metadata.languageMap.ru": "Russe", "metadata.languageMap.sv": "Suédois", "metadata.languageMap.th": "Thaï", diff --git a/web/i18n/hi-IN/billing.json b/web/i18n/hi-IN/billing.json index e490d8c33f..87843936c4 100644 --- a/web/i18n/hi-IN/billing.json +++ b/web/i18n/hi-IN/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "निःशुल्क सुविधाएँ:", "plans.community.name": "समुदाय", "plans.community.price": "मुक्त", + "plans.community.priceTip": "", "plans.enterprise.btnText": "बिक्री से संपर्क करें", "plans.enterprise.description": "बड़े पैमाने पर मिशन-क्रिटिकल सिस्टम के लिए पूर्ण क्षमताएं और समर्थन प्राप्त करें।", "plans.enterprise.features": [ diff --git a/web/i18n/id-ID/billing.json b/web/i18n/id-ID/billing.json index c29fad31d5..26bdeccdba 100644 --- a/web/i18n/id-ID/billing.json +++ b/web/i18n/id-ID/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Fitur Gratis:", "plans.community.name": "Masyarakat", "plans.community.price": "Bebas", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Hubungi Sales", "plans.enterprise.description": "Untuk perusahaan, memerlukan keamanan, kepatuhan, skalabilitas, kontrol, dan fitur yang lebih canggih di seluruh organisasi", "plans.enterprise.features": [ diff --git a/web/i18n/it-IT/billing.json b/web/i18n/it-IT/billing.json index 695cc2176e..b1192cc0e7 100644 --- a/web/i18n/it-IT/billing.json +++ b/web/i18n/it-IT/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Caratteristiche Gratuite:", "plans.community.name": "Comunità", "plans.community.price": "Gratuito", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Contatta le vendite", "plans.enterprise.description": "Ottieni tutte le capacità e il supporto per sistemi mission-critical su larga scala.", "plans.enterprise.features": [ diff --git a/web/i18n/it-IT/dataset-documents.json b/web/i18n/it-IT/dataset-documents.json index 139e5a5307..700e5a5254 100644 --- a/web/i18n/it-IT/dataset-documents.json +++ b/web/i18n/it-IT/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Norvegese", "metadata.languageMap.pl": "Polacco", "metadata.languageMap.pt": "Portoghese", + "metadata.languageMap.ro": "Rumeno", "metadata.languageMap.ru": "Russo", "metadata.languageMap.sv": "Svedese", "metadata.languageMap.th": "Thailandese", diff --git a/web/i18n/ja-JP/billing.json b/web/i18n/ja-JP/billing.json index 81bf41e5dd..344e934948 100644 --- a/web/i18n/ja-JP/billing.json +++ b/web/i18n/ja-JP/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "無料機能:", "plans.community.name": "コミュニティ", "plans.community.price": "無料", + "plans.community.priceTip": "", "plans.enterprise.btnText": "営業に相談", "plans.enterprise.description": "企業レベルのセキュリティとカスタマイズを実現", "plans.enterprise.features": [ diff --git a/web/i18n/ja-JP/dataset-documents.json b/web/i18n/ja-JP/dataset-documents.json index 9db8008dc4..9fa6f6da68 100644 --- a/web/i18n/ja-JP/dataset-documents.json +++ b/web/i18n/ja-JP/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "ノルウェー語", "metadata.languageMap.pl": "ポーランド語", "metadata.languageMap.pt": "ポルトガル語", + "metadata.languageMap.ro": "ルーマニア語", "metadata.languageMap.ru": "ロシア語", "metadata.languageMap.sv": "スウェーデン語", "metadata.languageMap.th": "タイ語", diff --git a/web/i18n/ko-KR/billing.json b/web/i18n/ko-KR/billing.json index 12de9c9d6b..87d34135fe 100644 --- a/web/i18n/ko-KR/billing.json +++ b/web/i18n/ko-KR/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "무료 기능:", "plans.community.name": "커뮤니티", "plans.community.price": "무료", + "plans.community.priceTip": "", "plans.enterprise.btnText": "판매 문의하기", "plans.enterprise.description": "대규모 미션 크리티컬 시스템을 위한 완전한 기능과 지원을 제공합니다.", "plans.enterprise.features": [ diff --git a/web/i18n/ko-KR/dataset-documents.json b/web/i18n/ko-KR/dataset-documents.json index de4706f34d..1e6433d53f 100644 --- a/web/i18n/ko-KR/dataset-documents.json +++ b/web/i18n/ko-KR/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "노르웨이어", "metadata.languageMap.pl": "폴란드어", "metadata.languageMap.pt": "포르투갈어", + "metadata.languageMap.ro": "루마니아어", "metadata.languageMap.ru": "러시아어", "metadata.languageMap.sv": "스웨덴어", "metadata.languageMap.th": "태국어", diff --git a/web/i18n/pl-PL/billing.json b/web/i18n/pl-PL/billing.json index 0450e23a9c..51a241fbf7 100644 --- a/web/i18n/pl-PL/billing.json +++ b/web/i18n/pl-PL/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Darmowe funkcje:", "plans.community.name": "Społeczność", "plans.community.price": "Darmowy", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Skontaktuj się z działem sprzedaży", "plans.enterprise.description": "Uzyskaj pełne możliwości i wsparcie dla systemów o kluczowym znaczeniu dla misji.", "plans.enterprise.features": [ diff --git a/web/i18n/pt-BR/billing.json b/web/i18n/pt-BR/billing.json index 3268746a8f..bdce6a8ca5 100644 --- a/web/i18n/pt-BR/billing.json +++ b/web/i18n/pt-BR/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Recursos Gratuitos:", "plans.community.name": "Comunidade", "plans.community.price": "Grátis", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Contate Vendas", "plans.enterprise.description": "Obtenha capacidades completas e suporte para sistemas críticos em larga escala.", "plans.enterprise.features": [ diff --git a/web/i18n/pt-BR/dataset-documents.json b/web/i18n/pt-BR/dataset-documents.json index 1fedace09e..d44b235d1e 100644 --- a/web/i18n/pt-BR/dataset-documents.json +++ b/web/i18n/pt-BR/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Norueguês", "metadata.languageMap.pl": "Polonês", "metadata.languageMap.pt": "Português", + "metadata.languageMap.ro": "Romeno", "metadata.languageMap.ru": "Russo", "metadata.languageMap.sv": "Sueco", "metadata.languageMap.th": "Tailandês", diff --git a/web/i18n/ro-RO/billing.json b/web/i18n/ro-RO/billing.json index 0297b9bdb1..40bb5c70db 100644 --- a/web/i18n/ro-RO/billing.json +++ b/web/i18n/ro-RO/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Funcții gratuite:", "plans.community.name": "Comunitate", "plans.community.price": "Gratuit", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Contactați Vânzări", "plans.enterprise.description": "Obțineți capacități și asistență complete pentru sisteme critice la scară largă.", "plans.enterprise.features": [ diff --git a/web/i18n/ro-RO/dataset-documents.json b/web/i18n/ro-RO/dataset-documents.json index 16ae6e9b10..a6ab3fdcd3 100644 --- a/web/i18n/ro-RO/dataset-documents.json +++ b/web/i18n/ro-RO/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Norvegiană", "metadata.languageMap.pl": "Poloneză", "metadata.languageMap.pt": "Portugheză", + "metadata.languageMap.ro": "Română", "metadata.languageMap.ru": "Rusă", "metadata.languageMap.sv": "Suedeză", "metadata.languageMap.th": "Tailandeză", diff --git a/web/i18n/ru-RU/billing.json b/web/i18n/ru-RU/billing.json index df60856898..12ebdb562a 100644 --- a/web/i18n/ru-RU/billing.json +++ b/web/i18n/ru-RU/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Бесплатные функции:", "plans.community.name": "Сообщество", "plans.community.price": "Свободно", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Связаться с отделом продаж", "plans.enterprise.description": "Получите полный набор возможностей и поддержку для крупномасштабных критически важных систем.", "plans.enterprise.features": [ diff --git a/web/i18n/ru-RU/dataset-documents.json b/web/i18n/ru-RU/dataset-documents.json index 8f3709cdd5..abbb6b9ced 100644 --- a/web/i18n/ru-RU/dataset-documents.json +++ b/web/i18n/ru-RU/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Норвежский", "metadata.languageMap.pl": "Польский", "metadata.languageMap.pt": "Португальский", + "metadata.languageMap.ro": "румынский", "metadata.languageMap.ru": "Русский", "metadata.languageMap.sv": "Шведский", "metadata.languageMap.th": "Тайский", diff --git a/web/i18n/sl-SI/billing.json b/web/i18n/sl-SI/billing.json index 847e120897..74c07cacc0 100644 --- a/web/i18n/sl-SI/billing.json +++ b/web/i18n/sl-SI/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Brezplačne funkcije:", "plans.community.name": "Skupnost", "plans.community.price": "Brezplačno", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Kontaktirajte prodajo", "plans.enterprise.description": "Pridobite vse zmogljivosti in podporo za velike sisteme kritične za misijo.", "plans.enterprise.features": [ diff --git a/web/i18n/th-TH/billing.json b/web/i18n/th-TH/billing.json index 98654164b7..599930792b 100644 --- a/web/i18n/th-TH/billing.json +++ b/web/i18n/th-TH/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "คุณสมบัติเสรี:", "plans.community.name": "ชุมชน", "plans.community.price": "ฟรี", + "plans.community.priceTip": "", "plans.enterprise.btnText": "ติดต่อฝ่ายขาย", "plans.enterprise.description": "รับความสามารถและการสนับสนุนเต็มรูปแบบสําหรับระบบที่สําคัญต่อภารกิจขนาดใหญ่", "plans.enterprise.features": [ diff --git a/web/i18n/th-TH/dataset-documents.json b/web/i18n/th-TH/dataset-documents.json index f54fb561f1..3c84da6944 100644 --- a/web/i18n/th-TH/dataset-documents.json +++ b/web/i18n/th-TH/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "นอร์เวย์", "metadata.languageMap.pl": "โปแลนด์", "metadata.languageMap.pt": "โปรตุเกส", + "metadata.languageMap.ro": "โรมาเนีย", "metadata.languageMap.ru": "รัสเซีย", "metadata.languageMap.sv": "สวีเดน", "metadata.languageMap.th": "ไทย", diff --git a/web/i18n/tr-TR/billing.json b/web/i18n/tr-TR/billing.json index 46ef11814c..a7c87dab78 100644 --- a/web/i18n/tr-TR/billing.json +++ b/web/i18n/tr-TR/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Ücretsiz Özellikler:", "plans.community.name": "Topluluk", "plans.community.price": "Ücretsiz", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Satış ile İletişime Geç", "plans.enterprise.description": "Büyük ölçekli kritik sistemler için tam yetenekler ve destek.", "plans.enterprise.features": [ diff --git a/web/i18n/uk-UA/billing.json b/web/i18n/uk-UA/billing.json index f2530e0274..bd627fa8bb 100644 --- a/web/i18n/uk-UA/billing.json +++ b/web/i18n/uk-UA/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Безкоштовні можливості:", "plans.community.name": "Спільнота", "plans.community.price": "Безкоштовно", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Зв'язатися з відділом продажу", "plans.enterprise.description": "Отримайте повні можливості та підтримку для масштабних критично важливих систем.", "plans.enterprise.features": [ diff --git a/web/i18n/uk-UA/dataset-documents.json b/web/i18n/uk-UA/dataset-documents.json index 75cdaf547f..d37815eb8d 100644 --- a/web/i18n/uk-UA/dataset-documents.json +++ b/web/i18n/uk-UA/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Норвезька", "metadata.languageMap.pl": "Польська", "metadata.languageMap.pt": "Португальська", + "metadata.languageMap.ro": "Румунська", "metadata.languageMap.ru": "Російська", "metadata.languageMap.sv": "Шведська", "metadata.languageMap.th": "Тайська", diff --git a/web/i18n/vi-VN/billing.json b/web/i18n/vi-VN/billing.json index c05b43e877..33ee5e5873 100644 --- a/web/i18n/vi-VN/billing.json +++ b/web/i18n/vi-VN/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "Tính năng miễn phí:", "plans.community.name": "Cộng đồng", "plans.community.price": "Miễn phí", + "plans.community.priceTip": "", "plans.enterprise.btnText": "Liên hệ với Bộ phận Bán hàng", "plans.enterprise.description": "Nhận toàn bộ khả năng và hỗ trợ cho các hệ thống quan trọng cho nhiệm vụ quy mô lớn.", "plans.enterprise.features": [ diff --git a/web/i18n/vi-VN/dataset-documents.json b/web/i18n/vi-VN/dataset-documents.json index 964d81275b..29318a0d2e 100644 --- a/web/i18n/vi-VN/dataset-documents.json +++ b/web/i18n/vi-VN/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "Tiếng Na Uy", "metadata.languageMap.pl": "Tiếng Ba Lan", "metadata.languageMap.pt": "Tiếng Bồ Đào Nha", + "metadata.languageMap.ro": "Tiếng Romania", "metadata.languageMap.ru": "Tiếng Nga", "metadata.languageMap.sv": "Tiếng Thụy Điển", "metadata.languageMap.th": "Tiếng Thái", diff --git a/web/i18n/zh-Hans/billing.json b/web/i18n/zh-Hans/billing.json index 7cdc3f3eab..e42edf0dc6 100644 --- a/web/i18n/zh-Hans/billing.json +++ b/web/i18n/zh-Hans/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "免费功能:", "plans.community.name": "Community", "plans.community.price": "免费", + "plans.community.priceTip": "", "plans.enterprise.btnText": "联系销售", "plans.enterprise.description": "适合需要组织级安全性、合规性、可扩展性、控制和定制解决方案的企业", "plans.enterprise.features": [ diff --git a/web/i18n/zh-Hans/dataset-documents.json b/web/i18n/zh-Hans/dataset-documents.json index 2fe105fe60..d81f487070 100644 --- a/web/i18n/zh-Hans/dataset-documents.json +++ b/web/i18n/zh-Hans/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "挪威语", "metadata.languageMap.pl": "波兰语", "metadata.languageMap.pt": "葡萄牙语", + "metadata.languageMap.ro": "罗马尼亚语", "metadata.languageMap.ru": "俄语", "metadata.languageMap.sv": "瑞典语", "metadata.languageMap.th": "泰语", diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json index 50d36e17cd..ae74ad9e18 100644 --- a/web/i18n/zh-Hans/workflow.json +++ b/web/i18n/zh-Hans/workflow.json @@ -357,6 +357,15 @@ "nodes.agent.strategyNotFoundDescAndSwitchVersion": "安装的插件版本不提供此策略。点击切换版本。", "nodes.agent.strategyNotInstallTooltip": "{{strategy}} 未安装", "nodes.agent.strategyNotSet": "代理策略未设置", + "nodes.agent.toolCondition.addCondition": "新增条件", + "nodes.agent.toolCondition.addFirstCondition": "暂未添加条件,请先新增一个条件。", + "nodes.agent.toolCondition.description": "开启后,根据条件加载该工具。", + "nodes.agent.toolCondition.logicalOperator": "当前逻辑:{{value}}", + "nodes.agent.toolCondition.noValueNeeded": "该运算符无需填写值。", + "nodes.agent.toolCondition.operatorPlaceholder": "选择运算符", + "nodes.agent.toolCondition.selectVariable": "选择变量...", + "nodes.agent.toolCondition.singleConditionTip": "请添加至少一个条件。", + "nodes.agent.toolCondition.title": "启用条件", "nodes.agent.toolNotAuthorizedTooltip": "{{tool}} 未授权", "nodes.agent.toolNotInstallTooltip": "{{tool}} 未安装", "nodes.agent.toolbox": "工具箱", diff --git a/web/i18n/zh-Hant/billing.json b/web/i18n/zh-Hant/billing.json index 5799f1823c..f1b7c7b549 100644 --- a/web/i18n/zh-Hant/billing.json +++ b/web/i18n/zh-Hant/billing.json @@ -20,6 +20,7 @@ "plans.community.includesTitle": "免費功能:", "plans.community.name": "社區", "plans.community.price": "免費", + "plans.community.priceTip": "", "plans.enterprise.btnText": "聯繫銷售", "plans.enterprise.description": "獲得大規模關鍵任務系統的完整功能和支援。", "plans.enterprise.features": [ diff --git a/web/i18n/zh-Hant/dataset-documents.json b/web/i18n/zh-Hant/dataset-documents.json index e18b5f61d7..48f01b803c 100644 --- a/web/i18n/zh-Hant/dataset-documents.json +++ b/web/i18n/zh-Hant/dataset-documents.json @@ -247,6 +247,7 @@ "metadata.languageMap.no": "挪威語", "metadata.languageMap.pl": "波蘭語", "metadata.languageMap.pt": "葡萄牙語", + "metadata.languageMap.ro": "羅馬尼亞語", "metadata.languageMap.ru": "俄語", "metadata.languageMap.sv": "瑞典語", "metadata.languageMap.th": "泰語",