config-prompt.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import produce from 'immer'
  6. import { ReactSortable } from 'react-sortablejs'
  7. import { v4 as uuid4 } from 'uuid'
  8. import cn from 'classnames'
  9. import type { PromptItem, ValueSelector, Var, Variable } from '../../../types'
  10. import { EditionType, PromptRole } from '../../../types'
  11. import useAvailableVarList from '../../_base/hooks/use-available-var-list'
  12. import ConfigPromptItem from './config-prompt-item'
  13. import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
  14. import AddButton from '@/app/components/workflow/nodes/_base/components/add-button'
  15. import { DragHandle } from '@/app/components/base/icons/src/vender/line/others'
  16. const i18nPrefix = 'workflow.nodes.llm'
  17. type Props = {
  18. readOnly: boolean
  19. nodeId: string
  20. filterVar: (payload: Var, selector: ValueSelector) => boolean
  21. isChatModel: boolean
  22. isChatApp: boolean
  23. payload: PromptItem | PromptItem[]
  24. onChange: (payload: PromptItem | PromptItem[]) => void
  25. isShowContext: boolean
  26. hasSetBlockStatus: {
  27. context: boolean
  28. history: boolean
  29. query: boolean
  30. }
  31. varList?: Variable[]
  32. handleAddVariable: (payload: any) => void
  33. }
  34. const ConfigPrompt: FC<Props> = ({
  35. readOnly,
  36. nodeId,
  37. filterVar,
  38. isChatModel,
  39. isChatApp,
  40. payload,
  41. onChange,
  42. isShowContext,
  43. hasSetBlockStatus,
  44. varList = [],
  45. handleAddVariable,
  46. }) => {
  47. const { t } = useTranslation()
  48. const payloadWithIds = (isChatModel && Array.isArray(payload))
  49. ? payload.map((item) => {
  50. const id = uuid4()
  51. return {
  52. id: item.id || id,
  53. p: {
  54. ...item,
  55. id: item.id || id,
  56. },
  57. }
  58. })
  59. : []
  60. const {
  61. availableVars,
  62. availableNodes,
  63. } = useAvailableVarList(nodeId, {
  64. onlyLeafNodeVar: false,
  65. filterVar,
  66. })
  67. const handleChatModePromptChange = useCallback((index: number) => {
  68. return (prompt: string) => {
  69. const newPrompt = produce(payload as PromptItem[], (draft) => {
  70. draft[index][draft[index].edition_type === EditionType.jinja2 ? 'jinja2_text' : 'text'] = prompt
  71. })
  72. onChange(newPrompt)
  73. }
  74. }, [onChange, payload])
  75. const handleChatModeEditionTypeChange = useCallback((index: number) => {
  76. return (editionType: EditionType) => {
  77. const newPrompt = produce(payload as PromptItem[], (draft) => {
  78. draft[index].edition_type = editionType
  79. })
  80. onChange(newPrompt)
  81. }
  82. }, [onChange, payload])
  83. const handleChatModeMessageRoleChange = useCallback((index: number) => {
  84. return (role: PromptRole) => {
  85. const newPrompt = produce(payload as PromptItem[], (draft) => {
  86. draft[index].role = role
  87. })
  88. onChange(newPrompt)
  89. }
  90. }, [onChange, payload])
  91. const handleAddPrompt = useCallback(() => {
  92. const newPrompt = produce(payload as PromptItem[], (draft) => {
  93. if (draft.length === 0) {
  94. draft.push({ role: PromptRole.system, text: '' })
  95. return
  96. }
  97. const isLastItemUser = draft[draft.length - 1].role === PromptRole.user
  98. draft.push({ role: isLastItemUser ? PromptRole.assistant : PromptRole.user, text: '' })
  99. })
  100. onChange(newPrompt)
  101. }, [onChange, payload])
  102. const handleRemove = useCallback((index: number) => {
  103. return () => {
  104. const newPrompt = produce(payload as PromptItem[], (draft) => {
  105. draft.splice(index, 1)
  106. })
  107. onChange(newPrompt)
  108. }
  109. }, [onChange, payload])
  110. const handleCompletionPromptChange = useCallback((prompt: string) => {
  111. const newPrompt = produce(payload as PromptItem, (draft) => {
  112. draft[draft.edition_type === EditionType.jinja2 ? 'jinja2_text' : 'text'] = prompt
  113. })
  114. onChange(newPrompt)
  115. }, [onChange, payload])
  116. const handleCompletionEditionTypeChange = useCallback((editionType: EditionType) => {
  117. const newPrompt = produce(payload as PromptItem, (draft) => {
  118. draft.edition_type = editionType
  119. })
  120. onChange(newPrompt)
  121. }, [onChange, payload])
  122. const canChooseSystemRole = (() => {
  123. if (isChatModel && Array.isArray(payload))
  124. return !payload.find(item => item.role === PromptRole.system)
  125. return false
  126. })()
  127. return (
  128. <div>
  129. {(isChatModel && Array.isArray(payload))
  130. ? (
  131. <div>
  132. <div className='space-y-2'>
  133. <ReactSortable className="space-y-1"
  134. list={payloadWithIds}
  135. setList={(list) => {
  136. if ((payload as PromptItem[])?.[0].role === PromptRole.system && list[0].p?.role !== PromptRole.system)
  137. return
  138. onChange(list.map(item => item.p))
  139. }}
  140. handle='.handle'
  141. ghostClass="opacity-50"
  142. animation={150}
  143. >
  144. {
  145. (payload as PromptItem[]).map((item, index) => {
  146. const canDrag = (() => {
  147. if (readOnly)
  148. return false
  149. if (index === 0 && item.role === PromptRole.system)
  150. return false
  151. return true
  152. })()
  153. return (
  154. <div key={item.id} className='relative group'>
  155. {canDrag && <DragHandle className='group-hover:block hidden absolute left-[-14px] top-2 w-3.5 h-3.5 text-gray-400' />}
  156. <ConfigPromptItem
  157. className={cn(canDrag && 'handle')}
  158. headerClassName={cn(canDrag && 'cursor-grab')}
  159. canNotChooseSystemRole={!canChooseSystemRole}
  160. canRemove={payload.length > 1 && !(index === 0 && item.role === PromptRole.system)}
  161. readOnly={readOnly}
  162. id={item.id!}
  163. handleChatModeMessageRoleChange={handleChatModeMessageRoleChange(index)}
  164. isChatModel={isChatModel}
  165. isChatApp={isChatApp}
  166. payload={item}
  167. onPromptChange={handleChatModePromptChange(index)}
  168. onEditionTypeChange={handleChatModeEditionTypeChange(index)}
  169. onRemove={handleRemove(index)}
  170. isShowContext={isShowContext}
  171. hasSetBlockStatus={hasSetBlockStatus}
  172. availableVars={availableVars}
  173. availableNodes={availableNodes}
  174. varList={varList}
  175. handleAddVariable={handleAddVariable}
  176. />
  177. </div>
  178. )
  179. })
  180. }
  181. </ReactSortable>
  182. </div>
  183. <AddButton
  184. className='mt-2'
  185. text={t(`${i18nPrefix}.addMessage`)}
  186. onClick={handleAddPrompt}
  187. />
  188. </div>
  189. )
  190. : (
  191. <div>
  192. <Editor
  193. instanceId={`${nodeId}-chat-workflow-llm-prompt-editor`}
  194. title={<span className='capitalize'>{t(`${i18nPrefix}.prompt`)}</span>}
  195. value={(payload as PromptItem).edition_type === EditionType.basic ? (payload as PromptItem).text : ((payload as PromptItem).jinja2_text || '')}
  196. onChange={handleCompletionPromptChange}
  197. readOnly={readOnly}
  198. isChatModel={isChatModel}
  199. isChatApp={isChatApp}
  200. isShowContext={isShowContext}
  201. hasSetBlockStatus={hasSetBlockStatus}
  202. nodesOutputVars={availableVars}
  203. availableNodes={availableNodes}
  204. isSupportJinja
  205. editionType={(payload as PromptItem).edition_type}
  206. varList={varList}
  207. onEditionTypeChange={handleCompletionEditionTypeChange}
  208. handleAddVariable={handleAddVariable}
  209. />
  210. </div>
  211. )}
  212. </div>
  213. )
  214. }
  215. export default React.memo(ConfigPrompt)