setting-built-in-tool.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useEffect, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import { useContext } from 'use-context-selector'
  6. import cn from 'classnames'
  7. import Drawer from '@/app/components/base/drawer-plus'
  8. import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
  9. import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
  10. import type { Collection, Tool } from '@/app/components/tools/types'
  11. import { CollectionType } from '@/app/components/tools/types'
  12. import { fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList, fetchWorkflowToolList } from '@/service/tools'
  13. import I18n from '@/context/i18n'
  14. import Button from '@/app/components/base/button'
  15. import Loading from '@/app/components/base/loading'
  16. import { DiagonalDividingLine } from '@/app/components/base/icons/src/public/common'
  17. import { getLanguage } from '@/i18n/language'
  18. import AppIcon from '@/app/components/base/app-icon'
  19. type Props = {
  20. collection: Collection
  21. isBuiltIn?: boolean
  22. isModel?: boolean
  23. toolName: string
  24. setting?: Record<string, any>
  25. readonly?: boolean
  26. onHide: () => void
  27. onSave?: (value: Record<string, any>) => void
  28. }
  29. const SettingBuiltInTool: FC<Props> = ({
  30. collection,
  31. isBuiltIn = true,
  32. isModel = true,
  33. toolName,
  34. setting = {},
  35. readonly,
  36. onHide,
  37. onSave,
  38. }) => {
  39. const { locale } = useContext(I18n)
  40. const language = getLanguage(locale)
  41. const { t } = useTranslation()
  42. const [isLoading, setIsLoading] = useState(true)
  43. const [tools, setTools] = useState<Tool[]>([])
  44. const currTool = tools.find(tool => tool.name === toolName)
  45. const formSchemas = currTool ? toolParametersToFormSchemas(currTool.parameters) : []
  46. const infoSchemas = formSchemas.filter((item: any) => item.form === 'llm')
  47. const settingSchemas = formSchemas.filter((item: any) => item.form !== 'llm')
  48. const hasSetting = settingSchemas.length > 0
  49. const [tempSetting, setTempSetting] = useState(setting)
  50. const [currType, setCurrType] = useState('info')
  51. const isInfoActive = currType === 'info'
  52. useEffect(() => {
  53. if (!collection)
  54. return
  55. (async () => {
  56. setIsLoading(true)
  57. try {
  58. const list = await new Promise<Tool[]>((resolve) => {
  59. (async function () {
  60. if (isModel)
  61. resolve(await fetchModelToolList(collection.name))
  62. else if (isBuiltIn)
  63. resolve(await fetchBuiltInToolList(collection.name))
  64. else if (collection.type === CollectionType.workflow)
  65. resolve(await fetchWorkflowToolList(collection.id))
  66. else
  67. resolve(await fetchCustomToolList(collection.name))
  68. }())
  69. })
  70. setTools(list)
  71. const currTool = list.find(tool => tool.name === toolName)
  72. if (currTool) {
  73. const formSchemas = toolParametersToFormSchemas(currTool.parameters)
  74. setTempSetting(addDefaultValue(setting, formSchemas))
  75. }
  76. }
  77. catch (e) { }
  78. setIsLoading(false)
  79. })()
  80. }, [collection?.name, collection?.id, collection?.type])
  81. useEffect(() => {
  82. setCurrType((!readonly && hasSetting) ? 'setting' : 'info')
  83. }, [hasSetting])
  84. const isValid = (() => {
  85. let valid = true
  86. settingSchemas.forEach((item: any) => {
  87. if (item.required && !tempSetting[item.name])
  88. valid = false
  89. })
  90. return valid
  91. })()
  92. const infoUI = (
  93. <div className='pt-2'>
  94. <div className='leading-5 text-sm font-medium text-gray-900'>
  95. {t('tools.setBuiltInTools.toolDescription')}
  96. </div>
  97. <div className='mt-1 leading-[18px] text-xs font-normal text-gray-600'>
  98. {currTool?.description[language]}
  99. </div>
  100. {infoSchemas.length > 0 && (
  101. <div className='mt-6'>
  102. <div className='flex items-center mb-4 leading-[18px] text-xs font-semibold text-gray-500 uppercase'>
  103. <div className='mr-3'>{t('tools.setBuiltInTools.parameters')}</div>
  104. <div className='grow w-0 h-px bg-[#f3f4f6]'></div>
  105. </div>
  106. <div className='space-y-4'>
  107. {infoSchemas.map((item: any, index) => (
  108. <div key={index}>
  109. <div className='flex items-center space-x-2 leading-[18px]'>
  110. <div className='text-[13px] font-semibold text-gray-900'>{item.label[language]}</div>
  111. <div className='text-xs font-medium text-gray-500'>{item.type === 'number-input' ? t('tools.setBuiltInTools.number') : t('tools.setBuiltInTools.string')}</div>
  112. {item.required && (
  113. <div className='text-xs font-medium text-[#EC4A0A]'>{t('tools.setBuiltInTools.required')}</div>
  114. )}
  115. </div>
  116. {item.human_description && (
  117. <div className='mt-1 leading-[18px] text-xs font-normal text-gray-600'>
  118. {item.human_description?.[language]}
  119. </div>
  120. )}
  121. </div>
  122. ))}
  123. </div>
  124. </div>
  125. )}
  126. </div>
  127. )
  128. const settingUI = (
  129. <Form
  130. value={tempSetting}
  131. onChange={setTempSetting}
  132. formSchemas={settingSchemas as any}
  133. isEditMode={false}
  134. showOnVariableMap={{}}
  135. validating={false}
  136. inputClassName='!bg-gray-50'
  137. readonly={readonly}
  138. />
  139. )
  140. return (
  141. <Drawer
  142. isShow
  143. onHide={onHide}
  144. title={(
  145. <div className='flex items-center'>
  146. {typeof collection.icon === 'string'
  147. ? (
  148. <div
  149. className='w-6 h-6 bg-cover bg-center rounded-md flex-shrink-0'
  150. style={{
  151. backgroundImage: `url(${collection.icon})`,
  152. }}
  153. ></div>
  154. )
  155. : (
  156. <AppIcon
  157. className='rounded-md'
  158. size='tiny'
  159. icon={(collection.icon as any)?.content}
  160. background={(collection.icon as any)?.background}
  161. />
  162. )}
  163. <div className='ml-2 leading-6 text-base font-semibold text-gray-900'>{currTool?.label[language]}</div>
  164. {(hasSetting && !readonly) && (<>
  165. <DiagonalDividingLine className='mx-4' />
  166. <div className='flex space-x-6'>
  167. <div
  168. className={cn(isInfoActive ? 'text-gray-900 font-semibold' : 'font-normal text-gray-600 cursor-pointer', 'relative text-base')}
  169. onClick={() => setCurrType('info')}
  170. >
  171. {t('tools.setBuiltInTools.info')}
  172. {isInfoActive && <div className='absolute left-0 bottom-[-16px] w-full h-0.5 bg-primary-600'></div>}
  173. </div>
  174. <div className={cn(!isInfoActive ? 'text-gray-900 font-semibold' : 'font-normal text-gray-600 cursor-pointer', 'relative text-base ')}
  175. onClick={() => setCurrType('setting')}
  176. >
  177. {t('tools.setBuiltInTools.setting')}
  178. {!isInfoActive && <div className='absolute left-0 bottom-[-16px] w-full h-0.5 bg-primary-600'></div>}
  179. </div>
  180. </div>
  181. </>)}
  182. </div>
  183. )}
  184. panelClassName='mt-[65px] !w-[405px]'
  185. maxWidthClassName='!max-w-[405px]'
  186. height='calc(100vh - 65px)'
  187. headerClassName='!border-b-black/5'
  188. body={
  189. <div className='h-full pt-3'>
  190. {isLoading
  191. ? <div className='flex h-full items-center'>
  192. <Loading type='app' />
  193. </div>
  194. : (<div className='flex flex-col h-full'>
  195. <div className='grow h-0 overflow-y-auto px-6'>
  196. {isInfoActive ? infoUI : settingUI}
  197. </div>
  198. {!readonly && !isInfoActive && (
  199. <div className='mt-2 shrink-0 flex justify-end py-4 px-6 space-x-2 rounded-b-[10px] bg-gray-50 border-t border-black/5'>
  200. <Button className='flex items-center h-8 !px-3 !text-[13px] font-medium !text-gray-700' onClick={onHide}>{t('common.operation.cancel')}</Button>
  201. <Button className='flex items-center h-8 !px-3 !text-[13px] font-medium' type='primary' disabled={!isValid} onClick={() => onSave?.(addDefaultValue(tempSetting, formSchemas))}>{t('common.operation.save')}</Button>
  202. </div>
  203. )}
  204. </div>)}
  205. </div>
  206. }
  207. isShowMask={false}
  208. clickOutsideNotOpen={false}
  209. />
  210. )
  211. }
  212. export default React.memo(SettingBuiltInTool)