-
{t('workflow.nodes.agent.pluginNotInstalled')}
-
{t('workflow.nodes.agent.pluginNotInstalledDesc')}
+ uninstalled={!currentProvider && inMarketPlace}
+ isInstalling={isPending}
+ onInstall={() => handleInstall()}
+ isError={!currentProvider && !inMarketPlace}
+ errorTip={
+
{t('plugin.detailPanel.toolSelector.uninstalledTitle')}
+
{t('plugin.detailPanel.toolSelector.uninstalledContent')}
- {t('workflow.nodes.agent.linkToPlugin')}
+ {t('plugin.detailPanel.toolSelector.uninstalledLink')}
}
/>
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx
index f2ff56d07bd71f..c393d70a2513ba 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx
@@ -6,6 +6,7 @@ import {
RiEqualizer2Line,
RiErrorWarningFill,
} from '@remixicon/react'
+import { Group } from '@/app/components/base/icons/src/vender/other'
import AppIcon from '@/app/components/base/app-icon'
import Switch from '@/app/components/base/switch'
import Button from '@/app/components/base/button'
@@ -61,10 +62,21 @@ const ToolItem = ({
open && 'bg-components-panel-on-panel-item-bg-hover shadow-sm',
isDeleting && 'hover:bg-state-destructive-hover border-state-destructive-border shadow-xs',
)}>
-
- {typeof icon === 'string' &&
}
- {typeof icon !== 'string' &&
}
-
+ {icon && (
+
+ {typeof icon === 'string' &&
}
+ {typeof icon !== 'string' &&
}
+
+ )}
+ {!icon && (
+
+ )}
{providerNameText}
{toolName}
diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts
index d36ba4109a8ced..18282b730d01b9 100644
--- a/web/app/components/plugins/types.ts
+++ b/web/app/components/plugins/types.ts
@@ -81,7 +81,7 @@ export type PluginManifestInMarket = {
icon: string
label: Record
category: PluginType
- version: string // conbine the other place to it
+ version: string // combine the other place to it
latest_version: string
brief: Record
introduction: string
@@ -108,6 +108,11 @@ export type PluginDetail = {
meta?: MetaData
}
+export type PluginInfoFromMarketPlace = {
+ category: PluginType
+ latest_package_identifier: string
+}
+
export type Plugin = {
type: 'plugin' | 'bundle' | 'model' | 'extension' | 'tool' | 'agent_strategy'
org: string
diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx
index 4b38111b1d82f2..638f5b7259aec2 100644
--- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx
+++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx
@@ -83,7 +83,6 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
}, [query, list])
// TODO: should be replaced by real data
const isExternalInstalled = true
- // TODO: 验证这玩意写对了没
const icon = list?.find(
coll => coll.tools?.find(tool => tool.name === value?.agent_strategy_name),
)?.icon as string | undefined
@@ -125,9 +124,10 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
onChange({
agent_strategy_name: tool!.tool_name,
agent_strategy_provider_name: tool!.provider_name,
- agent_parameters: tool!.params,
agent_strategy_label: tool!.tool_label,
agent_output_schema: tool!.output_schema,
+ agent_configurations: {},
+ agent_parameters: {},
})
setOpen(false)
}}
diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
index ce0613dedf1f05..8f10d91f9e289e 100644
--- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
+++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
@@ -19,7 +19,8 @@ export type Strategy = {
agent_strategy_provider_name: string
agent_strategy_name: string
agent_strategy_label: string
- agent_parameters?: ToolVarInputs
+ agent_configurations?: Record
+ agent_parameters?: Record
agent_output_schema: Record
}
diff --git a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx
index aca1a75f5d77cf..893472af5e3cd0 100644
--- a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx
+++ b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx
@@ -1,30 +1,40 @@
import Tooltip from '@/app/components/base/tooltip'
import Indicator from '@/app/components/header/indicator'
import classNames from '@/utils/classnames'
-import { useRef } from 'react'
+import { useMemo, useRef } from 'react'
+import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools } from '@/service/use-tools'
export type ToolIconProps = {
- src: string
- alt?: string
status?: 'error' | 'warning'
tooltip?: string
+ providerName: string
}
-export const ToolIcon = ({ src, status, tooltip, alt }: ToolIconProps) => {
+export const ToolIcon = ({ status, tooltip, providerName }: ToolIconProps) => {
const indicator = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined
const containerRef = useRef(null)
const notSuccess = (['error', 'warning'] as Array).includes(status)
+ const { data: buildInTools } = useAllBuiltInTools()
+ const { data: customTools } = useAllCustomTools()
+ const { data: workflowTools } = useAllWorkflowTools()
+ const currentProvider = useMemo(() => {
+ const mergedTools = [...(buildInTools || []), ...(customTools || []), ...(workflowTools || [])]
+ return mergedTools.find((toolWithProvider) => {
+ return toolWithProvider.name === providerName
+ })
+ }, [providerName, buildInTools, customTools, workflowTools])
return
+ {/* eslint-disable-next-line @next/next/no-img-element */}
diff --git a/web/app/components/workflow/nodes/agent/node.tsx b/web/app/components/workflow/nodes/agent/node.tsx
index 626f38b11f03a9..fa0513229528f0 100644
--- a/web/app/components/workflow/nodes/agent/node.tsx
+++ b/web/app/components/workflow/nodes/agent/node.tsx
@@ -8,14 +8,11 @@ import type { ToolIconProps } from './components/tool-icon'
import { ToolIcon } from './components/tool-icon'
import useConfig from './use-config'
import { useTranslation } from 'react-i18next'
-import { useInstalledPluginList } from '@/service/use-plugins'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
const AgentNode: FC
> = (props) => {
const { inputs, currentStrategy } = useConfig(props.id, props.data)
const { t } = useTranslation()
- const pluginList = useInstalledPluginList()
- // TODO: Implement models
const models = useMemo(() => {
if (!inputs) return []
// if selected, show in node
@@ -24,7 +21,7 @@ const AgentNode: FC> = (props) => {
const models = currentStrategy?.parameters
.filter(param => param.type === FormTypeEnum.modelSelector)
.reduce((acc, param) => {
- const item = inputs.agent_parameters?.[param.name]
+ const item = inputs.agent_configurations?.[param.name]
if (!item) {
if (param.required) {
acc.push({ param: param.name })
@@ -41,18 +38,29 @@ const AgentNode: FC> = (props) => {
const tools = useMemo(() => {
const tools: Array = []
currentStrategy?.parameters.forEach((param) => {
- if (['array[tool]', 'tool'].includes(param.type)) {
- const vari = inputs.agent_parameters?.[param.name]
- if (!vari) return
- if (Array.isArray(vari.value)) {
- // TODO: Implement array of tools
+ if (param.type === FormTypeEnum.toolSelector) {
+ const field = param.name
+ const value = inputs.agent_configurations?.[field]
+ if (value) {
+ tools.push({
+ providerName: value.provider_name as any,
+ })
}
- else {
- // TODO: Implement single tool
+ }
+ if (param.type === FormTypeEnum.multiToolSelector) {
+ const field = param.name
+ const value = inputs.agent_configurations?.[field]
+ if (value) {
+ (value as unknown as any[]).forEach((item) => {
+ tools.push({
+ providerName: item.provider_name,
+ })
+ })
}
}
})
- }, [currentStrategy, inputs.agent_parameters])
+ return tools
+ }, [currentStrategy?.parameters, inputs.agent_configurations])
return
{inputs.agent_strategy_name
?
> = (props) => {
{inputs.agent_strategy_label}
:
}
- {models.length &&
0 &&
{t('workflow.nodes.agent.model')}
}
@@ -85,25 +93,13 @@ const AgentNode: FC> = (props) => {
/>
})}
}
-
+ {tools.length > 0 &&
{t('workflow.nodes.agent.toolbox')}
}>
-
-
-
+ {tools.map(tool => )}
-
+ }
}
diff --git a/web/app/components/workflow/nodes/agent/panel.tsx b/web/app/components/workflow/nodes/agent/panel.tsx
index 4f1d2089489d9c..af4bf9dc88a74d 100644
--- a/web/app/components/workflow/nodes/agent/panel.tsx
+++ b/web/app/components/workflow/nodes/agent/panel.tsx
@@ -28,25 +28,27 @@ const AgentPanel: FC> = (props) => {
strategy={inputs.agent_strategy_name ? {
agent_strategy_provider_name: inputs.agent_strategy_provider_name!,
agent_strategy_name: inputs.agent_strategy_name!,
- agent_parameters: inputs.agent_parameters,
+ agent_configurations: inputs.agent_configurations,
agent_strategy_label: inputs.agent_strategy_label!,
agent_output_schema: inputs.output_schema,
+ agent_parameters: inputs.agent_parameters,
} : undefined}
onStrategyChange={(strategy) => {
setInputs({
...inputs,
agent_strategy_provider_name: strategy?.agent_strategy_provider_name,
agent_strategy_name: strategy?.agent_strategy_name,
+ agent_configurations: strategy?.agent_configurations,
agent_parameters: strategy?.agent_parameters,
agent_strategy_label: strategy?.agent_strategy_label,
output_schema: strategy!.agent_output_schema,
})
}}
formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []}
- formValue={inputs.agent_parameters || {}}
+ formValue={inputs.agent_configurations || {}}
onFormValueChange={value => setInputs({
...inputs,
- agent_parameters: value,
+ agent_configurations: value,
})}
/>
diff --git a/web/app/components/workflow/nodes/agent/types.ts b/web/app/components/workflow/nodes/agent/types.ts
index c3351c1d9d21a8..66e576cb9c53e2 100644
--- a/web/app/components/workflow/nodes/agent/types.ts
+++ b/web/app/components/workflow/nodes/agent/types.ts
@@ -5,7 +5,7 @@ export type AgentNodeType = CommonNodeType & {
agent_strategy_provider_name?: string
agent_strategy_name?: string
agent_strategy_label?: string
- agent_parameters?: Record
- agent_configurations?: Record
+ agent_parameters?: Record
+ agent_configurations?: Record
output_schema: Record
}
diff --git a/web/i18n/en-US/plugin.ts b/web/i18n/en-US/plugin.ts
index a706f8f0420cea..f3807baf53be10 100644
--- a/web/i18n/en-US/plugin.ts
+++ b/web/i18n/en-US/plugin.ts
@@ -74,6 +74,9 @@ const translation = {
auth: 'AUTHORIZATION',
settings: 'TOOL SETTINGS',
empty: 'Click the \'+\' button to add tools. You can add multiple tools.',
+ uninstalledTitle: 'Tool not installed',
+ uninstalledContent: 'This plugin is installed from the local/GitHub repository. Please use after installation.',
+ uninstalledLink: 'Manage in Plugins',
},
configureApp: 'Configure App',
configureModel: 'Configure model',
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts
index f58bc3fdd03422..64ea5b93171cd6 100644
--- a/web/i18n/en-US/workflow.ts
+++ b/web/i18n/en-US/workflow.ts
@@ -714,6 +714,11 @@ const translation = {
install: 'Install',
installing: 'Installing',
},
+ modelNotInMarketplace: {
+ title: 'Model not installed',
+ desc: 'This model is not installed from the marketplace. Please go to Plugins to reinstall.',
+ manageInPlugins: 'Manage in Plugins',
+ },
configureModel: 'Configure Model',
notAuthorized: 'Not Authorized',
model: 'model',
diff --git a/web/i18n/zh-Hans/plugin.ts b/web/i18n/zh-Hans/plugin.ts
index 4b7c764d9f18eb..f1eb6e954117d3 100644
--- a/web/i18n/zh-Hans/plugin.ts
+++ b/web/i18n/zh-Hans/plugin.ts
@@ -74,6 +74,9 @@ const translation = {
auth: '授权',
settings: '工具设置',
empty: '点击 "+" 按钮添加工具。您可以添加多个工具。',
+ uninstalledTitle: '工具未安装',
+ uninstalledContent: '此插件安装自 本地 / GitHub 仓库,请安装后使用。',
+ uninstalledLink: '在插件中管理',
},
configureApp: '应用设置',
configureModel: '模型设置',
diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts
index 82c92dc983608c..fce1fd74b2ec1e 100644
--- a/web/i18n/zh-Hans/workflow.ts
+++ b/web/i18n/zh-Hans/workflow.ts
@@ -714,6 +714,11 @@ const translation = {
install: '安装',
installing: '安装中',
},
+ modelNotInMarketplace: {
+ title: '模型未安装',
+ desc: '此模型未从市场安装。请转到插件重新安装。',
+ manageInPlugins: '在插件中管理',
+ },
model: '模型',
toolbox: '工具箱',
strategyNotSet: '代理策略未设置',
diff --git a/web/service/plugins.ts b/web/service/plugins.ts
index 28579488609df7..0a880b865fd432 100644
--- a/web/service/plugins.ts
+++ b/web/service/plugins.ts
@@ -5,6 +5,7 @@ import type {
InstallPackageResponse,
Permissions,
PluginDeclaration,
+ PluginInfoFromMarketPlace,
PluginManifestInMarket,
PluginTasksResponse,
TaskStatusResponse,
@@ -75,6 +76,13 @@ export const fetchBundleInfoFromMarketPlace = async ({
return getMarketplace<{ data: { version: { dependencies: Dependency[] } } }>(`/bundles/${org}/${name}/${version}`)
}
+export const fetchPluginInfoFromMarketPlace = async ({
+ org,
+ name,
+}: Record) => {
+ return getMarketplace<{ data: { plugin: PluginInfoFromMarketPlace, version: { version: string } } }>(`/plugins/${org}/${name}`)
+}
+
export const fetchMarketplaceCollections: Fetcher = ({ url }) => {
return get(url)
}
diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts
index a901d98c8e4ef4..5ad7d831d938ec 100644
--- a/web/service/use-plugins.ts
+++ b/web/service/use-plugins.ts
@@ -9,6 +9,7 @@ import type {
Permissions,
Plugin,
PluginDetail,
+ PluginInfoFromMarketPlace,
PluginTask,
PluginsFromMarketplaceByInfoResponse,
PluginsFromMarketplaceResponse,
@@ -91,6 +92,7 @@ export const useUpdatePackageFromMarketPlace = () => {
export const useVersionListOfPlugin = (pluginID: string) => {
return useQuery<{ data: VersionListResponse }>({
+ enabled: !!pluginID,
queryKey: [NAME_SPACE, 'versions', pluginID],
queryFn: () => getMarketplace<{ data: VersionListResponse }>(`/plugins/${pluginID}/versions`, { params: { page: 1, page_size: 100 } }),
})
@@ -399,6 +401,15 @@ export const useMutationClearAllTaskPlugin = () => {
})
}
+export const usePluginManifestInfo = (pluginUID: string) => {
+ return useQuery({
+ enabled: !!pluginUID,
+ queryKey: [[NAME_SPACE, 'manifest', pluginUID]],
+ queryFn: () => getMarketplace<{ data: { plugin: PluginInfoFromMarketPlace, version: { version: string } } }>(`/plugins/${pluginUID}`),
+ retry: 0,
+ })
+}
+
export const useDownloadPlugin = (info: { organization: string; pluginName: string; version: string }, needDownload: boolean) => {
return useQuery({
queryKey: [NAME_SPACE, 'downloadPlugin', info],