use-config.ts 9.6 KB

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