setting-built-in-tool.tsx 7.8 KB

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