mirror of
https://github.com/langgenius/dify.git
synced 2026-04-02 04:39:48 +08:00
feat: Introduce RAG tool recommendations and refactor related components for improved plugin management
This commit is contained in:
parent
f909040567
commit
572abae37a
@ -2,7 +2,7 @@ import { useModelList } from '@/app/components/header/account-setting/model-prov
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||
import { useInvalidateAllBuiltInTools, useInvalidateAllToolProviders } from '@/service/use-tools'
|
||||
import { useInvalidateAllBuiltInTools, useInvalidateAllToolProviders, useInvalidateRAGRecommendedPlugins } from '@/service/use-tools'
|
||||
import { useInvalidateStrategyProviders } from '@/service/use-strategy'
|
||||
import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../../types'
|
||||
import { PluginType } from '../../types'
|
||||
@ -23,6 +23,8 @@ const useRefreshPluginList = () => {
|
||||
const invalidateDataSourceListAuth = useInvalidDataSourceListAuth()
|
||||
|
||||
const invalidateStrategyProviders = useInvalidateStrategyProviders()
|
||||
|
||||
const invalidateRAGRecommendedPlugins = useInvalidateRAGRecommendedPlugins()
|
||||
return {
|
||||
refreshPluginList: (manifest?: PluginManifestInMarket | Plugin | PluginDeclaration | null, refreshAllType?: boolean) => {
|
||||
// installed list
|
||||
@ -32,6 +34,7 @@ const useRefreshPluginList = () => {
|
||||
if ((manifest && PluginType.tool.includes(manifest.category)) || refreshAllType) {
|
||||
invalidateAllToolProviders()
|
||||
invalidateAllBuiltInTools()
|
||||
invalidateRAGRecommendedPlugins()
|
||||
// TODO: update suggested tools. It's a function in hook useMarketplacePlugins,handleUpdatePlugins
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ import PluginList, { type ListProps } from '@/app/components/workflow/block-sele
|
||||
import { PluginType } from '../../plugins/types'
|
||||
import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import RAGToolSuggestions from './rag-tool-suggestions'
|
||||
import RAGToolRecommendations from './rag-tool-recommendations'
|
||||
|
||||
type AllToolsProps = {
|
||||
className?: string
|
||||
@ -148,7 +148,7 @@ const AllTools = ({
|
||||
onScroll={pluginRef.current?.handleScroll}
|
||||
>
|
||||
{isShowRAGRecommendations && (
|
||||
<RAGToolSuggestions
|
||||
<RAGToolRecommendations
|
||||
viewType={isSupportGroupView ? activeView : ViewType.flat}
|
||||
onSelect={onSelect}
|
||||
onTagsChange={onTagsChange}
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { useEffect, useImperativeHandle, useMemo, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useStickyScroll, { ScrollPosition } from '../use-sticky-scroll'
|
||||
import Item from './item'
|
||||
import type { Plugin } from '@/app/components/plugins/types.ts'
|
||||
import type { Plugin } from '@/app/components/plugins/types'
|
||||
import cn from '@/utils/classnames'
|
||||
import Link from 'next/link'
|
||||
import { RiArrowRightUpLine, RiSearchLine } from '@remixicon/react'
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
import type { Dispatch, SetStateAction } from 'react'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import type { OnSelectBlock } from '../types'
|
||||
import Tools from './tools'
|
||||
import { ToolTypeEnum } from './types'
|
||||
import type { ViewType } from './view-type-select'
|
||||
import type { OnSelectBlock } from '@/app/components/workflow/types'
|
||||
import type { ViewType } from '@/app/components/workflow/block-selector/view-type-select'
|
||||
import { RiMoreLine } from '@remixicon/react'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Link from 'next/link'
|
||||
import { getMarketplaceUrl } from '@/utils/var'
|
||||
import { useRAGRecommendedPlugins } from '@/service/use-tools'
|
||||
import List from './list'
|
||||
import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils'
|
||||
|
||||
type RAGToolSuggestionsProps = {
|
||||
type RAGToolRecommendationsProps = {
|
||||
viewType: ViewType
|
||||
onSelect: OnSelectBlock
|
||||
onTagsChange: Dispatch<SetStateAction<string[]>>
|
||||
}
|
||||
|
||||
const RAGToolSuggestions: React.FC<RAGToolSuggestionsProps> = ({
|
||||
const RAGToolRecommendations = ({
|
||||
viewType,
|
||||
onSelect,
|
||||
onTagsChange,
|
||||
}) => {
|
||||
}: RAGToolRecommendationsProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
@ -31,7 +31,13 @@ const RAGToolSuggestions: React.FC<RAGToolSuggestionsProps> = ({
|
||||
|
||||
const recommendedPlugins = useMemo(() => {
|
||||
if (ragRecommendedPlugins)
|
||||
return [...ragRecommendedPlugins.installed_recommended_plugins]
|
||||
return ragRecommendedPlugins.installed_recommended_plugins
|
||||
return []
|
||||
}, [ragRecommendedPlugins])
|
||||
|
||||
const unInstalledPlugins = useMemo(() => {
|
||||
if (ragRecommendedPlugins)
|
||||
return (ragRecommendedPlugins.uninstalled_recommended_plugins).map(getFormattedPlugin)
|
||||
return []
|
||||
}, [ragRecommendedPlugins])
|
||||
|
||||
@ -53,7 +59,7 @@ const RAGToolSuggestions: React.FC<RAGToolSuggestionsProps> = ({
|
||||
<Loading type='app' />
|
||||
</div>
|
||||
)}
|
||||
{!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && (
|
||||
{!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && unInstalledPlugins.length === 0 && (
|
||||
<p className='system-xs-regular px-3 py-1 text-text-tertiary'>
|
||||
<Trans
|
||||
i18nKey='pipeline.ragToolSuggestions.noRecommendationPluginsInstalled'
|
||||
@ -70,16 +76,13 @@ const RAGToolSuggestions: React.FC<RAGToolSuggestionsProps> = ({
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
{!isFetchingRAGRecommendedPlugins && recommendedPlugins.length > 0 && (
|
||||
{!isFetchingRAGRecommendedPlugins && (recommendedPlugins.length > 0 || unInstalledPlugins.length > 0) && (
|
||||
<>
|
||||
<Tools
|
||||
className='p-0'
|
||||
<List
|
||||
tools={recommendedPlugins}
|
||||
unInstalledPlugins={unInstalledPlugins}
|
||||
onSelect={onSelect}
|
||||
canNotSelectMultiple
|
||||
toolType={ToolTypeEnum.All}
|
||||
viewType={viewType}
|
||||
hasSearchText={false}
|
||||
/>
|
||||
<div
|
||||
className='flex cursor-pointer items-center gap-x-2 py-1 pl-3 pr-2'
|
||||
@ -98,4 +101,4 @@ const RAGToolSuggestions: React.FC<RAGToolSuggestionsProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(RAGToolSuggestions)
|
||||
export default React.memo(RAGToolRecommendations)
|
||||
@ -0,0 +1,102 @@
|
||||
import {
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import type { BlockEnum, ToolWithProvider } from '../../types'
|
||||
import type { ToolDefaultValue } from '../types'
|
||||
import { ViewType } from '../view-type-select'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { groupItems } from '../index-bar'
|
||||
import cn from '@/utils/classnames'
|
||||
import ToolListTreeView from '../tool/tool-list-tree-view/list'
|
||||
import ToolListFlatView from '../tool/tool-list-flat-view/list'
|
||||
import UninstalledItem from './uninstalled-item'
|
||||
import type { Plugin } from '@/app/components/plugins/types'
|
||||
|
||||
type ListProps = {
|
||||
onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void
|
||||
tools: ToolWithProvider[]
|
||||
viewType: ViewType
|
||||
unInstalledPlugins: Plugin[]
|
||||
className?: string
|
||||
}
|
||||
|
||||
const List = ({
|
||||
onSelect,
|
||||
tools,
|
||||
viewType,
|
||||
unInstalledPlugins,
|
||||
className,
|
||||
}: ListProps) => {
|
||||
const language = useGetLanguage()
|
||||
const isFlatView = viewType === ViewType.flat
|
||||
|
||||
const { letters, groups: withLetterAndGroupViewToolsData } = groupItems(tools, tool => tool.label[language][0])
|
||||
const treeViewToolsData = useMemo(() => {
|
||||
const result: Record<string, ToolWithProvider[]> = {}
|
||||
Object.keys(withLetterAndGroupViewToolsData).forEach((letter) => {
|
||||
Object.keys(withLetterAndGroupViewToolsData[letter]).forEach((groupName) => {
|
||||
if (!result[groupName])
|
||||
result[groupName] = []
|
||||
result[groupName].push(...withLetterAndGroupViewToolsData[letter][groupName])
|
||||
})
|
||||
})
|
||||
return result
|
||||
}, [withLetterAndGroupViewToolsData])
|
||||
|
||||
const listViewToolData = useMemo(() => {
|
||||
const result: ToolWithProvider[] = []
|
||||
letters.forEach((letter) => {
|
||||
Object.keys(withLetterAndGroupViewToolsData[letter]).forEach((groupName) => {
|
||||
result.push(...withLetterAndGroupViewToolsData[letter][groupName].map((item) => {
|
||||
return {
|
||||
...item,
|
||||
letter,
|
||||
}
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
return result
|
||||
}, [withLetterAndGroupViewToolsData, letters])
|
||||
|
||||
const toolRefs = useRef({})
|
||||
|
||||
return (
|
||||
<div className={cn('max-w-[100%] p-1', className)}>
|
||||
{!!tools.length && (
|
||||
isFlatView ? (
|
||||
<ToolListFlatView
|
||||
toolRefs={toolRefs}
|
||||
letters={letters}
|
||||
payload={listViewToolData}
|
||||
isShowLetterIndex={false}
|
||||
hasSearchText={false}
|
||||
onSelect={onSelect}
|
||||
canNotSelectMultiple
|
||||
indexBar={null}
|
||||
/>
|
||||
) : (
|
||||
<ToolListTreeView
|
||||
payload={treeViewToolsData}
|
||||
hasSearchText={false}
|
||||
onSelect={onSelect}
|
||||
canNotSelectMultiple
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{
|
||||
unInstalledPlugins.map((item) => {
|
||||
return (
|
||||
<UninstalledItem
|
||||
key={item.plugin_id}
|
||||
payload={item}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default List
|
||||
@ -0,0 +1,58 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { Plugin } from '@/app/components/plugins/types'
|
||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||
import I18n from '@/context/i18n'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { BlockEnum } from '../../types'
|
||||
import BlockIcon from '../../block-icon'
|
||||
|
||||
type UninstalledItemProps = {
|
||||
payload: Plugin
|
||||
}
|
||||
|
||||
const UninstalledItem = ({
|
||||
payload,
|
||||
}: UninstalledItemProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
|
||||
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
||||
obj?.[locale] || obj?.['en-US'] || obj?.en_US || ''
|
||||
const [isShowInstallModal, {
|
||||
setTrue: showInstallModal,
|
||||
setFalse: hideInstallModal,
|
||||
}] = useBoolean(false)
|
||||
|
||||
return (
|
||||
<div className='flex h-8 items-center rounded-lg pl-3 pr-2 hover:bg-state-base-hover'>
|
||||
<BlockIcon
|
||||
className='shrink-0'
|
||||
type={BlockEnum.Tool}
|
||||
toolIcon={payload.icon}
|
||||
/>
|
||||
<div className='ml-2 flex w-0 grow items-center'>
|
||||
<div className='system-sm-medium h-4 w-0 grow truncate leading-4 text-text-primary'>
|
||||
{getLocalizedText(payload.label)}
|
||||
</div>
|
||||
<div
|
||||
className='system-xs-medium cursor-pointer pl-1.5 text-components-button-secondary-accent-text'
|
||||
onClick={showInstallModal}
|
||||
>
|
||||
{t('plugin.installAction')}
|
||||
</div>
|
||||
{isShowInstallModal && (
|
||||
<InstallFromMarketplace
|
||||
uniqueIdentifier={payload.latest_package_identifier}
|
||||
manifest={payload}
|
||||
onSuccess={hideInstallModal}
|
||||
onClose={hideInstallModal}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(UninstalledItem)
|
||||
@ -30,7 +30,7 @@ type ToolsProps = {
|
||||
canChooseMCPTool?: boolean
|
||||
isShowRAGRecommendations?: boolean
|
||||
}
|
||||
const Blocks = ({
|
||||
const Tools = ({
|
||||
onSelect,
|
||||
canNotSelectMultiple,
|
||||
onSelectMultiple,
|
||||
@ -146,4 +146,4 @@ const Blocks = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(Blocks)
|
||||
export default memo(Tools)
|
||||
|
||||
@ -19,7 +19,7 @@ import type {
|
||||
} from '@/app/components/workflow/nodes/_base/components/error-handle/types'
|
||||
import type { WorkflowRetryConfig } from '@/app/components/workflow/nodes/_base/components/retry/types'
|
||||
import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { PluginMeta } from '../plugins/types'
|
||||
import type { Plugin, PluginMeta } from '@/app/components/plugins/types'
|
||||
import type { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
|
||||
import type { SchemaTypeDefinition } from '@/service/use-common'
|
||||
|
||||
@ -451,16 +451,9 @@ export type ToolWithProvider = Collection & {
|
||||
meta: PluginMeta
|
||||
}
|
||||
|
||||
export type UninstalledRecommendedPlugin = {
|
||||
plugin_id: string
|
||||
name: string
|
||||
icon: string
|
||||
plugin_unique_identifier: string
|
||||
}
|
||||
|
||||
export type RAGRecommendedPlugins = {
|
||||
installed_recommended_plugins: ToolWithProvider[]
|
||||
uninstalled_recommended_plugins: UninstalledRecommendedPlugin[]
|
||||
uninstalled_recommended_plugins: Plugin[]
|
||||
}
|
||||
|
||||
export enum SupportUploadFileTypes {
|
||||
|
||||
@ -39,7 +39,7 @@ import {
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
import { useInvalidateAllBuiltInTools, useInvalidateRAGRecommendedPlugins } from './use-tools'
|
||||
import { useInvalidateAllBuiltInTools } from './use-tools'
|
||||
import useReferenceSetting from '@/app/components/plugins/plugin-page/use-reference-setting'
|
||||
import { uninstallPlugin } from '@/service/plugins'
|
||||
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
||||
@ -135,14 +135,12 @@ export const useInstalledLatestVersion = (pluginIds: string[]) => {
|
||||
export const useInvalidateInstalledPluginList = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools()
|
||||
const invalidateRAGRecommendedPlugins = useInvalidateRAGRecommendedPlugins()
|
||||
return () => {
|
||||
queryClient.invalidateQueries(
|
||||
{
|
||||
queryKey: useInstalledPluginListKey,
|
||||
})
|
||||
invalidateAllBuiltInTools()
|
||||
invalidateRAGRecommendedPlugins()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user