merge feat/plugins

This commit is contained in:
zxhlyh
2024-12-27 18:11:52 +08:00
24 changed files with 440 additions and 232 deletions

View File

@ -7,7 +7,7 @@ import ActionList from './action-list'
import ModelList from './model-list'
import AgentStrategyList from './agent-strategy-list'
import Drawer from '@/app/components/base/drawer'
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
import type { PluginDetail } from '@/app/components/plugins/types'
import cn from '@/utils/classnames'
@ -33,9 +33,6 @@ const PluginDetailPanel: FC<Props> = ({
console.log('tool change', val)
setValue(val)
}
const testDelete = () => {
setValue(undefined)
}
if (!detail)
return null
@ -64,10 +61,10 @@ const PluginDetailPanel: FC<Props> = ({
{!!detail.declaration.model && <ModelList detail={detail} />}
{false && (
<div className='px-4 py-2'>
<ToolSelector
value={value}
onSelect={item => testChange(item)}
onDelete={testDelete}
<MultipleToolSelector
value={value || []}
label='TOOLS'
onChange={testChange}
/>
</div>
)}

View File

@ -1,12 +1,148 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import {
RiAddLine,
RiArrowDropDownLine,
RiQuestionLine,
} from '@remixicon/react'
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
import ActionButton from '@/app/components/base/action-button'
import Tooltip from '@/app/components/base/tooltip'
import Divider from '@/app/components/base/divider'
import type { ToolValue } from '@/app/components/plugins/plugin-detail-panel/tool-selector'
import cn from '@/utils/classnames'
type Props = {
value: any[]
disabled?: boolean
value: ToolValue[]
label: string
required?: boolean
tooltip?: any
supportCollapse?: boolean
scope?: string
onChange: (value: ToolValue[]) => void
}
const MultipleToolSelector = ({ value }: Props) => {
const MultipleToolSelector = ({
disabled,
value,
label,
required,
tooltip,
supportCollapse,
scope,
onChange,
}: Props) => {
const { t } = useTranslation()
const enabledCount = value.filter(item => item.enabled).length
// collapse control
const [collapse, setCollapse] = React.useState(false)
const handleCollapse = () => {
if (supportCollapse)
setCollapse(!collapse)
}
// add tool
const [open, setOpen] = React.useState(false)
const handleAdd = (val: ToolValue) => {
const newValue = [...value, val]
// deduplication
const deduplication = newValue.reduce((acc, cur) => {
if (!acc.find(item => item.provider_name === cur.provider_name && item.tool_name === cur.tool_name))
acc.push(cur)
return acc
}, [] as ToolValue[])
// update value
onChange(deduplication)
setOpen(false)
}
// delete tool
const handleDelete = (index: number) => {
const newValue = [...value]
newValue.splice(index, 1)
onChange(newValue)
}
// configure tool
const handleConfigure = (val: ToolValue, index: number) => {
const newValue = [...value]
newValue[index] = val
onChange(newValue)
}
return (
<div></div>
<>
<div className='flex items-center mb-1'>
<div
className={cn('relative grow flex items-center gap-0.5', supportCollapse && 'cursor-pointer')}
onClick={handleCollapse}
>
<div className='h-6 flex items-center text-text-secondary system-sm-semibold-uppercase'>{label}</div>
{required && <div className='text-error-main'>*</div>}
{tooltip && (
<Tooltip
popupContent={tooltip}
needsDelay
>
<div><RiQuestionLine className='w-3.5 h-3.5 text-text-quaternary hover:text-text-tertiary'/></div>
</Tooltip>
)}
{supportCollapse && (
<div className='absolute -left-4 top-1'>
<RiArrowDropDownLine
className={cn(
'w-4 h-4 text-text-tertiary',
collapse && 'transform -rotate-90',
)}
/>
</div>
)}
</div>
{value.length > 0 && (
<>
<div className='flex items-center gap-1 text-text-tertiary system-xs-medium'>
<span>{`${enabledCount}/${value.length}`}</span>
<span>{t('appDebug.agent.tools.enabled')}</span>
</div>
<Divider type='vertical' className='ml-3 mr-1 h-3' />
</>
)}
{!disabled && (
<ActionButton className='mx-1' onClick={() => setOpen(!open)}>
<RiAddLine className='w-4 h-4' />
</ActionButton>
)}
</div>
{!collapse && (
<>
<ToolSelector
scope={scope}
value={undefined}
onSelect={handleAdd}
controlledState={open}
onControlledStateChange={setOpen}
trigger={
<div className=''></div>
}
/>
{value.length === 0 && (
<div className='p-3 flex justify-center rounded-[10px] bg-background-section text-text-tertiary system-xs-regular'>{t('plugin.detailPanel.toolSelector.empty')}</div>
)}
{value.length > 0 && value.map((item, index) => (
<div className='mb-1' key={index}>
<ToolSelector
scope={scope}
value={item}
onSelect={item => handleConfigure(item, index)}
onDelete={() => handleDelete(index)}
supportEnableSwitch
/>
</div>
))}
</>
)}
</>
)
}

View File

@ -40,16 +40,20 @@ import type {
} from '@floating-ui/react'
import cn from '@/utils/classnames'
export type ToolValue = {
provider_name: string
tool_name: string
parameters?: Record<string, any>
enabled?: boolean
extra?: Record<string, any>
}
type Props = {
value?: {
provider_name: string
tool_name: string
parameters?: Record<string, any>
extra?: Record<string, any>
}
disabled?: boolean
placement?: Placement
offset?: OffsetOptions
scope?: string
value?: ToolValue
onSelect: (tool: {
provider_name: string
tool_name: string
@ -57,8 +61,11 @@ type Props = {
extra?: Record<string, any>
}) => void
onDelete?: () => void
supportEnableSwitch?: boolean
supportAddCustomTool?: boolean
scope?: string
trigger?: React.ReactNode
controlledState?: boolean
onControlledStateChange?: (state: boolean) => void
}
const ToolSelector: FC<Props> = ({
value,
@ -68,6 +75,10 @@ const ToolSelector: FC<Props> = ({
onSelect,
onDelete,
scope,
supportEnableSwitch,
trigger,
controlledState,
onControlledStateChange,
}) => {
const { t } = useTranslation()
const [isShow, onShowChange] = useState(false)
@ -95,14 +106,13 @@ const ToolSelector: FC<Props> = ({
provider_name: tool.provider_id,
tool_name: tool.tool_name,
parameters: paramValues,
enabled: tool.is_team_authorization,
extra: {
description: '',
},
}
onSelect(toolValue)
setIsShowChooseTool(false)
// if (tool.provider_type === CollectionType.builtIn && tool.is_team_authorization)
// onShowChange(false)
}
const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
@ -130,6 +140,13 @@ const ToolSelector: FC<Props> = ({
onSelect(toolValue as any)
}
const handleEnabledChange = (state: boolean) => {
onSelect({
...value,
enabled: state,
} as any)
}
// authorization
const { isCurrentWorkspaceManager } = useAppContext()
const [isShowSettingAuth, setShowSettingAuth] = useState(false)
@ -152,14 +169,15 @@ const ToolSelector: FC<Props> = ({
<PortalToFollowElem
placement={placement}
offset={offset}
open={isShow}
onOpenChange={onShowChange}
open={trigger ? controlledState : isShow}
onOpenChange={trigger ? onControlledStateChange : onShowChange}
>
<PortalToFollowElemTrigger
className='w-full'
onClick={handleTriggerClick}
>
{!value?.provider_name && (
{trigger}
{!trigger && !value?.provider_name && (
<ToolTrigger
isConfigure
open={isShow}
@ -167,16 +185,20 @@ const ToolSelector: FC<Props> = ({
provider={currentProvider}
/>
)}
{value?.provider_name && (
{!trigger && value?.provider_name && (
<ToolItem
open={isShow}
icon={currentProvider?.icon}
providerName={value.provider_name}
toolName={value.tool_name}
showSwitch={supportEnableSwitch}
switchValue={value.enabled}
onSwitchChange={handleEnabledChange}
onDelete={onDelete}
noAuth={currentProvider && !currentProvider.is_team_authorization}
onAuth={() => setShowSettingAuth(true)}
// uninstalled
// uninstalled TODO
// isError TODO
errorTip={<div className='space-y-1 text-xs'>
<h3 className='text-text-primary font-semibold'>{t('workflow.nodes.agent.pluginNotInstalled')}</h3>
<p className='text-text-secondary tracking-tight'>{t('workflow.nodes.agent.pluginNotInstalledDesc')}</p>

View File

@ -77,7 +77,10 @@ const ToolItem = ({
)}
<div
className='p-1 rounded-md text-text-tertiary cursor-pointer hover:text-text-destructive'
onClick={onDelete}
onClick={(e) => {
e.stopPropagation()
onDelete?.()
}}
onMouseOver={() => setIsDeleting(true)}
onMouseLeave={() => setIsDeleting(false)}
>
@ -85,11 +88,13 @@ const ToolItem = ({
</div>
</div>
{!isError && !uninstalled && !noAuth && showSwitch && (
<Switch
className='mr-1'
size='md'
defaultValue={switchValue}
onChange={onSwitchChange} />
<div className='mr-1' onClick={e => e.stopPropagation()}>
<Switch
size='md'
defaultValue={switchValue}
onChange={onSwitchChange}
/>
</div>
)}
{!isError && !uninstalled && noAuth && (
<Button variant='secondary' size='small' onClick={onAuth}>