{
+ if (!disabled || !tooltip)
+ return <>{children}>
+
+ return (
+
+ {children}} />
+
+ {tooltip}
+
+
+ )
+}
+
export const PublisherSummarySection = ({
debugWithMultipleModel = false,
draftUpdatedAt,
@@ -117,6 +140,7 @@ export const PublisherSummarySection = ({
upgradeHighlightStyle,
workflowTypeSwitchConfig,
workflowTypeSwitchDisabled,
+ workflowTypeSwitchDisabledReason,
}: SummarySectionProps) => {
const { t } = useTranslation()
@@ -178,43 +202,45 @@ export const PublisherSummarySection = ({
)}
{workflowTypeSwitchConfig && (
-
+
+
+ {
+ e.preventDefault()
+ e.stopPropagation()
+ }}
+ >
+
+
+ )}
+ />
+
+ {t(workflowTypeSwitchConfig.tipKey, { ns: 'workflow' })}
+
+
+
+
)}
{startNodeLimitExceeded && (
@@ -279,28 +305,6 @@ export const PublisherAccessSection = ({
)
}
-const ActionTooltip = ({
- disabled,
- tooltip,
- children,
-}: {
- disabled: boolean
- tooltip?: ReactNode
- children: ReactNode
-}) => {
- if (!disabled || !tooltip)
- return <>{children}>
-
- return (
-
- {children}
} />
-
- {tooltip}
-
-
- )
-}
-
export const PublisherActionsSection = ({
appDetail,
appURL,
diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/index.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/index.spec.tsx
index af38ca113f..479b95dcb6 100644
--- a/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/index.spec.tsx
+++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/index.spec.tsx
@@ -2,8 +2,21 @@ import type { ReactNode } from 'react'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { BlockEnum } from '@/app/components/workflow/types'
+import { AppTypeEnum } from '@/types/app'
import WorkflowOnboardingModal from '../index'
+const mockAppType = vi.hoisted<{ current?: string }>(() => ({
+ current: 'workflow',
+}))
+
+vi.mock('@/app/components/app/store', () => ({
+ useStore: (selector: (state: { appDetail: { type?: string } }) => unknown) => selector({
+ appDetail: {
+ type: mockAppType.current,
+ },
+ }),
+}))
+
vi.mock('@/app/components/workflow/block-selector', () => ({
default: function MockNodeSelector({
open,
@@ -44,6 +57,7 @@ describe('WorkflowOnboardingModal', () => {
beforeEach(() => {
vi.clearAllMocks()
+ mockAppType.current = AppTypeEnum.WORKFLOW
})
const renderComponent = (props = {}) => {
@@ -91,6 +105,15 @@ describe('WorkflowOnboardingModal', () => {
expect(getTriggerHeading()).toBeInTheDocument()
})
+ it('should hide the trigger starter in evaluation workflows', () => {
+ mockAppType.current = AppTypeEnum.EVALUATION
+
+ renderComponent()
+
+ expect(getUserInputHeading()).toBeInTheDocument()
+ expect(screen.queryByRole('heading', { name: 'workflow.onboarding.trigger' })).not.toBeInTheDocument()
+ })
+
it('should render ESC tip when shown', () => {
renderComponent({ isShow: true })
diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/start-node-selection-panel.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/start-node-selection-panel.spec.tsx
index b2496f8714..36544b568c 100644
--- a/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/start-node-selection-panel.spec.tsx
+++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/__tests__/start-node-selection-panel.spec.tsx
@@ -2,8 +2,21 @@ import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import { BlockEnum } from '@/app/components/workflow/types'
+import { AppTypeEnum } from '@/types/app'
import StartNodeSelectionPanel from '../start-node-selection-panel'
+const mockAppType = vi.hoisted<{ current?: string }>(() => ({
+ current: 'workflow',
+}))
+
+vi.mock('@/app/components/app/store', () => ({
+ useStore: (selector: (state: { appDetail: { type?: string } }) => unknown) => selector({
+ appDetail: {
+ type: mockAppType.current,
+ },
+ }),
+}))
+
// Mock NodeSelector component
vi.mock('@/app/components/workflow/block-selector', () => ({
default: function MockNodeSelector({
@@ -61,6 +74,7 @@ describe('StartNodeSelectionPanel', () => {
beforeEach(() => {
vi.clearAllMocks()
+ mockAppType.current = AppTypeEnum.WORKFLOW
})
// Helper function to render component
@@ -96,6 +110,15 @@ describe('StartNodeSelectionPanel', () => {
expect(screen.getByText('workflow.onboarding.triggerDescription')).toBeInTheDocument()
})
+ it('should hide the trigger option in evaluation workflows', () => {
+ mockAppType.current = AppTypeEnum.EVALUATION
+
+ renderComponent()
+
+ expect(screen.queryByText('workflow.onboarding.trigger')).not.toBeInTheDocument()
+ expect(screen.queryByTestId('node-selector')).not.toBeInTheDocument()
+ })
+
it('should render node selector component', () => {
// Arrange & Act
renderComponent()
diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx
index 883212cd2d..6c45ae6315 100644
--- a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx
+++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx
@@ -3,10 +3,12 @@ import type { FC } from 'react'
import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
+import { useStore as useAppStore } from '@/app/components/app/store'
import { Home, TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
import NodeSelector from '@/app/components/workflow/block-selector'
import { TabsEnum } from '@/app/components/workflow/block-selector/types'
import { BlockEnum } from '@/app/components/workflow/types'
+import { isEvaluationWorkflow } from '@/app/components/workflow/utils/evaluation-workflow'
import StartNodeOption from './start-node-option'
type StartNodeSelectionPanelProps = {
@@ -19,7 +21,9 @@ const StartNodeSelectionPanel: FC = ({
onSelectTrigger,
}) => {
const { t } = useTranslation()
+ const appType = useAppStore(s => s.appDetail?.type)
const [showTriggerSelector, setShowTriggerSelector] = useState(false)
+ const isEvaluationWorkflowType = isEvaluationWorkflow(appType)
const handleTriggerSelect = useCallback((nodeType: BlockEnum, toolConfig?: PluginDefaultValue) => {
setShowTriggerSelector(false)
@@ -39,34 +43,36 @@ const StartNodeSelectionPanel: FC = ({
onClick={onSelectUserInput}
/>
- (
-
-
-
- )}
- title={t('onboarding.trigger', { ns: 'workflow' })}
- description={t('onboarding.triggerDescription', { ns: 'workflow' })}
- onClick={() => setShowTriggerSelector(true)}
- />
- )}
- />
+ {!isEvaluationWorkflowType && (
+ (
+
+
+
+ )}
+ title={t('onboarding.trigger', { ns: 'workflow' })}
+ description={t('onboarding.triggerDescription', { ns: 'workflow' })}
+ onClick={() => setShowTriggerSelector(true)}
+ />
+ )}
+ />
+ )}
)
}
diff --git a/web/app/components/workflow-app/hooks/__tests__/use-available-nodes-meta-data.spec.ts b/web/app/components/workflow-app/hooks/__tests__/use-available-nodes-meta-data.spec.ts
index c92e438cb3..8a645d1ff9 100644
--- a/web/app/components/workflow-app/hooks/__tests__/use-available-nodes-meta-data.spec.ts
+++ b/web/app/components/workflow-app/hooks/__tests__/use-available-nodes-meta-data.spec.ts
@@ -1,13 +1,25 @@
import { renderHook } from '@testing-library/react'
import { BlockEnum } from '@/app/components/workflow/types'
+import { AppTypeEnum } from '@/types/app'
import { useAvailableNodesMetaData } from '../use-available-nodes-meta-data'
const mockUseIsChatMode = vi.fn()
+const mockAppType = vi.hoisted<{ current?: string }>(() => ({
+ current: 'workflow',
+}))
vi.mock('@/app/components/workflow-app/hooks/use-is-chat-mode', () => ({
useIsChatMode: () => mockUseIsChatMode(),
}))
+vi.mock('@/app/components/app/store', () => ({
+ useStore: (selector: (state: { appDetail: { type?: string } }) => unknown) => selector({
+ appDetail: {
+ type: mockAppType.current,
+ },
+ }),
+}))
+
vi.mock('@/context/i18n', () => ({
useDocLink: () => (path: string) => `/docs${path}`,
}))
@@ -15,6 +27,7 @@ vi.mock('@/context/i18n', () => ({
describe('useAvailableNodesMetaData', () => {
beforeEach(() => {
vi.clearAllMocks()
+ mockAppType.current = AppTypeEnum.WORKFLOW
})
it('should include chat-specific nodes and make the start node undeletable in chat mode', () => {
@@ -46,4 +59,18 @@ describe('useAvailableNodesMetaData', () => {
title: 'workflow.blocks.start',
})
})
+
+ it('should exclude human input and trigger nodes in evaluation workflows', () => {
+ mockUseIsChatMode.mockReturnValue(false)
+ mockAppType.current = AppTypeEnum.EVALUATION
+
+ const { result } = renderHook(() => useAvailableNodesMetaData())
+
+ expect(result.current.nodesMap?.[BlockEnum.Start]).toBeDefined()
+ expect(result.current.nodesMap?.[BlockEnum.End]).toBeDefined()
+ expect(result.current.nodesMap?.[BlockEnum.HumanInput]).toBeUndefined()
+ expect(result.current.nodesMap?.[BlockEnum.TriggerWebhook]).toBeUndefined()
+ expect(result.current.nodesMap?.[BlockEnum.TriggerSchedule]).toBeUndefined()
+ expect(result.current.nodesMap?.[BlockEnum.TriggerPlugin]).toBeUndefined()
+ })
})
diff --git a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts
index 0c5c1e4a40..33edaa2ed5 100644
--- a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts
+++ b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts
@@ -2,6 +2,7 @@ import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-sto
import type { DocPathWithoutLang } from '@/types/doc-paths'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
+import { useStore as useAppStore } from '@/app/components/app/store'
import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node'
import AnswerDefault from '@/app/components/workflow/nodes/answer/default'
import EndDefault from '@/app/components/workflow/nodes/end/default'
@@ -10,13 +11,16 @@ import TriggerPluginDefault from '@/app/components/workflow/nodes/trigger-plugin
import TriggerScheduleDefault from '@/app/components/workflow/nodes/trigger-schedule/default'
import TriggerWebhookDefault from '@/app/components/workflow/nodes/trigger-webhook/default'
import { BlockEnum } from '@/app/components/workflow/types'
+import { isEvaluationWorkflow, isEvaluationWorkflowRestrictedNodeType } from '@/app/components/workflow/utils/evaluation-workflow'
import { useDocLink } from '@/context/i18n'
import { useIsChatMode } from './use-is-chat-mode'
export const useAvailableNodesMetaData = () => {
const { t } = useTranslation()
const isChatMode = useIsChatMode()
+ const appType = useAppStore(s => s.appDetail?.type)
const docLink = useDocLink()
+ const isEvaluationWorkflowType = isEvaluationWorkflow(appType)
const startNodeMetaData = useMemo(() => ({
...StartDefault,
@@ -26,20 +30,27 @@ export const useAvailableNodesMetaData = () => {
},
}), [isChatMode])
- const mergedNodesMetaData = useMemo(() => [
- ...WORKFLOW_COMMON_NODES,
- startNodeMetaData,
- ...(
- isChatMode
- ? [AnswerDefault]
- : [
- EndDefault,
- TriggerWebhookDefault,
- TriggerScheduleDefault,
- TriggerPluginDefault,
- ]
- ),
- ], [isChatMode, startNodeMetaData])
+ const mergedNodesMetaData = useMemo(() => {
+ const nodes = [
+ ...WORKFLOW_COMMON_NODES,
+ startNodeMetaData,
+ ...(
+ isChatMode
+ ? [AnswerDefault]
+ : [
+ EndDefault,
+ TriggerWebhookDefault,
+ TriggerScheduleDefault,
+ TriggerPluginDefault,
+ ]
+ ),
+ ]
+
+ if (!isEvaluationWorkflowType)
+ return nodes
+
+ return nodes.filter(node => !isEvaluationWorkflowRestrictedNodeType(node.metaData.type))
+ }, [isChatMode, isEvaluationWorkflowType, startNodeMetaData])
const availableNodesMetaData = useMemo(() => mergedNodesMetaData.map((node) => {
const { metaData } = node
diff --git a/web/app/components/workflow/block-selector/__tests__/all-start-blocks.spec.tsx b/web/app/components/workflow/block-selector/__tests__/all-start-blocks.spec.tsx
index 2b28662b45..0ffe583bb1 100644
--- a/web/app/components/workflow/block-selector/__tests__/all-start-blocks.spec.tsx
+++ b/web/app/components/workflow/block-selector/__tests__/all-start-blocks.spec.tsx
@@ -8,7 +8,7 @@ import { useGetLanguage, useLocale } from '@/context/i18n'
import useTheme from '@/hooks/use-theme'
import { useFeaturedTriggersRecommendations } from '@/service/use-plugins'
import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins } from '@/service/use-triggers'
-import { Theme } from '@/types/app'
+import { AppTypeEnum, Theme } from '@/types/app'
import { defaultSystemFeatures } from '@/types/feature'
import { useAvailableNodesMetaData } from '../../../workflow-app/hooks'
import useNodes from '../../store/workflow/use-nodes'
@@ -19,6 +19,18 @@ vi.mock('@/context/global-public-context', () => ({
useGlobalPublicStore: vi.fn(),
}))
+const mockAppType = vi.hoisted<{ current?: string }>(() => ({
+ current: 'workflow',
+}))
+
+vi.mock('@/app/components/app/store', () => ({
+ useStore: (selector: (state: { appDetail: { type?: string } }) => unknown) => selector({
+ appDetail: {
+ type: mockAppType.current,
+ },
+ }),
+}))
+
vi.mock('@/context/i18n', () => ({
useGetLanguage: vi.fn(),
useLocale: vi.fn(),
@@ -179,6 +191,7 @@ const createAvailableNodesMetaData = (): ReturnType {
beforeEach(() => {
vi.clearAllMocks()
+ mockAppType.current = AppTypeEnum.WORKFLOW
mockUseGlobalPublicStore.mockImplementation(selector => selector(createGlobalPublicStoreState(false)))
mockUseGetLanguage.mockReturnValue('en_US')
mockUseLocale.mockReturnValue('en_US')
@@ -238,6 +251,26 @@ describe('AllStartBlocks', () => {
expect(await screen.findByRole('link', { name: /plugin\.findMoreInMarketplace/ })).toHaveAttribute('href', 'https://marketplace.test/start')
})
+
+ it('should hide trigger options in evaluation workflows', async () => {
+ mockAppType.current = AppTypeEnum.EVALUATION
+
+ render(
+ ,
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('workflow.blocks.start')).toBeInTheDocument()
+ })
+
+ expect(screen.queryByText('Provider One')).not.toBeInTheDocument()
+ expect(screen.queryByText('workflow.tabs.allTriggers')).not.toBeInTheDocument()
+ })
})
// Empty filter states should surface the request-to-community fallback.
diff --git a/web/app/components/workflow/block-selector/__tests__/blocks.spec.tsx b/web/app/components/workflow/block-selector/__tests__/blocks.spec.tsx
new file mode 100644
index 0000000000..27b8e68c7f
--- /dev/null
+++ b/web/app/components/workflow/block-selector/__tests__/blocks.spec.tsx
@@ -0,0 +1,51 @@
+import { render, screen } from '@testing-library/react'
+import { useStoreApi } from 'reactflow'
+import { AppTypeEnum } from '@/types/app'
+import { BlockEnum } from '../../types'
+import Blocks from '../blocks'
+
+const mockGetNodes = vi.fn(() => [])
+const mockAppType = vi.hoisted<{ current?: string }>(() => ({
+ current: 'workflow',
+}))
+
+vi.mock('reactflow', () => ({
+ useStoreApi: vi.fn(),
+}))
+
+vi.mock('@/app/components/app/store', () => ({
+ useStore: (selector: (state: { appDetail: { type?: string } }) => unknown) => selector({
+ appDetail: {
+ type: mockAppType.current,
+ },
+ }),
+}))
+
+const mockUseStoreApi = vi.mocked(useStoreApi)
+
+describe('Blocks', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ mockAppType.current = AppTypeEnum.WORKFLOW
+ mockUseStoreApi.mockReturnValue({
+ getState: () => ({
+ getNodes: mockGetNodes,
+ }),
+ } as unknown as ReturnType)
+ })
+
+ it('should hide human input in evaluation workflows', () => {
+ mockAppType.current = AppTypeEnum.EVALUATION
+
+ render(
+ ,
+ )
+
+ expect(screen.queryByText('workflow.blocks.human-input')).not.toBeInTheDocument()
+ expect(screen.getByText('workflow.blocks.llm')).toBeInTheDocument()
+ })
+})
diff --git a/web/app/components/workflow/block-selector/all-start-blocks.tsx b/web/app/components/workflow/block-selector/all-start-blocks.tsx
index f59edfbcbf..80ce02cc47 100644
--- a/web/app/components/workflow/block-selector/all-start-blocks.tsx
+++ b/web/app/components/workflow/block-selector/all-start-blocks.tsx
@@ -14,9 +14,11 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
+import { useStore as useAppStore } from '@/app/components/app/store'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
+import { filterEvaluationWorkflowRestrictedBlockTypes, isEvaluationWorkflow } from '@/app/components/workflow/utils/evaluation-workflow'
import { useGlobalPublicStore } from '@/context/global-public-context'
import Link from '@/next/link'
import { useFeaturedTriggersRecommendations } from '@/service/use-plugins'
@@ -54,13 +56,21 @@ const AllStartBlocks = ({
const { t } = useTranslation()
const [hasStartBlocksContent, setHasStartBlocksContent] = useState(false)
const [hasPluginContent, setHasPluginContent] = useState(false)
+ const appType = useAppStore(s => s.appDetail?.type)
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const pluginRef = useRef(null)
const wrapElemRef = useRef(null)
- const entryNodeTypes = availableBlocksTypes?.length
- ? availableBlocksTypes
- : ENTRY_NODE_TYPES
+ const entryNodeTypes = useMemo(() => {
+ const blockTypes = availableBlocksTypes?.length
+ ? availableBlocksTypes
+ : [...ENTRY_NODE_TYPES]
+
+ if (!isEvaluationWorkflow(appType))
+ return blockTypes
+
+ return filterEvaluationWorkflowRestrictedBlockTypes(blockTypes)
+ }, [appType, availableBlocksTypes])
const enableTriggerPlugin = entryNodeTypes.includes(BlockEnumValue.TriggerPlugin)
const { data: triggerProviders = [] } = useAllTriggerPlugins(enableTriggerPlugin)
const providerMap = useMemo(() => {
@@ -94,7 +104,8 @@ const AllStartBlocks = ({
const shouldShowFeatured = enableTriggerPlugin
&& enable_marketplace
&& !hasFilter
- const shouldShowTriggerListTitle = hasStartBlocksContent || hasPluginContent
+ const hasTriggerOptions = entryNodeTypes.some(type => type !== BlockEnumValue.Start)
+ const shouldShowTriggerListTitle = hasTriggerOptions && (hasStartBlocksContent || hasPluginContent)
const shouldShowMarketplaceFooter = enable_marketplace && !hasFilter
const handleStartBlocksContentChange = useCallback((hasContent: boolean) => {
diff --git a/web/app/components/workflow/block-selector/blocks.tsx b/web/app/components/workflow/block-selector/blocks.tsx
index 2425112335..429c25b21b 100644
--- a/web/app/components/workflow/block-selector/blocks.tsx
+++ b/web/app/components/workflow/block-selector/blocks.tsx
@@ -8,8 +8,10 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
+import { useStore as useAppStore } from '@/app/components/app/store'
import Badge from '@/app/components/base/badge'
import Tooltip from '@/app/components/base/tooltip'
+import { filterEvaluationWorkflowRestrictedBlockTypes, isEvaluationWorkflow } from '@/app/components/workflow/utils/evaluation-workflow'
import BlockIcon from '../block-icon'
import { BlockEnum } from '../types'
import { BLOCK_CLASSIFICATIONS } from './constants'
@@ -29,7 +31,14 @@ const Blocks = ({
}: BlocksProps) => {
const { t } = useTranslation()
const store = useStoreApi()
+ const appType = useAppStore(s => s.appDetail?.type)
const blocksFromHooks = useBlocks()
+ const filteredAvailableBlocksTypes = useMemo(() => {
+ if (!isEvaluationWorkflow(appType))
+ return availableBlocksTypes
+
+ return filterEvaluationWorkflowRestrictedBlockTypes(availableBlocksTypes)
+ }, [appType, availableBlocksTypes])
// Use external blocks if provided, otherwise fallback to hook-based blocks
const blocks = blocksFromProps || blocksFromHooks.map(block => ({
@@ -57,7 +66,7 @@ const Blocks = ({
return false
}
- return block.metaData.title.toLowerCase().includes(searchText.toLowerCase()) && availableBlocksTypes.includes(block.metaData.type)
+ return block.metaData.title.toLowerCase().includes(searchText.toLowerCase()) && filteredAvailableBlocksTypes.includes(block.metaData.type)
})
return {
@@ -65,7 +74,7 @@ const Blocks = ({
[classification]: list,
}
}, {} as Record)
- }, [blocks, searchText, availableBlocksTypes])
+ }, [blocks, filteredAvailableBlocksTypes, searchText])
const isEmpty = Object.values(groups).every(list => !list.length)
const renderGroup = useCallback((classification: BlockClassificationEnum) => {
diff --git a/web/app/components/workflow/utils/evaluation-workflow.ts b/web/app/components/workflow/utils/evaluation-workflow.ts
new file mode 100644
index 0000000000..40d4b2fb65
--- /dev/null
+++ b/web/app/components/workflow/utils/evaluation-workflow.ts
@@ -0,0 +1,20 @@
+import { AppTypeEnum } from '@/types/app'
+import { BlockEnum, TRIGGER_NODE_TYPES } from '../types'
+
+const EVALUATION_WORKFLOW_RESTRICTED_NODE_TYPES = new Set([
+ BlockEnum.HumanInput,
+ ...TRIGGER_NODE_TYPES,
+])
+
+export const isEvaluationWorkflow = (appType?: string) => appType === AppTypeEnum.EVALUATION
+
+export const isEvaluationWorkflowRestrictedNodeType = (nodeType?: string) => {
+ if (!nodeType)
+ return false
+
+ return EVALUATION_WORKFLOW_RESTRICTED_NODE_TYPES.has(nodeType)
+}
+
+export const filterEvaluationWorkflowRestrictedBlockTypes = (blockTypes: BlockEnum[]) => {
+ return blockTypes.filter(blockType => !isEvaluationWorkflowRestrictedNodeType(blockType))
+}
diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json
index 91161bbc04..944601ad33 100644
--- a/web/i18n/en-US/workflow.json
+++ b/web/i18n/en-US/workflow.json
@@ -216,6 +216,7 @@
"common.setVarValuePlaceholder": "Set variable",
"common.showRunHistory": "Show Run History",
"common.switchToEvaluationWorkflow": "Switch to Evaluation Workflow",
+ "common.switchToEvaluationWorkflowDisabledTip": "Evaluation workflows do not support Human Input nodes or Trigger start nodes.",
"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.",
diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json
index a82a89c581..1688dba6a7 100644
--- a/web/i18n/zh-Hans/workflow.json
+++ b/web/i18n/zh-Hans/workflow.json
@@ -216,6 +216,7 @@
"common.setVarValuePlaceholder": "设置变量值",
"common.showRunHistory": "显示运行历史",
"common.switchToEvaluationWorkflow": "切换为评测工作流",
+ "common.switchToEvaluationWorkflowDisabledTip": "评测工作流不支持 Human Input 节点或 Trigger 开始节点。",
"common.switchToEvaluationWorkflowTip": "将当前工作流转换为批量测试用的自定义评测器,并禁用公开 Web App 访问。",
"common.switchToStandardWorkflow": "切换为标准工作流",
"common.switchToStandardWorkflowTip": "将当前评测器转换回标准工作流,并恢复公开 Web App 访问。",