index.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useEffect, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import cn from 'classnames'
  6. import { AuthHeaderPrefix, AuthType, CollectionType, LOC } from '../types'
  7. import type { Collection, CustomCollectionBackend, Tool } from '../types'
  8. import Loading from '../../base/loading'
  9. import { ArrowNarrowRight } from '../../base/icons/src/vender/line/arrows'
  10. import Toast from '../../base/toast'
  11. import { ConfigurateMethodEnum } from '../../header/account-setting/model-provider-page/declarations'
  12. import Header from './header'
  13. import Item from './item'
  14. import AppIcon from '@/app/components/base/app-icon'
  15. import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
  16. import { fetchCustomCollection, removeBuiltInToolCredential, removeCustomCollection, updateBuiltInToolCredential, updateCustomCollection } from '@/service/tools'
  17. import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
  18. import type { AgentTool } from '@/types/app'
  19. import { MAX_TOOLS_NUM } from '@/config'
  20. import { useModalContext } from '@/context/modal-context'
  21. import { useProviderContext } from '@/context/provider-context'
  22. type Props = {
  23. collection: Collection | null
  24. list: Tool[]
  25. // onToolListChange: () => void // custom tools change
  26. loc: LOC
  27. addedTools?: AgentTool[]
  28. onAddTool?: (collection: Collection, payload: Tool) => void
  29. onRefreshData: () => void
  30. onCollectionRemoved: () => void
  31. isLoading: boolean
  32. }
  33. const ToolList: FC<Props> = ({
  34. collection,
  35. list,
  36. loc,
  37. addedTools,
  38. onAddTool,
  39. onRefreshData,
  40. onCollectionRemoved,
  41. isLoading,
  42. }) => {
  43. const { t } = useTranslation()
  44. const isInToolsPage = loc === LOC.tools
  45. const isBuiltIn = collection?.type === CollectionType.builtIn
  46. const isModel = collection?.type === CollectionType.model
  47. const needAuth = collection?.allow_delete
  48. const { setShowModelModal } = useModalContext()
  49. const [showSettingAuth, setShowSettingAuth] = useState(false)
  50. const { modelProviders: providers } = useProviderContext()
  51. const showSettingAuthModal = () => {
  52. if (isModel) {
  53. const provider = providers.find(item => item.provider === collection?.id)
  54. if (provider) {
  55. setShowModelModal({
  56. payload: {
  57. currentProvider: provider,
  58. currentConfigurateMethod: ConfigurateMethodEnum.predefinedModel,
  59. currentCustomConfigrationModelFixedFields: undefined,
  60. },
  61. onSaveCallback: () => {
  62. onRefreshData()
  63. },
  64. })
  65. }
  66. }
  67. else {
  68. setShowSettingAuth(true)
  69. }
  70. }
  71. const [customCollection, setCustomCollection] = useState<CustomCollectionBackend | null>(null)
  72. useEffect(() => {
  73. if (!collection)
  74. return
  75. (async () => {
  76. if (collection.type === CollectionType.custom) {
  77. const res = await fetchCustomCollection(collection.name)
  78. if (res.credentials.auth_type === AuthType.apiKey && !res.credentials.api_key_header_prefix) {
  79. if (res.credentials.api_key_value)
  80. res.credentials.api_key_header_prefix = AuthHeaderPrefix.custom
  81. }
  82. setCustomCollection({
  83. ...res,
  84. provider: collection.name,
  85. })
  86. }
  87. })()
  88. }, [collection])
  89. const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
  90. const doUpdateCustomToolCollection = async (data: CustomCollectionBackend) => {
  91. await updateCustomCollection(data)
  92. onRefreshData()
  93. Toast.notify({
  94. type: 'success',
  95. message: t('common.api.actionSuccess'),
  96. })
  97. setIsShowEditCustomCollectionModal(false)
  98. }
  99. const doRemoveCustomToolCollection = async () => {
  100. await removeCustomCollection(collection?.name as string)
  101. onCollectionRemoved()
  102. Toast.notify({
  103. type: 'success',
  104. message: t('common.api.actionSuccess'),
  105. })
  106. setIsShowEditCustomCollectionModal(false)
  107. }
  108. if (!collection || isLoading)
  109. return <Loading type='app' />
  110. const icon = <>{typeof collection.icon === 'string'
  111. ? (
  112. <div
  113. className='p-2 bg-cover bg-center border border-gray-100 rounded-lg'
  114. >
  115. <div className='w-6 h-6 bg-center bg-contain rounded-md'
  116. style={{
  117. backgroundImage: `url(${collection.icon})`,
  118. }}
  119. ></div>
  120. </div>
  121. )
  122. : (
  123. <AppIcon
  124. size='large'
  125. icon={collection.icon.content}
  126. background={collection.icon.background}
  127. />
  128. )}
  129. </>
  130. return (
  131. <div className='flex flex-col h-full pb-4'>
  132. <Header
  133. icon={icon}
  134. collection={collection}
  135. loc={loc}
  136. onShowAuth={() => showSettingAuthModal()}
  137. onShowEditCustomCollection={() => setIsShowEditCustomCollectionModal(true)}
  138. />
  139. <div className={cn(isInToolsPage ? 'px-6 pt-4' : 'px-4 pt-3')}>
  140. <div className='flex items-center h-[4.5] space-x-2 text-xs font-medium text-gray-500'>
  141. <div className=''>{t('tools.includeToolNum', {
  142. num: list.length,
  143. })}</div>
  144. {needAuth && (isBuiltIn || isModel) && !collection.is_team_authorization && (
  145. <>
  146. <div>·</div>
  147. <div
  148. className='flex items-center text-[#155EEF] cursor-pointer'
  149. onClick={() => showSettingAuthModal()}
  150. >
  151. <div>{t('tools.auth.setup')}</div>
  152. <ArrowNarrowRight className='ml-0.5 w-3 h-3' />
  153. </div>
  154. </>
  155. )}
  156. </div>
  157. </div>
  158. <div className={cn(isInToolsPage ? 'px-6' : 'px-4', 'grow h-0 pt-2 overflow-y-auto')}>
  159. {/* list */}
  160. <div className={cn(isInToolsPage ? 'grid-cols-3 gap-4' : 'grid-cols-1 gap-2', 'grid')}>
  161. {list.map(item => (
  162. <Item
  163. key={item.name}
  164. icon={icon}
  165. payload={item}
  166. collection={collection}
  167. isInToolsPage={isInToolsPage}
  168. isToolNumMax={(addedTools?.length || 0) >= MAX_TOOLS_NUM}
  169. added={!!addedTools?.find(v => v.provider_id === collection.id && v.provider_type === collection.type && v.tool_name === item.name)}
  170. onAdd={!isInToolsPage ? tool => onAddTool?.(collection as Collection, tool) : undefined}
  171. />
  172. ))}
  173. </div>
  174. </div>
  175. {showSettingAuth && (
  176. <ConfigCredential
  177. collection={collection}
  178. onCancel={() => setShowSettingAuth(false)}
  179. onSaved={async (value) => {
  180. await updateBuiltInToolCredential(collection.name, value)
  181. Toast.notify({
  182. type: 'success',
  183. message: t('common.api.actionSuccess'),
  184. })
  185. await onRefreshData()
  186. setShowSettingAuth(false)
  187. }}
  188. onRemove={async () => {
  189. await removeBuiltInToolCredential(collection.name)
  190. Toast.notify({
  191. type: 'success',
  192. message: t('common.api.actionSuccess'),
  193. })
  194. await onRefreshData()
  195. setShowSettingAuth(false)
  196. }}
  197. />
  198. )}
  199. {isShowEditCollectionToolModal && (
  200. <EditCustomToolModal
  201. payload={customCollection}
  202. onHide={() => setIsShowEditCustomCollectionModal(false)}
  203. onEdit={doUpdateCustomToolCollection}
  204. onRemove={doRemoveCustomToolCollection}
  205. />
  206. )}
  207. </div>
  208. )
  209. }
  210. export default React.memo(ToolList)