Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
commit
adba60d067
@ -44,13 +44,17 @@ const AgentTools: FC = () => {
|
||||
const [currentTool, setCurrentTool] = useState<AgentToolWithMoreInfo>(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,
|
||||
|
@ -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),
|
||||
|
39
web/app/components/plugins/install-plugin/base/version.tsx
Normal file
39
web/app/components/plugins/install-plugin/base/version.tsx
Normal file
@ -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<Props> = ({
|
||||
hasInstalled,
|
||||
installedVersion,
|
||||
toInstallVersion,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{
|
||||
!hasInstalled
|
||||
? (
|
||||
<Badge className='mx-1' size="s" state={BadgeState.Default}>{toInstallVersion}</Badge>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<Badge className='mx-1' size="s" state={BadgeState.Warning}>
|
||||
{`${installedVersion} -> ${toInstallVersion}`}
|
||||
</Badge>
|
||||
{/* <div className='flex px-0.5 justify-center items-center gap-0.5'>
|
||||
<div className='text-text-warning system-xs-medium'>Used in 3 apps</div>
|
||||
<RiInformation2Line className='w-4 h-4 text-text-tertiary' />
|
||||
</div> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default React.memo(Version)
|
@ -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<string, {
|
||||
installedVersion: string,
|
||||
uniqueIdentifier: string
|
||||
}> = {}
|
||||
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
|
@ -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<Props> = ({
|
||||
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<Props> = ({
|
||||
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<Props> = ({
|
||||
<Card
|
||||
className='w-full'
|
||||
payload={pluginManifestToCardPluginProps(payload)}
|
||||
titleLeft={<Badge className='mx-1' size="s" state={BadgeState.Default}>{payload.version}</Badge>}
|
||||
titleLeft={!isLoading && <Version
|
||||
hasInstalled={hasInstalled}
|
||||
installedVersion={installedVersion}
|
||||
toInstallVersion={toInstallVersion}
|
||||
/>}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -106,7 +144,7 @@ const Installed: FC<Props> = ({
|
||||
<Button
|
||||
variant='primary'
|
||||
className='min-w-[72px] flex space-x-0.5'
|
||||
disabled={isInstalling}
|
||||
disabled={isInstalling || isLoading}
|
||||
onClick={handleInstall}
|
||||
>
|
||||
{isInstalling && <RiLoader2Line className='w-4 h-4 animate-spin-slow' />}
|
||||
|
@ -1,16 +1,17 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { RiInformation2Line } from '@remixicon/react'
|
||||
import React, { useEffect } from 'react'
|
||||
// import { RiInformation2Line } from '@remixicon/react'
|
||||
import type { Plugin, PluginManifestInMarket } from '../../../types'
|
||||
import Card from '../../../card'
|
||||
import { pluginManifestInMarketToPluginProps } from '../../utils'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiLoader2Line } from '@remixicon/react'
|
||||
import Badge, { BadgeState } from '@/app/components/base/badge/index'
|
||||
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
|
||||
import { useInstallPackageFromMarketPlace, useUpdatePackageFromMarketPlace } from '@/service/use-plugins'
|
||||
import checkTaskStatus from '../../base/check-task-status'
|
||||
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
|
||||
import Version from '../../base/version'
|
||||
|
||||
const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
@ -32,13 +33,29 @@ const Installed: FC<Props> = ({
|
||||
onFailed,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const toInstallVersion = payload.version || payload.latest_version
|
||||
const pluginId = (payload as Plugin).plugin_id
|
||||
const { installedInfo, isLoading } = useCheckInstalled({
|
||||
pluginIds: [pluginId],
|
||||
enabled: !!pluginId,
|
||||
})
|
||||
const installedInfoPayload = installedInfo?.[pluginId]
|
||||
const installedVersion = installedInfoPayload?.installedVersion
|
||||
const hasInstalled = !!installedVersion
|
||||
|
||||
const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace()
|
||||
const { mutateAsync: updatePackageFromMarketPlace } = useUpdatePackageFromMarketPlace()
|
||||
const [isInstalling, setIsInstalling] = React.useState(false)
|
||||
const {
|
||||
check,
|
||||
stop,
|
||||
} = checkTaskStatus()
|
||||
|
||||
useEffect(() => {
|
||||
if (hasInstalled && toInstallVersion === installedVersion)
|
||||
onInstalled()
|
||||
}, [hasInstalled, toInstallVersion, installedVersion])
|
||||
|
||||
const handleCancel = () => {
|
||||
stop()
|
||||
onCancel()
|
||||
@ -50,10 +67,28 @@ const Installed: FC<Props> = ({
|
||||
setIsInstalling(true)
|
||||
|
||||
try {
|
||||
const {
|
||||
all_installed: isInstalled,
|
||||
task_id: taskId,
|
||||
} = await installPackageFromMarketPlace(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 installPackageFromMarketPlace(uniqueIdentifier)
|
||||
taskId = task_id
|
||||
isInstalled = all_installed
|
||||
}
|
||||
|
||||
if (isInstalled) {
|
||||
onInstalled()
|
||||
return
|
||||
@ -73,29 +108,6 @@ const Installed: FC<Props> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const toInstallVersion = '1.3.0'
|
||||
const supportCheckInstalled = false // TODO: check installed in beta version.
|
||||
|
||||
const versionInfo = useMemo(() => {
|
||||
return (<>{
|
||||
payload.latest_version === toInstallVersion || !supportCheckInstalled
|
||||
? (
|
||||
<Badge className='mx-1' size="s" state={BadgeState.Default}>{payload.version || payload.latest_version}</Badge>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<Badge className='mx-1' size="s" state={BadgeState.Warning}>
|
||||
{`${payload.latest_version} -> ${toInstallVersion}`}
|
||||
</Badge>
|
||||
<div className='flex px-0.5 justify-center items-center gap-0.5'>
|
||||
<div className='text-text-warning system-xs-medium'>Used in 3 apps</div>
|
||||
<RiInformation2Line className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}</>)
|
||||
}, [payload.latest_version, payload.version, supportCheckInstalled])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='flex flex-col px-6 py-3 justify-center items-start gap-4 self-stretch'>
|
||||
@ -106,7 +118,11 @@ const Installed: FC<Props> = ({
|
||||
<Card
|
||||
className='w-full'
|
||||
payload={pluginManifestInMarketToPluginProps(payload as PluginManifestInMarket)}
|
||||
titleLeft={versionInfo}
|
||||
titleLeft={!isLoading && <Version
|
||||
hasInstalled={hasInstalled}
|
||||
installedVersion={installedVersion}
|
||||
toInstallVersion={toInstallVersion}
|
||||
/>}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -120,7 +136,7 @@ const Installed: FC<Props> = ({
|
||||
<Button
|
||||
variant='primary'
|
||||
className='min-w-[72px] flex space-x-0.5'
|
||||
disabled={isInstalling}
|
||||
disabled={isInstalling || isLoading}
|
||||
onClick={handleInstall}
|
||||
>
|
||||
{isInstalling && <RiLoader2Line className='w-4 h-4 animate-spin-slow' />}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import Button from '@/app/components/base/button'
|
||||
@ -7,9 +7,9 @@ import Indicator from '@/app/components/header/indicator'
|
||||
import ToolItem from '@/app/components/tools/provider/tool-item'
|
||||
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
|
||||
import {
|
||||
useBuiltinProviderInfo,
|
||||
useAllToolProviders,
|
||||
useBuiltinTools,
|
||||
useInvalidateBuiltinProviderInfo,
|
||||
useInvalidateAllToolProviders,
|
||||
useRemoveProviderCredentials,
|
||||
useUpdateProviderCredentials,
|
||||
} from '@/service/use-tools'
|
||||
@ -26,14 +26,17 @@ const ActionList = ({
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
const providerBriefInfo = detail.declaration.tool.identity
|
||||
const providerKey = `${detail.plugin_id}/${providerBriefInfo.name}`
|
||||
const { data: provider } = useBuiltinProviderInfo(providerKey)
|
||||
const invalidateProviderInfo = useInvalidateBuiltinProviderInfo()
|
||||
const { data: collectionList = [] } = useAllToolProviders()
|
||||
const invalidateAllToolProviders = useInvalidateAllToolProviders()
|
||||
const provider = useMemo(() => {
|
||||
return collectionList.find(collection => collection.name === providerKey)
|
||||
}, [collectionList, providerKey])
|
||||
const { data } = useBuiltinTools(providerKey)
|
||||
|
||||
const [showSettingAuth, setShowSettingAuth] = useState(false)
|
||||
|
||||
const handleCredentialSettingUpdate = () => {
|
||||
invalidateProviderInfo(providerKey)
|
||||
invalidateAllToolProviders()
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('common.api.actionSuccess'),
|
||||
|
@ -34,6 +34,7 @@ const ConfigCredential: FC<Props> = ({
|
||||
const [credentialSchema, setCredentialSchema] = useState<any>(null)
|
||||
const { name: collectionName } = collection
|
||||
const [tempCredential, setTempCredential] = React.useState<any>({})
|
||||
const [isLoading, setIsLoading] = React.useState(false)
|
||||
useEffect(() => {
|
||||
fetchBuiltInToolCredentialSchema(collectionName).then(async (res) => {
|
||||
const toolCredentialSchemas = toolCredentialToFormSchemas(res)
|
||||
@ -45,14 +46,16 @@ const ConfigCredential: FC<Props> = ({
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleSave = () => {
|
||||
const handleSave = async () => {
|
||||
for (const field of credentialSchema) {
|
||||
if (field.required && !tempCredential[field.name]) {
|
||||
Toast.notify({ type: 'error', message: t('common.errorMsg.fieldRequired', { field: field.label[language] || field.label.en_US }) })
|
||||
return
|
||||
}
|
||||
}
|
||||
onSaved(tempCredential)
|
||||
setIsLoading(true)
|
||||
await onSaved(tempCredential)
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -102,7 +105,7 @@ const ConfigCredential: FC<Props> = ({
|
||||
}
|
||||
< div className='flex space-x-2'>
|
||||
<Button onClick={onCancel}>{t('common.operation.cancel')}</Button>
|
||||
<Button variant='primary' onClick={handleSave}>{t('common.operation.save')}</Button>
|
||||
<Button loading={isLoading} disabled={isLoading} variant='primary' onClick={handleSave}>{t('common.operation.save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -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',
|
||||
|
@ -117,7 +117,7 @@ const translation = {
|
||||
qualified: '高质量',
|
||||
recommend: '推荐',
|
||||
qualifiedTip: '调用系统默认的嵌入接口进行处理,以在用户查询时提供更高的准确度',
|
||||
warning: '请先完成模型供应商的 API KEY 设置。.',
|
||||
warning: '请先设置默认 embedding 模型',
|
||||
click: '前往设置',
|
||||
economical: '经济',
|
||||
economicalTip: '使用离线的向量引擎、关键词索引等方式,降低了准确度但无需花费 Token',
|
||||
|
@ -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) => {
|
||||
|
@ -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<InstalledPluginListResponse>({
|
||||
queryKey: useInstalledPluginListKey,
|
||||
@ -58,6 +78,16 @@ export const useInstallPackageFromMarketPlace = () => {
|
||||
})
|
||||
}
|
||||
|
||||
export const useUpdatePackageFromMarketPlace = () => {
|
||||
return useMutation({
|
||||
mutationFn: (body: object) => {
|
||||
return post<InstallPackageResponse>('/workspaces/current/plugin/upgrade/marketplace', {
|
||||
body,
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useVersionListOfPlugin = (pluginID: string) => {
|
||||
return useQuery<{ data: VersionListResponse }>({
|
||||
queryKey: [NAME_SPACE, 'versions', pluginID],
|
||||
|
@ -15,7 +15,7 @@ const NAME_SPACE = 'tools'
|
||||
|
||||
const useAllToolProvidersKey = [NAME_SPACE, 'allToolProviders']
|
||||
export const useAllToolProviders = () => {
|
||||
return useQuery({
|
||||
return useQuery<Collection[]>({
|
||||
queryKey: useAllToolProvidersKey,
|
||||
queryFn: () => get<Collection[]>('/workspaces/current/tool-providers'),
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user