refactor: strategy status

This commit is contained in:
AkaraChen 2025-01-03 15:25:10 +08:00
parent 2296bb162b
commit fbf9984d85
5 changed files with 78 additions and 52 deletions

View File

@ -1,4 +1,5 @@
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
import type { ReactNode } from 'react'
import { memo, useMemo, useState } from 'react' import { memo, useMemo, useState } from 'react'
import type { Strategy } from './agent-strategy' import type { Strategy } from './agent-strategy'
import classNames from '@/utils/classnames' import classNames from '@/utils/classnames'
@ -16,30 +17,31 @@ import type { StrategyPluginDetail } from '@/app/components/plugins/types'
import type { ToolWithProvider } from '../../../types' import type { ToolWithProvider } from '../../../types'
import { CollectionType } from '@/app/components/tools/types' import { CollectionType } from '@/app/components/tools/types'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
import type { StrategyStatus } from '../../agent/use-config'
import { useStrategyInfo } from '../../agent/use-config' import { useStrategyInfo } from '../../agent/use-config'
const NotInstallWarn = (props: { const NotFoundWarn = (props: {
strategyStatus: StrategyStatus title: ReactNode,
description: ReactNode
}) => { }) => {
// strategyStatus can be 'plugin-not-found-and-not-in-marketplace' | 'strategy-not-found' const { title, description } = props
const { strategyStatus } = props
const { t } = useTranslation() const { t } = useTranslation()
return <Tooltip return <Tooltip
popupContent={<div className='space-y-1 text-xs'> popupContent={
<h3 className='text-text-primary font-semibold'> <div className='space-y-1 text-xs'>
{t('workflow.nodes.agent.pluginNotInstalled')} <h3 className='text-text-primary font-semibold'>
</h3> {title}
<p className='text-text-secondary tracking-tight'> </h3>
{t('workflow.nodes.agent.pluginNotInstalledDesc')} <p className='text-text-secondary tracking-tight'>
</p> {description}
<p> </p>
<Link href={'/plugins'} className='text-text-accent tracking-tight'> <p>
{t('workflow.nodes.agent.linkToPlugin')} <Link href={'/plugins'} className='text-text-accent tracking-tight'>
</Link> {t('workflow.nodes.agent.linkToPlugin')}
</p> </Link>
</div>} </p>
</div>
}
needsDelay needsDelay
> >
<div> <div>
@ -98,8 +100,18 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
value?.agent_strategy_provider_name, value?.agent_strategy_provider_name,
value?.agent_strategy_name, value?.agent_strategy_name,
) )
const showError = ['strategy-not-found', 'plugin-not-found-and-not-in-marketplace'] const showPluginNotInstalledWarn = strategyStatus?.plugin?.source === 'external'
.includes(strategyStatus) && !strategyStatus.plugin.installed
const showUnsupportedStrategy = strategyStatus?.plugin.source === 'external'
&& strategyStatus.strategy === 'not-found'
const showSwitchVersion = strategyStatus?.strategy === 'not-found'
&& strategyStatus.plugin.source === 'marketplace' && strategyStatus.plugin.installed
const showInstallButton = strategyStatus?.strategy === 'not-found'
&& strategyStatus.plugin.source === 'marketplace' && !strategyStatus.plugin.installed
const icon = list?.find( const icon = list?.find(
coll => coll.tools?.find(tool => tool.name === value?.agent_strategy_name), coll => coll.tools?.find(tool => tool.name === value?.agent_strategy_name),
)?.icon as string | undefined )?.icon as string | undefined
@ -125,16 +137,23 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
{value?.agent_strategy_label || t('workflow.nodes.agent.strategy.selectTip')} {value?.agent_strategy_label || t('workflow.nodes.agent.strategy.selectTip')}
</p> </p>
{value && <div className='ml-auto flex items-center gap-1'> {value && <div className='ml-auto flex items-center gap-1'>
{strategyStatus === 'plugin-not-found' && <InstallPluginButton {showInstallButton && <InstallPluginButton
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
size={'small'} size={'small'}
uniqueIdentifier={value.plugin_unique_identifier} uniqueIdentifier={value.plugin_unique_identifier}
/>} />}
{showError {showPluginNotInstalledWarn
? <NotInstallWarn ? <NotFoundWarn
strategyStatus={strategyStatus} title={t('workflow.nodes.agent.pluginNotInstalled')}
description={t('workflow.nodes.agent.pluginNotInstalledDesc')}
/> />
: <RiArrowDownSLine className='size-4 text-text-tertiary' />} : showUnsupportedStrategy
? <NotFoundWarn
title={t('workflow.nodes.agent.unsupportedStrategy')}
description={t('workflow.nodes.agent.strategyNotFoundDesc')}
/>
: <RiArrowDownSLine className='size-4 text-text-tertiary' />
}
</div>} </div>}
</div> </div>
</PortalToFollowElemTrigger> </PortalToFollowElemTrigger>

View File

@ -89,20 +89,14 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
{inputs.agent_strategy_name {inputs.agent_strategy_name
? <SettingItem ? <SettingItem
label={t('workflow.nodes.agent.strategy.shortLabel')} label={t('workflow.nodes.agent.strategy.shortLabel')}
status={ status={currentStrategyStatus?.strategy === 'not-found' ? 'error' : undefined}
['plugin-not-found', 'strategy-not-found', 'plugin-not-found-and-not-in-marketplace'].includes(currentStrategyStatus)
? 'error'
: undefined
}
tooltip={ tooltip={
['plugin-not-found', 'strategy-not-found', 'plugin-not-found-and-not-in-marketplace'].includes(currentStrategyStatus) currentStrategyStatus?.strategy === 'not-found' ? t('workflow.nodes.agent.strategyNotInstallTooltip', {
? t('workflow.nodes.agent.strategyNotInstallTooltip', { plugin: pluginDetail?.declaration.label
plugin: pluginDetail?.declaration.label ? renderI18nObject(pluginDetail?.declaration.label)
? renderI18nObject(pluginDetail?.declaration.label) : undefined,
: undefined, strategy: inputs.agent_strategy_label,
strategy: inputs.agent_strategy_label, }) : undefined
})
: undefined
} }
> >
{inputs.agent_strategy_label} {inputs.agent_strategy_label}

View File

@ -13,7 +13,13 @@ import type { Var } from '../../types'
import { VarType as VarKindType } from '../../types' import { VarType as VarKindType } from '../../types'
import useAvailableVarList from '../_base/hooks/use-available-var-list' import useAvailableVarList from '../_base/hooks/use-available-var-list'
export type StrategyStatus = 'loading' | 'plugin-not-found' | 'plugin-not-found-and-not-in-marketplace' | 'strategy-not-found' | 'success' export type StrategyStatus = {
plugin: {
source: 'external' | 'marketplace'
installed: boolean
}
strategy: 'not-found' | 'normal'
}
export const useStrategyInfo = ( export const useStrategyInfo = (
strategyProviderName?: string, strategyProviderName?: string,
@ -29,16 +35,19 @@ export const useStrategyInfo = (
const marketplace = useFetchPluginsInMarketPlaceByIds([strategyProviderName!], { const marketplace = useFetchPluginsInMarketPlaceByIds([strategyProviderName!], {
retry: false, retry: false,
}) })
const strategyStatus: StrategyStatus = useMemo(() => { const strategyStatus: StrategyStatus | undefined = useMemo(() => {
if (strategyProvider.isLoading || marketplace.isLoading) return 'loading' if (strategyProvider.isLoading || marketplace.isLoading)
if (strategyProvider.isError) { return undefined
if (marketplace.data && marketplace.data.data.plugins.length === 0) const strategyExist = !!strategy
return 'plugin-not-found-and-not-in-marketplace' const isPluginInstalled = !strategyProvider.isError
const isInMarketplace = !!marketplace.data?.data.plugins.at(0)
return 'plugin-not-found' return {
plugin: {
source: isInMarketplace ? 'marketplace' : 'external',
installed: isPluginInstalled,
},
strategy: strategyExist ? 'normal' : 'not-found',
} }
if (!strategy) return 'strategy-not-found'
return 'success'
}, [strategy, marketplace, strategyProvider.isError, strategyProvider.isLoading]) }, [strategy, marketplace, strategyProvider.isError, strategyProvider.isLoading])
return { return {
strategyProvider, strategyProvider,

View File

@ -733,8 +733,10 @@ const translation = {
toolNotInstallTooltip: '{{tool}} is not installed', toolNotInstallTooltip: '{{tool}} is not installed',
toolNotAuthorizedTooltip: '{{tool}} Not Authorized', toolNotAuthorizedTooltip: '{{tool}} Not Authorized',
strategyNotInstallTooltip: '{{strategy}} is not installed', strategyNotInstallTooltip: '{{strategy}} is not installed',
strategyNotFoundInPlugin: '{{strategy}} is not found in {{plugin}}', unsupportedStrategy: 'Unsupported strategy',
strategyNotInstallAndNotInMarketplace: '{{strategy}} is not installed and not found in Marketplace', pluginNotFoundDesc: 'This plugin is installed from GitHub. Please go to Plugins to reinstall',
strategyNotFoundDesc: 'The installed plugin version does not provide this strategy.',
strategyNotFoundDescAndSwitchVersion: 'The installed plugin version does not provide this strategy. Click to switch version.',
modelSelectorTooltips: { modelSelectorTooltips: {
deprecated: 'This model is deprecated', deprecated: 'This model is deprecated',
}, },

View File

@ -733,8 +733,10 @@ const translation = {
toolNotInstallTooltip: '{{tool}} 未安装', toolNotInstallTooltip: '{{tool}} 未安装',
toolNotAuthorizedTooltip: '{{tool}} 未授权', toolNotAuthorizedTooltip: '{{tool}} 未授权',
strategyNotInstallTooltip: '{{strategy}} 未安装', strategyNotInstallTooltip: '{{strategy}} 未安装',
strategyNotInstallAndNotInMarketplace: '{{strategy}} 未安装且未在市场中找到', unsupportedStrategy: '不支持的策略',
strategyNotFoundInPlugin: '在 {{plugin}} 中未找到 {{strategy}}', strategyNotFoundDesc: '安装的插件版本不提供此策略。',
pluginNotFoundDesc: '此插件安装自 GitHub。请转到插件重新安装。',
strategyNotFoundDescAndSwitchVersion: '安装的插件版本不提供此策略。点击切换版本。',
modelSelectorTooltips: { modelSelectorTooltips: {
deprecated: '此模型已弃用', deprecated: '此模型已弃用',
}, },