Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

tool-picker.tsx 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React from 'react'
  4. import { useMemo, useState } from 'react'
  5. import {
  6. PortalToFollowElem,
  7. PortalToFollowElemContent,
  8. PortalToFollowElemTrigger,
  9. } from '@/app/components/base/portal-to-follow-elem'
  10. import type {
  11. OffsetOptions,
  12. Placement,
  13. } from '@floating-ui/react'
  14. import AllTools from '@/app/components/workflow/block-selector/all-tools'
  15. import type { ToolDefaultValue, ToolValue } from './types'
  16. import type { BlockEnum } from '@/app/components/workflow/types'
  17. import SearchBox from '@/app/components/plugins/marketplace/search-box'
  18. import { useTranslation } from 'react-i18next'
  19. import { useBoolean } from 'ahooks'
  20. import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal/modal'
  21. import {
  22. createCustomCollection,
  23. } from '@/service/tools'
  24. import type { CustomCollectionBackend } from '@/app/components/tools/types'
  25. import Toast from '@/app/components/base/toast'
  26. import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllCustomTools } from '@/service/use-tools'
  27. import cn from '@/utils/classnames'
  28. type Props = {
  29. panelClassName?: string
  30. disabled: boolean
  31. trigger: React.ReactNode
  32. placement?: Placement
  33. offset?: OffsetOptions
  34. isShow: boolean
  35. onShowChange: (isShow: boolean) => void
  36. onSelect: (tool: ToolDefaultValue) => void
  37. onSelectMultiple: (tools: ToolDefaultValue[]) => void
  38. supportAddCustomTool?: boolean
  39. scope?: string
  40. selectedTools?: ToolValue[]
  41. canChooseMCPTool?: boolean
  42. }
  43. const ToolPicker: FC<Props> = ({
  44. disabled,
  45. trigger,
  46. placement = 'right-start',
  47. offset = 0,
  48. isShow,
  49. onShowChange,
  50. onSelect,
  51. onSelectMultiple,
  52. supportAddCustomTool,
  53. scope = 'all',
  54. selectedTools,
  55. panelClassName,
  56. canChooseMCPTool,
  57. }) => {
  58. const { t } = useTranslation()
  59. const [searchText, setSearchText] = useState('')
  60. const [tags, setTags] = useState<string[]>([])
  61. const { data: buildInTools } = useAllBuiltInTools()
  62. const { data: customTools } = useAllCustomTools()
  63. const invalidateCustomTools = useInvalidateAllCustomTools()
  64. const { data: workflowTools } = useAllWorkflowTools()
  65. const { data: mcpTools } = useAllMCPTools()
  66. const { builtinToolList, customToolList, workflowToolList } = useMemo(() => {
  67. if (scope === 'plugins') {
  68. return {
  69. builtinToolList: buildInTools,
  70. customToolList: [],
  71. workflowToolList: [],
  72. }
  73. }
  74. if (scope === 'custom') {
  75. return {
  76. builtinToolList: [],
  77. customToolList: customTools,
  78. workflowToolList: [],
  79. }
  80. }
  81. if (scope === 'workflow') {
  82. return {
  83. builtinToolList: [],
  84. customToolList: [],
  85. workflowToolList: workflowTools,
  86. }
  87. }
  88. return {
  89. builtinToolList: buildInTools,
  90. customToolList: customTools,
  91. workflowToolList: workflowTools,
  92. }
  93. }, [scope, buildInTools, customTools, workflowTools])
  94. const handleAddedCustomTool = invalidateCustomTools
  95. const handleTriggerClick = () => {
  96. if (disabled) return
  97. onShowChange(true)
  98. }
  99. const handleSelect = (_type: BlockEnum, tool?: ToolDefaultValue) => {
  100. onSelect(tool!)
  101. }
  102. const handleSelectMultiple = (_type: BlockEnum, tools: ToolDefaultValue[]) => {
  103. onSelectMultiple(tools)
  104. }
  105. const [isShowEditCollectionToolModal, {
  106. setFalse: hideEditCustomCollectionModal,
  107. setTrue: showEditCustomCollectionModal,
  108. }] = useBoolean(false)
  109. const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => {
  110. await createCustomCollection(data)
  111. Toast.notify({
  112. type: 'success',
  113. message: t('common.api.actionSuccess'),
  114. })
  115. hideEditCustomCollectionModal()
  116. handleAddedCustomTool()
  117. }
  118. if (isShowEditCollectionToolModal) {
  119. return (
  120. <EditCustomToolModal
  121. positionLeft
  122. payload={null}
  123. onHide={hideEditCustomCollectionModal}
  124. onAdd={doCreateCustomToolCollection}
  125. />
  126. )
  127. }
  128. return (
  129. <PortalToFollowElem
  130. placement={placement}
  131. offset={offset}
  132. open={isShow}
  133. onOpenChange={onShowChange}
  134. >
  135. <PortalToFollowElemTrigger
  136. onClick={handleTriggerClick}
  137. >
  138. {trigger}
  139. </PortalToFollowElemTrigger>
  140. <PortalToFollowElemContent className='z-[1000]'>
  141. <div className={cn('relative min-h-20 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm', panelClassName)}>
  142. <div className='p-2 pb-1'>
  143. <SearchBox
  144. search={searchText}
  145. onSearchChange={setSearchText}
  146. tags={tags}
  147. onTagsChange={setTags}
  148. size='small'
  149. placeholder={t('plugin.searchTools')!}
  150. supportAddCustomTool={supportAddCustomTool}
  151. onAddedCustomTool={handleAddedCustomTool}
  152. onShowAddCustomCollectionModal={showEditCustomCollectionModal}
  153. inputClassName='grow'
  154. />
  155. </div>
  156. <AllTools
  157. className='mt-1'
  158. toolContentClassName='max-w-[100%]'
  159. tags={tags}
  160. searchText={searchText}
  161. onSelect={handleSelect}
  162. onSelectMultiple={handleSelectMultiple}
  163. buildInTools={builtinToolList || []}
  164. customTools={customToolList || []}
  165. workflowTools={workflowToolList || []}
  166. mcpTools={mcpTools || []}
  167. selectedTools={selectedTools}
  168. canChooseMCPTool={canChooseMCPTool}
  169. />
  170. </div>
  171. </PortalToFollowElemContent>
  172. </PortalToFollowElem>
  173. )
  174. }
  175. export default React.memo(ToolPicker)