mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
Feat: tool setting support variable (#13465)
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
This commit is contained in:
@ -36,6 +36,7 @@ export type AgentStrategyProps = {
|
||||
onFormValueChange: (value: ToolVarInputs) => void
|
||||
nodeOutputVars?: NodeOutPutVar[],
|
||||
availableNodes?: Node[],
|
||||
nodeId?: string
|
||||
}
|
||||
|
||||
type CustomSchema<Type, Field = {}> = Omit<CredentialFormSchema, 'type'> & { type: Type } & Field
|
||||
@ -46,7 +47,7 @@ type MultipleToolSelectorSchema = CustomSchema<'array[tools]'>
|
||||
type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema
|
||||
|
||||
export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes } = props
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId } = props
|
||||
const { t } = useTranslation()
|
||||
const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration)
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
@ -141,7 +142,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
]
|
||||
const renderField: ComponentProps<typeof Form<CustomField>>['customRenderField'] = (schema, props) => {
|
||||
switch (schema.type) {
|
||||
case 'tool-selector': {
|
||||
case FormTypeEnum.toolSelector: {
|
||||
const value = props.value[schema.variable]
|
||||
const onChange = (value: any) => {
|
||||
props.onChange({ ...props.value, [schema.variable]: value })
|
||||
@ -154,6 +155,9 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
tooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
|
||||
>
|
||||
<ToolSelector
|
||||
nodeId={props.nodeId || ''}
|
||||
nodeOutputVars={props.nodeOutputVars || []}
|
||||
availableNodes={props.availableNodes || []}
|
||||
scope={schema.scope}
|
||||
value={value}
|
||||
onSelect={item => onChange(item)}
|
||||
@ -162,13 +166,16 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
</Field>
|
||||
)
|
||||
}
|
||||
case 'array[tools]': {
|
||||
case FormTypeEnum.multiToolSelector: {
|
||||
const value = props.value[schema.variable]
|
||||
const onChange = (value: any) => {
|
||||
props.onChange({ ...props.value, [schema.variable]: value })
|
||||
}
|
||||
return (
|
||||
<MultipleToolSelector
|
||||
nodeId={props.nodeId || ''}
|
||||
nodeOutputVars={props.nodeOutputVars || []}
|
||||
availableNodes={props.availableNodes || []}
|
||||
scope={schema.scope}
|
||||
value={value || []}
|
||||
label={renderI18nObject(schema.label)}
|
||||
@ -199,6 +206,9 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
fieldLabelClassName='uppercase'
|
||||
customRenderField={renderField}
|
||||
override={override}
|
||||
nodeId={nodeId}
|
||||
nodeOutputVars={nodeOutputVars || []}
|
||||
availableNodes={availableNodes || []}
|
||||
/>
|
||||
</div>
|
||||
: <ListEmpty
|
||||
|
||||
@ -14,7 +14,7 @@ import type { ToolNodeType } from '../../../tool/types'
|
||||
import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types'
|
||||
import type { IterationNodeType } from '../../../iteration/types'
|
||||
import type { ListFilterNodeType } from '../../../list-operator/types'
|
||||
import { OUTPUT_FILE_SUB_VARIABLES } from '../../../if-else/default'
|
||||
import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants'
|
||||
import type { DocExtractorNodeType } from '../../../document-extractor/types'
|
||||
import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types'
|
||||
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
|
||||
|
||||
@ -64,6 +64,7 @@ type Props = {
|
||||
placeholder?: string
|
||||
minWidth?: number
|
||||
popupFor?: 'assigned' | 'toAssigned'
|
||||
zIndex?: number
|
||||
}
|
||||
|
||||
const VarReferencePicker: FC<Props> = ({
|
||||
@ -90,6 +91,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
placeholder,
|
||||
minWidth,
|
||||
popupFor,
|
||||
zIndex,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const store = useStoreApi()
|
||||
@ -386,7 +388,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
</>
|
||||
</WrapElem>
|
||||
<PortalToFollowElemContent style={{
|
||||
zIndex: 100,
|
||||
zIndex: zIndex || 100,
|
||||
}} className='mt-1'>
|
||||
{!isConstant && (
|
||||
<VarReferencePopup
|
||||
|
||||
@ -2,6 +2,7 @@ import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plug
|
||||
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
|
||||
import type { NodeDefault } from '../../types'
|
||||
import type { AgentNodeType } from './types'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { renderI18nObject } from '@/hooks/use-i18n'
|
||||
|
||||
const nodeDefault: NodeDefault<AgentNodeType> = {
|
||||
@ -37,6 +38,94 @@ const nodeDefault: NodeDefault<AgentNodeType> = {
|
||||
}
|
||||
}
|
||||
for (const param of strategy.parameters) {
|
||||
// single tool
|
||||
if (param.required && param.type === FormTypeEnum.toolSelector) {
|
||||
// no value
|
||||
const toolValue = payload.agent_parameters?.[param.name]?.value
|
||||
if (!toolValue) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.fieldRequired', { field: renderI18nObject(param.label, language) }),
|
||||
}
|
||||
}
|
||||
// not enabled
|
||||
else if (!toolValue.enabled) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.noValidTool', { field: renderI18nObject(param.label, language) }),
|
||||
}
|
||||
}
|
||||
// check form of tool
|
||||
else {
|
||||
const schemas = toolValue.schemas || []
|
||||
const userSettings = toolValue.settings
|
||||
const reasoningConfig = toolValue.parameters
|
||||
schemas.forEach((schema: any) => {
|
||||
if (schema?.required) {
|
||||
if (schema.form === 'form' && !userSettings[schema.name]?.value) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||
}
|
||||
}
|
||||
if (schema.form === 'llm' && reasoningConfig[schema.name].auto === 0 && !userSettings[schema.name]?.value) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// multiple tools
|
||||
if (param.required && param.type === FormTypeEnum.multiToolSelector) {
|
||||
const tools = payload.agent_parameters?.[param.name]?.value || []
|
||||
// no value
|
||||
if (!tools.length) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.fieldRequired', { field: renderI18nObject(param.label, language) }),
|
||||
}
|
||||
}
|
||||
// not enabled
|
||||
else if (tools.every((tool: any) => !tool.enabled)) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.noValidTool', { field: renderI18nObject(param.label, language) }),
|
||||
}
|
||||
}
|
||||
// check form of tools
|
||||
else {
|
||||
let validState = {
|
||||
isValid: true,
|
||||
errorMessage: '',
|
||||
}
|
||||
for (const tool of tools) {
|
||||
const schemas = tool.schemas || []
|
||||
const userSettings = tool.settings
|
||||
const reasoningConfig = tool.parameters
|
||||
schemas.forEach((schema: any) => {
|
||||
if (schema?.required) {
|
||||
if (schema.form === 'form' && !userSettings[schema.name]?.value) {
|
||||
return validState = {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||
}
|
||||
}
|
||||
if (schema.form === 'llm' && reasoningConfig[schema.name]?.auto === 0 && !reasoningConfig[schema.name]?.value) {
|
||||
return validState = {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return validState
|
||||
}
|
||||
}
|
||||
// common params
|
||||
if (param.required && !payload.agent_parameters?.[param.name]?.value) {
|
||||
return {
|
||||
isValid: false,
|
||||
|
||||
@ -103,6 +103,7 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
|
||||
onFormValueChange={onFormChange}
|
||||
nodeOutputVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
nodeId={props.id}
|
||||
/>
|
||||
</Field>
|
||||
<div>
|
||||
|
||||
@ -36,6 +36,7 @@ import ListFilterNode from './list-operator/node'
|
||||
import ListFilterPanel from './list-operator/panel'
|
||||
import AgentNode from './agent/node'
|
||||
import AgentPanel from './agent/panel'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
export const NodeComponentMap: Record<string, ComponentType<any>> = {
|
||||
[BlockEnum.Start]: StartNode,
|
||||
@ -82,3 +83,18 @@ export const PanelComponentMap: Record<string, ComponentType<any>> = {
|
||||
}
|
||||
|
||||
export const CUSTOM_NODE_TYPE = 'custom'
|
||||
|
||||
export const FILE_TYPE_OPTIONS = [
|
||||
{ value: 'image', i18nKey: 'image' },
|
||||
{ value: 'document', i18nKey: 'doc' },
|
||||
{ value: 'audio', i18nKey: 'audio' },
|
||||
{ value: 'video', i18nKey: 'video' },
|
||||
]
|
||||
|
||||
export const TRANSFER_METHOD = [
|
||||
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
|
||||
{ value: TransferMethod.remote_url, i18nKey: 'url' },
|
||||
]
|
||||
|
||||
export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method']
|
||||
export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method')
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
isComparisonOperatorNeedTranslate,
|
||||
isEmptyRelatedOperator,
|
||||
} from '../utils'
|
||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../default'
|
||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants'
|
||||
import type { ValueSelector } from '../../../types'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
|
||||
@ -21,7 +21,7 @@ import {
|
||||
} from '../../types'
|
||||
import { comparisonOperatorNotRequireValue, getOperators } from '../../utils'
|
||||
import ConditionNumberInput from '../condition-number-input'
|
||||
import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../default'
|
||||
import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../../constants'
|
||||
import ConditionWrap from '../condition-wrap'
|
||||
import ConditionOperator from './condition-operator'
|
||||
import ConditionInput from './condition-input'
|
||||
@ -39,7 +39,7 @@ import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
|
||||
|
||||
interface ConditionItemProps {
|
||||
type ConditionItemProps = {
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
caseId: string
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
comparisonOperatorNotRequireValue,
|
||||
isComparisonOperatorNeedTranslate,
|
||||
} from '../utils'
|
||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../default'
|
||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import cn from '@/utils/classnames'
|
||||
@ -20,7 +20,7 @@ import type {
|
||||
Node,
|
||||
} from '@/app/components/workflow/types'
|
||||
|
||||
interface ConditionValueProps {
|
||||
type ConditionValueProps = {
|
||||
variableSelector: string[]
|
||||
labelName?: string
|
||||
operator: ComparisonOperator
|
||||
|
||||
@ -12,7 +12,7 @@ import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, Handl
|
||||
import type { Node, NodeOutPutVar, Var } from '../../../types'
|
||||
import { VarType } from '../../../types'
|
||||
import { useGetAvailableVars } from '../../variable-assigner/hooks'
|
||||
import { SUB_VARIABLES } from '../default'
|
||||
import { SUB_VARIABLES } from '../../constants'
|
||||
import ConditionList from './condition-list'
|
||||
import ConditionAdd from './condition-add'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { BlockEnum, type NodeDefault } from '../../types'
|
||||
import { type IfElseNodeType, LogicalOperator } from './types'
|
||||
import { isEmptyRelatedOperator } from './utils'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
|
||||
const i18nPrefix = 'workflow.errorMsg'
|
||||
|
||||
@ -79,18 +78,3 @@ const nodeDefault: NodeDefault<IfElseNodeType> = {
|
||||
}
|
||||
|
||||
export default nodeDefault
|
||||
|
||||
export const FILE_TYPE_OPTIONS = [
|
||||
{ value: 'image', i18nKey: 'image' },
|
||||
{ value: 'document', i18nKey: 'doc' },
|
||||
{ value: 'audio', i18nKey: 'audio' },
|
||||
{ value: 'video', i18nKey: 'video' },
|
||||
]
|
||||
|
||||
export const TRANSFER_METHOD = [
|
||||
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
|
||||
{ value: TransferMethod.remote_url, i18nKey: 'url' },
|
||||
]
|
||||
|
||||
export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method']
|
||||
export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method')
|
||||
|
||||
@ -9,7 +9,7 @@ import { ComparisonOperator } from '../../if-else/types'
|
||||
import { comparisonOperatorNotRequireValue, getOperators } from '../../if-else/utils'
|
||||
import SubVariablePicker from './sub-variable-picker'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/if-else/default'
|
||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/constants'
|
||||
import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||
|
||||
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SUB_VARIABLES } from '../../if-else/default'
|
||||
import { SUB_VARIABLES } from '../../constants'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
|
||||
Reference in New Issue
Block a user