index.tsx 6.4 KB

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