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 40c4232270..9beb50eeba 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 @@ -44,13 +44,17 @@ const AgentTools: FC = () => { const [currentTool, setCurrentTool] = useState(null) const currentCollection = useMemo(() => { if (!currentTool) return null - const collection = collectionList.find(collection => collection.id === currentTool?.provider_id.split('/').pop() && collection.type === currentTool?.provider_type) + const collection = collectionList.find(collection => collection.id.split('/').pop() === currentTool?.provider_id.split('/').pop() && collection.type === currentTool?.provider_type) return collection }, [currentTool, collectionList]) const [isShowSettingTool, setIsShowSettingTool] = useState(false) const [isShowSettingAuth, setShowSettingAuth] = useState(false) const tools = (modelConfig?.agentConfig?.tools as AgentTool[] || []).map((item) => { - const collection = collectionList.find(collection => collection.id === item.provider_id.split('/').pop() && collection.type === item.provider_type) + const collection = collectionList.find( + collection => + collection.id.split('/').pop() === item.provider_id.split('/').pop() + && collection.type === item.provider_type, + ) const icon = collection?.icon return { ...item, diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 70ce4274d7..fbb375aa27 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -622,7 +622,7 @@ const Configuration: FC = () => { }).map((tool: any) => { return { ...tool, - isDeleted: res.deleted_tools?.includes(tool.tool_name), + isDeleted: res.deleted_tools?.some((deletedTool: any) => deletedTool.id === tool.id && deletedTool.tool_name === tool.tool_name), notAuthor: collectionList.find(c => tool.provider_id === c.id)?.is_team_authorization === false, ...(tool.provider_type === 'builtin' ? { provider_id: correctProvider(tool.provider_name), diff --git a/web/app/components/plugins/install-plugin/base/version.tsx b/web/app/components/plugins/install-plugin/base/version.tsx new file mode 100644 index 0000000000..891294f095 --- /dev/null +++ b/web/app/components/plugins/install-plugin/base/version.tsx @@ -0,0 +1,39 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import Badge, { BadgeState } from '@/app/components/base/badge/index' + +type Props = { + hasInstalled: boolean + installedVersion?: string + toInstallVersion: string +} + +const Version: FC = ({ + hasInstalled, + installedVersion, + toInstallVersion, +}) => { + return ( + <> + { + !hasInstalled + ? ( + {toInstallVersion} + ) + : ( + <> + + {`${installedVersion} -> ${toInstallVersion}`} + + {/*
+
Used in 3 apps
+ +
*/} + + ) + } + + ) +} +export default React.memo(Version) diff --git a/web/app/components/plugins/install-plugin/hooks/use-check-installed.tsx b/web/app/components/plugins/install-plugin/hooks/use-check-installed.tsx new file mode 100644 index 0000000000..5b35128049 --- /dev/null +++ b/web/app/components/plugins/install-plugin/hooks/use-check-installed.tsx @@ -0,0 +1,34 @@ +import { useCheckInstalled as useDoCheckInstalled } from '@/service/use-plugins' + +import { useMemo } from 'react' +type Props = { + pluginIds: string[], + enabled: boolean +} +const useCheckInstalled = (props: Props) => { + const { data, isLoading, error } = useDoCheckInstalled(props) + + const installedInfo = useMemo(() => { + if (!data) + return undefined + + const res: Record = {} + data?.plugins.forEach((plugin) => { + res[plugin.plugin_id] = { + installedVersion: plugin.declaration.version, + uniqueIdentifier: plugin.plugin_unique_identifier, + } + }) + return res + }, [data]) + return { + installedInfo, + isLoading, + error, + } +} + +export default useCheckInstalled diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx index 43c2a03319..d37275f19a 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx @@ -1,16 +1,16 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import React, { useEffect } from 'react' import type { PluginDeclaration } from '../../../types' import Card from '../../../card' import { pluginManifestToCardPluginProps } from '../../utils' import Button from '@/app/components/base/button' import { Trans, useTranslation } from 'react-i18next' import { RiLoader2Line } from '@remixicon/react' -import Badge, { BadgeState } from '@/app/components/base/badge/index' -import { useInstallPackageFromLocal } from '@/service/use-plugins' import checkTaskStatus from '../../base/check-task-status' -import { usePluginTaskList } from '@/service/use-plugins' +import { useInstallPackageFromLocal, usePluginTaskList, useUpdatePackageFromMarketPlace } from '@/service/use-plugins' +import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' +import Version from '../../base/version' const i18nPrefix = 'plugin.installModal' @@ -32,8 +32,24 @@ const Installed: FC = ({ onFailed, }) => { const { t } = useTranslation() + const toInstallVersion = payload.version + const pluginId = `${payload.author}/${payload.name}` + const { installedInfo, isLoading } = useCheckInstalled({ + pluginIds: [pluginId], + enabled: !!pluginId, + }) + const installedInfoPayload = installedInfo?.[pluginId] + const installedVersion = installedInfoPayload?.installedVersion + const hasInstalled = !!installedVersion + + useEffect(() => { + if (hasInstalled && toInstallVersion === installedVersion) + onInstalled() + }, [hasInstalled, toInstallVersion, installedVersion]) + const [isInstalling, setIsInstalling] = React.useState(false) const { mutateAsync: installPackageFromLocal } = useInstallPackageFromLocal() + const { mutateAsync: updatePackageFromMarketPlace } = useUpdatePackageFromMarketPlace() const { check, @@ -52,10 +68,28 @@ const Installed: FC = ({ onStartToInstall?.() try { - const { - all_installed: isInstalled, - task_id: taskId, - } = await installPackageFromLocal(uniqueIdentifier) + let taskId + let isInstalled + if (hasInstalled) { + const { + all_installed, + task_id, + } = await updatePackageFromMarketPlace({ + original_plugin_unique_identifier: installedInfoPayload.uniqueIdentifier, + new_plugin_unique_identifier: uniqueIdentifier, + }) + taskId = task_id + isInstalled = all_installed + } + else { + const { + all_installed, + task_id, + } = await installPackageFromLocal(uniqueIdentifier) + taskId = task_id + isInstalled = all_installed + } + if (isInstalled) { onInstalled() return @@ -92,7 +126,11 @@ const Installed: FC = ({ {payload.version}} + titleLeft={!isLoading && } /> @@ -106,7 +144,7 @@ const Installed: FC = ({ - + diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index de885671a7..88b55182c9 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -117,7 +117,7 @@ const translation = { qualified: 'High Quality', recommend: 'Recommend', qualifiedTip: 'Call default system embedding interface for processing to provide higher accuracy when users query.', - warning: 'Please set up the model provider API key first.', + warning: 'Please set up the default embedding model first.', click: 'Go to settings', economical: 'Economical', economicalTip: 'Use offline vector engines, keyword indexes, etc. to reduce accuracy without spending tokens', diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index fac809d7e2..5cd27fcbda 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -117,7 +117,7 @@ const translation = { qualified: '高质量', recommend: '推荐', qualifiedTip: '调用系统默认的嵌入接口进行处理,以在用户查询时提供更高的准确度', - warning: '请先完成模型供应商的 API KEY 设置。.', + warning: '请先设置默认 embedding 模型', click: '前往设置', economical: '经济', economicalTip: '使用离线的向量引擎、关键词索引等方式,降低了准确度但无需花费 Token', diff --git a/web/service/fetch.ts b/web/service/fetch.ts index 39f91f903f..5458d78b33 100644 --- a/web/service/fetch.ts +++ b/web/service/fetch.ts @@ -45,6 +45,8 @@ const afterResponseErrorCode = (otherOptions: IOtherOptions): AfterResponseHook globalThis.location.href = `${globalThis.location.origin}/signin` }) break + case 401: + return Promise.reject(response) // fall through default: bodyJson.then((data: ResponseError) => { diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 9bdf63c5c5..50003a2f1a 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -8,6 +8,7 @@ import type { PackageDependency, Permissions, Plugin, + PluginDetail, PluginTask, PluginsFromMarketplaceByInfoResponse, PluginsFromMarketplaceResponse, @@ -29,6 +30,25 @@ import { useInvalidateAllBuiltInTools } from './use-tools' const NAME_SPACE = 'plugins' const useInstalledPluginListKey = [NAME_SPACE, 'installedPluginList'] +export const useCheckInstalled = ({ + pluginIds, + enabled, +}: { + pluginIds: string[], + enabled: boolean +}) => { + return useQuery<{ plugins: PluginDetail[] }>({ + queryKey: [NAME_SPACE, 'checkInstalled'], + queryFn: () => post<{ plugins: PluginDetail[] }>('/workspaces/current/plugin/list/installations/ids', { + body: { + plugin_ids: pluginIds, + }, + }), + enabled, + staleTime: 0, // always fresh + }) +} + export const useInstalledPluginList = (disable?: boolean) => { return useQuery({ queryKey: useInstalledPluginListKey, @@ -58,6 +78,16 @@ export const useInstallPackageFromMarketPlace = () => { }) } +export const useUpdatePackageFromMarketPlace = () => { + return useMutation({ + mutationFn: (body: object) => { + return post('/workspaces/current/plugin/upgrade/marketplace', { + body, + }) + }, + }) +} + export const useVersionListOfPlugin = (pluginID: string) => { return useQuery<{ data: VersionListResponse }>({ queryKey: [NAME_SPACE, 'versions', pluginID], diff --git a/web/service/use-tools.ts b/web/service/use-tools.ts index bf9678332f..ceaa4b14b3 100644 --- a/web/service/use-tools.ts +++ b/web/service/use-tools.ts @@ -15,7 +15,7 @@ const NAME_SPACE = 'tools' const useAllToolProvidersKey = [NAME_SPACE, 'allToolProviders'] export const useAllToolProviders = () => { - return useQuery({ + return useQuery({ queryKey: useAllToolProvidersKey, queryFn: () => get('/workspaces/current/tool-providers'), })