From 17dfb3654f7f6c2ad6f16facd87919759b0cbc0e Mon Sep 17 00:00:00 2001 From: Yi Date: Wed, 8 Jan 2025 14:45:03 +0800 Subject: [PATCH 01/15] feat: add version incompatible case --- .../model-provider-page/model-icon/index.tsx | 6 +- .../agent-model-trigger.tsx | 9 ++- .../status-indicators.tsx | 72 +++++++++++++------ .../deprecated-model-trigger.tsx | 4 +- 4 files changed, 66 insertions(+), 25 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index e657106ab8..1139e5e030 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -13,12 +13,14 @@ type ModelIconProps = { modelName?: string className?: string isDeprecated?: boolean + isInModelList?: boolean } const ModelIcon: FC = ({ provider, className, modelName, isDeprecated = false, + isInModelList = false, }) => { const language = useLanguage() if (provider?.provider.includes('openai') && modelName?.includes('gpt-4o')) @@ -26,7 +28,7 @@ const ModelIcon: FC = ({ if (provider?.provider.includes('openai') && modelName?.startsWith('gpt-4')) return
- if (provider?.icon_small) { + if (provider?.icon_small && isInModelList) { return (
= ({ return (
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx index 6cc21699c4..f38440837c 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx @@ -26,6 +26,7 @@ import cn from '@/utils/classnames' import { useProviderContext } from '@/context/provider-context' import { RiEqualizer2Line } from '@remixicon/react' import { fetchPluginInfoFromMarketPlace } from '@/service/plugins' +import { fetchModelProviderModelList } from '@/service/common' export type AgentModelTriggerProps = { open?: boolean @@ -67,6 +68,7 @@ const AgentModelTrigger: FC = ({ const [pluginInfo, setPluginInfo] = useState(null) const [isPluginChecked, setIsPluginChecked] = useState(false) const [installed, setInstalled] = useState(false) + const [inModelList, setInModelList] = useState(false) const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const handleOpenModal = useModelModalHandler() useEffect(() => { @@ -77,6 +79,9 @@ const AgentModelTrigger: FC = ({ const name = parts[1] try { const pluginInfo = await fetchPluginInfoFromMarketPlace({ org, name }) + const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${providerName}/models`) + if (modelId && modelsData.data.find(item => item.model === modelId)) + setInModelList(true) if (pluginInfo.data.plugin.category === PluginType.model) setPluginInfo(pluginInfo.data.plugin) } @@ -89,7 +94,7 @@ const AgentModelTrigger: FC = ({ setIsPluginChecked(true) } })() - }, [providerName, modelProvider]) + }, [providerName, modelProvider, modelId]) if (modelId && !isPluginChecked) return null @@ -107,6 +112,7 @@ const AgentModelTrigger: FC = ({ provider={currentProvider || modelProvider} modelName={currentModel?.model || modelId} isDeprecated={hasDeprecated} + isInModelList={inModelList} /> = ({ { +const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disabled, pluginInfo, t }: StatusIndicatorsProps) => { + const renderTooltipContent = (title: string, description?: string, linkText?: string, linkHref?: string) => { + return ( +
+
{title}
+ {description && ( +
+ {description} +
+ )} + {linkText && linkHref && ( +
+ { + e.stopPropagation() + }} + > + {linkText} + +
+ )} +
+ ) + } return ( <> - {!needsConfiguration && modelProvider && disabled && ( + {/* plugin installed and model is in model list but disabled */} + {!needsConfiguration && modelProvider && disabled && inModelList && ( )} + {/* plugin installed from github/local and model is not in model list */} + { + modelProvider && !inModelList && ( + + + + ) + } + {/* plugin not installed and not in marketplace */} {!modelProvider && !pluginInfo && ( -
{t('workflow.nodes.agent.modelNotInMarketplace.title')}
-
- {t('workflow.nodes.agent.modelNotInMarketplace.desc')} -
-
- { - e.stopPropagation() - }} - > - {t('workflow.nodes.agent.linkToPlugin')} - -
-
- } + popupContent={renderTooltipContent( + t('workflow.nodes.agent.modelNotInMarketplace.title'), + t('workflow.nodes.agent.modelNotInMarketplace.desc'), + t('workflow.nodes.agent.linkToPlugin'), + '/plugins', + )} asChild={false} needsDelay > diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx index ce40ab7b22..207e0a7eaf 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx @@ -22,11 +22,11 @@ const ModelTrigger: FC = ({ return (
From 8c9c336b6407df8ab5b108f93c698bdea309f3c2 Mon Sep 17 00:00:00 2001 From: Yi Date: Wed, 8 Jan 2025 14:47:01 +0800 Subject: [PATCH 02/15] fix: isInModelList default value --- .../account-setting/model-provider-page/model-icon/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 1139e5e030..4c3f53a00b 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -20,7 +20,7 @@ const ModelIcon: FC = ({ className, modelName, isDeprecated = false, - isInModelList = false, + isInModelList = true, }) => { const language = useLanguage() if (provider?.provider.includes('openai') && modelName?.includes('gpt-4o')) From f63cc9e0691ce2f7eca2a15f6b96b469aeddd148 Mon Sep 17 00:00:00 2001 From: AkaraChen Date: Wed, 8 Jan 2025 14:48:36 +0800 Subject: [PATCH 03/15] chore: remove switch version component in strategy select --- .../components/agent-strategy-selector.tsx | 23 ++++--------------- web/i18n/en-US/workflow.ts | 1 - web/i18n/zh-Hans/workflow.ts | 1 - 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index db684ea2df..076d171108 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -17,10 +17,8 @@ import type { ToolWithProvider } from '../../../types' import { CollectionType } from '@/app/components/tools/types' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import { useStrategyInfo } from '../../agent/use-config' -import { SwitchPluginVersion } from './switch-plugin-version' import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hooks' -import { ToolTipContent } from '@/app/components/base/tooltip/content' const NotFoundWarn = (props: { title: ReactNode, @@ -104,15 +102,14 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => value?.agent_strategy_name, ) - const showPluginNotInstalledWarn = strategyStatus?.plugin?.source === 'external' + // plugin not found and is not found in marketplace + const showPluginNotInstalledWarn = !strategyStatus?.isExistInPlugin && strategyStatus?.plugin?.source === 'external' && !strategyStatus.plugin.installed - const showUnsupportedStrategy = strategyStatus?.plugin.source === 'external' - && !strategyStatus?.isExistInPlugin - - const showSwitchVersion = !strategyStatus?.isExistInPlugin - && strategyStatus?.plugin.source === 'marketplace' && strategyStatus.plugin.installed + // strategy not found + const showUnsupportedStrategy = !strategyStatus?.isExistInPlugin + // plugin not found and is founded in marketplace const showInstallButton = !strategyStatus?.isExistInPlugin && strategyStatus?.plugin.source === 'marketplace' && !strategyStatus.plugin.installed @@ -177,16 +174,6 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => /> : } - {showSwitchVersion && - {t('workflow.nodes.agent.strategyNotFoundDescAndSwitchVersion')} - } - onChange={() => { - // TODO: refresh all strategies - }} - />}
}
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 279654a857..b494a48e5b 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -740,7 +740,6 @@ const translation = { unsupportedStrategy: 'Unsupported strategy', 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: { deprecated: 'This model is deprecated', }, diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 80c53702ab..ded2a712a7 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -740,7 +740,6 @@ const translation = { unsupportedStrategy: '不支持的策略', strategyNotFoundDesc: '安装的插件版本不提供此策略。', pluginNotFoundDesc: '此插件安装自 GitHub。请转到插件重新安装。', - strategyNotFoundDescAndSwitchVersion: '安装的插件版本不提供此策略。点击切换版本。', modelSelectorTooltips: { deprecated: '此模型已弃用', }, From b0b373d2fb1d27577c56b30918e10008995e21de Mon Sep 17 00:00:00 2001 From: Yi Date: Wed, 8 Jan 2025 14:50:14 +0800 Subject: [PATCH 04/15] fix: revert the modelInList check in the model icon component --- .../account-setting/model-provider-page/model-icon/index.tsx | 4 +--- .../model-parameter-modal/agent-model-trigger.tsx | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 4c3f53a00b..0576672a4f 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -13,14 +13,12 @@ type ModelIconProps = { modelName?: string className?: string isDeprecated?: boolean - isInModelList?: boolean } const ModelIcon: FC = ({ provider, className, modelName, isDeprecated = false, - isInModelList = true, }) => { const language = useLanguage() if (provider?.provider.includes('openai') && modelName?.includes('gpt-4o')) @@ -28,7 +26,7 @@ const ModelIcon: FC = ({ if (provider?.provider.includes('openai') && modelName?.startsWith('gpt-4')) return
- if (provider?.icon_small && isInModelList) { + if (provider?.icon_small) { return (
= ({ provider={currentProvider || modelProvider} modelName={currentModel?.model || modelId} isDeprecated={hasDeprecated} - isInModelList={inModelList} /> Date: Wed, 8 Jan 2025 14:55:55 +0800 Subject: [PATCH 05/15] Revert "chore: remove switch version component in strategy select" This reverts commit f63cc9e0691ce2f7eca2a15f6b96b469aeddd148. --- .../components/agent-strategy-selector.tsx | 23 +++++++++++++++---- web/i18n/en-US/workflow.ts | 1 + web/i18n/zh-Hans/workflow.ts | 1 + 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index 076d171108..db684ea2df 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -17,8 +17,10 @@ import type { ToolWithProvider } from '../../../types' import { CollectionType } from '@/app/components/tools/types' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import { useStrategyInfo } from '../../agent/use-config' +import { SwitchPluginVersion } from './switch-plugin-version' import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hooks' +import { ToolTipContent } from '@/app/components/base/tooltip/content' const NotFoundWarn = (props: { title: ReactNode, @@ -102,14 +104,15 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => value?.agent_strategy_name, ) - // plugin not found and is not found in marketplace - const showPluginNotInstalledWarn = !strategyStatus?.isExistInPlugin && strategyStatus?.plugin?.source === 'external' + const showPluginNotInstalledWarn = strategyStatus?.plugin?.source === 'external' && !strategyStatus.plugin.installed - // strategy not found - const showUnsupportedStrategy = !strategyStatus?.isExistInPlugin + const showUnsupportedStrategy = strategyStatus?.plugin.source === 'external' + && !strategyStatus?.isExistInPlugin + + const showSwitchVersion = !strategyStatus?.isExistInPlugin + && strategyStatus?.plugin.source === 'marketplace' && strategyStatus.plugin.installed - // plugin not found and is founded in marketplace const showInstallButton = !strategyStatus?.isExistInPlugin && strategyStatus?.plugin.source === 'marketplace' && !strategyStatus.plugin.installed @@ -174,6 +177,16 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => /> : } + {showSwitchVersion && + {t('workflow.nodes.agent.strategyNotFoundDescAndSwitchVersion')} + } + onChange={() => { + // TODO: refresh all strategies + }} + />}
}
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index b494a48e5b..279654a857 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -740,6 +740,7 @@ const translation = { unsupportedStrategy: 'Unsupported strategy', 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: { deprecated: 'This model is deprecated', }, diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index ded2a712a7..80c53702ab 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -740,6 +740,7 @@ const translation = { unsupportedStrategy: '不支持的策略', strategyNotFoundDesc: '安装的插件版本不提供此策略。', pluginNotFoundDesc: '此插件安装自 GitHub。请转到插件重新安装。', + strategyNotFoundDescAndSwitchVersion: '安装的插件版本不提供此策略。点击切换版本。', modelSelectorTooltips: { deprecated: '此模型已弃用', }, From fa83f2e2faeecf7112acc2652bbf8c2eb9feda6b Mon Sep 17 00:00:00 2001 From: AkaraChen Date: Wed, 8 Jan 2025 15:13:19 +0800 Subject: [PATCH 06/15] fix: switch version plugin --- .../components/switch-plugin-version.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) 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 8f6f751bc3..fc517ccf1d 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 @@ -23,7 +23,10 @@ export const SwitchPluginVersion: FC = (props) => { const [pluginId] = uniqueIdentifier.split(':') const [isShow, setIsShow] = useState(false) const [isShowUpdateModal, { setTrue: showUpdateModal, setFalse: hideUpdateModal }] = useBoolean(false) - const [targetVersion, setTargetVersion] = useState() + const [target, setTarget] = useState<{ + version: string, + pluginUniqueIden: string; + }>() const pluginDetails = useCheckInstalled({ pluginIds: [pluginId], enabled: true, @@ -33,13 +36,8 @@ export const SwitchPluginVersion: FC = (props) => { const handleUpdatedFromMarketplace = useCallback(() => { hideUpdateModal() pluginDetails.refetch() - onChange?.(targetVersion!) - }, [hideUpdateModal, onChange, pluginDetails, targetVersion]) - - const targetUniqueIdentifier = (() => { - if (!targetVersion || !pluginDetail) return uniqueIdentifier - return uniqueIdentifier.replaceAll(pluginDetail.version, targetVersion) - })() + onChange?.(target!.version) + }, [hideUpdateModal, onChange, pluginDetails, target]) return
{isShowUpdateModal && pluginDetail && = (props) => { payload: pluginDetail.declaration, }, targetPackageInfo: { - id: targetUniqueIdentifier, - version: targetVersion!, + id: target!.pluginUniqueIden, + version: target!.version, }, }} onCancel={hideUpdateModal} @@ -62,7 +60,10 @@ export const SwitchPluginVersion: FC = (props) => { pluginID={pluginId} currentVersion={pluginDetail.version} onSelect={(state) => { - setTargetVersion(state.version) + setTarget({ + pluginUniqueIden: state.unique_identifier, + version: state.version, + }) showUpdateModal() }} trigger={ From 5481b42257fe1db738d0bd7e46bb6050928c9aa1 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 8 Jan 2025 15:28:29 +0800 Subject: [PATCH 07/15] dark mode for tracing dropdown --- .../overview/tracing/config-button.tsx | 10 ++- .../[appId]/overview/tracing/panel.tsx | 87 +++++++++++-------- .../overview/tracing/toggle-fold-btn.tsx | 45 ---------- web/context/app-context.tsx | 2 +- 4 files changed, 59 insertions(+), 85 deletions(-) delete mode 100644 web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/toggle-fold-btn.tsx diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx index 977e3f057c..39fc81a274 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx @@ -1,12 +1,14 @@ 'use client' import type { FC } from 'react' import React, { useCallback, useEffect, useRef, useState } from 'react' +import { + RiEqualizer2Line, +} from '@remixicon/react' import { useTranslation } from 'react-i18next' import type { PopupProps } from './config-popup' import ConfigPopup from './config-popup' import cn from '@/utils/classnames' import Button from '@/app/components/base/button' -import { Settings04 } from '@/app/components/base/icons/src/vender/line/general' import { PortalToFollowElem, PortalToFollowElemContent, @@ -52,15 +54,15 @@ const ConfigBtn: FC = ({ const triggerContent = hasConfigured ? ( -
- +
+
) : ( ) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx index bc724c1449..790705e9fb 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx @@ -1,6 +1,10 @@ 'use client' import type { FC } from 'react' import React, { useCallback, useEffect, useState } from 'react' +import { + RiArrowDownDoubleLine, + RiEqualizer2Line, +} from '@remixicon/react' import { useTranslation } from 'react-i18next' import { usePathname } from 'next/navigation' import { useBoolean } from 'ahooks' @@ -8,7 +12,6 @@ import type { LangFuseConfig, LangSmithConfig } from './type' import { TracingProvider } from './type' import TracingIcon from './tracing-icon' import ConfigButton from './config-button' -import cn from '@/utils/classnames' import { LangfuseIcon, LangsmithIcon } from '@/app/components/base/icons/src/public/tracing' import Indicator from '@/app/components/header/indicator' import { fetchTracingConfig as doFetchTracingConfig, fetchTracingStatus, updateTracingStatus } from '@/service/apps' @@ -16,6 +19,8 @@ import type { TracingStatus } from '@/models/app' import Toast from '@/app/components/base/toast' import { useAppContext } from '@/context/app-context' import Loading from '@/app/components/base/loading' +import Divider from '@/app/components/base/divider' +import cn from '@/utils/classnames' const I18N_PREFIX = 'app.tracing' @@ -27,7 +32,7 @@ const Title = ({ const { t } = useTranslation() return ( -
+
{t('common.appMenus.overview')}
) @@ -135,43 +140,55 @@ const Panel: FC = () => { return (
- <div className='flex items-center p-2 rounded-xl border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100' onClick={showPopup}> - {!inUseTracingProvider - ? <> - <TracingIcon size='md' className='mr-2' /> - <div className='leading-5 text-sm font-semibold text-gray-700'>{t(`${I18N_PREFIX}.title`)}</div> - </> - : <InUseProviderIcon className='ml-1 h-4' />} - - {hasConfiguredTracing && ( - <div className='ml-4 mr-1 flex items-center'> - <Indicator color={enabled ? 'green' : 'gray'} /> - <div className='ml-1.5 text-xs font-semibold text-gray-500 uppercase'> - {t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)} + <div + className={cn( + 'flex items-center p-2 rounded-xl bg-background-default-dodge border-t border-l-[0.5px] border-effects-highlight shadow-xs cursor-pointer hover:bg-background-default-lighter hover:border-effects-highlight-lightmode-off', + controlShowPopup && 'bg-background-default-lighter border-effects-highlight-lightmode-off', + )} + onClick={showPopup} + > + {!inUseTracingProvider && ( + <> + <TracingIcon size='md' /> + <div className='mx-2 system-sm-semibold text-text-secondary'>{t(`${I18N_PREFIX}.title`)}</div> + <div className='p-1 rounded-md'> + <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' /> </div> - </div> + <Divider type='vertical' className='h-3.5' /> + <div className='p-1 rounded-md'> + <RiArrowDownDoubleLine className='w-4 h-4 text-text-tertiary' /> + </div> + </> )} - {hasConfiguredTracing && ( - <div className='ml-2 w-px h-3.5 bg-gray-200'></div> + <> + <div className='ml-4 mr-1 flex items-center'> + <Indicator color={enabled ? 'green' : 'gray'} /> + <div className='ml-1.5 system-xs-semibold-uppercase text-text-tertiary'> + {t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)} + </div> + </div> + <InUseProviderIcon className='ml-1 h-4' /> + <Divider type='vertical' className='h-3.5' /> + <div className='flex items-center' onClick={e => e.stopPropagation()}> + <ConfigButton + appId={appId} + readOnly={readOnly} + hasConfigured + className='ml-2' + enabled={enabled} + onStatusChange={handleTracingEnabledChange} + chosenProvider={inUseTracingProvider} + onChooseProvider={handleChooseProvider} + langSmithConfig={langSmithConfig} + langFuseConfig={langFuseConfig} + onConfigUpdated={handleTracingConfigUpdated} + onConfigRemoved={handleTracingConfigRemoved} + controlShowPopup={controlShowPopup} + /> + </div> + </> )} - <div className='flex items-center' onClick={e => e.stopPropagation()}> - <ConfigButton - appId={appId} - readOnly={readOnly} - hasConfigured - className='ml-2' - enabled={enabled} - onStatusChange={handleTracingEnabledChange} - chosenProvider={inUseTracingProvider} - onChooseProvider={handleChooseProvider} - langSmithConfig={langSmithConfig} - langFuseConfig={langFuseConfig} - onConfigUpdated={handleTracingConfigUpdated} - onConfigRemoved={handleTracingConfigRemoved} - controlShowPopup={controlShowPopup} - /> - </div> </div> </div> ) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/toggle-fold-btn.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/toggle-fold-btn.tsx deleted file mode 100644 index 934eb681b9..0000000000 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/toggle-fold-btn.tsx +++ /dev/null @@ -1,45 +0,0 @@ -'use client' -import { ChevronDoubleDownIcon } from '@heroicons/react/20/solid' -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import React, { useCallback } from 'react' -import Tooltip from '@/app/components/base/tooltip' - -const I18N_PREFIX = 'app.tracing' - -type Props = { - isFold: boolean - onFoldChange: (isFold: boolean) => void -} - -const ToggleFoldBtn: FC<Props> = ({ - isFold, - onFoldChange, -}) => { - const { t } = useTranslation() - - const handleFoldChange = useCallback((e: React.MouseEvent<HTMLDivElement>) => { - e.stopPropagation() - onFoldChange(!isFold) - }, [isFold, onFoldChange]) - return ( - // text-[0px] to hide spacing between tooltip elements - <div className='shrink-0 cursor-pointer text-[0px]' onClick={handleFoldChange}> - <Tooltip - popupContent={t(`${I18N_PREFIX}.${isFold ? 'expand' : 'collapse'}`)} - > - {isFold && ( - <div className='p-1 rounded-md text-gray-500 hover:text-gray-800 hover:bg-black/5'> - <ChevronDoubleDownIcon className='w-4 h-4' /> - </div> - )} - {!isFold && ( - <div className='p-2 rounded-lg text-gray-500 border-[0.5px] border-gray-200 hover:text-gray-800 hover:bg-black/5'> - <ChevronDoubleDownIcon className='w-4 h-4 transform rotate-180' /> - </div> - )} - </Tooltip> - </div> - ) -} -export default React.memo(ToggleFoldBtn) diff --git a/web/context/app-context.tsx b/web/context/app-context.tsx index 7addfb83d4..25a313a76b 100644 --- a/web/context/app-context.tsx +++ b/web/context/app-context.tsx @@ -127,7 +127,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => setCurrentWorkspace(currentWorkspaceResponse) }, [currentWorkspaceResponse]) - const [theme, setTheme] = useState<Theme>(Theme.light) + const [theme, setTheme] = useState<Theme>(Theme.dark) const handleSetTheme = useCallback((theme: Theme) => { setTheme(theme) globalThis.document.documentElement.setAttribute('data-theme', theme) From 05d273d643244d7853d87c5c8bbfd8ee00bbdf51 Mon Sep 17 00:00:00 2001 From: JzoNg <jzongcode@gmail.com> Date: Wed, 8 Jan 2025 15:40:13 +0800 Subject: [PATCH 08/15] fix tracing dropdown --- .../overview/tracing/config-button.tsx | 26 +++---------------- .../[appId]/overview/tracing/panel.tsx | 18 ++++++++++--- web/context/app-context.tsx | 2 +- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx index 39fc81a274..ca6fa3a904 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx @@ -4,19 +4,15 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import { RiEqualizer2Line, } from '@remixicon/react' -import { useTranslation } from 'react-i18next' import type { PopupProps } from './config-popup' import ConfigPopup from './config-popup' import cn from '@/utils/classnames' -import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -const I18N_PREFIX = 'app.tracing' - type Props = { readOnly: boolean className?: string @@ -30,7 +26,6 @@ const ConfigBtn: FC<Props> = ({ controlShowPopup, ...popupProps }) => { - const { t } = useTranslation() const [open, doSetOpen] = useState(false) const openRef = useRef(open) const setOpen = useCallback((v: boolean) => { @@ -52,21 +47,6 @@ const ConfigBtn: FC<Props> = ({ if (popupProps.readOnly && !hasConfigured) return null - const triggerContent = hasConfigured - ? ( - <div className={cn(className, 'p-1 rounded-md')}> - <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' /> - </div> - ) - : ( - <Button variant='primary' - className={cn(className, '!h-8 !px-3 select-none')} - > - <RiEqualizer2Line className='mr-1 w-4 h-4' /> - <span className='text-[13px]'>{t(`${I18N_PREFIX}.config`)}</span> - </Button> - ) - return ( <PortalToFollowElem open={open} @@ -74,11 +54,13 @@ const ConfigBtn: FC<Props> = ({ placement='bottom-end' offset={{ mainAxis: 12, - crossAxis: hasConfigured ? 8 : 0, + crossAxis: hasConfigured ? 8 : 49, }} > <PortalToFollowElemTrigger onClick={handleTrigger}> - {triggerContent} + <div className={cn(className, 'p-1 rounded-md')}> + <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' /> + </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent className='z-[11]'> <ConfigPopup {...popupProps} /> diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx index 790705e9fb..06621a2ae4 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx @@ -3,7 +3,6 @@ import type { FC } from 'react' import React, { useCallback, useEffect, useState } from 'react' import { RiArrowDownDoubleLine, - RiEqualizer2Line, } from '@remixicon/react' import { useTranslation } from 'react-i18next' import { usePathname } from 'next/navigation' @@ -151,8 +150,21 @@ const Panel: FC = () => { <> <TracingIcon size='md' /> <div className='mx-2 system-sm-semibold text-text-secondary'>{t(`${I18N_PREFIX}.title`)}</div> - <div className='p-1 rounded-md'> - <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' /> + <div className='flex items-center' onClick={e => e.stopPropagation()}> + <ConfigButton + appId={appId} + readOnly={readOnly} + hasConfigured={false} + enabled={enabled} + onStatusChange={handleTracingEnabledChange} + chosenProvider={inUseTracingProvider} + onChooseProvider={handleChooseProvider} + langSmithConfig={langSmithConfig} + langFuseConfig={langFuseConfig} + onConfigUpdated={handleTracingConfigUpdated} + onConfigRemoved={handleTracingConfigRemoved} + controlShowPopup={controlShowPopup} + /> </div> <Divider type='vertical' className='h-3.5' /> <div className='p-1 rounded-md'> diff --git a/web/context/app-context.tsx b/web/context/app-context.tsx index 25a313a76b..7addfb83d4 100644 --- a/web/context/app-context.tsx +++ b/web/context/app-context.tsx @@ -127,7 +127,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => setCurrentWorkspace(currentWorkspaceResponse) }, [currentWorkspaceResponse]) - const [theme, setTheme] = useState<Theme>(Theme.dark) + const [theme, setTheme] = useState<Theme>(Theme.light) const handleSetTheme = useCallback((theme: Theme) => { setTheme(theme) globalThis.document.documentElement.setAttribute('data-theme', theme) From 4b01ef2b48ece91a5fe144c43eef50a94876a998 Mon Sep 17 00:00:00 2001 From: zxhlyh <jasonapring2015@outlook.com> Date: Wed, 8 Jan 2025 15:41:59 +0800 Subject: [PATCH 09/15] fix: chat log --- .../chat/chat/answer/workflow-process.tsx | 23 +---- .../steps/install.tsx | 5 ++ .../panel/debug-and-preview/chat-wrapper.tsx | 2 +- .../workflow/panel/debug-and-preview/hooks.ts | 88 +++++++------------ .../panel/debug-and-preview/index.tsx | 2 +- .../workflow/panel/workflow-preview.tsx | 3 +- web/app/components/workflow/run/index.tsx | 3 +- .../components/workflow/run/tracing-panel.tsx | 5 +- web/types/workflow.ts | 1 + 9 files changed, 46 insertions(+), 86 deletions(-) diff --git a/web/app/components/base/chat/chat/answer/workflow-process.tsx b/web/app/components/base/chat/chat/answer/workflow-process.tsx index 47ad5291db..c7e17d10d3 100644 --- a/web/app/components/base/chat/chat/answer/workflow-process.tsx +++ b/web/app/components/base/chat/chat/answer/workflow-process.tsx @@ -1,5 +1,4 @@ import { - useCallback, useEffect, useMemo, useState, @@ -15,9 +14,8 @@ import TracingPanel from '@/app/components/workflow/run/tracing-panel' import cn from '@/utils/classnames' import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general' import { WorkflowRunningStatus } from '@/app/components/workflow/types' -import { useStore as useAppStore } from '@/app/components/app/store' -interface WorkflowProcessProps { +type WorkflowProcessProps = { data: WorkflowProcess item?: ChatItem expand?: boolean @@ -26,7 +24,6 @@ interface WorkflowProcessProps { } const WorkflowProcessItem = ({ data, - item, expand = false, hideInfo = false, hideProcessDetail = false, @@ -54,22 +51,6 @@ const WorkflowProcessItem = ({ setCollapse(!expand) }, [expand]) - const setCurrentLogItem = useAppStore(s => s.setCurrentLogItem) - const setShowMessageLogModal = useAppStore(s => s.setShowMessageLogModal) - const setCurrentLogModalActiveTab = useAppStore(s => s.setCurrentLogModalActiveTab) - - const showIterationDetail = useCallback(() => { - setCurrentLogItem(item) - setCurrentLogModalActiveTab('TRACING') - setShowMessageLogModal(true) - }, [item, setCurrentLogItem, setCurrentLogModalActiveTab, setShowMessageLogModal]) - - const showRetryDetail = useCallback(() => { - setCurrentLogItem(item) - setCurrentLogModalActiveTab('TRACING') - setShowMessageLogModal(true) - }, [item, setCurrentLogItem, setCurrentLogModalActiveTab, setShowMessageLogModal]) - return ( <div className={cn( @@ -110,8 +91,6 @@ const WorkflowProcessItem = ({ { <TracingPanel list={data.tracing} - onShowIterationDetail={showIterationDetail} - onShowRetryDetail={showRetryDetail} hideNodeInfo={hideInfo} hideNodeProcessDetail={hideProcessDetail} /> diff --git a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx index a3bb50076b..01c4d44bad 100644 --- a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx @@ -12,6 +12,7 @@ import { useInstallPackageFromMarketPlace, useUpdatePackageFromMarketPlace } fro import checkTaskStatus from '../../base/check-task-status' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' import Version from '../../base/version' +import { usePluginTaskList } from '@/service/use-plugins' const i18nPrefix = 'plugin.installModal' @@ -50,6 +51,7 @@ const Installed: FC<Props> = ({ check, stop, } = checkTaskStatus() + const { handleRefetch } = usePluginTaskList() useEffect(() => { if (hasInstalled && uniqueIdentifier === installedInfoPayload.uniqueIdentifier) @@ -93,6 +95,9 @@ const Installed: FC<Props> = ({ onInstalled() return } + + handleRefetch() + const { status, error } = await check({ taskId, pluginUniqueIdentifier: uniqueIdentifier, diff --git a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx index eb0ca15ee0..42c30df7cf 100644 --- a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx @@ -28,7 +28,7 @@ import { import { useStore as useAppStore } from '@/app/components/app/store' import { getLastAnswer } from '@/app/components/base/chat/utils' -interface ChatWrapperProps { +type ChatWrapperProps = { showConversationVariableModal: boolean onConversationModalHide: () => void showInputsFieldsPanel: boolean diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts index 9dca8c0502..d91cf1082d 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -27,10 +27,9 @@ import { getProcessedFilesFromResponse, } from '@/app/components/base/file-uploader/utils' import type { FileEntity } from '@/app/components/base/file-uploader/types' -import type { NodeTracing } from '@/types/workflow' type GetAbortController = (abortController: AbortController) => void -interface SendCallback { +type SendCallback = { onGetSuggestedQuestions?: (responseItemId: string, getAbortController: GetAbortController) => Promise<any> } export const useChat = ( @@ -276,6 +275,7 @@ export const useChat = ( ) setSuggestQuestions(data) } + // eslint-disable-next-line unused-imports/no-unused-vars catch (error) { setSuggestQuestions([]) } @@ -331,8 +331,7 @@ export const useChat = ( responseItem.workflowProcess!.tracing!.push({ ...data, status: NodeRunningStatus.Running, - details: [], - } as any) + }) handleUpdateChatList(produce(chatListRef.current, (draft) => { const currentIndex = draft.findIndex(item => item.id === responseItem.id) draft[currentIndex] = { @@ -341,30 +340,21 @@ export const useChat = ( } })) }, - onIterationNext: ({ data }) => { - const tracing = responseItem.workflowProcess!.tracing! - const iterations = tracing.find(item => item.node_id === data.node_id - && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))! - iterations.details!.push([]) - - handleUpdateChatList(produce(chatListRef.current, (draft) => { - const currentIndex = draft.length - 1 - draft[currentIndex] = responseItem - })) - }, onIterationFinish: ({ data }) => { - const tracing = responseItem.workflowProcess!.tracing! - const iterationsIndex = tracing.findIndex(item => item.node_id === data.node_id - && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))! - tracing[iterationsIndex] = { - ...tracing[iterationsIndex], - ...data, - status: NodeRunningStatus.Succeeded, - } as any - handleUpdateChatList(produce(chatListRef.current, (draft) => { - const currentIndex = draft.length - 1 - draft[currentIndex] = responseItem - })) + const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.id === data.id) + if (currentTracingIndex > -1) { + responseItem.workflowProcess!.tracing[currentTracingIndex] = { + ...responseItem.workflowProcess!.tracing[currentTracingIndex], + ...data, + } + handleUpdateChatList(produce(chatListRef.current, (draft) => { + const currentIndex = draft.findIndex(item => item.id === responseItem.id) + draft[currentIndex] = { + ...draft[currentIndex], + ...responseItem, + } + })) + } }, onNodeStarted: ({ data }) => { if (data.iteration_id) @@ -386,16 +376,7 @@ export const useChat = ( if (data.iteration_id) return - const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => { - if (!item.execution_metadata?.parallel_id) - return item.node_id === data.node_id - return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id) - }) - if (responseItem.workflowProcess!.tracing[currentIndex].retryDetail) - responseItem.workflowProcess!.tracing[currentIndex].retryDetail?.push(data as NodeTracing) - else - responseItem.workflowProcess!.tracing[currentIndex].retryDetail = [data as NodeTracing] - + responseItem.workflowProcess!.tracing!.push(data) handleUpdateChatList(produce(chatListRef.current, (draft) => { const currentIndex = draft.findIndex(item => item.id === responseItem.id) draft[currentIndex] = { @@ -408,27 +389,20 @@ export const useChat = ( if (data.iteration_id) return - const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => { - if (!item.execution_metadata?.parallel_id) - return item.node_id === data.node_id - return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id) - }) - responseItem.workflowProcess!.tracing[currentIndex] = { - ...(responseItem.workflowProcess!.tracing[currentIndex]?.extras - ? { extras: responseItem.workflowProcess!.tracing[currentIndex].extras } - : {}), - ...(responseItem.workflowProcess!.tracing[currentIndex]?.retryDetail - ? { retryDetail: responseItem.workflowProcess!.tracing[currentIndex].retryDetail } - : {}), - ...data, - } as any - handleUpdateChatList(produce(chatListRef.current, (draft) => { - const currentIndex = draft.findIndex(item => item.id === responseItem.id) - draft[currentIndex] = { - ...draft[currentIndex], - ...responseItem, + const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.id === data.id) + if (currentTracingIndex > -1) { + responseItem.workflowProcess!.tracing[currentTracingIndex] = { + ...responseItem.workflowProcess!.tracing[currentTracingIndex], + ...data, } - })) + handleUpdateChatList(produce(chatListRef.current, (draft) => { + const currentIndex = draft.findIndex(item => item.id === responseItem.id) + draft[currentIndex] = { + ...draft[currentIndex], + ...responseItem, + } + })) + } }, }, ) diff --git a/web/app/components/workflow/panel/debug-and-preview/index.tsx b/web/app/components/workflow/panel/debug-and-preview/index.tsx index 1e74a5c27d..d4a3f24d4a 100644 --- a/web/app/components/workflow/panel/debug-and-preview/index.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/index.tsx @@ -22,7 +22,7 @@ import Tooltip from '@/app/components/base/tooltip' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import { useStore } from '@/app/components/workflow/store' -export interface ChatWrapperRefType { +export type ChatWrapperRefType = { handleRestart: () => void } const DebugAndPreview = () => { diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index aa64229e30..b4e4d4c5d1 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -24,7 +24,6 @@ import Toast from '../../base/toast' import InputsPanel from './inputs-panel' import cn from '@/utils/classnames' import Loading from '@/app/components/base/loading' -import formatNodeList from '@/app/components/workflow/run/utils/format-log' const WorkflowPreview = () => { const { t } = useTranslation() @@ -161,7 +160,7 @@ const WorkflowPreview = () => { {currentTab === 'TRACING' && ( <TracingPanel className='bg-background-section-burn' - list={formatNodeList(workflowRunningData?.tracing || [], t)} + list={workflowRunningData?.tracing || []} /> )} {currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && ( diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index c1249f7224..eaa88d2df8 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -13,7 +13,6 @@ import { fetchRunDetail, fetchTracingList } from '@/service/log' import type { NodeTracing } from '@/types/workflow' import type { WorkflowRunDetailResponse } from '@/models/log' import { useStore as useAppStore } from '@/app/components/app/store' -import formatNodeList from './utils/format-log' export type RunProps = { hideResult?: boolean activeTab?: 'RESULT' | 'DETAIL' | 'TRACING' @@ -61,7 +60,7 @@ const RunPanel: FC<RunProps> = ({ hideResult, activeTab = 'RESULT', runID, getRe const { data: nodeList } = await fetchTracingList({ url: `/apps/${appID}/workflow-runs/${runID}/node-executions`, }) - setList(formatNodeList(nodeList, t)) + setList(nodeList) } catch (err) { notify({ diff --git a/web/app/components/workflow/run/tracing-panel.tsx b/web/app/components/workflow/run/tracing-panel.tsx index 758e9cfcf8..7739c8f836 100644 --- a/web/app/components/workflow/run/tracing-panel.tsx +++ b/web/app/components/workflow/run/tracing-panel.tsx @@ -11,10 +11,12 @@ import { RiArrowDownSLine, RiMenu4Line, } from '@remixicon/react' +import { useTranslation } from 'react-i18next' import { useLogs } from './hooks' import NodePanel from './node' import SpecialResultPanel from './special-result-panel' import type { NodeTracing } from '@/types/workflow' +import formatNodeList from '@/app/components/workflow/run/utils/format-log' type TracingPanelProps = { list: NodeTracing[] @@ -29,7 +31,8 @@ const TracingPanel: FC<TracingPanelProps> = ({ hideNodeInfo = false, hideNodeProcessDetail = false, }) => { - const treeNodes = list + const { t } = useTranslation() + const treeNodes = formatNodeList(list, t) const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(new Set()) const [hoveredParallel, setHoveredParallel] = useState<string | null>(null) diff --git a/web/types/workflow.ts b/web/types/workflow.ts index 9287cba471..b152c3615b 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -23,6 +23,7 @@ export type NodeTracing = { index: number predecessor_node_id: string node_id: string + iteration_id?: string node_type: BlockEnum title: string inputs: any From 23b29b1d21e01db0d77e964f71596f6e9ce3ad6f Mon Sep 17 00:00:00 2001 From: Yi <yxiaoisme@gmail.com> Date: Wed, 8 Jan 2025 15:54:04 +0800 Subject: [PATCH 10/15] fix: check-dependencies typo and fetch model list typo --- .../agent-model-trigger.tsx | 15 ++++++--- .../status-indicators.tsx | 33 +++++++------------ .../workflow/plugin-dependency/hooks.ts | 4 +-- web/service/use-plugins.ts | 4 +-- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx index 716914118d..068be5bbf3 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx @@ -73,15 +73,22 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({ const handleOpenModal = useModelModalHandler() useEffect(() => { (async () => { + if (modelId && currentProvider) { + try { + const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${currentProvider?.provider}/models`) + if (modelId && modelsData.data.find(item => item.model === modelId)) + setInModelList(true) + } + catch (error) { + // pass + } + } if (providerName && !modelProvider) { const parts = providerName.split('/') const org = parts[0] const name = parts[1] try { const pluginInfo = await fetchPluginInfoFromMarketPlace({ org, name }) - const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${providerName}/models`) - if (modelId && modelsData.data.find(item => item.model === modelId)) - setInModelList(true) if (pluginInfo.data.plugin.category === PluginType.model) setPluginInfo(pluginInfo.data.plugin) } @@ -94,7 +101,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({ setIsPluginChecked(true) } })() - }, [providerName, modelProvider, modelId]) + }, [providerName, modelProvider, modelId, currentProvider]) if (modelId && !isPluginChecked) return null diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx index 929eb5c2a6..d5bda7caf6 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx @@ -39,32 +39,23 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disa return ( <> {/* plugin installed and model is in model list but disabled */} - {!needsConfiguration && modelProvider && disabled && inModelList && ( - <Tooltip - popupContent={t('workflow.nodes.agent.modelSelectorTooltips.deprecated')} - asChild={false} - > - <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> - </Tooltip> - )} {/* plugin installed from github/local and model is not in model list */} - { - modelProvider && !inModelList && ( - <Tooltip - popupContent={renderTooltipContent( + {!needsConfiguration && modelProvider && disabled && ( + <Tooltip + popupContent={inModelList ? t('workflow.nodes.agent.modelSelectorTooltips.deprecated') + : renderTooltipContent( t('workflow.nodes.agent.modelNotSupport.title'), t('workflow.nodes.agent.modelNotSupport.desc'), !pluginInfo ? t('workflow.nodes.agent.linkToPlugin') : '', !pluginInfo ? '/plugins' : '', - )} - asChild={false} - needsDelay - > - <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> - </Tooltip> - ) - } - {/* plugin not installed and not in marketplace */} + ) + } + asChild={false} + needsDelay={!inModelList} + > + <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> + </Tooltip> + )} {!modelProvider && !pluginInfo && ( <Tooltip popupContent={renderTooltipContent( diff --git a/web/app/components/workflow/plugin-dependency/hooks.ts b/web/app/components/workflow/plugin-dependency/hooks.ts index 73c3a481da..1aa52cf028 100644 --- a/web/app/components/workflow/plugin-dependency/hooks.ts +++ b/web/app/components/workflow/plugin-dependency/hooks.ts @@ -1,9 +1,9 @@ import { useCallback } from 'react' import { useStore as usePluginDependenciesStore } from './store' -import { useMutationCheckDependecies } from '@/service/use-plugins' +import { useMutationCheckDependencies } from '@/service/use-plugins' export const usePluginDependencies = () => { - const { mutateAsync } = useMutationCheckDependecies() + const { mutateAsync } = useMutationCheckDependencies() const handleCheckPluginDependencies = useCallback(async (appId: string) => { const { leaked_dependencies } = await mutateAsync(appId) diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index a33ca9af46..66e55c6431 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -423,10 +423,10 @@ export const useDownloadPlugin = (info: { organization: string; pluginName: stri }) } -export const useMutationCheckDependecies = () => { +export const useMutationCheckDependencies = () => { return useMutation({ mutationFn: (appId: string) => { - return get<{ leaked_dependencies: Dependency[] }>(`/apps/import/${appId}/check-dependencies`) + return get<{ leaked_dependencies: Dependency[] }>(`/apps/imports/${appId}/check-dependencies`) }, }) } From 2fd083629d5dfa124ef2959c62ecb21e3249fba5 Mon Sep 17 00:00:00 2001 From: AkaraChen <akarachen@outlook.com> Date: Wed, 8 Jan 2025 15:58:05 +0800 Subject: [PATCH 11/15] feat: new switch plugin version --- .../plugins/plugin-mutation-model/index.tsx | 38 ++++++----- .../components/switch-plugin-version.tsx | 63 ++++++++++++++----- .../workflow/nodes/agent/default.ts | 2 +- web/i18n/en-US/workflow.ts | 13 +++- web/i18n/zh-Hans/workflow.ts | 7 +++ 5 files changed, 89 insertions(+), 34 deletions(-) diff --git a/web/app/components/plugins/plugin-mutation-model/index.tsx b/web/app/components/plugins/plugin-mutation-model/index.tsx index fadfd9a957..36ab670ab3 100644 --- a/web/app/components/plugins/plugin-mutation-model/index.tsx +++ b/web/app/components/plugins/plugin-mutation-model/index.tsx @@ -8,14 +8,15 @@ import type { UseMutationResult } from '@tanstack/react-query' type Props = { plugin: Plugin - onSave: () => void onCancel: () => void - mutation: UseMutationResult + mutation: Pick<UseMutationResult, 'isSuccess' | 'isPending'> + mutate: () => void confirmButtonText: ReactNode cancelButtonText: ReactNode modelTitle: ReactNode description: ReactNode cardTitleLeft: ReactNode + modalBottomLeft?: ReactNode } const PluginMutationModal: FC<Props> = ({ @@ -27,6 +28,8 @@ const PluginMutationModal: FC<Props> = ({ modelTitle, description, cardTitleLeft, + mutate, + modalBottomLeft, }: Props) => { return ( <Modal @@ -47,20 +50,25 @@ const PluginMutationModal: FC<Props> = ({ titleLeft={cardTitleLeft} /> </div> - <div className='flex pt-5 justify-end items-center gap-2 self-stretch'> - {mutation.isPending && ( - <Button onClick={onCancel}> - {cancelButtonText} + <div className='flex pt-5 items-center gap-2 self-stretch'> + <div> + {modalBottomLeft} + </div> + <div className='ml-auto flex gap-2'> + {!mutation.isPending && ( + <Button onClick={onCancel}> + {cancelButtonText} + </Button> + )} + <Button + variant='primary' + loading={mutation.isPending} + onClick={mutate} + disabled={mutation.isPending} + > + {confirmButtonText} </Button> - )} - <Button - variant='primary' - loading={mutation.isPending} - onClick={mutation.mutate} - disabled={mutation.isPending} - > - {confirmButtonText} - </Button> + </div> </div> </Modal> ) 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 fc517ccf1d..6d0aed8a44 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 @@ -3,13 +3,18 @@ import Badge from '@/app/components/base/badge' import Tooltip from '@/app/components/base/tooltip' import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker' -import { RiArrowLeftRightLine } from '@remixicon/react' +import { RiArrowLeftRightLine, RiExternalLinkLine } from '@remixicon/react' import type { ReactNode } from 'react' import { type FC, useCallback, useState } from 'react' -import UpdateFromMarketplace from '@/app/components/plugins/update-plugin/from-market-place' import { useBoolean } from 'ahooks' -import { useCheckInstalled } from '@/service/use-plugins' +import { useCheckInstalled, useUpdatePackageFromMarketPlace } from '@/service/use-plugins' import cn from '@/utils/classnames' +import PluginMutationModel from '@/app/components/plugins/plugin-mutation-model' +import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' +import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils' +import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index' +import Link from 'next/link' +import { useTranslation } from 'react-i18next' export type SwitchPluginVersionProps = { uniqueIdentifier: string @@ -38,21 +43,49 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => { pluginDetails.refetch() onChange?.(target!.version) }, [hideUpdateModal, onChange, pluginDetails, target]) + const { getIconUrl } = useGetIcon() + const icon = pluginDetail?.declaration.icon ? getIconUrl(pluginDetail.declaration.icon) : undefined + const mutation = useUpdatePackageFromMarketPlace() + const install = () => { + mutation.mutate( + { + new_plugin_unique_identifier: target!.pluginUniqueIden, + original_plugin_unique_identifier: uniqueIdentifier, + }, + { + onSuccess() { + handleUpdatedFromMarketplace() + }, + }) + } + const { t } = useTranslation() return <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod='hover'> <div className={cn('w-fit', className)}> - {isShowUpdateModal && pluginDetail && <UpdateFromMarketplace - payload={{ - originalPackageInfo: { - id: uniqueIdentifier, - payload: pluginDetail.declaration, - }, - targetPackageInfo: { - id: target!.pluginUniqueIden, - version: target!.version, - }, - }} + {isShowUpdateModal && pluginDetail && <PluginMutationModel onCancel={hideUpdateModal} - onSave={handleUpdatedFromMarketplace} + plugin={pluginManifestToCardPluginProps({ + ...pluginDetail.declaration, + icon: icon!, + })} + mutation={mutation} + mutate={install} + confirmButtonText={t('workflow.nodes.agent.installPlugin.install')} + cancelButtonText={t('workflow.nodes.agent.installPlugin.cancel')} + modelTitle={t('workflow.nodes.agent.installPlugin.title')} + description={t('workflow.nodes.agent.installPlugin.desc')} + cardTitleLeft={<> + <Badge2 className='mx-1' size="s" state={BadgeState.Warning}> + {`${pluginDetail.version} -> ${target!.version}`} + </Badge2> + </>} + modalBottomLeft={ + <Link className='flex justify-center items-center gap-1' href={'TODO: add changelog url'} target='_blank'> + <span className='text-text-accent system-xs-regular text-xs'> + {t('workflow.nodes.agent.installPlugin.changelog')} + </span> + <RiExternalLinkLine className='text-text-accent size-3' /> + </Link> + } />} {pluginDetail && <PluginVersionPicker isShow={isShow} diff --git a/web/app/components/workflow/nodes/agent/default.ts b/web/app/components/workflow/nodes/agent/default.ts index 4d7965c77f..26c004b188 100644 --- a/web/app/components/workflow/nodes/agent/default.ts +++ b/web/app/components/workflow/nodes/agent/default.ts @@ -26,7 +26,7 @@ const nodeDefault: NodeDefault<AgentNodeType> = { if (!strategy) { return { isValid: false, - errorMessage: t('workflow.checkList.strategyNotSelected'), + errorMessage: t('workflow.nodes.agent.checkList.strategyNotSelected'), } } for (const param of strategy.parameters) { diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 279654a857..b25a1b4915 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -755,9 +755,16 @@ const translation = { }, json: 'agent generated json', }, - }, - checkList: { - strategyNotSelected: 'Strategy not selected', + checkList: { + strategyNotSelected: 'Strategy not selected', + }, + installPlugin: { + title: 'Install Plugin', + desc: 'About to install the following plugin', + changelog: 'Change log', + install: 'Install', + cancel: 'Cancel', + }, }, }, tracing: { diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 80c53702ab..4cbd5246bf 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -758,6 +758,13 @@ const translation = { checkList: { strategyNotSelected: '未选择策略', }, + installPlugin: { + title: '安装插件', + desc: '即将安装以下插件', + changelog: '更新日志', + install: '安装', + cancel: '取消', + }, }, }, tracing: { From f58eef74b35845cc90324ec2d96893a4c4b7b9e3 Mon Sep 17 00:00:00 2001 From: AkaraChen <akarachen@outlook.com> Date: Wed, 8 Jan 2025 16:19:41 +0800 Subject: [PATCH 12/15] feat: switch plugin version changelog --- .../nodes/_base/components/switch-plugin-version.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 6d0aed8a44..05a673ee2b 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 @@ -15,6 +15,7 @@ import { pluginManifestToCardPluginProps } from '@/app/components/plugins/instal import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index' import Link from 'next/link' import { useTranslation } from 'react-i18next' +import { marketplaceUrlPrefix } from '@/config' export type SwitchPluginVersionProps = { uniqueIdentifier: string @@ -79,7 +80,11 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => { </Badge2> </>} modalBottomLeft={ - <Link className='flex justify-center items-center gap-1' href={'TODO: add changelog url'} target='_blank'> + <Link + className='flex justify-center items-center gap-1' + href={`${marketplaceUrlPrefix}/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`} + target='_blank' + > <span className='text-text-accent system-xs-regular text-xs'> {t('workflow.nodes.agent.installPlugin.changelog')} </span> From 369e3eb97fafa5779c4468f2886a65e1391f4f51 Mon Sep 17 00:00:00 2001 From: Yi <yxiaoisme@gmail.com> Date: Wed, 8 Jan 2025 16:39:56 +0800 Subject: [PATCH 13/15] feat: add version switch in model selector --- .../model-parameter-modal/agent-model-trigger.tsx | 4 ++-- .../model-parameter-modal/status-indicators.tsx | 10 +++++++--- .../nodes/_base/components/switch-plugin-version.tsx | 2 +- web/i18n/en-US/workflow.ts | 1 + web/i18n/zh-Hans/workflow.ts | 1 + 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx index 068be5bbf3..be56b95a95 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx @@ -83,7 +83,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({ // pass } } - if (providerName && !modelProvider) { + if (providerName) { const parts = providerName.split('/') const org = parts[0] const name = parts[1] @@ -101,7 +101,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({ setIsPluginChecked(true) } })() - }, [providerName, modelProvider, modelId, currentProvider]) + }, [providerName, modelId, currentProvider]) if (modelId && !isPluginChecked) return null diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx index d5bda7caf6..1c28ef3019 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx @@ -1,5 +1,7 @@ import Tooltip from '@/app/components/base/tooltip' import Link from 'next/link' +import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version' +import { useInstalledPluginList } from '@/service/use-plugins' import { RiErrorWarningFill } from '@remixicon/react' type StatusIndicatorsProps = { @@ -12,9 +14,10 @@ type StatusIndicatorsProps = { } const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disabled, pluginInfo, t }: StatusIndicatorsProps) => { + const { data: pluginList } = useInstalledPluginList() const renderTooltipContent = (title: string, description?: string, linkText?: string, linkHref?: string) => { return ( - <div className='flex w-[240px] max-w-[240px] gap-1 flex-col px-1 py-1.5'> + <div className='flex w-[240px] max-w-[240px] gap-1 flex-col px-1 py-1.5' onClick={e => e.stopPropagation()}> <div className='text-text-primary title-xs-semi-bold'>{title}</div> {description && ( <div className='min-w-[200px] text-text-secondary body-xs-regular'> @@ -36,6 +39,7 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disa </div> ) } + // const installedPluginUniqueIdentifier = pluginList?.plugins.find(plugin => plugin.name === pluginInfo.name)?.plugin_unique_identifier return ( <> {/* plugin installed and model is in model list but disabled */} @@ -45,7 +49,7 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disa popupContent={inModelList ? t('workflow.nodes.agent.modelSelectorTooltips.deprecated') : renderTooltipContent( t('workflow.nodes.agent.modelNotSupport.title'), - t('workflow.nodes.agent.modelNotSupport.desc'), + !pluginInfo ? t('workflow.nodes.agent.modelNotSupport.desc') : t('workflow.nodes.agent.modelNotSupport.descForVersionSwitch'), !pluginInfo ? t('workflow.nodes.agent.linkToPlugin') : '', !pluginInfo ? '/plugins' : '', ) @@ -53,7 +57,7 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disa asChild={false} needsDelay={!inModelList} > - <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> + {!pluginInfo ? <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> : <SwitchPluginVersion uniqueIdentifier={pluginList?.plugins.find(plugin => plugin.name === pluginInfo.name)?.plugin_unique_identifier ?? ''} />} </Tooltip> )} {!modelProvider && !pluginInfo && ( 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 05a673ee2b..2eee37c5c5 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 @@ -61,7 +61,7 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => { } const { t } = useTranslation() return <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod='hover'> - <div className={cn('w-fit', className)}> + <div className={cn('w-fit flex items-center justify-center', className)}> {isShowUpdateModal && pluginDetail && <PluginMutationModel onCancel={hideUpdateModal} plugin={pluginManifestToCardPluginProps({ diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index b25a1b4915..8e8d1ea9d9 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -725,6 +725,7 @@ const translation = { modelNotSupport: { title: 'Unsupported Model', desc: 'The installed plugin version does not provide this model.', + descForVersionSwitch: 'The installed plugin version does not provide this model. Click to switch version.', }, configureModel: 'Configure Model', notAuthorized: 'Not Authorized', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 4cbd5246bf..8992770fb8 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -725,6 +725,7 @@ const translation = { modelNotSupport: { title: '不支持的模型', desc: '已安装的插件版本不提供此模型。', + descForVersionSwitch: '已安装的插件版本不提供此模型。点击切换版本。', }, model: '模型', toolbox: '工具箱', From 0fdf7b23c6fb65a6b4bc6d22a78db0cb0bd58878 Mon Sep 17 00:00:00 2001 From: WTW0313 <twwu@dify.ai> Date: Wed, 8 Jan 2025 16:41:16 +0800 Subject: [PATCH 14/15] feat: add 'agent' tag to plugin constants and translations --- web/app/components/plugins/constants.ts | 1 + web/i18n/en-US/plugin-tags.ts | 1 + web/i18n/zh-Hans/plugin-tags.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/web/app/components/plugins/constants.ts b/web/app/components/plugins/constants.ts index c43a1ae946..40a3f0da83 100644 --- a/web/app/components/plugins/constants.ts +++ b/web/app/components/plugins/constants.ts @@ -1,4 +1,5 @@ export const tagKeys = [ + 'agent', 'search', 'image', 'videos', diff --git a/web/i18n/en-US/plugin-tags.ts b/web/i18n/en-US/plugin-tags.ts index e96f415053..d2177b2848 100644 --- a/web/i18n/en-US/plugin-tags.ts +++ b/web/i18n/en-US/plugin-tags.ts @@ -2,6 +2,7 @@ const translation = { allTags: 'All Tags', searchTags: 'Search Tags', tags: { + agent: 'Agent', search: 'Search', image: 'Image', videos: 'Videos', diff --git a/web/i18n/zh-Hans/plugin-tags.ts b/web/i18n/zh-Hans/plugin-tags.ts index 4c9b2c6370..c133992b38 100644 --- a/web/i18n/zh-Hans/plugin-tags.ts +++ b/web/i18n/zh-Hans/plugin-tags.ts @@ -2,6 +2,7 @@ const translation = { allTags: '所有标签', searchTags: '搜索标签', tags: { + agent: 'Agent', search: '搜索', image: '图片', videos: '视频', From 586f9d8d891c17efe2e1958a97abf2762f9c59f9 Mon Sep 17 00:00:00 2001 From: zxhlyh <jasonapring2015@outlook.com> Date: Wed, 8 Jan 2025 16:46:07 +0800 Subject: [PATCH 15/15] fix: agent log --- .../workflow/run/agent-log/agent-log-item.tsx | 2 +- .../workflow/run/agent-log/agent-log-nav.tsx | 55 +++++++++++++------ .../run/agent-log/agent-log-trigger.tsx | 6 +- .../run/agent-log/agent-result-panel.tsx | 10 ++-- web/i18n/en-US/run-log.ts | 2 + web/i18n/zh-Hans/run-log.ts | 2 + 6 files changed, 53 insertions(+), 24 deletions(-) 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 8403dd68dc..36b1d78bc6 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 @@ -27,7 +27,7 @@ const AgentLogItem = ({ const [expanded, setExpanded] = useState(false) return ( - <div className='border-[0.5px] border-components-panel-border rounded-[10px]'> + <div className='bg-background-default border-[0.5px] border-components-panel-border rounded-[10px]'> <div className={cn( 'flex items-center pl-1.5 pt-2 pr-3 pb-2 cursor-pointer', diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx index d0cffc6a58..0506f5ded0 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx @@ -1,4 +1,5 @@ import { RiArrowLeftLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' import AgentLogNavMore from './agent-log-nav-more' import Button from '@/app/components/base/button' import type { AgentLogItemWithChildren } from '@/types/workflow' @@ -11,10 +12,14 @@ const AgentLogNav = ({ agentOrToolLogItemStack, onShowAgentOrToolLog, }: AgentLogNavProps) => { - const options = agentOrToolLogItemStack.slice(2) + const { t } = useTranslation() + const agentOrToolLogItemStackLength = agentOrToolLogItemStack.length + const first = agentOrToolLogItemStack[0] + const mid = agentOrToolLogItemStack.slice(1, -1) + const end = agentOrToolLogItemStack.at(-1) return ( - <div className='flex items-center p-1 pr-3 h-8'> + <div className='flex items-center p-1 pr-3 h-8 bg-components-panel-bg'> <Button className='shrink-0 px-[5px]' size='small' @@ -24,32 +29,48 @@ const AgentLogNav = ({ }} > <RiArrowLeftLine className='mr-1 w-3.5 h-3.5' /> - Agent + AGENT </Button> <div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div> - <Button - className='shrink-0 px-[5px]' - size='small' - variant='ghost-accent' - onClick={() => {}} - > - Agent strategy - </Button> { - !!options.length && ( + agentOrToolLogItemStackLength > 1 + ? ( + <Button + className='shrink-0 px-[5px]' + size='small' + variant='ghost-accent' + onClick={() => onShowAgentOrToolLog(first)} + > + {t('workflow.nodes.agent.strategy.label')} + </Button> + ) + : ( + <div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'> + {t('workflow.nodes.agent.strategy.label')} + </div> + ) + } + { + !!mid.length && ( <> <div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div> <AgentLogNavMore - options={options} + options={mid} onShowAgentOrToolLog={onShowAgentOrToolLog} /> </> ) } - <div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div> - <div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'> - Run Actions - </div> + { + !!end && agentOrToolLogItemStackLength > 2 && ( + <> + <div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div> + <div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'> + {end.label} + </div> + </> + ) + } </div> ) } 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 987c3afc2a..589624f559 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 @@ -1,4 +1,5 @@ import { RiArrowRightLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' import type { AgentLogItemWithChildren, NodeTracing, @@ -12,12 +13,13 @@ const AgentLogTrigger = ({ nodeInfo, onShowAgentOrToolLog, }: AgentLogTriggerProps) => { + const { t } = useTranslation() const { agentLog } = nodeInfo return ( <div className='bg-components-button-tertiary-bg rounded-[10px]'> <div className='flex items-center px-3 pt-2 system-2xs-medium-uppercase text-text-tertiary'> - Agent strategy + {t('workflow.nodes.agent.strategy.label')} </div> <div className='flex items-center pl-3 pt-1 pr-2 pb-1.5'> <div className='shrink-0 w-5 h-5'></div> @@ -28,7 +30,7 @@ const AgentLogTrigger = ({ onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren) }} > - Detail + {t('runLog.detail')} <RiArrowRightLine className='ml-0.5 w-3.5 h-3.5' /> </div> </div> diff --git a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx index d02e69f8da..e2d2b24966 100644 --- a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx +++ b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx @@ -1,4 +1,5 @@ import { RiAlertFill } from '@remixicon/react' +import { useTranslation } from 'react-i18next' import AgentLogItem from './agent-log-item' import AgentLogNav from './agent-log-nav' import type { AgentLogItemWithChildren } from '@/types/workflow' @@ -13,17 +14,18 @@ const AgentResultPanel = ({ agentOrToolLogListMap, onShowAgentOrToolLog, }: AgentResultPanelProps) => { + const { t } = useTranslation() const top = agentOrToolLogItemStack[agentOrToolLogItemStack.length - 1] const list = agentOrToolLogListMap[top.id] return ( - <div className='overflow-y-auto'> + <div className='bg-background-section overflow-y-auto'> <AgentLogNav agentOrToolLogItemStack={agentOrToolLogItemStack} onShowAgentOrToolLog={onShowAgentOrToolLog} /> { - <div className='p-2'> + <div className='p-2 space-y-1'> { list.map(item => ( <AgentLogItem @@ -37,7 +39,7 @@ const AgentResultPanel = ({ } { top.hasCircle && ( - <div className='flex items-center rounded-xl px-3 pr-2 border border-components-panel-border bg-components-panel-bg-blur shadow-md'> + <div className='flex items-center mt-1 rounded-xl px-3 pr-2 border border-components-panel-border bg-components-panel-bg-blur shadow-md'> <div className='absolute inset-0 opacity-[0.4] rounded-xl' style={{ @@ -46,7 +48,7 @@ const AgentResultPanel = ({ ></div> <RiAlertFill className='mr-1.5 w-4 h-4 text-text-warning-secondary' /> <div className='system-xs-medium text-text-primary'> - There is circular invocation of tools/nodes in the current workflow. + {t('runLog.circularInvocationTip')} </div> </div> ) diff --git a/web/i18n/en-US/run-log.ts b/web/i18n/en-US/run-log.ts index 33fe5c1735..3c851f4548 100644 --- a/web/i18n/en-US/run-log.ts +++ b/web/i18n/en-US/run-log.ts @@ -24,6 +24,8 @@ const translation = { link: 'detail panel', tipRight: ' view it.', }, + actionLogs: 'Action Logs', + circularInvocationTip: 'There is circular invocation of tools/nodes in the current workflow.', } export default translation diff --git a/web/i18n/zh-Hans/run-log.ts b/web/i18n/zh-Hans/run-log.ts index 225874d827..dc93e9aeb0 100644 --- a/web/i18n/zh-Hans/run-log.ts +++ b/web/i18n/zh-Hans/run-log.ts @@ -24,6 +24,8 @@ const translation = { link: '详细信息面板', tipRight: '查看它。', }, + actionLogs: 'Action 日志', + circularInvocationTip: '当前工作流中存在工具/节点的循环调用。', } export default translation