mirror of https://github.com/langgenius/dify.git
Merge 73f554aa01 into 9a6b4147bc
This commit is contained in:
commit
47a6d4fbd5
|
|
@ -1,6 +1,6 @@
|
||||||
import json
|
import json
|
||||||
from collections.abc import Generator, Mapping, Sequence
|
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 packaging.version import Version
|
||||||
from pydantic import ValidationError
|
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.node import Node
|
||||||
from core.workflow.nodes.base.variable_template_parser import VariableTemplateParser
|
from core.workflow.nodes.base.variable_template_parser import VariableTemplateParser
|
||||||
from core.workflow.runtime import VariablePool
|
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 extensions.ext_database import db
|
||||||
from factories import file_factory
|
from factories import file_factory
|
||||||
from factories.agent_factory import get_plugin_agent_strategy
|
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}
|
agent_parameters_dictionary = {parameter.name: parameter for parameter in agent_parameters}
|
||||||
|
condition_processor = ConditionProcessor()
|
||||||
|
|
||||||
result: dict[str, Any] = {}
|
result: dict[str, Any] = {}
|
||||||
for parameter_name in node_data.agent_parameters:
|
for parameter_name in node_data.agent_parameters:
|
||||||
|
|
@ -219,7 +222,32 @@ class AgentNode(Node[AgentNodeData]):
|
||||||
value = parameter_value
|
value = parameter_value
|
||||||
if parameter.type == "array[tools]":
|
if parameter.type == "array[tools]":
|
||||||
value = cast(list[dict[str, Any]], value)
|
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)
|
value = self._filter_mcp_type_tool(strategy, value)
|
||||||
for tool in value:
|
for tool in value:
|
||||||
if "schemas" in tool:
|
if "schemas" in tool:
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import ToolTrigger from '@/app/components/plugins/plugin-detail-panel/tool-selec
|
||||||
import { CollectionType } from '@/app/components/tools/types'
|
import { CollectionType } from '@/app/components/tools/types'
|
||||||
import { generateFormValue, getPlainValue, getStructureValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
import { generateFormValue, getPlainValue, getStructureValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||||
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
|
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 ToolForm from '@/app/components/workflow/nodes/tool/components/tool-form'
|
||||||
import { MARKETPLACE_API_PREFIX } from '@/config'
|
import { MARKETPLACE_API_PREFIX } from '@/config'
|
||||||
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||||
|
|
@ -131,6 +132,7 @@ const ToolSelector: FC<Props> = ({
|
||||||
description: tool.tool_description,
|
description: tool.tool_description,
|
||||||
},
|
},
|
||||||
schemas: tool.paramSchemas,
|
schemas: tool.paramSchemas,
|
||||||
|
activation_condition: value?.activation_condition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleSelectTool = (tool: ToolDefaultValue) => {
|
const handleSelectTool = (tool: ToolDefaultValue) => {
|
||||||
|
|
@ -153,6 +155,15 @@ const ToolSelector: FC<Props> = ({
|
||||||
} as any)
|
} as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleActivationConditionChange = (condition?: ToolValue['activation_condition']) => {
|
||||||
|
if (!value)
|
||||||
|
return
|
||||||
|
onSelect({
|
||||||
|
...value,
|
||||||
|
activation_condition: condition,
|
||||||
|
} as any)
|
||||||
|
}
|
||||||
|
|
||||||
// tool settings & params
|
// tool settings & params
|
||||||
const currentToolSettings = useMemo(() => {
|
const currentToolSettings = useMemo(() => {
|
||||||
if (!currentProvider)
|
if (!currentProvider)
|
||||||
|
|
@ -272,7 +283,7 @@ const ToolSelector: FC<Props> = ({
|
||||||
)}
|
)}
|
||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className="z-10">
|
<PortalToFollowElemContent className="z-10">
|
||||||
<div className={cn('relative max-h-[642px] min-h-20 w-[361px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pb-4 shadow-lg backdrop-blur-sm', 'overflow-y-auto pb-2')}>
|
<div className={cn('relative max-h-[642px] min-h-20 w-[480px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pb-4 shadow-lg backdrop-blur-sm', 'overflow-y-auto pb-2')}>
|
||||||
<>
|
<>
|
||||||
<div className="system-xl-semibold px-4 pb-1 pt-3.5 text-text-primary">{t(`detailPanel.toolSelector.${isEdit ? 'toolSetting' : 'title'}`, { ns: 'plugin' })}</div>
|
<div className="system-xl-semibold px-4 pb-1 pt-3.5 text-text-primary">{t(`detailPanel.toolSelector.${isEdit ? 'toolSetting' : 'title'}`, { ns: 'plugin' })}</div>
|
||||||
{/* base form */}
|
{/* base form */}
|
||||||
|
|
@ -401,6 +412,20 @@ const ToolSelector: FC<Props> = ({
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{value?.provider_name && nodeId && (
|
||||||
|
<>
|
||||||
|
<Divider className="my-1 w-full" />
|
||||||
|
<div className="px-4 py-2">
|
||||||
|
<AgentToolConditionEditor
|
||||||
|
value={value.activation_condition}
|
||||||
|
onChange={handleActivationConditionChange}
|
||||||
|
availableVars={nodeOutputVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemContent>
|
</PortalToFollowElemContent>
|
||||||
|
|
|
||||||
|
|
@ -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 { Collection, Event } from '../../tools/types'
|
||||||
import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
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 {
|
export enum TabsEnum {
|
||||||
Start = 'start',
|
Start = 'start',
|
||||||
|
|
@ -56,7 +57,7 @@ export type ToolDefaultValue = PluginCommonDefaultValue & {
|
||||||
paramSchemas: Record<string, unknown>[]
|
paramSchemas: Record<string, unknown>[]
|
||||||
output_schema?: Record<string, unknown>
|
output_schema?: Record<string, unknown>
|
||||||
credential_id?: string
|
credential_id?: string
|
||||||
meta?: PluginMeta
|
meta?: WorkflowPluginMeta
|
||||||
plugin_id?: string
|
plugin_id?: string
|
||||||
provider_icon?: Collection['icon']
|
provider_icon?: Collection['icon']
|
||||||
provider_icon_dark?: Collection['icon']
|
provider_icon_dark?: Collection['icon']
|
||||||
|
|
@ -87,6 +88,7 @@ export type ToolValue = {
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
extra?: { description?: string } & Record<string, unknown>
|
extra?: { description?: string } & Record<string, unknown>
|
||||||
credential_id?: string
|
credential_id?: string
|
||||||
|
activation_condition?: AgentToolActivationCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DataSourceItem = {
|
export type DataSourceItem = {
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={open}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
placement='bottom-start'
|
||||||
|
offset={{
|
||||||
|
mainAxis: 4,
|
||||||
|
crossAxis: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger onClick={() => !disabled && setOpen(!open)}>
|
||||||
|
<Button
|
||||||
|
size='small'
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<RiAddLine className='mr-1 h-3.5 w-3.5' />
|
||||||
|
{t('nodes.agent.toolCondition.addCondition')}
|
||||||
|
</Button>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[1000]'>
|
||||||
|
<div className='w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
|
||||||
|
<VarReferenceVars
|
||||||
|
vars={variables}
|
||||||
|
isSupportFileVar
|
||||||
|
onChange={handleSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConditionAdd
|
||||||
|
|
@ -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 (
|
||||||
|
<PromptEditor
|
||||||
|
key={controlPromptEditorRerenderKey}
|
||||||
|
compact
|
||||||
|
value={value}
|
||||||
|
placeholder={t('nodes.ifElse.enterValue') || ''}
|
||||||
|
workflowVariableBlock={{
|
||||||
|
show: true,
|
||||||
|
variables: [],
|
||||||
|
workflowNodesMap: availableNodes.reduce((acc, node) => {
|
||||||
|
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
|
||||||
|
|
@ -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 <div className='system-xs-regular text-text-tertiary'>{t('nodes.agent.toolCondition.noValueNeeded')}</div>
|
||||||
|
|
||||||
|
if (condition.varType === VarType.boolean) {
|
||||||
|
if (typeof condition.value === 'string' && condition.value !== 'true' && condition.value !== 'false') {
|
||||||
|
return (
|
||||||
|
<ConditionInput
|
||||||
|
value={condition.value}
|
||||||
|
onChange={handleTextValueChange}
|
||||||
|
disabled={disabled}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className='p-1'>
|
||||||
|
<BoolValue
|
||||||
|
value={booleanValue}
|
||||||
|
onChange={handleBooleanValueChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedValue = typeof condition.value === 'string' || typeof condition.value === 'number'
|
||||||
|
? String(condition.value)
|
||||||
|
: ''
|
||||||
|
|
||||||
|
if (condition.varType === VarType.number) {
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
value={normalizedValue}
|
||||||
|
onChange={event => handleTextValueChange(event.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const textValue = typeof condition.value === 'string' ? condition.value : ''
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConditionInput
|
||||||
|
value={textValue}
|
||||||
|
onChange={handleTextValueChange}
|
||||||
|
disabled={disabled}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('mb-1 flex w-full last-of-type:mb-0', className)}>
|
||||||
|
<div className='flex-1 rounded-lg bg-components-input-bg-normal'>
|
||||||
|
<div className='flex items-center p-1'>
|
||||||
|
<div className='w-0 grow'>
|
||||||
|
<ConditionVarSelector
|
||||||
|
open={open}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
valueSelector={condition.variable_selector}
|
||||||
|
varType={condition.varType}
|
||||||
|
availableVars={availableVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
onSelect={handleSelectVar}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='mx-1 h-3 w-[1px] bg-divider-regular' />
|
||||||
|
<ConditionOperator
|
||||||
|
varType={condition.varType}
|
||||||
|
value={condition.comparison_operator}
|
||||||
|
onSelect={handleOperatorChange}
|
||||||
|
disabled={disabled || !condition.variable_selector}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='border-t border-divider-subtle px-3 py-2'>
|
||||||
|
{renderValueInput()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='ml-1 mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-lg text-text-tertiary transition-colors hover:bg-state-destructive-hover hover:text-text-destructive'
|
||||||
|
onClick={onRemove}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<RiDeleteBinLine className='h-4 w-4' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ConditionItem)
|
||||||
|
|
@ -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 (
|
||||||
|
<div className={containerClassName}>
|
||||||
|
{hasMultiple && (
|
||||||
|
<div className='absolute bottom-0 left-0 top-0 w-[60px]'>
|
||||||
|
<div className='absolute bottom-4 left-[46px] top-4 w-2.5 rounded-l-[8px] border border-r-0 border-divider-deep'></div>
|
||||||
|
<div className='absolute right-0 top-1/2 h-[29px] w-4 -translate-y-1/2 bg-components-panel-bg'></div>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='absolute right-1 top-1/2 flex h-5 -translate-y-1/2 items-center gap-1 rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-1.5 text-[10px] font-semibold uppercase text-text-accent-secondary shadow-xs disabled:cursor-not-allowed disabled:opacity-60'
|
||||||
|
onClick={onToggleLogicalOperator}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{logicalOperator.toUpperCase()}
|
||||||
|
<RiLoopLeftLine className='h-3 w-3' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{conditions.map(condition => (
|
||||||
|
<ConditionItem
|
||||||
|
key={condition.id}
|
||||||
|
className=''
|
||||||
|
condition={condition}
|
||||||
|
availableVars={availableVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={onChange}
|
||||||
|
onRemove={() => onRemove(condition.id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ConditionList)
|
||||||
|
|
@ -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 (
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={open}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
placement='bottom-end'
|
||||||
|
offset={{
|
||||||
|
mainAxis: 4,
|
||||||
|
crossAxis: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger
|
||||||
|
onClick={() => {
|
||||||
|
if (!disabled)
|
||||||
|
setOpen(v => !v)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={cn('h-7 shrink-0 px-2', !selectedOption && 'opacity-50')}
|
||||||
|
size='small'
|
||||||
|
variant='ghost'
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{selectedOption?.label ?? placeholder}
|
||||||
|
<RiArrowDownSLine className='ml-1 h-3.5 w-3.5' />
|
||||||
|
</Button>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-10'>
|
||||||
|
<div className='rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'>
|
||||||
|
{options.map(option => (
|
||||||
|
<div
|
||||||
|
key={option.value}
|
||||||
|
className='flex h-7 cursor-pointer items-center rounded-lg px-3 py-1.5 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover'
|
||||||
|
onClick={() => {
|
||||||
|
onSelect(option.value)
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConditionOperator
|
||||||
|
|
@ -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 (
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={open}
|
||||||
|
onOpenChange={(state) => {
|
||||||
|
if (!disabled)
|
||||||
|
onOpenChange(state)
|
||||||
|
}}
|
||||||
|
placement='bottom-start'
|
||||||
|
offset={{
|
||||||
|
mainAxis: 4,
|
||||||
|
crossAxis: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger onClick={handleTriggerClick}>
|
||||||
|
<div className={cn('cursor-pointer', disabled && '!cursor-not-allowed opacity-60')}>
|
||||||
|
{valueSelector && valueSelector.length > 0 ? (
|
||||||
|
<VariableTag
|
||||||
|
valueSelector={valueSelector}
|
||||||
|
varType={varType ?? VarType.string}
|
||||||
|
isShort
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className='system-xs-regular text-text-tertiary'>{t('nodes.agent.toolCondition.selectVariable')}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[1000]'>
|
||||||
|
<div className='w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
|
||||||
|
<VarReferenceVars
|
||||||
|
vars={availableVars}
|
||||||
|
isSupportFileVar
|
||||||
|
onChange={handleSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ConditionVarSelector)
|
||||||
|
|
@ -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<AgentToolActivationCondition>(() => 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 (
|
||||||
|
<div className=''>
|
||||||
|
<div className='flex items-start justify-between gap-3'>
|
||||||
|
<div>
|
||||||
|
<div className='system-sm-semibold text-text-primary'>{t('nodes.agent.toolCondition.title')}</div>
|
||||||
|
<div className='system-xs-regular text-text-tertiary'>{t('nodes.agent.toolCondition.description')}</div>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
defaultValue={isEnabled}
|
||||||
|
onChange={handleToggle}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isEnabled && (
|
||||||
|
<div className='space-y-3'>
|
||||||
|
<div className='rounded-[10px] bg-components-panel-bg px-3 py-2'>
|
||||||
|
{hasConditions && (
|
||||||
|
<div className='mb-2'>
|
||||||
|
<ConditionList
|
||||||
|
conditions={currentValue.conditions}
|
||||||
|
logicalOperator={currentValue.logical_operator}
|
||||||
|
availableVars={availableVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={handleConditionChange}
|
||||||
|
onRemove={handleRemoveCondition}
|
||||||
|
onToggleLogicalOperator={handleToggleLogicalOperator}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={cn(
|
||||||
|
'flex items-center justify-between pr-[30px]',
|
||||||
|
hasConditions && currentValue.conditions.length > 1 && 'ml-[60px]',
|
||||||
|
!hasConditions && 'mt-1',
|
||||||
|
)}>
|
||||||
|
<ConditionAdd
|
||||||
|
variables={availableVars}
|
||||||
|
onSelect={handleAddCondition}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!hasConditions && (
|
||||||
|
<div className='system-xs-regular text-text-tertiary'>
|
||||||
|
{t('nodes.agent.toolCondition.addFirstCondition')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hasConditions && currentValue.conditions.length <= 1 && (
|
||||||
|
<div className='system-xs-regular text-text-tertiary'>
|
||||||
|
{t('nodes.agent.toolCondition.singleConditionTip')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(AgentToolConditionEditor)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as AgentToolConditionEditor } from './editor'
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { ToolVarInputs } from '../tool/types'
|
import type { ToolVarInputs } from '../tool/types'
|
||||||
import type { PluginMeta } from '@/app/components/plugins/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 & {
|
export type AgentNodeType = CommonNodeType & {
|
||||||
agent_strategy_provider_name?: string
|
agent_strategy_provider_name?: string
|
||||||
|
|
@ -18,3 +18,22 @@ export type AgentNodeType = CommonNodeType & {
|
||||||
export enum AgentFeature {
|
export enum AgentFeature {
|
||||||
HISTORY_MESSAGES = 'history-messages',
|
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[]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,9 +163,11 @@ const useConfig = (id: string, payload: AgentNodeType) => {
|
||||||
VarKindType.array,
|
VarKindType.array,
|
||||||
VarKindType.number,
|
VarKindType.number,
|
||||||
VarKindType.string,
|
VarKindType.string,
|
||||||
|
VarKindType.boolean,
|
||||||
VarKindType.secret,
|
VarKindType.secret,
|
||||||
VarKindType.arrayString,
|
VarKindType.arrayString,
|
||||||
VarKindType.arrayNumber,
|
VarKindType.arrayNumber,
|
||||||
|
VarKindType.arrayBoolean,
|
||||||
VarKindType.file,
|
VarKindType.file,
|
||||||
VarKindType.arrayFile,
|
VarKindType.arrayFile,
|
||||||
].includes(varPayload.type)
|
].includes(varPayload.type)
|
||||||
|
|
|
||||||
|
|
@ -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 ''
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import { CronExpressionParser } from 'cron-parser'
|
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
|
// Convert a UTC date from cron-parser to user timezone representation
|
||||||
// This ensures consistency with other execution time calculations
|
// This ensures consistency with other execution time calculations
|
||||||
const convertToUserTimezoneRepresentation = (utcDate: Date, timezone: string): Date => {
|
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
|
// 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
|
// Convert CronDate objects to Date objects and ensure they represent
|
||||||
// the time in user timezone (consistent with execution-time-calculator.ts)
|
// the time in user timezone (consistent with execution-time-calculator.ts)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "ميزات مجانية:",
|
"plans.community.includesTitle": "ميزات مجانية:",
|
||||||
"plans.community.name": "مجتمع",
|
"plans.community.name": "مجتمع",
|
||||||
"plans.community.price": "مجاني",
|
"plans.community.price": "مجاني",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "اتصل بالمبيعات",
|
"plans.enterprise.btnText": "اتصل بالمبيعات",
|
||||||
"plans.enterprise.description": "للمؤسسات التي تتطلب أمانًا وامتثالًا وقابلية للتوسع وتحكمًا وحلولًا مخصصة على مستوى المؤسسة",
|
"plans.enterprise.description": "للمؤسسات التي تتطلب أمانًا وامتثالًا وقابلية للتوسع وتحكمًا وحلولًا مخصصة على مستوى المؤسسة",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Kostenlose Funktionen:",
|
"plans.community.includesTitle": "Kostenlose Funktionen:",
|
||||||
"plans.community.name": "Gemeinschaft",
|
"plans.community.name": "Gemeinschaft",
|
||||||
"plans.community.price": "Kostenlos",
|
"plans.community.price": "Kostenlos",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Vertrieb kontaktieren",
|
"plans.enterprise.btnText": "Vertrieb kontaktieren",
|
||||||
"plans.enterprise.description": "Erhalten Sie volle Fähigkeiten und Unterstützung für großangelegte, missionskritische Systeme.",
|
"plans.enterprise.description": "Erhalten Sie volle Fähigkeiten und Unterstützung für großangelegte, missionskritische Systeme.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Norwegisch",
|
"metadata.languageMap.no": "Norwegisch",
|
||||||
"metadata.languageMap.pl": "Polnisch",
|
"metadata.languageMap.pl": "Polnisch",
|
||||||
"metadata.languageMap.pt": "Portugiesisch",
|
"metadata.languageMap.pt": "Portugiesisch",
|
||||||
|
"metadata.languageMap.ro": "Rumänisch",
|
||||||
"metadata.languageMap.ru": "Russisch",
|
"metadata.languageMap.ru": "Russisch",
|
||||||
"metadata.languageMap.sv": "Schwedisch",
|
"metadata.languageMap.sv": "Schwedisch",
|
||||||
"metadata.languageMap.th": "Thai",
|
"metadata.languageMap.th": "Thai",
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,15 @@
|
||||||
"nodes.agent.strategyNotFoundDescAndSwitchVersion": "The installed plugin version does not provide this strategy. Click to switch version.",
|
"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.strategyNotInstallTooltip": "{{strategy}} is not installed",
|
||||||
"nodes.agent.strategyNotSet": "Agentic strategy Not Set",
|
"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.toolNotAuthorizedTooltip": "{{tool}} Not Authorized",
|
||||||
"nodes.agent.toolNotInstallTooltip": "{{tool}} is not installed",
|
"nodes.agent.toolNotInstallTooltip": "{{tool}} is not installed",
|
||||||
"nodes.agent.toolbox": "toolbox",
|
"nodes.agent.toolbox": "toolbox",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Características gratuitas:",
|
"plans.community.includesTitle": "Características gratuitas:",
|
||||||
"plans.community.name": "Comunidad",
|
"plans.community.name": "Comunidad",
|
||||||
"plans.community.price": "Gratis",
|
"plans.community.price": "Gratis",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Contactar ventas",
|
"plans.enterprise.btnText": "Contactar ventas",
|
||||||
"plans.enterprise.description": "Obtén capacidades completas y soporte para sistemas críticos a gran escala.",
|
"plans.enterprise.description": "Obtén capacidades completas y soporte para sistemas críticos a gran escala.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Noruego",
|
"metadata.languageMap.no": "Noruego",
|
||||||
"metadata.languageMap.pl": "Polaco",
|
"metadata.languageMap.pl": "Polaco",
|
||||||
"metadata.languageMap.pt": "Portugués",
|
"metadata.languageMap.pt": "Portugués",
|
||||||
|
"metadata.languageMap.ro": "Rumano",
|
||||||
"metadata.languageMap.ru": "Ruso",
|
"metadata.languageMap.ru": "Ruso",
|
||||||
"metadata.languageMap.sv": "Sueco",
|
"metadata.languageMap.sv": "Sueco",
|
||||||
"metadata.languageMap.th": "Tailandés",
|
"metadata.languageMap.th": "Tailandés",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "ویژگیهای رایگان:",
|
"plans.community.includesTitle": "ویژگیهای رایگان:",
|
||||||
"plans.community.name": "جامعه",
|
"plans.community.name": "جامعه",
|
||||||
"plans.community.price": "رایگان",
|
"plans.community.price": "رایگان",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "تماس با فروش",
|
"plans.enterprise.btnText": "تماس با فروش",
|
||||||
"plans.enterprise.description": "دریافت کاملترین قابلیتها و پشتیبانی برای سیستمهای بزرگ و بحرانی.",
|
"plans.enterprise.description": "دریافت کاملترین قابلیتها و پشتیبانی برای سیستمهای بزرگ و بحرانی.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Fonctionnalités gratuites :",
|
"plans.community.includesTitle": "Fonctionnalités gratuites :",
|
||||||
"plans.community.name": "Communauté",
|
"plans.community.name": "Communauté",
|
||||||
"plans.community.price": "Gratuit",
|
"plans.community.price": "Gratuit",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Contacter les ventes",
|
"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.description": "Obtenez toutes les capacités et le support pour les systèmes à grande échelle et critiques pour la mission.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Norvégien",
|
"metadata.languageMap.no": "Norvégien",
|
||||||
"metadata.languageMap.pl": "Polonais",
|
"metadata.languageMap.pl": "Polonais",
|
||||||
"metadata.languageMap.pt": "Portugais",
|
"metadata.languageMap.pt": "Portugais",
|
||||||
|
"metadata.languageMap.ro": "Roumain",
|
||||||
"metadata.languageMap.ru": "Russe",
|
"metadata.languageMap.ru": "Russe",
|
||||||
"metadata.languageMap.sv": "Suédois",
|
"metadata.languageMap.sv": "Suédois",
|
||||||
"metadata.languageMap.th": "Thaï",
|
"metadata.languageMap.th": "Thaï",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "निःशुल्क सुविधाएँ:",
|
"plans.community.includesTitle": "निःशुल्क सुविधाएँ:",
|
||||||
"plans.community.name": "समुदाय",
|
"plans.community.name": "समुदाय",
|
||||||
"plans.community.price": "मुक्त",
|
"plans.community.price": "मुक्त",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "बिक्री से संपर्क करें",
|
"plans.enterprise.btnText": "बिक्री से संपर्क करें",
|
||||||
"plans.enterprise.description": "बड़े पैमाने पर मिशन-क्रिटिकल सिस्टम के लिए पूर्ण क्षमताएं और समर्थन प्राप्त करें।",
|
"plans.enterprise.description": "बड़े पैमाने पर मिशन-क्रिटिकल सिस्टम के लिए पूर्ण क्षमताएं और समर्थन प्राप्त करें।",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Fitur Gratis:",
|
"plans.community.includesTitle": "Fitur Gratis:",
|
||||||
"plans.community.name": "Masyarakat",
|
"plans.community.name": "Masyarakat",
|
||||||
"plans.community.price": "Bebas",
|
"plans.community.price": "Bebas",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Hubungi Sales",
|
"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.description": "Untuk perusahaan, memerlukan keamanan, kepatuhan, skalabilitas, kontrol, dan fitur yang lebih canggih di seluruh organisasi",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Caratteristiche Gratuite:",
|
"plans.community.includesTitle": "Caratteristiche Gratuite:",
|
||||||
"plans.community.name": "Comunità",
|
"plans.community.name": "Comunità",
|
||||||
"plans.community.price": "Gratuito",
|
"plans.community.price": "Gratuito",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Contatta le vendite",
|
"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.description": "Ottieni tutte le capacità e il supporto per sistemi mission-critical su larga scala.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Norvegese",
|
"metadata.languageMap.no": "Norvegese",
|
||||||
"metadata.languageMap.pl": "Polacco",
|
"metadata.languageMap.pl": "Polacco",
|
||||||
"metadata.languageMap.pt": "Portoghese",
|
"metadata.languageMap.pt": "Portoghese",
|
||||||
|
"metadata.languageMap.ro": "Rumeno",
|
||||||
"metadata.languageMap.ru": "Russo",
|
"metadata.languageMap.ru": "Russo",
|
||||||
"metadata.languageMap.sv": "Svedese",
|
"metadata.languageMap.sv": "Svedese",
|
||||||
"metadata.languageMap.th": "Thailandese",
|
"metadata.languageMap.th": "Thailandese",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "無料機能:",
|
"plans.community.includesTitle": "無料機能:",
|
||||||
"plans.community.name": "コミュニティ",
|
"plans.community.name": "コミュニティ",
|
||||||
"plans.community.price": "無料",
|
"plans.community.price": "無料",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "営業に相談",
|
"plans.enterprise.btnText": "営業に相談",
|
||||||
"plans.enterprise.description": "企業レベルのセキュリティとカスタマイズを実現",
|
"plans.enterprise.description": "企業レベルのセキュリティとカスタマイズを実現",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "ノルウェー語",
|
"metadata.languageMap.no": "ノルウェー語",
|
||||||
"metadata.languageMap.pl": "ポーランド語",
|
"metadata.languageMap.pl": "ポーランド語",
|
||||||
"metadata.languageMap.pt": "ポルトガル語",
|
"metadata.languageMap.pt": "ポルトガル語",
|
||||||
|
"metadata.languageMap.ro": "ルーマニア語",
|
||||||
"metadata.languageMap.ru": "ロシア語",
|
"metadata.languageMap.ru": "ロシア語",
|
||||||
"metadata.languageMap.sv": "スウェーデン語",
|
"metadata.languageMap.sv": "スウェーデン語",
|
||||||
"metadata.languageMap.th": "タイ語",
|
"metadata.languageMap.th": "タイ語",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "무료 기능:",
|
"plans.community.includesTitle": "무료 기능:",
|
||||||
"plans.community.name": "커뮤니티",
|
"plans.community.name": "커뮤니티",
|
||||||
"plans.community.price": "무료",
|
"plans.community.price": "무료",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "판매 문의하기",
|
"plans.enterprise.btnText": "판매 문의하기",
|
||||||
"plans.enterprise.description": "대규모 미션 크리티컬 시스템을 위한 완전한 기능과 지원을 제공합니다.",
|
"plans.enterprise.description": "대규모 미션 크리티컬 시스템을 위한 완전한 기능과 지원을 제공합니다.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "노르웨이어",
|
"metadata.languageMap.no": "노르웨이어",
|
||||||
"metadata.languageMap.pl": "폴란드어",
|
"metadata.languageMap.pl": "폴란드어",
|
||||||
"metadata.languageMap.pt": "포르투갈어",
|
"metadata.languageMap.pt": "포르투갈어",
|
||||||
|
"metadata.languageMap.ro": "루마니아어",
|
||||||
"metadata.languageMap.ru": "러시아어",
|
"metadata.languageMap.ru": "러시아어",
|
||||||
"metadata.languageMap.sv": "스웨덴어",
|
"metadata.languageMap.sv": "스웨덴어",
|
||||||
"metadata.languageMap.th": "태국어",
|
"metadata.languageMap.th": "태국어",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Darmowe funkcje:",
|
"plans.community.includesTitle": "Darmowe funkcje:",
|
||||||
"plans.community.name": "Społeczność",
|
"plans.community.name": "Społeczność",
|
||||||
"plans.community.price": "Darmowy",
|
"plans.community.price": "Darmowy",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Skontaktuj się z działem sprzedaży",
|
"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.description": "Uzyskaj pełne możliwości i wsparcie dla systemów o kluczowym znaczeniu dla misji.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Recursos Gratuitos:",
|
"plans.community.includesTitle": "Recursos Gratuitos:",
|
||||||
"plans.community.name": "Comunidade",
|
"plans.community.name": "Comunidade",
|
||||||
"plans.community.price": "Grátis",
|
"plans.community.price": "Grátis",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Contate Vendas",
|
"plans.enterprise.btnText": "Contate Vendas",
|
||||||
"plans.enterprise.description": "Obtenha capacidades completas e suporte para sistemas críticos em larga escala.",
|
"plans.enterprise.description": "Obtenha capacidades completas e suporte para sistemas críticos em larga escala.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Norueguês",
|
"metadata.languageMap.no": "Norueguês",
|
||||||
"metadata.languageMap.pl": "Polonês",
|
"metadata.languageMap.pl": "Polonês",
|
||||||
"metadata.languageMap.pt": "Português",
|
"metadata.languageMap.pt": "Português",
|
||||||
|
"metadata.languageMap.ro": "Romeno",
|
||||||
"metadata.languageMap.ru": "Russo",
|
"metadata.languageMap.ru": "Russo",
|
||||||
"metadata.languageMap.sv": "Sueco",
|
"metadata.languageMap.sv": "Sueco",
|
||||||
"metadata.languageMap.th": "Tailandês",
|
"metadata.languageMap.th": "Tailandês",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Funcții gratuite:",
|
"plans.community.includesTitle": "Funcții gratuite:",
|
||||||
"plans.community.name": "Comunitate",
|
"plans.community.name": "Comunitate",
|
||||||
"plans.community.price": "Gratuit",
|
"plans.community.price": "Gratuit",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Contactați Vânzări",
|
"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.description": "Obțineți capacități și asistență complete pentru sisteme critice la scară largă.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Norvegiană",
|
"metadata.languageMap.no": "Norvegiană",
|
||||||
"metadata.languageMap.pl": "Poloneză",
|
"metadata.languageMap.pl": "Poloneză",
|
||||||
"metadata.languageMap.pt": "Portugheză",
|
"metadata.languageMap.pt": "Portugheză",
|
||||||
|
"metadata.languageMap.ro": "Română",
|
||||||
"metadata.languageMap.ru": "Rusă",
|
"metadata.languageMap.ru": "Rusă",
|
||||||
"metadata.languageMap.sv": "Suedeză",
|
"metadata.languageMap.sv": "Suedeză",
|
||||||
"metadata.languageMap.th": "Tailandeză",
|
"metadata.languageMap.th": "Tailandeză",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Бесплатные функции:",
|
"plans.community.includesTitle": "Бесплатные функции:",
|
||||||
"plans.community.name": "Сообщество",
|
"plans.community.name": "Сообщество",
|
||||||
"plans.community.price": "Свободно",
|
"plans.community.price": "Свободно",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Связаться с отделом продаж",
|
"plans.enterprise.btnText": "Связаться с отделом продаж",
|
||||||
"plans.enterprise.description": "Получите полный набор возможностей и поддержку для крупномасштабных критически важных систем.",
|
"plans.enterprise.description": "Получите полный набор возможностей и поддержку для крупномасштабных критически важных систем.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Норвежский",
|
"metadata.languageMap.no": "Норвежский",
|
||||||
"metadata.languageMap.pl": "Польский",
|
"metadata.languageMap.pl": "Польский",
|
||||||
"metadata.languageMap.pt": "Португальский",
|
"metadata.languageMap.pt": "Португальский",
|
||||||
|
"metadata.languageMap.ro": "румынский",
|
||||||
"metadata.languageMap.ru": "Русский",
|
"metadata.languageMap.ru": "Русский",
|
||||||
"metadata.languageMap.sv": "Шведский",
|
"metadata.languageMap.sv": "Шведский",
|
||||||
"metadata.languageMap.th": "Тайский",
|
"metadata.languageMap.th": "Тайский",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Brezplačne funkcije:",
|
"plans.community.includesTitle": "Brezplačne funkcije:",
|
||||||
"plans.community.name": "Skupnost",
|
"plans.community.name": "Skupnost",
|
||||||
"plans.community.price": "Brezplačno",
|
"plans.community.price": "Brezplačno",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Kontaktirajte prodajo",
|
"plans.enterprise.btnText": "Kontaktirajte prodajo",
|
||||||
"plans.enterprise.description": "Pridobite vse zmogljivosti in podporo za velike sisteme kritične za misijo.",
|
"plans.enterprise.description": "Pridobite vse zmogljivosti in podporo za velike sisteme kritične za misijo.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "คุณสมบัติเสรี:",
|
"plans.community.includesTitle": "คุณสมบัติเสรี:",
|
||||||
"plans.community.name": "ชุมชน",
|
"plans.community.name": "ชุมชน",
|
||||||
"plans.community.price": "ฟรี",
|
"plans.community.price": "ฟรี",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "ติดต่อฝ่ายขาย",
|
"plans.enterprise.btnText": "ติดต่อฝ่ายขาย",
|
||||||
"plans.enterprise.description": "รับความสามารถและการสนับสนุนเต็มรูปแบบสําหรับระบบที่สําคัญต่อภารกิจขนาดใหญ่",
|
"plans.enterprise.description": "รับความสามารถและการสนับสนุนเต็มรูปแบบสําหรับระบบที่สําคัญต่อภารกิจขนาดใหญ่",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "นอร์เวย์",
|
"metadata.languageMap.no": "นอร์เวย์",
|
||||||
"metadata.languageMap.pl": "โปแลนด์",
|
"metadata.languageMap.pl": "โปแลนด์",
|
||||||
"metadata.languageMap.pt": "โปรตุเกส",
|
"metadata.languageMap.pt": "โปรตุเกส",
|
||||||
|
"metadata.languageMap.ro": "โรมาเนีย",
|
||||||
"metadata.languageMap.ru": "รัสเซีย",
|
"metadata.languageMap.ru": "รัสเซีย",
|
||||||
"metadata.languageMap.sv": "สวีเดน",
|
"metadata.languageMap.sv": "สวีเดน",
|
||||||
"metadata.languageMap.th": "ไทย",
|
"metadata.languageMap.th": "ไทย",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Ücretsiz Özellikler:",
|
"plans.community.includesTitle": "Ücretsiz Özellikler:",
|
||||||
"plans.community.name": "Topluluk",
|
"plans.community.name": "Topluluk",
|
||||||
"plans.community.price": "Ücretsiz",
|
"plans.community.price": "Ücretsiz",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Satış ile İletişime Geç",
|
"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.description": "Büyük ölçekli kritik sistemler için tam yetenekler ve destek.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Безкоштовні можливості:",
|
"plans.community.includesTitle": "Безкоштовні можливості:",
|
||||||
"plans.community.name": "Спільнота",
|
"plans.community.name": "Спільнота",
|
||||||
"plans.community.price": "Безкоштовно",
|
"plans.community.price": "Безкоштовно",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "Зв'язатися з відділом продажу",
|
"plans.enterprise.btnText": "Зв'язатися з відділом продажу",
|
||||||
"plans.enterprise.description": "Отримайте повні можливості та підтримку для масштабних критично важливих систем.",
|
"plans.enterprise.description": "Отримайте повні можливості та підтримку для масштабних критично важливих систем.",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Норвезька",
|
"metadata.languageMap.no": "Норвезька",
|
||||||
"metadata.languageMap.pl": "Польська",
|
"metadata.languageMap.pl": "Польська",
|
||||||
"metadata.languageMap.pt": "Португальська",
|
"metadata.languageMap.pt": "Португальська",
|
||||||
|
"metadata.languageMap.ro": "Румунська",
|
||||||
"metadata.languageMap.ru": "Російська",
|
"metadata.languageMap.ru": "Російська",
|
||||||
"metadata.languageMap.sv": "Шведська",
|
"metadata.languageMap.sv": "Шведська",
|
||||||
"metadata.languageMap.th": "Тайська",
|
"metadata.languageMap.th": "Тайська",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "Tính năng miễn phí:",
|
"plans.community.includesTitle": "Tính năng miễn phí:",
|
||||||
"plans.community.name": "Cộng đồng",
|
"plans.community.name": "Cộng đồng",
|
||||||
"plans.community.price": "Miễn phí",
|
"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.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.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": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "Tiếng Na Uy",
|
"metadata.languageMap.no": "Tiếng Na Uy",
|
||||||
"metadata.languageMap.pl": "Tiếng Ba Lan",
|
"metadata.languageMap.pl": "Tiếng Ba Lan",
|
||||||
"metadata.languageMap.pt": "Tiếng Bồ Đào Nha",
|
"metadata.languageMap.pt": "Tiếng Bồ Đào Nha",
|
||||||
|
"metadata.languageMap.ro": "Tiếng Romania",
|
||||||
"metadata.languageMap.ru": "Tiếng Nga",
|
"metadata.languageMap.ru": "Tiếng Nga",
|
||||||
"metadata.languageMap.sv": "Tiếng Thụy Điển",
|
"metadata.languageMap.sv": "Tiếng Thụy Điển",
|
||||||
"metadata.languageMap.th": "Tiếng Thái",
|
"metadata.languageMap.th": "Tiếng Thái",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "免费功能:",
|
"plans.community.includesTitle": "免费功能:",
|
||||||
"plans.community.name": "Community",
|
"plans.community.name": "Community",
|
||||||
"plans.community.price": "免费",
|
"plans.community.price": "免费",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "联系销售",
|
"plans.enterprise.btnText": "联系销售",
|
||||||
"plans.enterprise.description": "适合需要组织级安全性、合规性、可扩展性、控制和定制解决方案的企业",
|
"plans.enterprise.description": "适合需要组织级安全性、合规性、可扩展性、控制和定制解决方案的企业",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "挪威语",
|
"metadata.languageMap.no": "挪威语",
|
||||||
"metadata.languageMap.pl": "波兰语",
|
"metadata.languageMap.pl": "波兰语",
|
||||||
"metadata.languageMap.pt": "葡萄牙语",
|
"metadata.languageMap.pt": "葡萄牙语",
|
||||||
|
"metadata.languageMap.ro": "罗马尼亚语",
|
||||||
"metadata.languageMap.ru": "俄语",
|
"metadata.languageMap.ru": "俄语",
|
||||||
"metadata.languageMap.sv": "瑞典语",
|
"metadata.languageMap.sv": "瑞典语",
|
||||||
"metadata.languageMap.th": "泰语",
|
"metadata.languageMap.th": "泰语",
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,15 @@
|
||||||
"nodes.agent.strategyNotFoundDescAndSwitchVersion": "安装的插件版本不提供此策略。点击切换版本。",
|
"nodes.agent.strategyNotFoundDescAndSwitchVersion": "安装的插件版本不提供此策略。点击切换版本。",
|
||||||
"nodes.agent.strategyNotInstallTooltip": "{{strategy}} 未安装",
|
"nodes.agent.strategyNotInstallTooltip": "{{strategy}} 未安装",
|
||||||
"nodes.agent.strategyNotSet": "代理策略未设置",
|
"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.toolNotAuthorizedTooltip": "{{tool}} 未授权",
|
||||||
"nodes.agent.toolNotInstallTooltip": "{{tool}} 未安装",
|
"nodes.agent.toolNotInstallTooltip": "{{tool}} 未安装",
|
||||||
"nodes.agent.toolbox": "工具箱",
|
"nodes.agent.toolbox": "工具箱",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"plans.community.includesTitle": "免費功能:",
|
"plans.community.includesTitle": "免費功能:",
|
||||||
"plans.community.name": "社區",
|
"plans.community.name": "社區",
|
||||||
"plans.community.price": "免費",
|
"plans.community.price": "免費",
|
||||||
|
"plans.community.priceTip": "",
|
||||||
"plans.enterprise.btnText": "聯繫銷售",
|
"plans.enterprise.btnText": "聯繫銷售",
|
||||||
"plans.enterprise.description": "獲得大規模關鍵任務系統的完整功能和支援。",
|
"plans.enterprise.description": "獲得大規模關鍵任務系統的完整功能和支援。",
|
||||||
"plans.enterprise.features": [
|
"plans.enterprise.features": [
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@
|
||||||
"metadata.languageMap.no": "挪威語",
|
"metadata.languageMap.no": "挪威語",
|
||||||
"metadata.languageMap.pl": "波蘭語",
|
"metadata.languageMap.pl": "波蘭語",
|
||||||
"metadata.languageMap.pt": "葡萄牙語",
|
"metadata.languageMap.pt": "葡萄牙語",
|
||||||
|
"metadata.languageMap.ro": "羅馬尼亞語",
|
||||||
"metadata.languageMap.ru": "俄語",
|
"metadata.languageMap.ru": "俄語",
|
||||||
"metadata.languageMap.sv": "瑞典語",
|
"metadata.languageMap.sv": "瑞典語",
|
||||||
"metadata.languageMap.th": "泰語",
|
"metadata.languageMap.th": "泰語",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue