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.

all-tools.tsx 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import type {
  2. Dispatch,
  3. SetStateAction,
  4. } from 'react'
  5. import {
  6. useEffect,
  7. useMemo,
  8. useRef,
  9. useState,
  10. } from 'react'
  11. import type {
  12. BlockEnum,
  13. OnSelectBlock,
  14. ToolWithProvider,
  15. } from '../types'
  16. import type { ToolDefaultValue, ToolValue } from './types'
  17. import { ToolTypeEnum } from './types'
  18. import Tools from './tools'
  19. import { useToolTabs } from './hooks'
  20. import ViewTypeSelect, { ViewType } from './view-type-select'
  21. import cn from '@/utils/classnames'
  22. import { useGetLanguage } from '@/context/i18n'
  23. import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list'
  24. import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list'
  25. import { PluginType } from '../../plugins/types'
  26. import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'
  27. import { useGlobalPublicStore } from '@/context/global-public-context'
  28. import RAGToolSuggestions from './rag-tool-suggestions'
  29. import { useRAGRecommendedPlugins } from '@/service/use-tools'
  30. type AllToolsProps = {
  31. className?: string
  32. toolContentClassName?: string
  33. searchText: string
  34. tags: ListProps['tags']
  35. buildInTools: ToolWithProvider[]
  36. customTools: ToolWithProvider[]
  37. workflowTools: ToolWithProvider[]
  38. mcpTools: ToolWithProvider[]
  39. onSelect: OnSelectBlock
  40. canNotSelectMultiple?: boolean
  41. onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void
  42. selectedTools?: ToolValue[]
  43. canChooseMCPTool?: boolean
  44. onTagsChange: Dispatch<SetStateAction<string[]>>
  45. isInRAGPipeline?: boolean
  46. }
  47. const DEFAULT_TAGS: AllToolsProps['tags'] = []
  48. const AllTools = ({
  49. className,
  50. toolContentClassName,
  51. searchText,
  52. tags = DEFAULT_TAGS,
  53. onSelect,
  54. canNotSelectMultiple,
  55. onSelectMultiple,
  56. buildInTools,
  57. workflowTools,
  58. customTools,
  59. mcpTools = [],
  60. selectedTools,
  61. canChooseMCPTool,
  62. onTagsChange,
  63. isInRAGPipeline = false,
  64. }: AllToolsProps) => {
  65. const language = useGetLanguage()
  66. const tabs = useToolTabs()
  67. const [activeTab, setActiveTab] = useState(ToolTypeEnum.All)
  68. const [activeView, setActiveView] = useState<ViewType>(ViewType.flat)
  69. const hasFilter = searchText || tags.length > 0
  70. const isMatchingKeywords = (text: string, keywords: string) => {
  71. return text.toLowerCase().includes(keywords.toLowerCase())
  72. }
  73. const tools = useMemo(() => {
  74. let mergedTools: ToolWithProvider[] = []
  75. if (activeTab === ToolTypeEnum.All)
  76. mergedTools = [...buildInTools, ...customTools, ...workflowTools, ...mcpTools]
  77. if (activeTab === ToolTypeEnum.BuiltIn)
  78. mergedTools = buildInTools
  79. if (activeTab === ToolTypeEnum.Custom)
  80. mergedTools = customTools
  81. if (activeTab === ToolTypeEnum.Workflow)
  82. mergedTools = workflowTools
  83. if (activeTab === ToolTypeEnum.MCP)
  84. mergedTools = mcpTools
  85. if (!hasFilter)
  86. return mergedTools.filter(toolWithProvider => toolWithProvider.tools.length > 0)
  87. return mergedTools.filter((toolWithProvider) => {
  88. return isMatchingKeywords(toolWithProvider.name, searchText) || toolWithProvider.tools.some((tool) => {
  89. return tool.label[language].toLowerCase().includes(searchText.toLowerCase()) || tool.name.toLowerCase().includes(searchText.toLowerCase())
  90. })
  91. })
  92. }, [activeTab, buildInTools, customTools, workflowTools, mcpTools, searchText, language, hasFilter])
  93. const {
  94. queryPluginsWithDebounced: fetchPlugins,
  95. plugins: notInstalledPlugins = [],
  96. } = useMarketplacePlugins()
  97. const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
  98. useEffect(() => {
  99. if (!enable_marketplace) return
  100. if (searchText || tags.length > 0) {
  101. fetchPlugins({
  102. query: searchText,
  103. tags,
  104. category: PluginType.tool,
  105. })
  106. }
  107. }, [searchText, tags, enable_marketplace])
  108. const pluginRef = useRef<ListRef>(null)
  109. const wrapElemRef = useRef<HTMLDivElement>(null)
  110. const isSupportGroupView = [ToolTypeEnum.All, ToolTypeEnum.BuiltIn].includes(activeTab)
  111. const isShowRAGRecommendations = isInRAGPipeline && activeTab === ToolTypeEnum.All && !searchText
  112. const { data: ragRecommendedPlugins = [] } = useRAGRecommendedPlugins(isShowRAGRecommendations)
  113. const recommendedPlugins = useMemo(() => {
  114. return ragRecommendedPlugins.filter(plugin => !plugin.installed)
  115. }, [ragRecommendedPlugins])
  116. return (
  117. <div className={cn('min-w-[400px] max-w-[500px]', className)}>
  118. <div className='flex items-center justify-between border-b border-divider-subtle px-3'>
  119. <div className='flex h-8 items-center space-x-1'>
  120. {
  121. tabs.map(tab => (
  122. <div
  123. className={cn(
  124. 'flex h-6 cursor-pointer items-center rounded-md px-2 hover:bg-state-base-hover',
  125. 'text-xs font-medium text-text-secondary',
  126. activeTab === tab.key && 'bg-state-base-hover-alt',
  127. )}
  128. key={tab.key}
  129. onClick={() => setActiveTab(tab.key)}
  130. >
  131. {tab.name}
  132. </div>
  133. ))
  134. }
  135. </div>
  136. {isSupportGroupView && (
  137. <ViewTypeSelect viewType={activeView} onChange={setActiveView} />
  138. )}
  139. </div>
  140. <div
  141. ref={wrapElemRef}
  142. className='max-h-[464px] overflow-y-auto'
  143. onScroll={pluginRef.current?.handleScroll}
  144. >
  145. {recommendedPlugins.length > 0 && (
  146. <RAGToolSuggestions
  147. tools={recommendedPlugins}
  148. viewType={isSupportGroupView ? activeView : ViewType.flat}
  149. onSelect={onSelect}
  150. onTagsChange={onTagsChange}
  151. />
  152. )}
  153. <Tools
  154. className={toolContentClassName}
  155. tools={tools}
  156. onSelect={onSelect}
  157. canNotSelectMultiple={canNotSelectMultiple}
  158. onSelectMultiple={onSelectMultiple}
  159. toolType={activeTab}
  160. viewType={isSupportGroupView ? activeView : ViewType.flat}
  161. hasSearchText={!!searchText}
  162. selectedTools={selectedTools}
  163. canChooseMCPTool={canChooseMCPTool}
  164. isShowRAGRecommendations={isShowRAGRecommendations}
  165. />
  166. {/* Plugins from marketplace */}
  167. {enable_marketplace && (
  168. <PluginList
  169. ref={pluginRef}
  170. wrapElemRef={wrapElemRef}
  171. list={notInstalledPlugins}
  172. searchText={searchText}
  173. toolContentClassName={toolContentClassName}
  174. tags={tags}
  175. />
  176. )}
  177. </div>
  178. </div>
  179. )
  180. }
  181. export default AllTools