use-config.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import { useCallback, useEffect, useMemo, useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import produce from 'immer'
  4. import { useBoolean } from 'ahooks'
  5. import { useStore } from '../../store'
  6. import { type ToolNodeType, type ToolVarInputs, VarType } from './types'
  7. import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
  8. import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
  9. import { CollectionType } from '@/app/components/tools/types'
  10. import { updateBuiltInToolCredential } from '@/service/tools'
  11. import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
  12. import Toast from '@/app/components/base/toast'
  13. import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
  14. import { VarType as VarVarType } from '@/app/components/workflow/types'
  15. import type { InputVar, ValueSelector, Var } from '@/app/components/workflow/types'
  16. import useOneStepRun from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run'
  17. import {
  18. useFetchToolsData,
  19. useNodesReadOnly,
  20. } from '@/app/components/workflow/hooks'
  21. import { canFindTool } from '@/utils'
  22. const useConfig = (id: string, payload: ToolNodeType) => {
  23. const { nodesReadOnly: readOnly } = useNodesReadOnly()
  24. const { handleFetchAllTools } = useFetchToolsData()
  25. const { t } = useTranslation()
  26. const language = useLanguage()
  27. const { inputs, setInputs: doSetInputs } = useNodeCrud<ToolNodeType>(id, payload)
  28. /*
  29. * tool_configurations: tool setting, not dynamic setting
  30. * tool_parameters: tool dynamic setting(by user)
  31. * output_schema: tool dynamic output
  32. */
  33. const { provider_id, provider_type, tool_name, tool_configurations, output_schema } = inputs
  34. const isBuiltIn = provider_type === CollectionType.builtIn
  35. const buildInTools = useStore(s => s.buildInTools)
  36. const customTools = useStore(s => s.customTools)
  37. const workflowTools = useStore(s => s.workflowTools)
  38. const currentTools = (() => {
  39. switch (provider_type) {
  40. case CollectionType.builtIn:
  41. return buildInTools
  42. case CollectionType.custom:
  43. return customTools
  44. case CollectionType.workflow:
  45. return workflowTools
  46. default:
  47. return []
  48. }
  49. })()
  50. const currCollection = currentTools.find(item => canFindTool(item.id, provider_id))
  51. // Auth
  52. const needAuth = !!currCollection?.allow_delete
  53. const isAuthed = !!currCollection?.is_team_authorization
  54. const isShowAuthBtn = isBuiltIn && needAuth && !isAuthed
  55. const [showSetAuth, {
  56. setTrue: showSetAuthModal,
  57. setFalse: hideSetAuthModal,
  58. }] = useBoolean(false)
  59. const handleSaveAuth = useCallback(async (value: any) => {
  60. await updateBuiltInToolCredential(currCollection?.name as string, value)
  61. Toast.notify({
  62. type: 'success',
  63. message: t('common.api.actionSuccess'),
  64. })
  65. handleFetchAllTools(provider_type)
  66. hideSetAuthModal()
  67. }, [currCollection?.name, hideSetAuthModal, t, handleFetchAllTools, provider_type])
  68. const currTool = currCollection?.tools.find(tool => tool.name === tool_name)
  69. const formSchemas = useMemo(() => {
  70. return currTool ? toolParametersToFormSchemas(currTool.parameters) : []
  71. }, [currTool])
  72. const toolInputVarSchema = formSchemas.filter((item: any) => item.form === 'llm')
  73. // use setting
  74. const toolSettingSchema = formSchemas.filter((item: any) => item.form !== 'llm')
  75. const hasShouldTransferTypeSettingInput = toolSettingSchema.some(item => item.type === 'boolean' || item.type === 'number-input')
  76. const setInputs = useCallback((value: ToolNodeType) => {
  77. if (!hasShouldTransferTypeSettingInput) {
  78. doSetInputs(value)
  79. return
  80. }
  81. const newInputs = produce(value, (draft) => {
  82. const newConfig = { ...draft.tool_configurations }
  83. Object.keys(draft.tool_configurations).forEach((key) => {
  84. const schema = formSchemas.find(item => item.variable === key)
  85. const value = newConfig[key]
  86. if (schema?.type === 'boolean') {
  87. if (typeof value === 'string')
  88. newConfig[key] = Number.parseInt(value, 10)
  89. if (typeof value === 'boolean')
  90. newConfig[key] = value ? 1 : 0
  91. }
  92. if (schema?.type === 'number-input') {
  93. if (typeof value === 'string' && value !== '')
  94. newConfig[key] = Number.parseFloat(value)
  95. }
  96. })
  97. draft.tool_configurations = newConfig
  98. })
  99. doSetInputs(newInputs)
  100. }, [doSetInputs, formSchemas, hasShouldTransferTypeSettingInput])
  101. const [notSetDefaultValue, setNotSetDefaultValue] = useState(false)
  102. const toolSettingValue = (() => {
  103. if (notSetDefaultValue)
  104. return tool_configurations
  105. return addDefaultValue(tool_configurations, toolSettingSchema)
  106. })()
  107. const setToolSettingValue = useCallback((value: Record<string, any>) => {
  108. setNotSetDefaultValue(true)
  109. setInputs({
  110. ...inputs,
  111. tool_configurations: value,
  112. })
  113. }, [inputs, setInputs])
  114. useEffect(() => {
  115. if (!currTool)
  116. return
  117. const inputsWithDefaultValue = produce(inputs, (draft) => {
  118. if (!draft.tool_configurations || Object.keys(draft.tool_configurations).length === 0)
  119. draft.tool_configurations = addDefaultValue(tool_configurations, toolSettingSchema)
  120. if (!draft.tool_parameters)
  121. draft.tool_parameters = {}
  122. })
  123. setInputs(inputsWithDefaultValue)
  124. // eslint-disable-next-line react-hooks/exhaustive-deps
  125. }, [currTool])
  126. // setting when call
  127. const setInputVar = useCallback((value: ToolVarInputs) => {
  128. setInputs({
  129. ...inputs,
  130. tool_parameters: value,
  131. })
  132. }, [inputs, setInputs])
  133. const [currVarIndex, setCurrVarIndex] = useState(-1)
  134. const currVarType = toolInputVarSchema[currVarIndex]?._type
  135. const handleOnVarOpen = useCallback((index: number) => {
  136. setCurrVarIndex(index)
  137. }, [])
  138. const filterVar = useCallback((varPayload: Var) => {
  139. if (currVarType)
  140. return varPayload.type === currVarType
  141. return varPayload.type !== VarVarType.arrayFile
  142. }, [currVarType])
  143. const isLoading = currTool && (isBuiltIn ? !currCollection : false)
  144. // single run
  145. const [inputVarValues, doSetInputVarValues] = useState<Record<string, any>>({})
  146. const setInputVarValues = (value: Record<string, any>) => {
  147. doSetInputVarValues(value)
  148. // eslint-disable-next-line ts/no-use-before-define
  149. setRunInputData(value)
  150. }
  151. // fill single run form variable with constant value first time
  152. const inputVarValuesWithConstantValue = () => {
  153. const res = produce(inputVarValues, (draft) => {
  154. Object.keys(inputs.tool_parameters).forEach((key: string) => {
  155. const { type, value } = inputs.tool_parameters[key]
  156. if (type === VarType.constant && (value === undefined || value === null))
  157. draft.tool_parameters[key].value = value
  158. })
  159. })
  160. return res
  161. }
  162. const {
  163. isShowSingleRun,
  164. hideSingleRun,
  165. getInputVars,
  166. runningStatus,
  167. setRunInputData,
  168. handleRun: doHandleRun,
  169. handleStop,
  170. runResult,
  171. } = useOneStepRun<ToolNodeType>({
  172. id,
  173. data: inputs,
  174. defaultRunInputData: {},
  175. moreDataForCheckValid: {
  176. toolInputsSchema: (() => {
  177. const formInputs: InputVar[] = []
  178. toolInputVarSchema.forEach((item: any) => {
  179. formInputs.push({
  180. label: item.label[language] || item.label.en_US,
  181. variable: item.variable,
  182. type: item.type,
  183. required: item.required,
  184. })
  185. })
  186. return formInputs
  187. })(),
  188. notAuthed: isShowAuthBtn,
  189. toolSettingSchema,
  190. language,
  191. },
  192. })
  193. const hadVarParams = Object.keys(inputs.tool_parameters)
  194. .filter(key => inputs.tool_parameters[key].type !== VarType.constant)
  195. .map(k => inputs.tool_parameters[k])
  196. const varInputs = getInputVars(hadVarParams.map((p) => {
  197. if (p.type === VarType.variable) {
  198. // handle the old wrong value not crash the page
  199. if (!(p.value as any).join)
  200. return `{{#${p.value}#}}`
  201. return `{{#${(p.value as ValueSelector).join('.')}#}}`
  202. }
  203. return p.value as string
  204. }))
  205. const singleRunForms = (() => {
  206. const forms: FormProps[] = [{
  207. inputs: varInputs,
  208. values: inputVarValuesWithConstantValue(),
  209. onChange: setInputVarValues,
  210. }]
  211. return forms
  212. })()
  213. const handleRun = (submitData: Record<string, any>) => {
  214. const varTypeInputKeys = Object.keys(inputs.tool_parameters)
  215. .filter(key => inputs.tool_parameters[key].type === VarType.variable)
  216. const shouldAdd = varTypeInputKeys.length > 0
  217. if (!shouldAdd) {
  218. doHandleRun(submitData)
  219. return
  220. }
  221. const addMissedVarData = { ...submitData }
  222. Object.keys(submitData).forEach((key) => {
  223. const value = submitData[key]
  224. varTypeInputKeys.forEach((inputKey) => {
  225. const inputValue = inputs.tool_parameters[inputKey].value as ValueSelector
  226. if (`#${inputValue.join('.')}#` === key)
  227. addMissedVarData[inputKey] = value
  228. })
  229. })
  230. doHandleRun(addMissedVarData)
  231. }
  232. const outputSchema = useMemo(() => {
  233. const res: any[] = []
  234. if (!output_schema)
  235. return []
  236. Object.keys(output_schema.properties).forEach((outputKey) => {
  237. const output = output_schema.properties[outputKey]
  238. res.push({
  239. name: outputKey,
  240. type: output.type === 'array'
  241. ? `Array[${output.items?.type.slice(0, 1).toLocaleUpperCase()}${output.items?.type.slice(1)}]`
  242. : `${output.type.slice(0, 1).toLocaleUpperCase()}${output.type.slice(1)}`,
  243. description: output.description,
  244. })
  245. })
  246. return res
  247. }, [output_schema])
  248. return {
  249. readOnly,
  250. inputs,
  251. currTool,
  252. toolSettingSchema,
  253. toolSettingValue,
  254. setToolSettingValue,
  255. toolInputVarSchema,
  256. setInputVar,
  257. handleOnVarOpen,
  258. filterVar,
  259. currCollection,
  260. isShowAuthBtn,
  261. showSetAuth,
  262. showSetAuthModal,
  263. hideSetAuthModal,
  264. handleSaveAuth,
  265. isLoading,
  266. isShowSingleRun,
  267. hideSingleRun,
  268. inputVarValues,
  269. varInputs,
  270. setInputVarValues,
  271. singleRunForms,
  272. runningStatus,
  273. handleRun,
  274. handleStop,
  275. runResult,
  276. outputSchema,
  277. }
  278. }
  279. export default useConfig