mirror of https://github.com/langgenius/dify.git
refactor: use bivariance to normalize node metadata types
This commit is contained in:
parent
ef8d0f497d
commit
4707a319e5
|
|
@ -1,4 +1,5 @@
|
|||
import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store'
|
||||
import type { CommonNodeType, NodeDefault, NodeDefaultBase } from '@/app/components/workflow/types'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node'
|
||||
|
|
@ -29,25 +30,47 @@ export const useAvailableNodesMetaData = () => {
|
|||
'/use-dify/knowledge/knowledge-pipeline/knowledge-pipeline-orchestration',
|
||||
), [docLink])
|
||||
|
||||
const availableNodesMetaData = useMemo(() => mergedNodesMetaData.map((node) => {
|
||||
const { metaData } = node
|
||||
const title = t(`blocks.${metaData.type}`, { ns: 'workflow' })
|
||||
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||
return {
|
||||
...node,
|
||||
metaData: {
|
||||
const availableNodesMetaData = useMemo<NodeDefaultBase[]>(() => {
|
||||
const toNodeDefaultBase = (
|
||||
node: NodeDefault<CommonNodeType>,
|
||||
metaData: NodeDefaultBase['metaData'],
|
||||
defaultValue: Partial<CommonNodeType>,
|
||||
): NodeDefaultBase => {
|
||||
return {
|
||||
...node,
|
||||
metaData,
|
||||
defaultValue,
|
||||
checkValid: (payload: CommonNodeType, translator, moreDataForCheckValid) => {
|
||||
// normalize validator signature for shared metadata storage.
|
||||
return node.checkValid(payload, translator, moreDataForCheckValid)
|
||||
},
|
||||
getOutputVars: node.getOutputVars
|
||||
? (payload: CommonNodeType, allPluginInfoList, ragVariables, utils) => {
|
||||
// normalize output var signature for shared metadata storage.
|
||||
return node.getOutputVars!(payload, allPluginInfoList, ragVariables, utils)
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
return mergedNodesMetaData.map((node) => {
|
||||
// normalize per-node defaults into a shared metadata shape.
|
||||
const typedNode = node as NodeDefault<CommonNodeType>
|
||||
const { metaData } = typedNode
|
||||
const title = t(`blocks.${metaData.type}`, { ns: 'workflow' })
|
||||
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||
return toNodeDefaultBase(typedNode, {
|
||||
...metaData,
|
||||
title,
|
||||
description,
|
||||
helpLinkUri,
|
||||
},
|
||||
defaultValue: {
|
||||
...node.defaultValue,
|
||||
}, {
|
||||
...typedNode.defaultValue,
|
||||
type: metaData.type,
|
||||
title,
|
||||
},
|
||||
}
|
||||
}), [mergedNodesMetaData, t])
|
||||
})
|
||||
})
|
||||
}, [mergedNodesMetaData, t, helpLinkUri])
|
||||
|
||||
const availableNodesMetaDataMap = useMemo(() => availableNodesMetaData.reduce((acc, node) => {
|
||||
acc![node.metaData.type] = node
|
||||
|
|
|
|||
|
|
@ -131,8 +131,7 @@ const SubGraphMain: FC<SubGraphMainProps> = (props) => {
|
|||
nodes={nodes}
|
||||
edges={edges}
|
||||
viewport={viewport}
|
||||
// eslint-disable-next-line ts/no-explicit-any -- TODO: remove after typing boundary
|
||||
hooksStore={hooksStore as any}
|
||||
hooksStore={hooksStore}
|
||||
allowSelectionWhenReadOnly
|
||||
canvasReadOnly
|
||||
interactionMode={InteractionMode.Subgraph}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store'
|
||||
import type { CommonNodeType, NodeDefault, NodeDefaultBase } from '@/app/components/workflow/types'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node'
|
||||
|
|
@ -7,24 +8,46 @@ import { BlockEnum } from '@/app/components/workflow/types'
|
|||
export const useAvailableNodesMetaData = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const availableNodesMetaData = useMemo(() => WORKFLOW_COMMON_NODES.map((node) => {
|
||||
const { metaData } = node
|
||||
const title = t(`blocks.${metaData.type}`, { ns: 'workflow' })
|
||||
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||
return {
|
||||
...node,
|
||||
metaData: {
|
||||
const availableNodesMetaData = useMemo<NodeDefaultBase[]>(() => {
|
||||
const toNodeDefaultBase = (
|
||||
node: NodeDefault<CommonNodeType>,
|
||||
metaData: NodeDefaultBase['metaData'],
|
||||
defaultValue: Partial<CommonNodeType>,
|
||||
): NodeDefaultBase => {
|
||||
return {
|
||||
...node,
|
||||
metaData,
|
||||
defaultValue,
|
||||
checkValid: (payload: CommonNodeType, translator, moreDataForCheckValid) => {
|
||||
// normalize validator signature for shared metadata storage.
|
||||
return node.checkValid(payload, translator, moreDataForCheckValid)
|
||||
},
|
||||
getOutputVars: node.getOutputVars
|
||||
? (payload: CommonNodeType, allPluginInfoList, ragVariables, utils) => {
|
||||
// normalize output var signature for shared metadata storage.
|
||||
return node.getOutputVars!(payload, allPluginInfoList, ragVariables, utils)
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
return WORKFLOW_COMMON_NODES.map((node) => {
|
||||
// normalize per-node defaults into a shared metadata shape.
|
||||
const typedNode = node as NodeDefault<CommonNodeType>
|
||||
const { metaData } = typedNode
|
||||
const title = t(`blocks.${metaData.type}`, { ns: 'workflow' })
|
||||
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||
return toNodeDefaultBase(typedNode, {
|
||||
...metaData,
|
||||
title,
|
||||
description,
|
||||
},
|
||||
defaultValue: {
|
||||
...node.defaultValue,
|
||||
}, {
|
||||
...typedNode.defaultValue,
|
||||
type: metaData.type,
|
||||
title,
|
||||
},
|
||||
}
|
||||
}), [t])
|
||||
})
|
||||
})
|
||||
}, [t])
|
||||
|
||||
const availableNodesMetaDataMap = useMemo(() => availableNodesMetaData.reduce((acc, node) => {
|
||||
acc![node.metaData.type] = node
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store'
|
||||
import type { CommonNodeType, NodeDefault, NodeDefaultBase } from '@/app/components/workflow/types'
|
||||
import type { DocPathWithoutLang } from '@/types/doc-paths'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
|
@ -41,26 +42,48 @@ export const useAvailableNodesMetaData = () => {
|
|||
),
|
||||
], [isChatMode, startNodeMetaData])
|
||||
|
||||
const availableNodesMetaData = useMemo(() => mergedNodesMetaData.map((node) => {
|
||||
const { metaData } = node
|
||||
const title = t(`blocks.${metaData.type}`, { ns: 'workflow' })
|
||||
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||
const helpLinkPath = `/use-dify/nodes/${metaData.helpLinkUri}` as DocPathWithoutLang
|
||||
return {
|
||||
...node,
|
||||
metaData: {
|
||||
const availableNodesMetaData = useMemo<NodeDefaultBase[]>(() => {
|
||||
const toNodeDefaultBase = (
|
||||
node: NodeDefault<CommonNodeType>,
|
||||
metaData: NodeDefaultBase['metaData'],
|
||||
defaultValue: Partial<CommonNodeType>,
|
||||
): NodeDefaultBase => {
|
||||
return {
|
||||
...node,
|
||||
metaData,
|
||||
defaultValue,
|
||||
checkValid: (payload: CommonNodeType, translator, moreDataForCheckValid) => {
|
||||
// normalize validator signature for shared metadata storage.
|
||||
return node.checkValid(payload, translator, moreDataForCheckValid)
|
||||
},
|
||||
getOutputVars: node.getOutputVars
|
||||
? (payload: CommonNodeType, allPluginInfoList, ragVariables, utils) => {
|
||||
// normalize output var signature for shared metadata storage.
|
||||
return node.getOutputVars!(payload, allPluginInfoList, ragVariables, utils)
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
return mergedNodesMetaData.map((node) => {
|
||||
// normalize per-node defaults into a shared metadata shape.
|
||||
const typedNode = node as NodeDefault<CommonNodeType>
|
||||
const { metaData } = typedNode
|
||||
const title = t(`blocks.${metaData.type}`, { ns: 'workflow' })
|
||||
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||
const helpLinkPath = `/use-dify/nodes/${metaData.helpLinkUri}` as DocPathWithoutLang
|
||||
return toNodeDefaultBase(typedNode, {
|
||||
...metaData,
|
||||
title,
|
||||
description,
|
||||
helpLinkUri: docLink(helpLinkPath),
|
||||
},
|
||||
defaultValue: {
|
||||
...node.defaultValue,
|
||||
}, {
|
||||
...typedNode.defaultValue,
|
||||
type: metaData.type,
|
||||
title,
|
||||
},
|
||||
}
|
||||
}), [mergedNodesMetaData, t, docLink])
|
||||
})
|
||||
})
|
||||
}, [mergedNodesMetaData, t, docLink])
|
||||
|
||||
const availableNodesMetaDataMap = useMemo(() => availableNodesMetaData.reduce((acc, node) => {
|
||||
acc![node.metaData.type] = node
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { NodeDefault } from '../types'
|
||||
import type { NodeDefaultBase } from '../types'
|
||||
import type { BlockClassificationEnum } from './types'
|
||||
import { groupBy } from 'es-toolkit/compat'
|
||||
import {
|
||||
|
|
@ -19,7 +19,7 @@ type BlocksProps = {
|
|||
searchText: string
|
||||
onSelect: (type: BlockEnum) => void
|
||||
availableBlocksTypes?: BlockEnum[]
|
||||
blocks?: NodeDefault[]
|
||||
blocks?: NodeDefaultBase[]
|
||||
}
|
||||
const Blocks = ({
|
||||
searchText,
|
||||
|
|
@ -44,7 +44,7 @@ const Blocks = ({
|
|||
},
|
||||
defaultValue: {},
|
||||
checkValid: () => ({ isValid: true }),
|
||||
}) as NodeDefault)
|
||||
}) as NodeDefaultBase)
|
||||
|
||||
const groups = useMemo(() => {
|
||||
return BLOCK_CLASSIFICATIONS.reduce((acc, classification) => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import type {
|
|||
} from 'react'
|
||||
import type {
|
||||
CommonNodeType,
|
||||
NodeDefault,
|
||||
NodeDefaultBase,
|
||||
OnSelectBlock,
|
||||
ToolWithProvider,
|
||||
} from '../types'
|
||||
|
|
@ -49,7 +49,7 @@ export type NodeSelectorProps = {
|
|||
asChild?: boolean
|
||||
availableBlocksTypes?: BlockEnum[]
|
||||
disabled?: boolean
|
||||
blocks?: NodeDefault[]
|
||||
blocks?: NodeDefaultBase[]
|
||||
dataSources?: ToolWithProvider[]
|
||||
noBlocks?: boolean
|
||||
noTools?: boolean
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { Dispatch, FC, SetStateAction } from 'react'
|
||||
import type {
|
||||
BlockEnum,
|
||||
NodeDefault,
|
||||
NodeDefaultBase,
|
||||
OnSelectBlock,
|
||||
ToolWithProvider,
|
||||
} from '../types'
|
||||
|
|
@ -28,7 +28,7 @@ export type TabsProps = {
|
|||
onTagsChange: Dispatch<SetStateAction<string[]>>
|
||||
onSelect: OnSelectBlock
|
||||
availableBlocksTypes?: BlockEnum[]
|
||||
blocks: NodeDefault[]
|
||||
blocks: NodeDefaultBase[]
|
||||
dataSources?: ToolWithProvider[]
|
||||
tabs: Array<{
|
||||
key: TabsEnum
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { FileUpload } from '../../base/features/types'
|
|||
import type {
|
||||
BlockEnum,
|
||||
Node,
|
||||
NodeDefault,
|
||||
NodeDefaultBase,
|
||||
ToolWithProvider,
|
||||
ValueSelector,
|
||||
} from '@/app/components/workflow/types'
|
||||
|
|
@ -16,14 +16,17 @@ import {
|
|||
useStore as useZustandStore,
|
||||
} from 'zustand'
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
import { InteractionMode } from '@/app/components/workflow'
|
||||
import { HooksStoreContext } from './provider'
|
||||
|
||||
export type AvailableNodesMetaData = {
|
||||
nodes: NodeDefault[]
|
||||
nodesMap?: Record<BlockEnum, NodeDefault<any>>
|
||||
nodes: NodeDefaultBase[]
|
||||
nodesMap: Record<BlockEnum, NodeDefaultBase>
|
||||
}
|
||||
|
||||
export type HooksStore = ReturnType<typeof createHooksStore>
|
||||
export type CommonHooksFnMap = {
|
||||
interactionMode?: 'default' | 'subgraph'
|
||||
interactionMode?: InteractionMode
|
||||
doSyncWorkflowDraft: (
|
||||
notRefreshWhenSyncError?: boolean,
|
||||
callback?: {
|
||||
|
|
@ -78,7 +81,7 @@ export type Shape = {
|
|||
} & CommonHooksFnMap
|
||||
|
||||
export const createHooksStore = ({
|
||||
interactionMode = 'default',
|
||||
interactionMode = InteractionMode.Default,
|
||||
doSyncWorkflowDraft = async () => noop(),
|
||||
syncWorkflowDraftWhenPageClose = noop,
|
||||
handleRefreshWorkflowDraft = noop,
|
||||
|
|
@ -97,6 +100,7 @@ export const createHooksStore = ({
|
|||
subGraphSelectableNodeTypes,
|
||||
availableNodesMetaData = {
|
||||
nodes: [],
|
||||
nodesMap: {} as Record<BlockEnum, NodeDefaultBase>,
|
||||
},
|
||||
getWorkflowRunAndTraceUrl = () => ({
|
||||
runUrl: '',
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const useResizablePanels = () => {
|
|||
const resolvedCodePanelHeight = useMemo(() => {
|
||||
if (!maxCodePanelHeight)
|
||||
return codePanelHeight
|
||||
// Reason: Clamp the panel height so the output area always has space.
|
||||
// Clamp the panel height so the output area always has space.
|
||||
return Math.min(codePanelHeight, maxCodePanelHeight)
|
||||
}, [codePanelHeight, maxCodePanelHeight])
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { NestedNodeConfig } from '@/app/components/workflow/nodes/_base/typ
|
|||
import type { CodeNodeType, OutputVar } from '@/app/components/workflow/nodes/code/types'
|
||||
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type {
|
||||
CommonNodeType,
|
||||
NodeDefaultBase,
|
||||
PromptItem,
|
||||
PromptTemplateItem,
|
||||
Node as WorkflowNode,
|
||||
|
|
@ -40,7 +40,7 @@ export const getDefaultOutputKey = (outputs?: OutputVar): string => {
|
|||
const keys = Object.keys(outputs)
|
||||
if (keys.length === 0)
|
||||
return ''
|
||||
// Reason: 'result' is the conventional default output key for code nodes
|
||||
// 'result' is the conventional default output key for code nodes
|
||||
if (keys.includes('result'))
|
||||
return 'result'
|
||||
return keys[0]
|
||||
|
|
@ -132,10 +132,7 @@ const buildPromptTemplateWithText = (
|
|||
return [...promptTemplate, defaultUserPrompt] as PromptTemplateItem[]
|
||||
}
|
||||
|
||||
type NodesMetaDataMap = Record<BlockEnum, {
|
||||
defaultValue?: Partial<LLMNodeType | CodeNodeType>
|
||||
checkValid?: (data: CommonNodeType, t: (key: string, options?: Record<string, unknown>) => string, moreData?: unknown) => { isValid: boolean, errorMessage?: string }
|
||||
}>
|
||||
type NodesMetaDataMap = Record<BlockEnum, Pick<NodeDefaultBase, 'defaultValue' | 'checkValid'>>
|
||||
|
||||
type ConfigsMap = {
|
||||
flowId?: string
|
||||
|
|
|
|||
|
|
@ -340,7 +340,24 @@ export type NodeOutPutVar = {
|
|||
isFlat?: boolean
|
||||
}
|
||||
|
||||
export type NodeDefault<T = {}> = {
|
||||
// allow node default validators with narrower payload types to be stored in shared collections.
|
||||
type CheckValidFn<T> = {
|
||||
bivarianceHack: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean, errorMessage?: string }
|
||||
}['bivarianceHack']
|
||||
|
||||
// allow node output var generators with narrower payload types to be stored in shared collections.
|
||||
type GetOutputVarsFn<T> = {
|
||||
bivarianceHack: (
|
||||
payload: T,
|
||||
allPluginInfoList: Record<string, ToolWithProvider[]>,
|
||||
ragVariables?: Var[],
|
||||
utils?: {
|
||||
schemaTypeDefinitions?: SchemaTypeDefinition[]
|
||||
},
|
||||
) => Var[]
|
||||
}['bivarianceHack']
|
||||
|
||||
export type NodeDefaultBase = {
|
||||
metaData: {
|
||||
classification: BlockClassificationEnum
|
||||
sort: number
|
||||
|
|
@ -355,12 +372,16 @@ export type NodeDefault<T = {}> = {
|
|||
isSingleton?: boolean
|
||||
isTypeFixed?: boolean
|
||||
}
|
||||
defaultValue: Partial<T>
|
||||
defaultValue: Partial<CommonNodeType>
|
||||
defaultRunInputData?: Record<string, any>
|
||||
checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean, errorMessage?: string }
|
||||
getOutputVars?: (payload: T, allPluginInfoList: Record<string, ToolWithProvider[]>, ragVariables?: Var[], utils?: {
|
||||
schemaTypeDefinitions?: SchemaTypeDefinition[]
|
||||
}) => Var[]
|
||||
checkValid: CheckValidFn<CommonNodeType>
|
||||
getOutputVars?: GetOutputVarsFn<CommonNodeType>
|
||||
}
|
||||
|
||||
export type NodeDefault<T extends CommonNodeType = CommonNodeType> = Omit<NodeDefaultBase, 'defaultValue' | 'checkValid' | 'getOutputVars'> & {
|
||||
defaultValue: Partial<T>
|
||||
checkValid: CheckValidFn<T>
|
||||
getOutputVars?: GetOutputVarsFn<T>
|
||||
}
|
||||
|
||||
export type OnSelectBlock = (type: BlockEnum, pluginDefaultValue?: PluginDefaultValue) => void
|
||||
|
|
|
|||
Loading…
Reference in New Issue