You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import { useCallback, useEffect, useRef, useState } from 'react'
  2. import produce from 'immer'
  3. import type { Memory, MoreInfo, ValueSelector, Var } from '../../types'
  4. import { ChangeType, VarType } from '../../types'
  5. import { useStore } from '../../store'
  6. import {
  7. useIsChatMode,
  8. useNodesReadOnly,
  9. useWorkflow,
  10. } from '../../hooks'
  11. import useOneStepRun from '../_base/hooks/use-one-step-run'
  12. import useConfigVision from '../../hooks/use-config-vision'
  13. import type { Param, ParameterExtractorNodeType, ReasoningModeType } from './types'
  14. import { useModelListAndDefaultModelAndCurrentProviderAndModel, useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
  15. import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
  16. import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
  17. import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants'
  18. import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
  19. import { supportFunctionCall } from '@/utils/tool-call'
  20. const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
  21. const { nodesReadOnly: readOnly } = useNodesReadOnly()
  22. const { handleOutVarRenameChange } = useWorkflow()
  23. const isChatMode = useIsChatMode()
  24. const defaultConfig = useStore(s => s.nodesDefaultConfigs)[payload.type]
  25. const [defaultRolePrefix, setDefaultRolePrefix] = useState<{ user: string; assistant: string }>({ user: '', assistant: '' })
  26. const { inputs, setInputs: doSetInputs } = useNodeCrud<ParameterExtractorNodeType>(id, payload)
  27. const inputRef = useRef(inputs)
  28. const setInputs = useCallback((newInputs: ParameterExtractorNodeType) => {
  29. if (newInputs.memory && !newInputs.memory.role_prefix) {
  30. const newPayload = produce(newInputs, (draft) => {
  31. draft.memory!.role_prefix = defaultRolePrefix
  32. })
  33. doSetInputs(newPayload)
  34. inputRef.current = newPayload
  35. return
  36. }
  37. doSetInputs(newInputs)
  38. inputRef.current = newInputs
  39. }, [doSetInputs, defaultRolePrefix])
  40. const filterVar = useCallback((varPayload: Var) => {
  41. return [VarType.string].includes(varPayload.type)
  42. }, [])
  43. const handleInputVarChange = useCallback((newInputVar: ValueSelector | string) => {
  44. const newInputs = produce(inputs, (draft) => {
  45. draft.query = newInputVar as ValueSelector || []
  46. })
  47. setInputs(newInputs)
  48. }, [inputs, setInputs])
  49. const handleExactParamsChange = useCallback((newParams: Param[], moreInfo?: MoreInfo) => {
  50. const newInputs = produce(inputs, (draft) => {
  51. draft.parameters = newParams
  52. })
  53. setInputs(newInputs)
  54. if (moreInfo && moreInfo?.type === ChangeType.changeVarName && moreInfo.payload)
  55. handleOutVarRenameChange(id, [id, moreInfo.payload.beforeKey], [id, moreInfo.payload.afterKey!])
  56. }, [handleOutVarRenameChange, id, inputs, setInputs])
  57. const addExtractParameter = useCallback((payload: Param) => {
  58. const newInputs = produce(inputs, (draft) => {
  59. if (!draft.parameters)
  60. draft.parameters = []
  61. draft.parameters.push(payload)
  62. })
  63. setInputs(newInputs)
  64. }, [inputs, setInputs])
  65. // model
  66. const model = inputs.model || {
  67. provider: '',
  68. name: '',
  69. mode: 'chat',
  70. completion_params: {
  71. temperature: 0.7,
  72. },
  73. }
  74. const modelMode = inputs.model?.mode
  75. const isChatModel = modelMode === 'chat'
  76. const isCompletionModel = !isChatModel
  77. const {
  78. isVisionModel,
  79. handleVisionResolutionEnabledChange,
  80. handleVisionResolutionChange,
  81. handleModelChanged: handleVisionConfigAfterModelChanged,
  82. } = useConfigVision(model, {
  83. payload: inputs.vision,
  84. onChange: (newPayload) => {
  85. const newInputs = produce(inputs, (draft) => {
  86. draft.vision = newPayload
  87. })
  88. setInputs(newInputs)
  89. },
  90. })
  91. const appendDefaultPromptConfig = useCallback((draft: ParameterExtractorNodeType, defaultConfig: any, _passInIsChatMode?: boolean) => {
  92. const promptTemplates = defaultConfig.prompt_templates
  93. if (!isChatModel) {
  94. setDefaultRolePrefix({
  95. user: promptTemplates.completion_model.conversation_histories_role.user_prefix,
  96. assistant: promptTemplates.completion_model.conversation_histories_role.assistant_prefix,
  97. })
  98. }
  99. }, [isChatModel])
  100. const [modelChanged, setModelChanged] = useState(false)
  101. const {
  102. currentProvider,
  103. currentModel,
  104. } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.textGeneration)
  105. const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => {
  106. const newInputs = produce(inputRef.current, (draft) => {
  107. draft.model.provider = model.provider
  108. draft.model.name = model.modelId
  109. draft.model.mode = model.mode!
  110. const isModeChange = model.mode !== inputRef.current.model?.mode
  111. if (isModeChange && defaultConfig && Object.keys(defaultConfig).length > 0)
  112. appendDefaultPromptConfig(draft, defaultConfig, model.mode === 'chat')
  113. })
  114. setInputs(newInputs)
  115. setModelChanged(true)
  116. }, [setInputs, defaultConfig, appendDefaultPromptConfig])
  117. useEffect(() => {
  118. if (currentProvider?.provider && currentModel?.model && !model.provider) {
  119. handleModelChanged({
  120. provider: currentProvider?.provider,
  121. modelId: currentModel?.model,
  122. mode: currentModel?.model_properties?.mode as string,
  123. })
  124. }
  125. }, [model?.provider, currentProvider, currentModel, handleModelChanged])
  126. // change to vision model to set vision enabled, else disabled
  127. useEffect(() => {
  128. if (!modelChanged)
  129. return
  130. setModelChanged(false)
  131. handleVisionConfigAfterModelChanged()
  132. // eslint-disable-next-line react-hooks/exhaustive-deps
  133. }, [isVisionModel, modelChanged])
  134. const {
  135. currentModel: currModel,
  136. } = useTextGenerationCurrentProviderAndModelAndModelList(
  137. {
  138. provider: model.provider,
  139. model: model.name,
  140. },
  141. )
  142. const isSupportFunctionCall = supportFunctionCall(currModel?.features)
  143. const filterInputVar = useCallback((varPayload: Var) => {
  144. return [VarType.number, VarType.string].includes(varPayload.type)
  145. }, [])
  146. const filterVisionInputVar = useCallback((varPayload: Var) => {
  147. return [VarType.file, VarType.arrayFile].includes(varPayload.type)
  148. }, [])
  149. const {
  150. availableVars,
  151. availableNodesWithParent,
  152. } = useAvailableVarList(id, {
  153. onlyLeafNodeVar: false,
  154. filterVar: filterInputVar,
  155. })
  156. const {
  157. availableVars: availableVisionVars,
  158. } = useAvailableVarList(id, {
  159. onlyLeafNodeVar: false,
  160. filterVar: filterVisionInputVar,
  161. })
  162. const handleCompletionParamsChange = useCallback((newParams: Record<string, any>) => {
  163. const newInputs = produce(inputs, (draft) => {
  164. draft.model.completion_params = newParams
  165. })
  166. setInputs(newInputs)
  167. }, [inputs, setInputs])
  168. const handleInstructionChange = useCallback((newInstruction: string) => {
  169. const newInputs = produce(inputs, (draft) => {
  170. draft.instruction = newInstruction
  171. })
  172. setInputs(newInputs)
  173. }, [inputs, setInputs])
  174. const hasSetBlockStatus = {
  175. history: false,
  176. query: isChatMode ? checkHasQueryBlock(inputs.instruction) : false,
  177. context: false,
  178. }
  179. const handleMemoryChange = useCallback((newMemory?: Memory) => {
  180. const newInputs = produce(inputs, (draft) => {
  181. draft.memory = newMemory
  182. })
  183. setInputs(newInputs)
  184. }, [inputs, setInputs])
  185. const handleReasoningModeChange = useCallback((newReasoningMode: ReasoningModeType) => {
  186. const newInputs = produce(inputs, (draft) => {
  187. draft.reasoning_mode = newReasoningMode
  188. })
  189. setInputs(newInputs)
  190. }, [inputs, setInputs])
  191. const handleImportFromTool = useCallback((params: Param[]) => {
  192. const newInputs = produce(inputs, (draft) => {
  193. draft.parameters = params
  194. })
  195. setInputs(newInputs)
  196. }, [inputs, setInputs])
  197. // single run
  198. const {
  199. isShowSingleRun,
  200. hideSingleRun,
  201. getInputVars,
  202. runningStatus,
  203. handleRun,
  204. handleStop,
  205. runInputData,
  206. runInputDataRef,
  207. setRunInputData,
  208. runResult,
  209. } = useOneStepRun<ParameterExtractorNodeType>({
  210. id,
  211. data: inputs,
  212. defaultRunInputData: {
  213. 'query': '',
  214. '#files#': [],
  215. },
  216. })
  217. const varInputs = getInputVars([inputs.instruction])
  218. const inputVarValues = (() => {
  219. const vars: Record<string, any> = {}
  220. Object.keys(runInputData)
  221. .forEach((key) => {
  222. vars[key] = runInputData[key]
  223. })
  224. return vars
  225. })()
  226. const setInputVarValues = useCallback((newPayload: Record<string, any>) => {
  227. setRunInputData(newPayload)
  228. }, [setRunInputData])
  229. const visionFiles = runInputData['#files#']
  230. const setVisionFiles = useCallback((newFiles: any[]) => {
  231. setRunInputData({
  232. ...runInputDataRef.current,
  233. '#files#': newFiles,
  234. })
  235. }, [runInputDataRef, setRunInputData])
  236. return {
  237. readOnly,
  238. handleInputVarChange,
  239. filterVar,
  240. isChatMode,
  241. inputs,
  242. isChatModel,
  243. isCompletionModel,
  244. handleModelChanged,
  245. handleCompletionParamsChange,
  246. handleImportFromTool,
  247. handleExactParamsChange,
  248. addExtractParameter,
  249. handleInstructionChange,
  250. hasSetBlockStatus,
  251. availableVars,
  252. availableNodesWithParent,
  253. availableVisionVars,
  254. isSupportFunctionCall,
  255. handleReasoningModeChange,
  256. handleMemoryChange,
  257. varInputs,
  258. inputVarValues,
  259. isVisionModel,
  260. handleVisionResolutionEnabledChange,
  261. handleVisionResolutionChange,
  262. isShowSingleRun,
  263. hideSingleRun,
  264. runningStatus,
  265. handleRun,
  266. handleStop,
  267. runResult,
  268. setInputVarValues,
  269. visionFiles,
  270. setVisionFiles,
  271. }
  272. }
  273. export default useConfig