diff --git a/web/app/components/plugins/card/index.tsx b/web/app/components/plugins/card/index.tsx index ff86a3c39c..04ef0dd1ee 100644 --- a/web/app/components/plugins/card/index.tsx +++ b/web/app/components/plugins/card/index.tsx @@ -11,7 +11,7 @@ import Placeholder from './base/placeholder' import cn from '@/utils/classnames' import { useGetLanguage } from '@/context/i18n' import { getLanguage } from '@/i18n/language' -import { useCategories } from '../hooks' +import { useSingleCategories } from '../hooks' import { renderI18nObject } from '@/hooks/use-i18n' export type Props = { @@ -43,7 +43,7 @@ const Card = ({ }: Props) => { const defaultLocale = useGetLanguage() const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale - const { categoriesMap } = useCategories() + const { categoriesMap } = useSingleCategories() const { category, type, name, org, label, brief, icon, verified } = payload const isBundle = !['plugin', 'model', 'tool', 'extension', 'agent_strategy'].includes(type) const cornerMark = isBundle ? categoriesMap.bundle?.label : categoriesMap[category]?.label diff --git a/web/app/components/plugins/hooks.ts b/web/app/components/plugins/hooks.ts index 7c3003497b..371b769019 100644 --- a/web/app/components/plugins/hooks.ts +++ b/web/app/components/plugins/hooks.ts @@ -64,3 +64,31 @@ export const useCategories = (translateFromOut?: TFunction) => { categoriesMap, } } + +export const useSingleCategories = (translateFromOut?: TFunction) => { + const { t: translation } = useTranslation() + const t = translateFromOut || translation + + const categories = categoryKeys.map((category) => { + if (category === 'agent') { + return { + name: 'agent_strategy', + label: t(`plugin.categorySingle.${category}`), + } + } + return { + name: category, + label: t(`plugin.categorySingle.${category}`), + } + }) + + const categoriesMap = categories.reduce((acc, category) => { + acc[category.name] = category + return acc + }, {} as Record) + + return { + categories, + categoriesMap, + } +} diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx index 692b2ecd26..4d20c0877d 100644 --- a/web/app/components/plugins/plugin-detail-panel/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -9,7 +9,6 @@ import AgentStrategyList from './agent-strategy-list' import Drawer from '@/app/components/base/drawer' import type { PluginDetail } from '@/app/components/plugins/types' import cn from '@/utils/classnames' -import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector' type Props = { detail?: PluginDetail @@ -28,16 +27,6 @@ const PluginDetailPanel: FC = ({ onUpdate() } - const [value, setValue] = React.useState({ - provider_name: 'langgenius/google/google', - tool_name: 'google_search', - }) - - const testHandle = (item: any) => { - console.log(item) - setValue(item) - } - if (!detail) return null @@ -63,17 +52,6 @@ const PluginDetailPanel: FC = ({ {!!detail.declaration.agent_strategy && } {!!detail.declaration.endpoint && } {!!detail.declaration.model && } - {false && ( -
- testHandle(item)} - onDelete={() => testHandle(null)} - supportEnableSwitch - /> -
- )} )} diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx index 220edc22ca..e20673f7fa 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx @@ -102,7 +102,7 @@ const ToolSelector: FC = ({ const currentProvider = useMemo(() => { const mergedTools = [...(buildInTools || []), ...(customTools || []), ...(workflowTools || [])] return mergedTools.find((toolWithProvider) => { - return toolWithProvider.id === value?.provider_name && toolWithProvider.tools.some(tool => tool.name === value?.tool_name) + return toolWithProvider.id === value?.provider_name }) }, [value, buildInTools, customTools, workflowTools]) @@ -172,7 +172,9 @@ const ToolSelector: FC = ({ }) // install from marketplace - + const currentTool = useMemo(() => { + return currentProvider?.tools.find(tool => tool.name === value?.tool_name) + }, [currentProvider?.tools, value?.tool_name]) const manifestIcon = useMemo(() => { if (!manifest) return '' @@ -193,7 +195,10 @@ const ToolSelector: FC = ({ > { + if (!currentProvider || !currentTool) return + handleTriggerClick() + }} > {trigger} {!trigger && !value?.provider_name && ( @@ -214,19 +219,22 @@ const ToolSelector: FC = ({ switchValue={value.enabled} onSwitchChange={handleEnabledChange} onDelete={onDelete} - noAuth={currentProvider && !currentProvider.is_team_authorization} + noAuth={currentProvider && currentTool && !currentProvider.is_team_authorization} onAuth={() => setShowSettingAuth(true)} uninstalled={!currentProvider && inMarketPlace} + versionMismatch={currentProvider && inMarketPlace && !currentTool} installInfo={manifest?.latest_package_identifier} onInstall={() => handleInstall()} - isError={!currentProvider && !inMarketPlace} - errorTip={
-

{t('plugin.detailPanel.toolSelector.uninstalledTitle')}

-

{t('plugin.detailPanel.toolSelector.uninstalledContent')}

-

- {t('plugin.detailPanel.toolSelector.uninstalledLink')} -

-
} + isError={(!currentProvider || !currentTool) && !inMarketPlace} + errorTip={ +
+

{currentTool ? t('plugin.detailPanel.toolSelector.uninstalledTitle') : t('plugin.detailPanel.toolSelector.unsupportedTitle')}

+

{currentTool ? t('plugin.detailPanel.toolSelector.uninstalledContent') : t('plugin.detailPanel.toolSelector.unsupportedContent')}

+

+ {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 45a9abd592..3e32e66929 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 @@ -13,7 +13,9 @@ import Button from '@/app/components/base/button' import Indicator from '@/app/components/header/indicator' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' +import { ToolTipContent } from '@/app/components/base/tooltip/content' import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' +import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version' import cn from '@/utils/classnames' type Props = { @@ -31,6 +33,7 @@ type Props = { uninstalled?: boolean installInfo?: string onInstall?: () => void + versionMismatch?: boolean open: boolean } @@ -50,10 +53,11 @@ const ToolItem = ({ onInstall, isError, errorTip, + versionMismatch, }: Props) => { const { t } = useTranslation() const providerNameText = providerName?.split('/').pop() - const isTransparent = uninstalled || isError + const isTransparent = uninstalled || versionMismatch || isError const [isDeleting, setIsDeleting] = useState(false) return ( @@ -82,7 +86,7 @@ const ToolItem = ({
{toolName}
- {!noAuth && !isError && !uninstalled && ( + {!noAuth && !isError && !uninstalled && !versionMismatch && ( @@ -99,7 +103,7 @@ const ToolItem = ({
- {!isError && !uninstalled && !noAuth && showSwitch && ( + {!isError && !uninstalled && !noAuth && !versionMismatch && showSwitch && (
e.stopPropagation()}>
)} - {!isError && !uninstalled && noAuth && ( + {!isError && !uninstalled && !versionMismatch && noAuth && ( )} + {!isError && !uninstalled && versionMismatch && installInfo && ( +
e.stopPropagation()}> + + {`${t('plugin.detailPanel.toolSelector.unsupportedContent')} ${t('plugin.detailPanel.toolSelector.unsupportedContent2')}`} + + } + onChange={() => { + onInstall?.() + }} + /> +
+ )} {!isError && uninstalled && installInfo && ( e.stopPropagation()} diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index ef564230ea..3dd520d39a 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -22,7 +22,7 @@ import cn from '@/utils/classnames' import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config' import { useInvalidateInstalledPluginList } from '@/service/use-plugins' import { useInvalidateAllBuiltInTools, useInvalidateAllToolProviders } from '@/service/use-tools' -import { useCategories } from '../hooks' +import { useSingleCategories } from '../hooks' import { useProviderContext } from '@/context/provider-context' import { useRenderI18nObject } from '@/hooks/use-i18n' @@ -36,7 +36,7 @@ const PluginItem: FC = ({ plugin, }) => { const { t } = useTranslation() - const { categoriesMap } = useCategories() + const { categoriesMap } = useSingleCategories() const currentPluginID = usePluginPageContext(v => v.currentPluginID) const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID) const invalidateInstalledPluginList = useInvalidateInstalledPluginList() diff --git a/web/app/components/plugins/plugin-mutation-model/index.tsx b/web/app/components/plugins/plugin-mutation-model/index.tsx new file mode 100644 index 0000000000..fadfd9a957 --- /dev/null +++ b/web/app/components/plugins/plugin-mutation-model/index.tsx @@ -0,0 +1,71 @@ +import type { FC, ReactNode } from 'react' +import React, { memo } from 'react' +import Card from '@/app/components/plugins/card' +import Modal from '@/app/components/base/modal' +import Button from '@/app/components/base/button' +import type { Plugin } from '../types' +import type { UseMutationResult } from '@tanstack/react-query' + +type Props = { + plugin: Plugin + onSave: () => void + onCancel: () => void + mutation: UseMutationResult + confirmButtonText: ReactNode + cancelButtonText: ReactNode + modelTitle: ReactNode + description: ReactNode + cardTitleLeft: ReactNode +} + +const PluginMutationModal: FC = ({ + plugin, + onCancel, + mutation, + confirmButtonText, + cancelButtonText, + modelTitle, + description, + cardTitleLeft, +}: Props) => { + return ( + +
+ {description} +
+
+ +
+
+ {mutation.isPending && ( + + )} + +
+
+ ) +} + +PluginMutationModal.displayName = 'PluginMutationModal' + +export default memo(PluginMutationModal) diff --git a/web/app/components/workflow/constants.ts b/web/app/components/workflow/constants.ts index 922caded51..87f1e01f56 100644 --- a/web/app/components/workflow/constants.ts +++ b/web/app/components/workflow/constants.ts @@ -404,6 +404,7 @@ export const SUPPORT_OUTPUT_VARS_NODE = [ BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner, BlockEnum.VariableAggregator, BlockEnum.QuestionClassifier, BlockEnum.ParameterExtractor, BlockEnum.Iteration, BlockEnum.DocExtractor, BlockEnum.ListFilter, + BlockEnum.Agent, ] export const LLM_OUTPUT_STRUCT: Var[] = [ diff --git a/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx b/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx index c375d3198a..8f6f751bc3 100644 --- a/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx +++ b/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx @@ -6,19 +6,20 @@ import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-v import { RiArrowLeftRightLine } from '@remixicon/react' import type { ReactNode } from 'react' import { type FC, useCallback, useState } from 'react' -import cn from '@/utils/classnames' import UpdateFromMarketplace from '@/app/components/plugins/update-plugin/from-market-place' import { useBoolean } from 'ahooks' import { useCheckInstalled } from '@/service/use-plugins' +import cn from '@/utils/classnames' export type SwitchPluginVersionProps = { uniqueIdentifier: string tooltip?: ReactNode onChange?: (version: string) => void + className?: string } export const SwitchPluginVersion: FC = (props) => { - const { uniqueIdentifier, tooltip, onChange } = props + const { uniqueIdentifier, tooltip, onChange, className } = props const [pluginId] = uniqueIdentifier.split(':') const [isShow, setIsShow] = useState(false) const [isShowUpdateModal, { setTrue: showUpdateModal, setFalse: hideUpdateModal }] = useBoolean(false) @@ -40,7 +41,7 @@ export const SwitchPluginVersion: FC = (props) => { return uniqueIdentifier.replaceAll(pluginDetail.version, targetVersion) })() return -
+
{isShowUpdateModal && pluginDetail && { + Object.keys(payload.output_schema?.properties || {}).forEach((outputKey) => { const output = payload.output_schema.properties[outputKey] outputs.push({ variable: outputKey, type: output.type === 'array' ? `Array[${output.items?.type.slice(0, 1).toLocaleUpperCase()}${output.items?.type.slice(1)}]` as VarType : `${output.type.slice(0, 1).toLocaleUpperCase()}${output.type.slice(1)}` as VarType, - // TODO: is this required? - // @ts-expect-error todo added - description: output.description, }) }) res.vars = [ diff --git a/web/app/components/workflow/run/agent-log/agent-log-item.tsx b/web/app/components/workflow/run/agent-log/agent-log-item.tsx index 26c734874d..8403dd68dc 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-item.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-item.tsx @@ -30,26 +30,26 @@ const AgentLogItem = ({
setExpanded(!expanded)} > { expanded - ? - : + ? + : }
{label}
-
0.02s
+ {/*
0.02s
*/}
{ expanded && (
{ - !!children.length && ( + !!children?.length && ( { diff --git a/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx b/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx index 825f59f085..987c3afc2a 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx @@ -24,7 +24,9 @@ const AgentLogTrigger = ({
onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)} + onClick={() => { + onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren) + }} > Detail diff --git a/web/app/components/workflow/run/special-result-panel.tsx b/web/app/components/workflow/run/special-result-panel.tsx index 52e3786fad..cdd77894f5 100644 --- a/web/app/components/workflow/run/special-result-panel.tsx +++ b/web/app/components/workflow/run/special-result-panel.tsx @@ -36,7 +36,10 @@ const SpecialResultPanel = ({ handleShowAgentOrToolLog, }: SpecialResultPanelProps) => { return ( - <> +
{ + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + }}> { !!showRetryDetail && !!retryResultList?.length && setShowRetryDetailFalse && ( ) } - +
) } diff --git a/web/app/components/workflow/run/tracing-panel.tsx b/web/app/components/workflow/run/tracing-panel.tsx index d65b7b73eb..758e9cfcf8 100644 --- a/web/app/components/workflow/run/tracing-panel.tsx +++ b/web/app/components/workflow/run/tracing-panel.tsx @@ -166,7 +166,13 @@ const TracingPanel: FC = ({ } return ( -
+
{ + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + }} + > {treeNodes.map(renderNode)}
) diff --git a/web/i18n/en-US/plugin.ts b/web/i18n/en-US/plugin.ts index f3807baf53..603b403435 100644 --- a/web/i18n/en-US/plugin.ts +++ b/web/i18n/en-US/plugin.ts @@ -3,10 +3,17 @@ const translation = { all: 'All', models: 'Models', tools: 'Tools', - agents: 'Agent Strategy', + agents: 'Agent Strategies', extensions: 'Extensions', bundles: 'Bundles', }, + categorySingle: { + model: 'Model', + tool: 'Tool', + agent: 'Agent Strategy', + extension: 'Extension', + bundle: 'Bundle', + }, search: 'Search', allCategories: 'All Categories', searchCategories: 'Search Categories', @@ -77,6 +84,9 @@ const translation = { uninstalledTitle: 'Tool not installed', uninstalledContent: 'This plugin is installed from the local/GitHub repository. Please use after installation.', uninstalledLink: 'Manage in Plugins', + unsupportedTitle: 'Unsupported Action', + unsupportedContent: 'The installed plugin version does not provide this action.', + unsupportedContent2: 'Click to switch version.', }, configureApp: 'Configure App', configureModel: 'Configure model', diff --git a/web/i18n/zh-Hans/plugin.ts b/web/i18n/zh-Hans/plugin.ts index f1eb6e9541..55d7dd81f0 100644 --- a/web/i18n/zh-Hans/plugin.ts +++ b/web/i18n/zh-Hans/plugin.ts @@ -7,6 +7,13 @@ const translation = { extensions: '扩展', bundles: '插件集', }, + categorySingle: { + model: '模型', + tool: '工具', + agent: 'Agent 策略', + extension: '扩展', + bundle: '插件集', + }, search: '搜索', allCategories: '所有类别', searchCategories: '搜索类别', @@ -77,6 +84,9 @@ const translation = { uninstalledTitle: '工具未安装', uninstalledContent: '此插件安装自 本地 / GitHub 仓库,请安装后使用。', uninstalledLink: '在插件中管理', + unsupportedTitle: '不支持的 Action', + unsupportedContent: '已安装的插件版本不提供这个 action。', + unsupportedContent2: '点击切换版本', }, configureApp: '应用设置', configureModel: '模型设置', diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index f9c8f0a2c2..a33ca9af46 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -41,7 +41,7 @@ export const useCheckInstalled = ({ enabled: boolean }) => { return useQuery<{ plugins: PluginDetail[] }>({ - queryKey: [NAME_SPACE, 'checkInstalled'], + queryKey: [NAME_SPACE, 'checkInstalled', pluginIds], queryFn: () => post<{ plugins: PluginDetail[] }>('/workspaces/current/plugin/list/installations/ids', { body: { plugin_ids: pluginIds, @@ -82,8 +82,9 @@ export const useInstallPackageFromMarketPlace = (options?: MutateOptions { +export const useUpdatePackageFromMarketPlace = (options?: MutateOptions) => { return useMutation({ + ...options, mutationFn: (body: object) => { return post('/workspaces/current/plugin/upgrade/marketplace', { body, @@ -364,7 +365,7 @@ export const usePluginTaskList = () => { queryKey: usePluginTaskListKey, queryFn: async () => { const currentData = await get<{ tasks: PluginTask[] }>('/workspaces/current/plugin/tasks?page=1&page_size=100') - const taskDone = currentData.tasks.every(task => task.status === TaskStatus.success) + const taskDone = currentData.tasks.every(task => task.status === TaskStatus.success || task.status === TaskStatus.failed) if (taskDone) setEnabled(false)