From 6651c1c5da7bfcc1fc1d1db11d840250f1ea046b Mon Sep 17 00:00:00 2001 From: JzoNg Date: Fri, 3 Apr 2026 14:22:50 +0800 Subject: [PATCH] feat(web): workflow switch --- .../components/app/app-publisher/index.tsx | 98 ++++++++++++++++++- web/i18n/en-US/workflow.json | 6 ++ web/i18n/zh-Hans/workflow.json | 6 ++ 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index 0b5f6298290..e48d523bff0 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -21,6 +21,11 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' +import { + Tooltip as UITooltip, + TooltipContent as UITooltipContent, + TooltipTrigger as UITooltipTrigger, +} from '@/app/components/base/ui/tooltip' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import WorkflowToolConfigureButton from '@/app/components/tools/workflow-tool/configure-button' import { appDefaultIconBackground } from '@/config' @@ -31,7 +36,8 @@ import { AccessMode } from '@/models/access-control' import { useAppWhiteListSubjects, useGetUserCanAccessApp } from '@/service/access-control' import { fetchAppDetailDirect } from '@/service/apps' import { fetchInstalledAppList } from '@/service/explore' -import { AppModeEnum } from '@/types/app' +import { useConvertWorkflowTypeMutation } from '@/service/use-apps' +import { AppModeEnum, AppTypeEnum } from '@/types/app' import { basePath } from '@/utils/var' import Divider from '../../base/divider' import Loading from '../../base/loading' @@ -108,6 +114,21 @@ export type AppPublisherProps = { const PUBLISH_SHORTCUT = ['ctrl', '⇧', 'P'] +const WORKFLOW_TYPE_SWITCH_CONFIG = { + [AppTypeEnum.WORKFLOW]: { + targetType: AppTypeEnum.EVALUATION, + publishLabelKey: 'common.publishAsEvaluationWorkflow', + switchLabelKey: 'common.switchToEvaluationWorkflow', + tipKey: 'common.switchToEvaluationWorkflowTip', + }, + [AppTypeEnum.EVALUATION]: { + targetType: AppTypeEnum.WORKFLOW, + publishLabelKey: 'common.publishAsStandardWorkflow', + switchLabelKey: 'common.switchToStandardWorkflow', + tipKey: 'common.switchToStandardWorkflowTip', + }, +} as const + const AppPublisher = ({ disabled = false, publishDisabled = false, @@ -142,10 +163,13 @@ const AppPublisher = ({ const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const { formatTimeFromNow } = useFormatTimeFromNow() const { app_base_url: appBaseURL = '', access_token: accessToken = '' } = appDetail?.site ?? {} + const { mutateAsync: convertWorkflowType, isPending: isConvertingWorkflowType } = useConvertWorkflowTypeMutation() const appMode = (appDetail?.mode !== AppModeEnum.COMPLETION && appDetail?.mode !== AppModeEnum.WORKFLOW) ? AppModeEnum.CHAT : appDetail.mode const appURL = `${appBaseURL}${basePath}/${appMode}/${accessToken}` const isChatApp = [AppModeEnum.CHAT, AppModeEnum.AGENT_CHAT, AppModeEnum.COMPLETION].includes(appDetail?.mode || AppModeEnum.CHAT) + const workflowTypeSwitchConfig = appDetail?.type ? WORKFLOW_TYPE_SWITCH_CONFIG[appDetail.type as keyof typeof WORKFLOW_TYPE_SWITCH_CONFIG] : undefined + const isEvaluationWorkflowType = appDetail?.type === AppTypeEnum.EVALUATION const { data: userCanAccessApp, isLoading: isGettingUserCanAccessApp, refetch } = useGetUserCanAccessApp({ appId: appDetail?.id, enabled: false }) const { data: appAccessSubjects, isLoading: isGettingAppWhiteListSubjects } = useAppWhiteListSubjects(appDetail?.id, open && systemFeatures.webapp_auth.enabled && appDetail?.access_mode === AccessMode.SPECIFIC_GROUPS_MEMBERS) @@ -236,6 +260,35 @@ const AppPublisher = ({ } }, [appDetail, setAppDetail]) + const handleWorkflowTypeSwitch = useCallback(async () => { + if (!appDetail?.id || !workflowTypeSwitchConfig) + return + + try { + await convertWorkflowType({ + params: { + appId: appDetail.id, + }, + query: { + target_type: workflowTypeSwitchConfig.targetType, + }, + }) + + if (!publishedAt) + await handlePublish() + + const latestAppDetail = await fetchAppDetailDirect({ + url: '/apps', + id: appDetail.id, + }) + setAppDetail(latestAppDetail) + + if (publishedAt) + setOpen(false) + } + catch { } + }, [appDetail?.id, convertWorkflowType, handlePublish, publishedAt, setAppDetail, workflowTypeSwitchConfig]) + useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (e) => { e.preventDefault() if (publishDisabled || published) @@ -336,6 +389,45 @@ const AppPublisher = ({ ) } + {workflowTypeSwitchConfig && ( + + )} {showStartNodeLimitHint && (

)}

- {(systemFeatures.webapp_auth.enabled && (isGettingUserCanAccessApp || isGettingAppWhiteListSubjects)) + {!isEvaluationWorkflowType && (systemFeatures.webapp_auth.enabled && (isGettingUserCanAccessApp || isGettingAppWhiteListSubjects)) ?
- : ( + : !isEvaluationWorkflowType && ( <> {systemFeatures.webapp_auth.enabled && ( diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json index 8d590627de1..91161bbc04c 100644 --- a/web/i18n/en-US/workflow.json +++ b/web/i18n/en-US/workflow.json @@ -198,6 +198,8 @@ "common.previewPlaceholder": "Enter content in the box below to start debugging the Chatbot", "common.processData": "Process Data", "common.publish": "Publish", + "common.publishAsEvaluationWorkflow": "Publish as Evaluation Workflow", + "common.publishAsStandardWorkflow": "Publish as Standard Workflow", "common.publishUpdate": "Publish Update", "common.published": "Published", "common.publishedAt": "Published", @@ -213,6 +215,10 @@ "common.searchVar": "Search variable", "common.setVarValuePlaceholder": "Set variable", "common.showRunHistory": "Show Run History", + "common.switchToEvaluationWorkflow": "Switch to Evaluation Workflow", + "common.switchToEvaluationWorkflowTip": "Turns this workflow into a custom evaluator for batch testing. Disables public Web App access.", + "common.switchToStandardWorkflow": "Switch to Standard Workflow", + "common.switchToStandardWorkflowTip": "Turns this evaluator back into a standard workflow and restores public Web App access.", "common.syncingData": "Syncing data, just a few seconds.", "common.tagBound": "Number of apps using this tag", "common.undo": "Undo", diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json index d603c1b27d6..a82a89c581d 100644 --- a/web/i18n/zh-Hans/workflow.json +++ b/web/i18n/zh-Hans/workflow.json @@ -198,6 +198,8 @@ "common.previewPlaceholder": "在下面的框中输入内容开始调试聊天机器人", "common.processData": "数据处理", "common.publish": "发布", + "common.publishAsEvaluationWorkflow": "发布为评测工作流", + "common.publishAsStandardWorkflow": "发布为标准工作流", "common.publishUpdate": "发布更新", "common.published": "已发布", "common.publishedAt": "发布于", @@ -213,6 +215,10 @@ "common.searchVar": "搜索变量", "common.setVarValuePlaceholder": "设置变量值", "common.showRunHistory": "显示运行历史", + "common.switchToEvaluationWorkflow": "切换为评测工作流", + "common.switchToEvaluationWorkflowTip": "将当前工作流转换为批量测试用的自定义评测器,并禁用公开 Web App 访问。", + "common.switchToStandardWorkflow": "切换为标准工作流", + "common.switchToStandardWorkflowTip": "将当前评测器转换回标准工作流,并恢复公开 Web App 访问。", "common.syncingData": "同步数据中,只需几秒钟。", "common.tagBound": "使用此标签的应用数量", "common.undo": "撤销",