Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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