dify/web/app/components/workflow/nodes/tool/use-config.ts
qiuqiua 9ef6b90843
feat: sync main branch (#31938)
Signed-off-by: majiayu000 <1835304752@qq.com>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Signed-off-by: -LAN- <laipz8200@outlook.com>
Signed-off-by: yihong0618 <zouzou0208@gmail.com>
Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com>
Co-authored-by: 盐粒 Yanli <yanli@dify.ai>
Co-authored-by: wangxiaolei <fatelei@gmail.com>
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Cursx <33718736+Cursx@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: lif <1835304752@qq.com>
Co-authored-by: 非法操作 <hjlarry@163.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: fenglin <790872612@qq.com>
Co-authored-by: qiaofenglin <qiaofenglin@baidu.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: TomoOkuyama <49631611+TomoOkuyama@users.noreply.github.com>
Co-authored-by: Tomo Okuyama <tomo.okuyama@intersystems.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: zyssyz123 <916125788@qq.com>
Co-authored-by: hj24 <mambahj24@gmail.com>
Co-authored-by: Coding On Star <447357187@qq.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.ai>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: Xiangxuan Qu <fghpdf@outlook.com>
Co-authored-by: fghpdf <fghpdf@users.noreply.github.com>
Co-authored-by: coopercoder <whitetiger0127@163.com>
Co-authored-by: zhaiguangpeng <zhaiguangpeng@didiglobal.com>
Co-authored-by: Junyan Qin (Chin) <rockchinq@gmail.com>
Co-authored-by: E.G <146701565+GlobalStar117@users.noreply.github.com>
Co-authored-by: GlobalStar117 <GlobalStar117@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: heyszt <270985384@qq.com>
Co-authored-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Co-authored-by: Yeuoly <45712896+Yeuoly@users.noreply.github.com>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: moonpanda <chuanzegao@163.com>
Co-authored-by: warlocgao <warlocgao@tencent.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: KVOJJJin <jzongcode@gmail.com>
Co-authored-by: eux <euxx@users.noreply.github.com>
Co-authored-by: bangjiehan <bangjiehan@gmail.com>
Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com>
Co-authored-by: Jyong <76649700+JohnJyong@users.noreply.github.com>
Co-authored-by: Nie Ronghua <nieronghua@sf-express.com>
Co-authored-by: JQSevenMiao <141806521+JQSevenMiao@users.noreply.github.com>
Co-authored-by: jiasiqi <jiasiqi3@tal.com>
Co-authored-by: Seokrin Taron Sung <sungsjade@gmail.com>
Co-authored-by: CrabSAMA <40541269+CrabSAMA@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: yihong <zouzou0208@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com>
Co-authored-by: yessenia <yessenia.contact@gmail.com>
Co-authored-by: Jax <anobaka@qq.com>
Co-authored-by: niveshdandyan <155956228+niveshdandyan@users.noreply.github.com>
Co-authored-by: OSS Contributor <oss-contributor@example.com>
Co-authored-by: niveshdandyan <niveshdandyan@users.noreply.github.com>
Co-authored-by: Sean Kenneth Doherty <Smaster7772@gmail.com>
2026-02-04 19:04:24 +08:00

304 lines
9.0 KiB
TypeScript

import type { ToolNodeType, ToolVarInputs } from './types'
import type { InputVar } from '@/app/components/workflow/types'
import { useBoolean } from 'ahooks'
import { produce } from 'immer'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { CollectionType } from '@/app/components/tools/types'
import {
getConfiguredValue,
toolParametersToFormSchemas,
} from '@/app/components/tools/utils/to-form-schema'
import {
useNodesReadOnly,
} from '@/app/components/workflow/hooks'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import { updateBuiltInToolCredential } from '@/service/tools'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllMCPTools,
useAllWorkflowTools,
useInvalidToolsByType,
} from '@/service/use-tools'
import { canFindTool } from '@/utils'
import { useWorkflowStore } from '../../store'
const useConfig = (id: string, payload: ToolNodeType) => {
const workflowStore = useWorkflowStore()
const { nodesReadOnly: readOnly } = useNodesReadOnly()
const { t } = useTranslation()
const language = useLanguage()
const { inputs, setInputs: doSetInputs } = useNodeCrud<ToolNodeType>(
id,
payload,
)
/*
* tool_configurations: tool setting, not dynamic setting (form type = form)
* tool_parameters: tool dynamic setting(form type = llm)
*/
const {
provider_id,
provider_type,
tool_name,
tool_configurations,
tool_parameters,
} = inputs
const isBuiltIn = provider_type === CollectionType.builtIn
const { data: buildInTools } = useAllBuiltInTools()
const { data: customTools } = useAllCustomTools()
const { data: workflowTools } = useAllWorkflowTools()
const { data: mcpTools } = useAllMCPTools()
const currentTools = useMemo(() => {
switch (provider_type) {
case CollectionType.builtIn:
return buildInTools || []
case CollectionType.custom:
return customTools || []
case CollectionType.workflow:
return workflowTools || []
case CollectionType.mcp:
return mcpTools || []
default:
return []
}
}, [buildInTools, customTools, mcpTools, provider_type, workflowTools])
const currCollection = useMemo(() => {
return currentTools.find(item => canFindTool(item.id, provider_id))
}, [currentTools, provider_id])
// Auth
const needAuth = !!currCollection?.allow_delete
const isAuthed = !!currCollection?.is_team_authorization
const isShowAuthBtn = isBuiltIn && needAuth && !isAuthed
const [
showSetAuth,
{ setTrue: showSetAuthModal, setFalse: hideSetAuthModal },
] = useBoolean(false)
const invalidToolsByType = useInvalidToolsByType(provider_type)
const handleSaveAuth = useCallback(
async (value: any) => {
await updateBuiltInToolCredential(currCollection?.name as string, value)
Toast.notify({
type: 'success',
message: t('api.actionSuccess', { ns: 'common' }),
})
invalidToolsByType()
hideSetAuthModal()
},
[
currCollection?.name,
hideSetAuthModal,
t,
invalidToolsByType,
provider_type,
],
)
const currTool = useMemo(() => {
return currCollection?.tools.find(tool => tool.name === tool_name)
}, [currCollection, tool_name])
const formSchemas = useMemo(() => {
return currTool ? toolParametersToFormSchemas(currTool.parameters) : []
}, [currTool])
const toolInputVarSchema = useMemo(() => {
return formSchemas.filter((item: any) => item.form === 'llm')
}, [formSchemas])
// use setting
const toolSettingSchema = useMemo(() => {
return formSchemas.filter((item: any) => item.form !== 'llm')
}, [formSchemas])
const hasShouldTransferTypeSettingInput = toolSettingSchema.some(
item => item.type === 'boolean' || item.type === 'number-input',
)
const setInputs = useCallback(
(value: ToolNodeType) => {
if (!hasShouldTransferTypeSettingInput) {
doSetInputs(value)
return
}
const newInputs = produce(value, (draft) => {
const newConfig = { ...draft.tool_configurations }
Object.keys(draft.tool_configurations).forEach((key) => {
const schema = formSchemas.find(item => item.variable === key)
const value = newConfig[key]
if (schema?.type === 'boolean') {
if (typeof value === 'string')
newConfig[key] = value === 'true' || value === '1'
if (typeof value === 'number')
newConfig[key] = value === 1
}
if (schema?.type === 'number-input') {
if (typeof value === 'string' && value !== '')
newConfig[key] = Number.parseFloat(value)
}
})
draft.tool_configurations = newConfig
})
doSetInputs(newInputs)
},
[doSetInputs, formSchemas, hasShouldTransferTypeSettingInput],
)
const [notSetDefaultValue, setNotSetDefaultValue] = useState(false)
const toolSettingValue = useMemo(() => {
if (notSetDefaultValue)
return tool_configurations
return getConfiguredValue(tool_configurations, toolSettingSchema)
}, [notSetDefaultValue, toolSettingSchema, tool_configurations])
const setToolSettingValue = useCallback(
(value: Record<string, any>) => {
setNotSetDefaultValue(true)
setInputs({
...inputs,
tool_configurations: value,
})
},
[inputs, setInputs],
)
const formattingParameters = () => {
const inputsWithDefaultValue = produce(inputs, (draft) => {
if (
!draft.tool_configurations
|| Object.keys(draft.tool_configurations).length === 0
) {
draft.tool_configurations = getConfiguredValue(
tool_configurations,
toolSettingSchema,
) as ToolVarInputs
}
if (
!draft.tool_parameters
|| Object.keys(draft.tool_parameters).length === 0
) {
draft.tool_parameters = getConfiguredValue(
tool_parameters,
toolInputVarSchema,
) as ToolVarInputs
}
})
return inputsWithDefaultValue
}
useEffect(() => {
if (!currTool)
return
const inputsWithDefaultValue = formattingParameters()
const { setControlPromptEditorRerenderKey } = workflowStore.getState()
setInputs(inputsWithDefaultValue)
setTimeout(() => setControlPromptEditorRerenderKey(Date.now()))
}, [currTool])
// setting when call
const setInputVar = useCallback(
(value: ToolVarInputs) => {
setInputs({
...inputs,
tool_parameters: value,
})
},
[inputs, setInputs],
)
const isLoading = currTool && (isBuiltIn ? !currCollection : false)
const getMoreDataForCheckValid = () => {
return {
toolInputsSchema: (() => {
const formInputs: InputVar[] = []
toolInputVarSchema.forEach((item: any) => {
formInputs.push({
label: item.label[language] || item.label.en_US,
variable: item.variable,
type: item.type,
required: item.required,
})
})
return formInputs
})(),
notAuthed: isShowAuthBtn,
toolSettingSchema,
language,
}
}
const outputSchema = useMemo(() => {
const res: any[] = []
const output_schema = currTool?.output_schema
if (!output_schema || !output_schema.properties)
return res
Object.keys(output_schema.properties).forEach((outputKey) => {
const output = output_schema.properties[outputKey]
const type = output.type
if (type === 'object') {
res.push({
name: outputKey,
value: output,
})
}
else {
res.push({
name: outputKey,
type:
output.type === 'array'
? `Array[${output.items?.type
? output.items.type.slice(0, 1).toLocaleUpperCase()
+ output.items.type.slice(1)
: 'Unknown'
}]`
: `${output.type
? output.type.slice(0, 1).toLocaleUpperCase()
+ output.type.slice(1)
: 'Unknown'
}`,
description: output.description,
})
}
})
return res
}, [currTool])
const hasObjectOutput = useMemo(() => {
const output_schema = currTool?.output_schema
if (!output_schema || !output_schema.properties)
return false
const properties = output_schema.properties
return Object.keys(properties).some(
key => properties[key].type === 'object',
)
}, [currTool])
return {
readOnly,
inputs,
currTool,
toolSettingSchema,
toolSettingValue,
setToolSettingValue,
toolInputVarSchema,
setInputVar,
currCollection,
isShowAuthBtn,
showSetAuth,
showSetAuthModal,
hideSetAuthModal,
handleSaveAuth,
isLoading,
outputSchema,
hasObjectOutput,
getMoreDataForCheckValid,
}
}
export default useConfig