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.

setting-built-in-tool.tsx 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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 {
  7. RiArrowLeftLine,
  8. RiCloseLine,
  9. } from '@remixicon/react'
  10. import Drawer from '@/app/components/base/drawer'
  11. import Loading from '@/app/components/base/loading'
  12. import ActionButton from '@/app/components/base/action-button'
  13. import Icon from '@/app/components/plugins/card/base/card-icon'
  14. import OrgInfo from '@/app/components/plugins/card/base/org-info'
  15. import Description from '@/app/components/plugins/card/base/description'
  16. import TabSlider from '@/app/components/base/tab-slider-plain'
  17. import Button from '@/app/components/base/button'
  18. import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
  19. import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
  20. import type { Collection, Tool } from '@/app/components/tools/types'
  21. import { CollectionType } from '@/app/components/tools/types'
  22. import { fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList, fetchWorkflowToolList } from '@/service/tools'
  23. import I18n from '@/context/i18n'
  24. import { getLanguage } from '@/i18n-config/language'
  25. import cn from '@/utils/classnames'
  26. import type { ToolWithProvider } from '@/app/components/workflow/types'
  27. import {
  28. AuthCategory,
  29. PluginAuthInAgent,
  30. } from '@/app/components/plugins/plugin-auth'
  31. type Props = {
  32. showBackButton?: boolean
  33. collection: Collection | ToolWithProvider
  34. isBuiltIn?: boolean
  35. isModel?: boolean
  36. toolName: string
  37. setting?: Record<string, any>
  38. readonly?: boolean
  39. onHide: () => void
  40. onSave?: (value: Record<string, any>) => void
  41. credentialId?: string
  42. onAuthorizationItemClick?: (id: string) => void
  43. }
  44. const SettingBuiltInTool: FC<Props> = ({
  45. showBackButton = false,
  46. collection,
  47. isBuiltIn = true,
  48. isModel = true,
  49. toolName,
  50. setting = {},
  51. readonly,
  52. onHide,
  53. onSave,
  54. credentialId,
  55. onAuthorizationItemClick,
  56. }) => {
  57. const { locale } = useContext(I18n)
  58. const language = getLanguage(locale)
  59. const { t } = useTranslation()
  60. const passedTools = (collection as ToolWithProvider).tools
  61. const hasPassedTools = passedTools?.length > 0
  62. const [isLoading, setIsLoading] = useState(!hasPassedTools)
  63. const [tools, setTools] = useState<Tool[]>(hasPassedTools ? passedTools : [])
  64. const currTool = tools.find(tool => tool.name === toolName)
  65. const formSchemas = currTool ? toolParametersToFormSchemas(currTool.parameters) : []
  66. const infoSchemas = formSchemas.filter(item => item.form === 'llm')
  67. const settingSchemas = formSchemas.filter(item => item.form !== 'llm')
  68. const hasSetting = settingSchemas.length > 0
  69. const [tempSetting, setTempSetting] = useState(setting)
  70. const [currType, setCurrType] = useState('info')
  71. const isInfoActive = currType === 'info'
  72. useEffect(() => {
  73. if (!collection || hasPassedTools)
  74. return
  75. (async () => {
  76. setIsLoading(true)
  77. try {
  78. const list = await new Promise<Tool[]>((resolve) => {
  79. (async function () {
  80. if (isModel)
  81. resolve(await fetchModelToolList(collection.name))
  82. else if (isBuiltIn)
  83. resolve(await fetchBuiltInToolList(collection.name))
  84. else if (collection.type === CollectionType.workflow)
  85. resolve(await fetchWorkflowToolList(collection.id))
  86. else
  87. resolve(await fetchCustomToolList(collection.name))
  88. }())
  89. })
  90. setTools(list)
  91. const currTool = list.find(tool => tool.name === toolName)
  92. if (currTool) {
  93. const formSchemas = toolParametersToFormSchemas(currTool.parameters)
  94. setTempSetting(addDefaultValue(setting, formSchemas))
  95. }
  96. }
  97. catch { }
  98. setIsLoading(false)
  99. })()
  100. }, [collection?.name, collection?.id, collection?.type])
  101. useEffect(() => {
  102. setCurrType((!readonly && hasSetting) ? 'setting' : 'info')
  103. }, [hasSetting])
  104. const isValid = (() => {
  105. let valid = true
  106. settingSchemas.forEach((item) => {
  107. if (item.required && !tempSetting[item.name])
  108. valid = false
  109. })
  110. return valid
  111. })()
  112. const getType = (type: string) => {
  113. if (type === 'number-input')
  114. return t('tools.setBuiltInTools.number')
  115. if (type === 'text-input')
  116. return t('tools.setBuiltInTools.string')
  117. if (type === 'checkbox')
  118. return 'boolean'
  119. if (type === 'file')
  120. return t('tools.setBuiltInTools.file')
  121. return type
  122. }
  123. const infoUI = (
  124. <div className=''>
  125. {infoSchemas.length > 0 && (
  126. <div className='space-y-1 py-2'>
  127. {infoSchemas.map((item, index) => (
  128. <div key={index} className='py-1'>
  129. <div className='flex items-center gap-2'>
  130. <div className='code-sm-semibold text-text-secondary'>{item.label[language]}</div>
  131. <div className='system-xs-regular text-text-tertiary'>
  132. {getType(item.type)}
  133. </div>
  134. {item.required && (
  135. <div className='system-xs-medium text-text-warning-secondary'>{t('tools.setBuiltInTools.required')}</div>
  136. )}
  137. </div>
  138. {item.human_description && (
  139. <div className='system-xs-regular mt-0.5 text-text-tertiary'>
  140. {item.human_description?.[language]}
  141. </div>
  142. )}
  143. </div>
  144. ))}
  145. </div>
  146. )}
  147. </div>
  148. )
  149. const settingUI = (
  150. <Form
  151. value={tempSetting}
  152. onChange={setTempSetting}
  153. formSchemas={settingSchemas}
  154. isEditMode={false}
  155. showOnVariableMap={{}}
  156. validating={false}
  157. readonly={readonly}
  158. />
  159. )
  160. return (
  161. <Drawer
  162. isOpen
  163. clickOutsideNotOpen={false}
  164. onClose={onHide}
  165. footer={null}
  166. mask={false}
  167. positionCenter={false}
  168. panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
  169. >
  170. <>
  171. {isLoading && <Loading type='app' />}
  172. {!isLoading && (
  173. <>
  174. {/* header */}
  175. <div className='relative border-b border-divider-subtle p-4 pb-3'>
  176. <div className='absolute right-3 top-3'>
  177. <ActionButton onClick={onHide}>
  178. <RiCloseLine className='h-4 w-4' />
  179. </ActionButton>
  180. </div>
  181. {showBackButton && (
  182. <div
  183. className='system-xs-semibold-uppercase mb-2 flex cursor-pointer items-center gap-1 text-text-accent-secondary'
  184. onClick={onHide}
  185. >
  186. <RiArrowLeftLine className='h-4 w-4' />
  187. BACK
  188. </div>
  189. )}
  190. <div className='flex items-center gap-1'>
  191. <Icon size='tiny' className='h-6 w-6' src={collection.icon} />
  192. <OrgInfo
  193. packageNameClassName='w-auto'
  194. orgName={collection.author}
  195. packageName={collection.name.split('/').pop() || ''}
  196. />
  197. </div>
  198. <div className='system-md-semibold mt-1 text-text-primary'>{currTool?.label[language]}</div>
  199. {!!currTool?.description[language] && (
  200. <Description className='mb-2 mt-3 h-auto' text={currTool.description[language]} descriptionLineRows={2}></Description>
  201. )}
  202. {
  203. collection.allow_delete && collection.type === CollectionType.builtIn && (
  204. <PluginAuthInAgent
  205. pluginPayload={{
  206. provider: collection.name,
  207. category: AuthCategory.tool,
  208. }}
  209. credentialId={credentialId}
  210. onAuthorizationItemClick={onAuthorizationItemClick}
  211. />
  212. )
  213. }
  214. </div>
  215. {/* form */}
  216. <div className='h-full'>
  217. <div className='flex h-full flex-col'>
  218. {(hasSetting && !readonly) ? (
  219. <TabSlider
  220. className='mt-1 shrink-0 px-4'
  221. itemClassName='py-3'
  222. noBorderBottom
  223. value={currType}
  224. onChange={(value) => {
  225. setCurrType(value)
  226. }}
  227. options={[
  228. { value: 'info', text: t('tools.setBuiltInTools.parameters')! },
  229. { value: 'setting', text: t('tools.setBuiltInTools.setting')! },
  230. ]}
  231. />
  232. ) : (
  233. <div className='system-sm-semibold-uppercase p-4 pb-1 text-text-primary'>{t('tools.setBuiltInTools.parameters')}</div>
  234. )}
  235. <div className='h-0 grow overflow-y-auto px-4'>
  236. {isInfoActive ? infoUI : settingUI}
  237. </div>
  238. {!readonly && !isInfoActive && (
  239. <div className='mt-2 flex shrink-0 justify-end space-x-2 rounded-b-[10px] border-t border-divider-regular bg-components-panel-bg px-6 py-4'>
  240. <Button className='flex h-8 items-center !px-3 !text-[13px] font-medium ' onClick={onHide}>{t('common.operation.cancel')}</Button>
  241. <Button className='flex h-8 items-center !px-3 !text-[13px] font-medium' variant='primary' disabled={!isValid} onClick={() => onSave?.(addDefaultValue(tempSetting, formSchemas))}>{t('common.operation.save')}</Button>
  242. </div>
  243. )}
  244. </div>
  245. </div>
  246. </>
  247. )}
  248. </>
  249. </Drawer>
  250. )
  251. }
  252. export default React.memo(SettingBuiltInTool)