mirror of
https://github.com/langgenius/dify.git
synced 2026-05-13 08:57:28 +08:00
feat: Unify sandbox detection and apply Agent icon override
This commit is contained in:
parent
e528112394
commit
68f7f2f19b
@ -4,6 +4,7 @@ import type { DocPathWithoutLang } from '@/types/doc-paths'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||||
import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node'
|
import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node'
|
||||||
import AnswerDefault from '@/app/components/workflow/nodes/answer/default'
|
import AnswerDefault from '@/app/components/workflow/nodes/answer/default'
|
||||||
import EndDefault from '@/app/components/workflow/nodes/end/default'
|
import EndDefault from '@/app/components/workflow/nodes/end/default'
|
||||||
@ -18,7 +19,9 @@ import { useIsChatMode } from './use-is-chat-mode'
|
|||||||
export const useAvailableNodesMetaData = () => {
|
export const useAvailableNodesMetaData = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const isChatMode = useIsChatMode()
|
const isChatMode = useIsChatMode()
|
||||||
const isSandboxed = useAppStore(s => s.appDetail?.runtime_type === 'sandboxed')
|
const isSandboxFeatureEnabled = useFeatures(s => s.features.sandbox?.enabled) ?? false
|
||||||
|
const isSandboxRuntime = useAppStore(s => s.appDetail?.runtime_type === 'sandboxed')
|
||||||
|
const isSandboxed = isSandboxFeatureEnabled || isSandboxRuntime
|
||||||
const docLink = useDocLink()
|
const docLink = useDocLink()
|
||||||
|
|
||||||
const startNodeMetaData = useMemo(() => ({
|
const startNodeMetaData = useMemo(() => ({
|
||||||
@ -76,10 +79,14 @@ export const useAvailableNodesMetaData = () => {
|
|||||||
const title = isSandboxed && metaData.type === BlockEnum.LLM
|
const title = isSandboxed && metaData.type === BlockEnum.LLM
|
||||||
? t('blocks.agent', { ns: 'workflow' })
|
? t('blocks.agent', { ns: 'workflow' })
|
||||||
: t(`blocks.${metaData.type}` as const, { ns: 'workflow' })
|
: t(`blocks.${metaData.type}` as const, { ns: 'workflow' })
|
||||||
|
const iconTypeOverride = isSandboxed && metaData.type === BlockEnum.LLM
|
||||||
|
? BlockEnum.Agent
|
||||||
|
: undefined
|
||||||
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||||
const helpLinkPath = `/use-dify/nodes/${metaData.helpLinkUri}` as DocPathWithoutLang
|
const helpLinkPath = `/use-dify/nodes/${metaData.helpLinkUri}` as DocPathWithoutLang
|
||||||
return toNodeDefaultBase(typedNode, {
|
return toNodeDefaultBase(typedNode, {
|
||||||
...metaData,
|
...metaData,
|
||||||
|
iconType: iconTypeOverride,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
helpLinkUri: docLink(helpLinkPath),
|
helpLinkUri: docLink(helpLinkPath),
|
||||||
@ -87,6 +94,7 @@ export const useAvailableNodesMetaData = () => {
|
|||||||
...typedNode.defaultValue,
|
...typedNode.defaultValue,
|
||||||
type: metaData.type,
|
type: metaData.type,
|
||||||
title,
|
title,
|
||||||
|
_iconTypeOverride: iconTypeOverride,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, [mergedNodesMetaData, t, docLink, isSandboxed])
|
}, [mergedNodesMetaData, t, docLink, isSandboxed])
|
||||||
|
|||||||
@ -8,12 +8,20 @@ import {
|
|||||||
import answerDefault from '@/app/components/workflow/nodes/answer/default'
|
import answerDefault from '@/app/components/workflow/nodes/answer/default'
|
||||||
import llmDefault from '@/app/components/workflow/nodes/llm/default'
|
import llmDefault from '@/app/components/workflow/nodes/llm/default'
|
||||||
import startDefault from '@/app/components/workflow/nodes/start/default'
|
import startDefault from '@/app/components/workflow/nodes/start/default'
|
||||||
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
import { generateNewNode } from '@/app/components/workflow/utils'
|
import { generateNewNode } from '@/app/components/workflow/utils'
|
||||||
|
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||||
|
import { storage } from '@/utils/storage'
|
||||||
import { useIsChatMode } from './use-is-chat-mode'
|
import { useIsChatMode } from './use-is-chat-mode'
|
||||||
|
|
||||||
export const useWorkflowTemplate = () => {
|
export const useWorkflowTemplate = () => {
|
||||||
const isChatMode = useIsChatMode()
|
const isChatMode = useIsChatMode()
|
||||||
const isSandboxed = useAppStore(s => s.appDetail?.runtime_type === 'sandboxed')
|
const appDetail = useAppStore(s => s.appDetail)
|
||||||
|
const isSandboxedByType = appDetail?.runtime_type === 'sandboxed'
|
||||||
|
const isSandboxedBySelection = appDetail?.id
|
||||||
|
? storage.getBoolean(`${STORAGE_KEYS.LOCAL.WORKFLOW.SANDBOX_RUNTIME_PREFIX}${appDetail.id}`) === true
|
||||||
|
: false
|
||||||
|
const isSandboxed = isSandboxedByType || isSandboxedBySelection
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { newNode: startNode } = generateNewNode({
|
const { newNode: startNode } = generateNewNode({
|
||||||
@ -39,6 +47,7 @@ export const useWorkflowTemplate = () => {
|
|||||||
query_prompt_template: '{{#sys.query#}}\n\n{{#sys.files#}}',
|
query_prompt_template: '{{#sys.query#}}\n\n{{#sys.files#}}',
|
||||||
},
|
},
|
||||||
selected: true,
|
selected: true,
|
||||||
|
_iconTypeOverride: isSandboxed ? BlockEnum.Agent : undefined,
|
||||||
type: llmDefault.metaData.type,
|
type: llmDefault.metaData.type,
|
||||||
title: llmTitle,
|
title: llmTitle,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import OnlineUsers from '@/app/components/workflow/header/online-users'
|
|||||||
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
|
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
|
||||||
import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-status'
|
import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-status'
|
||||||
import {
|
import {
|
||||||
|
BlockEnum,
|
||||||
SupportUploadFileTypes,
|
SupportUploadFileTypes,
|
||||||
ViewType,
|
ViewType,
|
||||||
} from '@/app/components/workflow/types'
|
} from '@/app/components/workflow/types'
|
||||||
@ -221,14 +222,32 @@ const WorkflowAppWithAdditionalContext = () => {
|
|||||||
}
|
}
|
||||||
}, [workflowStore])
|
}, [workflowStore])
|
||||||
|
|
||||||
|
const isSandboxRuntime = appDetail?.runtime_type === 'sandboxed'
|
||||||
|
const isSandboxFeatureEnabled = data?.features?.sandbox?.enabled === true
|
||||||
|
const isSandboxed = isSandboxRuntime || isSandboxFeatureEnabled
|
||||||
|
|
||||||
const nodesData = useMemo(() => {
|
const nodesData = useMemo(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
const processedNodes = initialNodes(data.graph.nodes, data.graph.edges)
|
const processedNodes = initialNodes(data.graph.nodes, data.graph.edges)
|
||||||
collaborationManager.setNodes([], processedNodes)
|
const resolvedNodes = isSandboxed
|
||||||
return processedNodes
|
? processedNodes.map((node) => {
|
||||||
|
if (node.data.type !== BlockEnum.LLM)
|
||||||
|
return node
|
||||||
|
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
data: {
|
||||||
|
...node.data,
|
||||||
|
_iconTypeOverride: BlockEnum.Agent,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: processedNodes
|
||||||
|
collaborationManager.setNodes([], resolvedNodes)
|
||||||
|
return resolvedNodes
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}, [data])
|
}, [data, isSandboxed])
|
||||||
|
|
||||||
const edgesData = useMemo(() => {
|
const edgesData = useMemo(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -304,7 +323,7 @@ const WorkflowAppWithAdditionalContext = () => {
|
|||||||
}, [replayRunId, workflowStore, getWorkflowRunAndTraceUrl])
|
}, [replayRunId, workflowStore, getWorkflowRunAndTraceUrl])
|
||||||
|
|
||||||
const isDataReady = !(!data || isLoading || isLoadingCurrentWorkspace || !currentWorkspace.id)
|
const isDataReady = !(!data || isLoading || isLoadingCurrentWorkspace || !currentWorkspace.id)
|
||||||
const sandboxEnabled = data?.features?.sandbox?.enabled === true
|
const sandboxEnabled = isSandboxFeatureEnabled
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isDataReady || !appId)
|
if (!isDataReady || !appId)
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { memo } from 'react'
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
useSyncExternalStore,
|
||||||
|
} from 'react'
|
||||||
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
|
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||||
import { Folder as FolderLine } from '@/app/components/base/icons/src/vender/line/files'
|
import { Folder as FolderLine } from '@/app/components/base/icons/src/vender/line/files'
|
||||||
import {
|
import {
|
||||||
Agent,
|
Agent,
|
||||||
@ -28,7 +35,9 @@ import {
|
|||||||
WebhookLine,
|
WebhookLine,
|
||||||
WindowCursor,
|
WindowCursor,
|
||||||
} from '@/app/components/base/icons/src/vender/workflow'
|
} from '@/app/components/base/icons/src/vender/workflow'
|
||||||
|
import { STORAGE_KEYS } from '@/config/storage-keys'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
|
import { storage } from '@/utils/storage'
|
||||||
import { BlockEnum } from './types'
|
import { BlockEnum } from './types'
|
||||||
|
|
||||||
type BlockIconProps = {
|
type BlockIconProps = {
|
||||||
@ -114,13 +123,45 @@ const ICON_CONTAINER_BG_COLOR_MAP: Record<string, string> = {
|
|||||||
[BlockEnum.TriggerWebhook]: 'bg-util-colors-blue-blue-500',
|
[BlockEnum.TriggerWebhook]: 'bg-util-colors-blue-blue-500',
|
||||||
[BlockEnum.TriggerPlugin]: 'bg-util-colors-blue-blue-500',
|
[BlockEnum.TriggerPlugin]: 'bg-util-colors-blue-blue-500',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useDisplayBlockType = (type: BlockEnum) => {
|
||||||
|
const appDetail = useAppStore(s => s.appDetail)
|
||||||
|
const featuresStore = useFeaturesStore()
|
||||||
|
|
||||||
|
const subscribe = useCallback((listener: () => void) => {
|
||||||
|
if (!featuresStore)
|
||||||
|
return () => {}
|
||||||
|
return featuresStore.subscribe(listener)
|
||||||
|
}, [featuresStore])
|
||||||
|
|
||||||
|
const getSnapshot = useCallback(() => {
|
||||||
|
if (!featuresStore)
|
||||||
|
return false
|
||||||
|
return featuresStore.getState().features.sandbox?.enabled ?? false
|
||||||
|
}, [featuresStore])
|
||||||
|
|
||||||
|
const isSandboxFeatureEnabled = useSyncExternalStore(subscribe, getSnapshot, () => false)
|
||||||
|
const isSandboxRuntime = appDetail?.runtime_type === 'sandboxed'
|
||||||
|
const isSandboxSelection = useMemo(() => {
|
||||||
|
if (!appDetail?.id)
|
||||||
|
return false
|
||||||
|
return storage.getBoolean(`${STORAGE_KEYS.LOCAL.WORKFLOW.SANDBOX_RUNTIME_PREFIX}${appDetail.id}`) === true
|
||||||
|
}, [appDetail?.id])
|
||||||
|
|
||||||
|
const isSandboxed = isSandboxRuntime || isSandboxFeatureEnabled || isSandboxSelection
|
||||||
|
return isSandboxed && type === BlockEnum.LLM
|
||||||
|
? BlockEnum.Agent
|
||||||
|
: type
|
||||||
|
}
|
||||||
|
|
||||||
const BlockIcon: FC<BlockIconProps> = ({
|
const BlockIcon: FC<BlockIconProps> = ({
|
||||||
type,
|
type,
|
||||||
size = 'sm',
|
size = 'sm',
|
||||||
className,
|
className,
|
||||||
toolIcon,
|
toolIcon,
|
||||||
}) => {
|
}) => {
|
||||||
const isToolOrDataSourceOrTriggerPlugin = type === BlockEnum.Tool || type === BlockEnum.DataSource || type === BlockEnum.TriggerPlugin
|
const displayType = useDisplayBlockType(type)
|
||||||
|
const isToolOrDataSourceOrTriggerPlugin = displayType === BlockEnum.Tool || displayType === BlockEnum.DataSource || displayType === BlockEnum.TriggerPlugin
|
||||||
const showDefaultIcon = !isToolOrDataSourceOrTriggerPlugin || !toolIcon
|
const showDefaultIcon = !isToolOrDataSourceOrTriggerPlugin || !toolIcon
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -128,7 +169,7 @@ const BlockIcon: FC<BlockIconProps> = ({
|
|||||||
cn(
|
cn(
|
||||||
'flex items-center justify-center border-[0.5px] border-white/2 text-white',
|
'flex items-center justify-center border-[0.5px] border-white/2 text-white',
|
||||||
ICON_CONTAINER_CLASSNAME_SIZE_MAP[size],
|
ICON_CONTAINER_CLASSNAME_SIZE_MAP[size],
|
||||||
showDefaultIcon && ICON_CONTAINER_BG_COLOR_MAP[type],
|
showDefaultIcon && ICON_CONTAINER_BG_COLOR_MAP[displayType],
|
||||||
toolIcon && '!shadow-none',
|
toolIcon && '!shadow-none',
|
||||||
className,
|
className,
|
||||||
)
|
)
|
||||||
@ -136,7 +177,7 @@ const BlockIcon: FC<BlockIconProps> = ({
|
|||||||
>
|
>
|
||||||
{
|
{
|
||||||
showDefaultIcon && (
|
showDefaultIcon && (
|
||||||
getIcon(type, (type === BlockEnum.TriggerSchedule || type === BlockEnum.TriggerWebhook)
|
getIcon(displayType, (displayType === BlockEnum.TriggerSchedule || displayType === BlockEnum.TriggerWebhook)
|
||||||
? (size === 'xs' ? 'w-4 h-4' : 'w-4.5 h-4.5')
|
? (size === 'xs' ? 'w-4 h-4' : 'w-4.5 h-4.5')
|
||||||
: (size === 'xs' ? 'w-3 h-3' : 'w-3.5 h-3.5'))
|
: (size === 'xs' ? 'w-3 h-3' : 'w-3.5 h-3.5'))
|
||||||
)
|
)
|
||||||
@ -175,9 +216,11 @@ export const VarBlockIcon: FC<BlockIconProps> = ({
|
|||||||
type,
|
type,
|
||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
|
const displayType = useDisplayBlockType(type)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{getIcon(type, `w-3 h-3 ${className}`)}
|
{getIcon(displayType, `w-3 h-3 ${className}`)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,7 +103,7 @@ const Blocks = ({
|
|||||||
<BlockIcon
|
<BlockIcon
|
||||||
size="md"
|
size="md"
|
||||||
className="mb-2"
|
className="mb-2"
|
||||||
type={block.metaData.type}
|
type={block.metaData.iconType || block.metaData.type}
|
||||||
/>
|
/>
|
||||||
<div className="system-md-medium mb-1 text-text-primary">{block.metaData.title}</div>
|
<div className="system-md-medium mb-1 text-text-primary">{block.metaData.title}</div>
|
||||||
<div className="system-xs-regular text-text-tertiary">{block.metaData.description}</div>
|
<div className="system-xs-regular text-text-tertiary">{block.metaData.description}</div>
|
||||||
@ -117,7 +117,7 @@ const Blocks = ({
|
|||||||
>
|
>
|
||||||
<BlockIcon
|
<BlockIcon
|
||||||
className="mr-2 shrink-0"
|
className="mr-2 shrink-0"
|
||||||
type={block.metaData.type}
|
type={block.metaData.iconType || block.metaData.type}
|
||||||
/>
|
/>
|
||||||
<div className="grow text-sm text-text-secondary">{block.metaData.title}</div>
|
<div className="grow text-sm text-text-secondary">{block.metaData.title}</div>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -100,7 +100,7 @@ const NextStep = ({
|
|||||||
<div className="flex py-1">
|
<div className="flex py-1">
|
||||||
<div className="relative flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border-[0.5px] border-divider-regular bg-background-default shadow-xs">
|
<div className="relative flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border-[0.5px] border-divider-regular bg-background-default shadow-xs">
|
||||||
<BlockIcon
|
<BlockIcon
|
||||||
type={selectedNode!.data.type}
|
type={selectedNode!.data._iconTypeOverride ?? selectedNode!.data.type}
|
||||||
toolIcon={toolIcon}
|
toolIcon={toolIcon}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -42,7 +42,7 @@ const Item = ({
|
|||||||
className="group relative flex h-9 cursor-pointer items-center rounded-lg border-[0.5px] border-divider-regular bg-background-default px-2 text-xs text-text-secondary shadow-xs last-of-type:mb-0 hover:bg-background-default-hover"
|
className="group relative flex h-9 cursor-pointer items-center rounded-lg border-[0.5px] border-divider-regular bg-background-default px-2 text-xs text-text-secondary shadow-xs last-of-type:mb-0 hover:bg-background-default-hover"
|
||||||
>
|
>
|
||||||
<BlockIcon
|
<BlockIcon
|
||||||
type={data.type}
|
type={data._iconTypeOverride ?? data.type}
|
||||||
toolIcon={toolIcon}
|
toolIcon={toolIcon}
|
||||||
className="mr-1.5 shrink-0"
|
className="mr-1.5 shrink-0"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -527,7 +527,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
|||||||
<div className="flex items-center px-4 pb-1 pt-4">
|
<div className="flex items-center px-4 pb-1 pt-4">
|
||||||
<BlockIcon
|
<BlockIcon
|
||||||
className="mr-1 shrink-0"
|
className="mr-1 shrink-0"
|
||||||
type={data.type}
|
type={data._iconTypeOverride ?? data.type}
|
||||||
toolIcon={toolIcon}
|
toolIcon={toolIcon}
|
||||||
size="md"
|
size="md"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -69,11 +69,8 @@ const BaseNode: FC<BaseNodeProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const nodeRef = useRef<HTMLDivElement>(null)
|
const nodeRef = useRef<HTMLDivElement>(null)
|
||||||
const { nodesReadOnly } = useNodesReadOnly()
|
const { nodesReadOnly } = useNodesReadOnly()
|
||||||
const { _subGraphEntry, _iconTypeOverride } = data as {
|
const { _subGraphEntry } = data
|
||||||
_subGraphEntry?: boolean
|
const iconType = data._iconTypeOverride ?? data.type
|
||||||
_iconTypeOverride?: BlockEnum
|
|
||||||
}
|
|
||||||
const iconType = _iconTypeOverride ?? data.type
|
|
||||||
|
|
||||||
const { handleNodeIterationChildSizeChange } = useNodeIterationInteractions()
|
const { handleNodeIterationChildSizeChange } = useNodeIterationInteractions()
|
||||||
const { handleNodeLoopChildSizeChange } = useNodeLoopInteractions()
|
const { handleNodeLoopChildSizeChange } = useNodeLoopInteractions()
|
||||||
|
|||||||
@ -91,6 +91,8 @@ export type CommonNodeType<T = {}> = {
|
|||||||
_retryIndex?: number
|
_retryIndex?: number
|
||||||
_dataSourceStartToAdd?: boolean
|
_dataSourceStartToAdd?: boolean
|
||||||
_isTempNode?: boolean
|
_isTempNode?: boolean
|
||||||
|
_subGraphEntry?: boolean
|
||||||
|
_iconTypeOverride?: BlockEnum
|
||||||
isInIteration?: boolean
|
isInIteration?: boolean
|
||||||
iteration_id?: string
|
iteration_id?: string
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
@ -369,6 +371,7 @@ export type NodeDefaultBase = {
|
|||||||
classification: BlockClassificationEnum
|
classification: BlockClassificationEnum
|
||||||
sort: number
|
sort: number
|
||||||
type: BlockEnum
|
type: BlockEnum
|
||||||
|
iconType?: BlockEnum
|
||||||
title: string
|
title: string
|
||||||
author: string
|
author: string
|
||||||
description?: string
|
description?: string
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user