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.

index.tsx 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import React from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import {
  4. RiAddLine,
  5. RiQuestionLine,
  6. } from '@remixicon/react'
  7. import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
  8. import ActionButton from '@/app/components/base/action-button'
  9. import Tooltip from '@/app/components/base/tooltip'
  10. import Divider from '@/app/components/base/divider'
  11. import type { ToolValue } from '@/app/components/workflow/block-selector/types'
  12. import type { Node } from 'reactflow'
  13. import type { NodeOutPutVar } from '@/app/components/workflow/types'
  14. import cn from '@/utils/classnames'
  15. import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general'
  16. import { useAllMCPTools } from '@/service/use-tools'
  17. type Props = {
  18. disabled?: boolean
  19. value: ToolValue[]
  20. label: string
  21. required?: boolean
  22. tooltip?: any
  23. supportCollapse?: boolean
  24. scope?: string
  25. onChange: (value: ToolValue[]) => void
  26. nodeOutputVars: NodeOutPutVar[],
  27. availableNodes: Node[],
  28. nodeId?: string
  29. canChooseMCPTool?: boolean
  30. }
  31. const MultipleToolSelector = ({
  32. disabled,
  33. value = [],
  34. label,
  35. required,
  36. tooltip,
  37. supportCollapse,
  38. scope,
  39. onChange,
  40. nodeOutputVars,
  41. availableNodes,
  42. nodeId,
  43. canChooseMCPTool,
  44. }: Props) => {
  45. const { t } = useTranslation()
  46. const { data: mcpTools } = useAllMCPTools()
  47. const enabledCount = value.filter((item) => {
  48. const isMCPTool = mcpTools?.find(tool => tool.id === item.provider_name)
  49. if(isMCPTool)
  50. return item.enabled && canChooseMCPTool
  51. return item.enabled
  52. }).length
  53. // collapse control
  54. const [collapse, setCollapse] = React.useState(false)
  55. const handleCollapse = () => {
  56. if (supportCollapse)
  57. setCollapse(!collapse)
  58. }
  59. // add tool
  60. const [open, setOpen] = React.useState(false)
  61. const [panelShowState, setPanelShowState] = React.useState(true)
  62. const handleAdd = (val: ToolValue) => {
  63. const newValue = [...value, val]
  64. // deduplication
  65. const deduplication = newValue.reduce((acc, cur) => {
  66. if (!acc.find(item => item.provider_name === cur.provider_name && item.tool_name === cur.tool_name))
  67. acc.push(cur)
  68. return acc
  69. }, [] as ToolValue[])
  70. // update value
  71. onChange(deduplication)
  72. setOpen(false)
  73. }
  74. const handleAddMultiple = (val: ToolValue[]) => {
  75. const newValue = [...value, ...val]
  76. // deduplication
  77. const deduplication = newValue.reduce((acc, cur) => {
  78. if (!acc.find(item => item.provider_name === cur.provider_name && item.tool_name === cur.tool_name))
  79. acc.push(cur)
  80. return acc
  81. }, [] as ToolValue[])
  82. // update value
  83. onChange(deduplication)
  84. setOpen(false)
  85. }
  86. // delete tool
  87. const handleDelete = (index: number) => {
  88. const newValue = [...value]
  89. newValue.splice(index, 1)
  90. onChange(newValue)
  91. }
  92. // configure tool
  93. const handleConfigure = (val: ToolValue, index: number) => {
  94. const newValue = [...value]
  95. newValue[index] = val
  96. onChange(newValue)
  97. }
  98. return (
  99. <>
  100. <div className='mb-1 flex items-center'>
  101. <div
  102. className={cn('relative flex grow items-center gap-0.5', supportCollapse && 'cursor-pointer')}
  103. onClick={handleCollapse}
  104. >
  105. <div className='system-sm-semibold-uppercase flex h-6 items-center text-text-secondary'>{label}</div>
  106. {required && <div className='text-red-500'>*</div>}
  107. {tooltip && (
  108. <Tooltip
  109. popupContent={tooltip}
  110. >
  111. <div><RiQuestionLine className='h-3.5 w-3.5 text-text-quaternary hover:text-text-tertiary' /></div>
  112. </Tooltip>
  113. )}
  114. {supportCollapse && (
  115. <ArrowDownRoundFill
  116. className={cn(
  117. 'h-4 w-4 cursor-pointer text-text-quaternary group-hover/collapse:text-text-secondary',
  118. collapse && 'rotate-[270deg]',
  119. )}
  120. />
  121. )}
  122. </div>
  123. {value.length > 0 && (
  124. <>
  125. <div className='system-xs-medium flex items-center gap-1 text-text-tertiary'>
  126. <span>{`${enabledCount}/${value.length}`}</span>
  127. <span>{t('appDebug.agent.tools.enabled')}</span>
  128. </div>
  129. <Divider type='vertical' className='ml-3 mr-1 h-3' />
  130. </>
  131. )}
  132. {!disabled && (
  133. <ActionButton className='mx-1' onClick={() => {
  134. setCollapse(false)
  135. setOpen(!open)
  136. setPanelShowState(true)
  137. }}>
  138. <RiAddLine className='h-4 w-4' />
  139. </ActionButton>
  140. )}
  141. </div>
  142. {!collapse && (
  143. <>
  144. {value.length === 0 && (
  145. <div className='system-xs-regular flex justify-center rounded-[10px] bg-background-section p-3 text-text-tertiary'>{t('plugin.detailPanel.toolSelector.empty')}</div>
  146. )}
  147. {value.length > 0 && value.map((item, index) => (
  148. <div className='mb-1' key={index}>
  149. <ToolSelector
  150. nodeId={nodeId}
  151. nodeOutputVars={nodeOutputVars}
  152. availableNodes={availableNodes}
  153. scope={scope}
  154. value={item}
  155. selectedTools={value}
  156. onSelect={item => handleConfigure(item, index)}
  157. onSelectMultiple={handleAddMultiple}
  158. onDelete={() => handleDelete(index)}
  159. supportEnableSwitch
  160. canChooseMCPTool={canChooseMCPTool}
  161. isEdit
  162. />
  163. </div>
  164. ))}
  165. </>
  166. )}
  167. <ToolSelector
  168. nodeId={nodeId}
  169. nodeOutputVars={nodeOutputVars}
  170. availableNodes={availableNodes}
  171. scope={scope}
  172. value={undefined}
  173. selectedTools={value}
  174. onSelect={handleAdd}
  175. controlledState={open}
  176. onControlledStateChange={setOpen}
  177. trigger={
  178. <div className=''></div>
  179. }
  180. panelShowState={panelShowState}
  181. onPanelShowStateChange={setPanelShowState}
  182. isEdit={false}
  183. canChooseMCPTool={canChooseMCPTool}
  184. onSelectMultiple={handleAddMultiple}
  185. />
  186. </>
  187. )
  188. }
  189. export default MultipleToolSelector