mirror of
https://github.com/langgenius/dify.git
synced 2026-09-05 00:31:19 +08:00
fix(workflow): allow human input in iteration and loop (#40061)
This commit is contained in:
parent
336dd0b8c0
commit
d0a86cfd4e
@ -145,7 +145,7 @@ describe('useAvailableBlocks', () => {
|
||||
})
|
||||
|
||||
describe('inContainer filtering', () => {
|
||||
it('should exclude Iteration, Loop, End, DataSource, KnowledgeBase, HumanInput when inContainer=true', () => {
|
||||
it('should allow HumanInput while excluding unsupported blocks when inContainer=true', () => {
|
||||
const { result } = renderWorkflowHook(() => useAvailableBlocks(BlockEnum.LLM, true), {
|
||||
hooksStoreProps,
|
||||
})
|
||||
@ -155,7 +155,7 @@ describe('useAvailableBlocks', () => {
|
||||
expect(result.current.availableNextBlocks).not.toContain(BlockEnum.End)
|
||||
expect(result.current.availableNextBlocks).not.toContain(BlockEnum.DataSource)
|
||||
expect(result.current.availableNextBlocks).not.toContain(BlockEnum.KnowledgeBase)
|
||||
expect(result.current.availableNextBlocks).not.toContain(BlockEnum.HumanInput)
|
||||
expect(result.current.availableNextBlocks).toContain(BlockEnum.HumanInput)
|
||||
})
|
||||
|
||||
it('should exclude LoopEnd when not in container', () => {
|
||||
|
||||
@ -1183,15 +1183,14 @@ describe('useNodesInteractions', () => {
|
||||
)
|
||||
})
|
||||
|
||||
// Nested container paste restrictions should stay aligned with available block filtering.
|
||||
describe('nested container paste restrictions', () => {
|
||||
// Nested container paste behavior should stay aligned with available block filtering.
|
||||
describe('nested container paste behavior', () => {
|
||||
const disallowedNestedPasteNodeTypes = [
|
||||
BlockEnum.End,
|
||||
BlockEnum.Iteration,
|
||||
BlockEnum.Loop,
|
||||
BlockEnum.DataSource,
|
||||
BlockEnum.KnowledgeBase,
|
||||
BlockEnum.HumanInput,
|
||||
]
|
||||
|
||||
const createNodeMeta = (type: BlockEnum) => ({
|
||||
@ -1205,7 +1204,7 @@ describe('useNodesInteractions', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const runDisallowedPasteScenario = async (
|
||||
const pasteNodeIntoContainer = async (
|
||||
containerType: BlockEnum.Iteration | BlockEnum.Loop,
|
||||
nodeType: BlockEnum,
|
||||
) => {
|
||||
@ -1263,23 +1262,48 @@ describe('useNodesInteractions', () => {
|
||||
|
||||
const pastedNodes = rfState.setNodes.mock.calls.at(-1)?.[0] as Node[]
|
||||
|
||||
expect(pastedNodes).toHaveLength(1)
|
||||
expect(pastedNodes[0]?.id).toBe(containerId)
|
||||
expect(pastedNodes[0]?.data._children).toEqual([])
|
||||
expect(
|
||||
pastedNodes.some((node) => node.data.type === nodeType && node.parentId === containerId),
|
||||
).toBe(false)
|
||||
return { containerId, pastedNodes }
|
||||
}
|
||||
|
||||
it.each(disallowedNestedPasteNodeTypes)(
|
||||
'should not paste %s into an iteration container',
|
||||
async (nodeType) => {
|
||||
await runDisallowedPasteScenario(BlockEnum.Iteration, nodeType)
|
||||
const { containerId, pastedNodes } = await pasteNodeIntoContainer(
|
||||
BlockEnum.Iteration,
|
||||
nodeType,
|
||||
)
|
||||
|
||||
expect(pastedNodes).toHaveLength(1)
|
||||
expect(pastedNodes[0]?.id).toBe(containerId)
|
||||
expect(pastedNodes[0]?.data._children).toEqual([])
|
||||
},
|
||||
)
|
||||
|
||||
it('should not paste human-input into a loop container', async () => {
|
||||
await runDisallowedPasteScenario(BlockEnum.Loop, BlockEnum.HumanInput)
|
||||
})
|
||||
it.each([BlockEnum.Iteration, BlockEnum.Loop] as const)(
|
||||
'should paste human-input into a %s container',
|
||||
async (containerType) => {
|
||||
const { containerId, pastedNodes } = await pasteNodeIntoContainer(
|
||||
containerType,
|
||||
BlockEnum.HumanInput,
|
||||
)
|
||||
const container = pastedNodes.find((node) => node.id === containerId)
|
||||
const pastedHumanInput = pastedNodes.find(
|
||||
(node) => node.data.type === BlockEnum.HumanInput && node.parentId === containerId,
|
||||
)
|
||||
const isIteration = containerType === BlockEnum.Iteration
|
||||
|
||||
expect(pastedHumanInput).toBeDefined()
|
||||
expect(pastedHumanInput?.data).toMatchObject({
|
||||
isInIteration: isIteration,
|
||||
iteration_id: isIteration ? containerId : undefined,
|
||||
isInLoop: !isIteration,
|
||||
loop_id: isIteration ? undefined : containerId,
|
||||
})
|
||||
expect(container?.data._children).toContainEqual({
|
||||
nodeId: pastedHumanInput?.id,
|
||||
nodeType: BlockEnum.HumanInput,
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -11,8 +11,7 @@ const availableBlocksFilter = (nodeType: BlockEnum, inContainer?: boolean) => {
|
||||
nodeType === BlockEnum.Loop ||
|
||||
nodeType === BlockEnum.End ||
|
||||
nodeType === BlockEnum.DataSource ||
|
||||
nodeType === BlockEnum.KnowledgeBase ||
|
||||
nodeType === BlockEnum.HumanInput)
|
||||
nodeType === BlockEnum.KnowledgeBase)
|
||||
)
|
||||
return false
|
||||
|
||||
|
||||
@ -1786,7 +1786,6 @@ export const useNodesInteractions = () => {
|
||||
BlockEnum.Loop,
|
||||
BlockEnum.DataSource,
|
||||
BlockEnum.KnowledgeBase,
|
||||
BlockEnum.HumanInput,
|
||||
]
|
||||
// Same-canvas copy keeps the source container selected, so only treat a
|
||||
// selected container as the paste target when it is not part of the clipboard.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user