diff --git a/web/app/components/app/configuration/config/agent/agent-tools/group-auth-control.tsx b/web/app/components/app/configuration/config/agent/agent-tools/group-auth-control.tsx new file mode 100644 index 0000000000..2272d4558c --- /dev/null +++ b/web/app/components/app/configuration/config/agent/agent-tools/group-auth-control.tsx @@ -0,0 +1,142 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import { RiArrowDownSLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import Indicator from '@/app/components/header/indicator' +import Authorize from '@/app/components/plugins/plugin-auth/authorize' +import Authorized from '@/app/components/plugins/plugin-auth/authorized' +import { AuthCategory } from '@/app/components/plugins/plugin-auth' +import { usePluginAuth } from '@/app/components/plugins/plugin-auth/hooks/use-plugin-auth' +import type { Credential } from '@/app/components/plugins/plugin-auth/types' +import cn from '@/utils/classnames' +import type { CollectionType } from '@/app/components/tools/types' + +type GroupAuthControlProps = { + providerId: string + providerName: string + providerType: CollectionType + credentialId?: string + onChange: (credentialId: string) => void +} + +const GroupAuthControl: FC = ({ + providerId, + providerName, + providerType, + credentialId, + onChange, +}) => { + const { t } = useTranslation() + const { + isAuthorized, + canOAuth, + canApiKey, + credentials, + disabled, + invalidPluginCredentialInfo, + notAllowCustomCredential, + } = usePluginAuth({ + provider: providerName, + providerType, + category: AuthCategory.tool, + detail: { id: providerId, name: providerName, type: providerType } as any, + }, true) + + const extraAuthorizationItems: Credential[] = [ + { + id: '__workspace_default__', + name: t('plugin.auth.workspaceDefault'), + provider: '', + is_default: !credentialId, + isWorkspaceDefault: true, + }, + ] + + const handleAuthorizationItemClick = useCallback((id: string) => { + onChange(id === '__workspace_default__' ? '' : id) + }, [onChange]) + + const renderTrigger = useCallback((open?: boolean) => { + let label = '' + let removed = false + let unavailable = false + let color = 'green' + if (!credentialId) { + label = t('plugin.auth.workspaceDefault') + } + else { + const credential = credentials.find(c => c.id === credentialId) + label = credential ? credential.name : t('plugin.auth.authRemoved') + removed = !credential + unavailable = !!credential?.not_allowed_to_use && !credential?.from_enterprise + if (removed) + color = 'red' + else if (unavailable) + color = 'gray' + } + + return ( + + ) + }, [credentialId, credentials, t]) + + if (!isAuthorized) { + return ( + + ) + } + + return ( + + ) +} + +export default React.memo(GroupAuthControl) diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx index 133f41074f..5e03d3e529 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx @@ -18,13 +18,13 @@ import AppIcon from '@/app/components/base/app-icon' import Button from '@/app/components/base/button' import Indicator from '@/app/components/header/indicator' import Switch from '@/app/components/base/switch' +import GroupAuthControl from './group-auth-control' import ConfigContext from '@/context/debug-configuration' import type { AgentTool } from '@/types/app' import { type Collection, CollectionType } from '@/app/components/tools/types' import { MAX_TOOLS_NUM } from '@/config' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import Tooltip from '@/app/components/base/tooltip' -import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other' import cn from '@/utils/classnames' import ToolPicker from '@/app/components/workflow/block-selector/tool-picker' import type { ToolDefaultValue, ToolValue } from '@/app/components/workflow/block-selector/types' @@ -207,133 +207,191 @@ const AgentTools: FC = () => { } > -
- {tools.map((item: AgentTool & { icon: any; collection?: Collection }, index) => ( -
+ {Object.values( + tools.reduce((acc, item, idx) => { + const key = item.provider_id + if (!acc[key]) { + acc[key] = { + providerId: item.provider_id, + providerName: getProviderShowName(item) || '', + icon: item.icon, + providerType: item.provider_type, + tools: [] as (AgentTool & { __index: number })[], + } + } + acc[key].tools.push({ ...item, __index: idx }) + return acc + }, {} as Record), + ).map(group => ( +
{ + // 调试:查看 provider 及其工具数据 + console.log('provider group', group) + }} > -
- {item.isDeleted && } - {!item.isDeleted && ( -
- {typeof item.icon === 'string' &&
} - {typeof item.icon !== 'string' && } -
- )} -
- {getProviderShowName(item)} - {item.tool_label} - {!item.isDeleted && ( - -
{item.tool_name}
-
{t('tools.toolNameUsageTip')}
-
copy(item.tool_name)}>{t('tools.copyToolName')}
-
- } - > -
-
- -
-
- - )} +
+ {typeof group.icon === 'string' + ?
+ : } +
{group.providerName}
+
+ !!t.credential_id)?.credential_id} + onChange={(id) => { + const newModelConfig = produce(modelConfig, (draft) => { + draft.agentConfig.tools.forEach((tool: any) => { + if (tool.provider_id === group.providerId) + tool.credential_id = id + }) + }) + setModelConfig(newModelConfig) + formattingChangedDispatcher() + }} + />
-
-
- {item.isDeleted && ( -
- -
- -
-
-
{ - const newModelConfig = produce(modelConfig, (draft) => { - draft.agentConfig.tools.splice(index, 1) - }) - setModelConfig(newModelConfig) - formattingChangedDispatcher() - }} - onMouseOver={() => setIsDeleting(index)} - onMouseLeave={() => setIsDeleting(-1)} - > - -
-
- )} - {!item.isDeleted && ( -
- {!item.notAuthor && ( - -
{ - setCurrentTool(item) - setIsShowSettingTool(true) - }}> - -
-
- )} -
{ - const newModelConfig = produce(modelConfig, (draft) => { - draft.agentConfig.tools.splice(index, 1) - }) - setModelConfig(newModelConfig) - formattingChangedDispatcher() - }} - onMouseOver={() => setIsDeleting(index)} - onMouseLeave={() => setIsDeleting(-1)} - > - -
-
- )} -
- {!item.notAuthor && ( - { - const newModelConfig = produce(modelConfig, (draft) => { - (draft.agentConfig.tools[index] as any).enabled = enabled - }) - setModelConfig(newModelConfig) - formattingChangedDispatcher() - }} /> - )} - {item.notAuthor && ( - - )} -
+ }} + > + {t('tools.notAuthorized')} + + + )} +
+
+ {group.tools.map(item => ( +
{ + // 调试:查看工具行数据 + + console.log('tool item', item) + }} + > +
+
+ {item.tool_label} + {item.tool_name} + {!item.isDeleted && ( + +
{item.tool_name}
+
{t('tools.toolNameUsageTip')}
+
copy(item.tool_name)}>{t('tools.copyToolName')}
+
+ } + > +
+
+ +
+
+ + )} +
+
+ +
+ {item.isDeleted && ( +
+ +
+ +
+
+
{ + const newModelConfig = produce(modelConfig, (draft) => { + draft.agentConfig.tools.splice(item.__index, 1) + }) + setModelConfig(newModelConfig) + formattingChangedDispatcher() + }} + onMouseOver={() => setIsDeleting(item.__index)} + onMouseLeave={() => setIsDeleting(-1)} + > + +
+
+ )} + {!item.isDeleted && ( +
+ {!item.notAuthor && ( + +
{ + setCurrentTool(item as any) + setIsShowSettingTool(true) + }}> + +
+
+ )} +
{ + const newModelConfig = produce(modelConfig, (draft) => { + draft.agentConfig.tools.splice(item.__index, 1) + }) + setModelConfig(newModelConfig) + formattingChangedDispatcher() + }} + onMouseOver={() => setIsDeleting(item.__index)} + onMouseLeave={() => setIsDeleting(-1)} + > + +
+
+ )} +
+ {!item.notAuthor && ( + { + const newModelConfig = produce(modelConfig, (draft) => { + (draft.agentConfig.tools[item.__index] as any).enabled = enabled + }) + setModelConfig(newModelConfig) + formattingChangedDispatcher() + }} /> + )} +
+
+
+ ))}
))} -
+
{isShowSettingTool && (