chore: align prompt editor var checks with use-check-list checks (#34715)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
非法操作 2026-04-08 21:29:07 +08:00 committed by GitHub
parent 4fb3fab82d
commit ccfc8c6f15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 237 additions and 159 deletions

View File

@ -120,7 +120,10 @@ describe('HITLInputBlock', () => {
})
await waitFor(() => {
expect(onWorkflowMapUpdate).toHaveBeenCalledWith(workflowNodesMap)
expect(onWorkflowMapUpdate).toHaveBeenCalledWith({
workflowNodesMap,
availableVariables: [],
})
})
})
})

View File

@ -148,7 +148,10 @@ describe('HITLInputVariableBlockComponent', () => {
editor!.update(() => {
$getRoot().selectEnd()
})
handled = editor!.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, createWorkflowNodesMap())
handled = editor!.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, {
workflowNodesMap: createWorkflowNodesMap(),
availableVariables: [],
})
})
expect(handled).toBe(true)

View File

@ -22,7 +22,7 @@ const HITLInputReplacementBlock = ({
onFormInputsChange,
onFormInputItemRename,
onFormInputItemRemove,
workflowNodesMap,
workflowNodesMap = {},
getVarType,
variables,
readonly,

View File

@ -14,6 +14,7 @@ import {
useEffect,
} from 'react'
import { CustomTextNode } from '../custom-text/node'
import { UPDATE_WORKFLOW_NODES_MAP as WORKFLOW_UPDATE_WORKFLOW_NODES_MAP } from '../workflow-variable-block'
import {
$createHITLInputNode,
HITLInputNode,
@ -21,11 +22,13 @@ import {
export const INSERT_HITL_INPUT_BLOCK_COMMAND = createCommand('INSERT_HITL_INPUT_BLOCK_COMMAND')
export const DELETE_HITL_INPUT_BLOCK_COMMAND = createCommand('DELETE_HITL_INPUT_BLOCK_COMMAND')
export const UPDATE_WORKFLOW_NODES_MAP = createCommand('UPDATE_WORKFLOW_NODES_MAP')
export const UPDATE_WORKFLOW_NODES_MAP = WORKFLOW_UPDATE_WORKFLOW_NODES_MAP
const HITLInputBlock = memo(({
onInsert,
onDelete,
workflowNodesMap,
workflowNodesMap = {},
variables: workflowAvailableVariables,
getVarType,
readonly,
}: HITLInputBlockType) => {
@ -33,9 +36,12 @@ const HITLInputBlock = memo(({
useEffect(() => {
editor.update(() => {
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap)
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, {
workflowNodesMap: workflowNodesMap || {},
availableVariables: workflowAvailableVariables || [],
})
})
}, [editor, workflowNodesMap])
}, [editor, workflowNodesMap, workflowAvailableVariables])
useEffect(() => {
if (!editor.hasNodes([HITLInputNode]))

View File

@ -1,3 +1,4 @@
import type { UpdateWorkflowNodesMapPayload } from '../workflow-variable-block'
import type { WorkflowNodesMap } from '../workflow-variable-block/node'
import type { ValueSelector, Var } from '@/app/components/workflow/types'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
@ -98,9 +99,8 @@ const HITLInputVariableBlockComponent = ({
return mergeRegister(
editor.registerCommand(
UPDATE_WORKFLOW_NODES_MAP,
(workflowNodesMap: WorkflowNodesMap) => {
setLocalWorkflowNodesMap(workflowNodesMap)
(payload: UpdateWorkflowNodesMapPayload) => {
setLocalWorkflowNodesMap(payload.workflowNodesMap)
return true
},
COMMAND_PRIORITY_EDITOR,

View File

@ -1,4 +1,5 @@
import type { LexicalEditor } from 'lexical'
import type { UpdateWorkflowNodesMapPayload } from '../index'
import type { ValueSelector, Var } from '@/app/components/workflow/types'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { mergeRegister } from '@lexical/utils'
@ -216,7 +217,7 @@ describe('WorkflowVariableBlockComponent', () => {
})
})
it('should mark env variable invalid when not found in environmentVariables', () => {
it('should treat env variable as valid regardless of environmentVariables contents', () => {
const environmentVariables: Var[] = [{ variable: 'env.valid_key', type: VarType.string }]
render(
@ -229,7 +230,7 @@ describe('WorkflowVariableBlockComponent', () => {
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: expect.any(String),
errorMsg: undefined,
}))
})
@ -281,7 +282,7 @@ describe('WorkflowVariableBlockComponent', () => {
}))
})
it('should evaluate env fallback selector tokens when classifier is forced', () => {
it('should mark forced env branch invalid when selector prefix is missing', () => {
mockForcedVariableKind.value = 'env'
const environmentVariables: Var[] = [{ variable: '.', type: VarType.string }]
@ -295,7 +296,7 @@ describe('WorkflowVariableBlockComponent', () => {
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: undefined,
errorMsg: expect.any(String),
}))
})
@ -330,7 +331,7 @@ describe('WorkflowVariableBlockComponent', () => {
}))
})
it('should mark conversation variable invalid when not found in conversationVariables', () => {
it('should treat conversation variable as valid regardless of conversationVariables contents', () => {
const conversationVariables: Var[] = [{ variable: 'conversation.other', type: VarType.string }]
render(
@ -343,7 +344,7 @@ describe('WorkflowVariableBlockComponent', () => {
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: expect.any(String),
errorMsg: undefined,
}))
})
@ -364,7 +365,7 @@ describe('WorkflowVariableBlockComponent', () => {
}))
})
it('should evaluate conversation fallback selector tokens when classifier is forced', () => {
it('should mark forced conversation branch invalid when selector prefix is missing', () => {
mockForcedVariableKind.value = 'conversation'
const conversationVariables: Var[] = [{ variable: '.', type: VarType.string }]
@ -378,7 +379,7 @@ describe('WorkflowVariableBlockComponent', () => {
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: undefined,
errorMsg: expect.any(String),
}))
})
@ -427,7 +428,7 @@ describe('WorkflowVariableBlockComponent', () => {
}))
})
it('should mark rag variable invalid when not found in ragVariables', () => {
it('should treat rag variable as valid regardless of ragVariables contents', () => {
const ragVariables: Var[] = [{ variable: 'rag.shared.other', type: VarType.string }]
render(
@ -440,7 +441,7 @@ describe('WorkflowVariableBlockComponent', () => {
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: expect.any(String),
errorMsg: undefined,
}))
})
@ -461,7 +462,7 @@ describe('WorkflowVariableBlockComponent', () => {
}))
})
it('should evaluate rag fallback selector tokens when classifier is forced', () => {
it('should mark forced rag branch invalid when selector prefix is missing', () => {
mockForcedVariableKind.value = 'rag'
const ragVariables: Var[] = [{ variable: '..', type: VarType.string }]
@ -475,7 +476,7 @@ describe('WorkflowVariableBlockComponent', () => {
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: undefined,
errorMsg: expect.any(String),
}))
})
@ -488,20 +489,81 @@ describe('WorkflowVariableBlockComponent', () => {
/>,
)
const updateHandler = mockRegisterCommand.mock.calls[0][1] as (map: Record<string, unknown>) => boolean
const updateHandler = mockRegisterCommand.mock.calls[0][1] as (payload: UpdateWorkflowNodesMapPayload) => boolean
let result = false
act(() => {
result = updateHandler({
'node-1': {
title: 'Updated',
type: BlockEnum.LLM,
width: 100,
height: 50,
position: { x: 0, y: 0 },
workflowNodesMap: {
'node-1': {
title: 'Updated',
type: BlockEnum.LLM,
width: 100,
height: 50,
position: { x: 0, y: 0 },
},
},
availableVariables: [],
})
})
expect(result).toBe(true)
})
it('should mark non-special variable invalid when source key is missing in availableVariables', () => {
render(
<WorkflowVariableBlockComponent
nodeKey="k"
variables={['node-1', 'missing_key']}
workflowNodesMap={{
'node-1': {
title: 'Node A',
type: BlockEnum.LLM,
width: 200,
height: 100,
position: { x: 0, y: 0 },
},
}}
availableVariables={[
{
nodeId: 'node-1',
title: 'Node A',
vars: [{ variable: 'existing_key', type: VarType.string }],
},
]}
/>,
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: expect.any(String),
}))
})
it('should keep non-special variable valid when source key exists in availableVariables', () => {
render(
<WorkflowVariableBlockComponent
nodeKey="k"
variables={['node-1', 'existing_key']}
workflowNodesMap={{
'node-1': {
title: 'Node A',
type: BlockEnum.LLM,
width: 200,
height: 100,
position: { x: 0, y: 0 },
},
}}
availableVariables={[
{
nodeId: 'node-1',
title: 'Node A',
vars: [{ variable: 'existing_key', type: VarType.string }],
},
]}
/>,
)
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
errorMsg: undefined,
}))
})
})

View File

@ -105,7 +105,10 @@ describe('WorkflowVariableBlock', () => {
)
expect(mockUpdate).toHaveBeenCalled()
expect(mockDispatchCommand).toHaveBeenCalledWith(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap)
expect(mockDispatchCommand).toHaveBeenCalledWith(UPDATE_WORKFLOW_NODES_MAP, {
workflowNodesMap,
availableVariables: [],
})
})
it('should throw when WorkflowVariableBlockNode is not registered', () => {
@ -137,6 +140,7 @@ describe('WorkflowVariableBlock', () => {
['node-1', 'answer'],
workflowNodesMap,
getVarType,
[],
)
expect($insertNodes).toHaveBeenCalledWith([{ id: 'workflow-node' }])
expect(onInsert).toHaveBeenCalledTimes(1)

View File

@ -1,5 +1,5 @@
import type { Klass, LexicalEditor, LexicalNode } from 'lexical'
import type { Var } from '@/app/components/workflow/types'
import type { NodeOutPutVar } from '@/app/components/workflow/types'
import { createEditor } from 'lexical'
import { Type } from '@/app/components/workflow/nodes/llm/types'
import { BlockEnum, VarType } from '@/app/components/workflow/types'
@ -57,45 +57,43 @@ describe('WorkflowVariableBlockNode', () => {
it('should decorate with component props from node state', () => {
runInEditor(() => {
const getVarType = vi.fn(() => Type.number)
const environmentVariables: Var[] = [{ variable: 'env.key', type: VarType.string }]
const conversationVariables: Var[] = [{ variable: 'conversation.topic', type: VarType.string }]
const ragVariables: Var[] = [{ variable: 'rag.shared.answer', type: VarType.string }]
const availableVariables: NodeOutPutVar[] = [{
nodeId: 'node-1',
title: 'Node A',
vars: [{ variable: 'answer', type: VarType.string }],
}]
const node = new WorkflowVariableBlockNode(
['node-1', 'answer'],
{ 'node-1': { title: 'A', type: BlockEnum.LLM } },
getVarType,
'decorator-key',
environmentVariables,
conversationVariables,
ragVariables,
availableVariables,
)
const decorated = node.decorate()
expect(decorated.props.nodeKey).toBe('decorator-key')
expect(decorated.props.variables).toEqual(['node-1', 'answer'])
expect(decorated.props.workflowNodesMap).toEqual({ 'node-1': { title: 'A', type: BlockEnum.LLM } })
expect(decorated.props.environmentVariables).toEqual(environmentVariables)
expect(decorated.props.conversationVariables).toEqual(conversationVariables)
expect(decorated.props.ragVariables).toEqual(ragVariables)
expect(decorated.props.availableVariables).toEqual(availableVariables)
})
})
it('should export and import json with full payload', () => {
it('should export and import json with available variables payload', () => {
runInEditor(() => {
const getVarType = vi.fn(() => Type.string)
const environmentVariables: Var[] = [{ variable: 'env.key', type: VarType.string }]
const conversationVariables: Var[] = [{ variable: 'conversation.topic', type: VarType.string }]
const ragVariables: Var[] = [{ variable: 'rag.shared.answer', type: VarType.string }]
const availableVariables: NodeOutPutVar[] = [{
nodeId: 'node-1',
title: 'Node A',
vars: [{ variable: 'answer', type: VarType.string }],
}]
const node = new WorkflowVariableBlockNode(
['node-1', 'answer'],
{ 'node-1': { title: 'A', type: BlockEnum.LLM } },
getVarType,
undefined,
environmentVariables,
conversationVariables,
ragVariables,
availableVariables,
)
expect(node.exportJSON()).toEqual({
@ -104,9 +102,7 @@ describe('WorkflowVariableBlockNode', () => {
variables: ['node-1', 'answer'],
workflowNodesMap: { 'node-1': { title: 'A', type: BlockEnum.LLM } },
getVarType,
environmentVariables,
conversationVariables,
ragVariables,
availableVariables,
})
const imported = WorkflowVariableBlockNode.importJSON({
@ -115,48 +111,51 @@ describe('WorkflowVariableBlockNode', () => {
variables: ['node-2', 'result'],
workflowNodesMap: { 'node-2': { title: 'B', type: BlockEnum.Tool } },
getVarType,
environmentVariables,
conversationVariables,
ragVariables,
availableVariables,
})
expect(imported).toBeInstanceOf(WorkflowVariableBlockNode)
expect(imported.getVariables()).toEqual(['node-2', 'result'])
expect(imported.getWorkflowNodesMap()).toEqual({ 'node-2': { title: 'B', type: BlockEnum.Tool } })
expect(imported.getAvailableVariables()).toEqual(availableVariables)
})
})
it('should return getters and text content in expected format', () => {
runInEditor(() => {
const getVarType = vi.fn(() => Type.string)
const environmentVariables: Var[] = [{ variable: 'env.key', type: VarType.string }]
const conversationVariables: Var[] = [{ variable: 'conversation.topic', type: VarType.string }]
const ragVariables: Var[] = [{ variable: 'rag.shared.answer', type: VarType.string }]
const availableVariables: NodeOutPutVar[] = [{
nodeId: 'node-1',
title: 'Node A',
vars: [{ variable: 'answer', type: VarType.string }],
}]
const node = new WorkflowVariableBlockNode(
['node-1', 'answer'],
{ 'node-1': { title: 'A', type: BlockEnum.LLM } },
getVarType,
undefined,
environmentVariables,
conversationVariables,
ragVariables,
availableVariables,
)
expect(node.getVariables()).toEqual(['node-1', 'answer'])
expect(node.getWorkflowNodesMap()).toEqual({ 'node-1': { title: 'A', type: BlockEnum.LLM } })
expect(node.getVarType()).toBe(getVarType)
expect(node.getEnvironmentVariables()).toEqual(environmentVariables)
expect(node.getConversationVariables()).toEqual(conversationVariables)
expect(node.getRagVariables()).toEqual(ragVariables)
expect(node.getAvailableVariables()).toEqual(availableVariables)
expect(node.getTextContent()).toBe('{{#node-1.answer#}}')
})
})
it('should create node helper and type guard checks', () => {
runInEditor(() => {
const node = $createWorkflowVariableBlockNode(['node-1', 'answer'], {}, undefined)
const availableVariables: NodeOutPutVar[] = [{
nodeId: 'node-1',
title: 'Node A',
vars: [{ variable: 'answer', type: VarType.string }],
}]
const node = $createWorkflowVariableBlockNode(['node-1', 'answer'], {}, undefined, availableVariables)
expect(node).toBeInstanceOf(WorkflowVariableBlockNode)
expect(node.getAvailableVariables()).toEqual(availableVariables)
expect($isWorkflowVariableBlockNode(node)).toBe(true)
expect($isWorkflowVariableBlockNode(null)).toBe(false)
expect($isWorkflowVariableBlockNode(undefined)).toBe(false)

View File

@ -183,12 +183,7 @@ describe('WorkflowVariableBlockReplacementBlock', () => {
['node-1', 'output'],
workflowNodesMap,
getVarType,
variables[0].vars,
variables[1].vars,
[
{ variable: 'ragVarA', type: VarType.string, isRagVariable: true },
{ variable: 'rag.shared.answer', type: VarType.string, isRagVariable: true },
],
variables,
)
expect($applyNodeReplacement).toHaveBeenCalledWith({ type: 'workflow-node' })
expect(created).toEqual({ type: 'workflow-node' })
@ -214,8 +209,6 @@ describe('WorkflowVariableBlockReplacementBlock', () => {
workflowNodesMap,
undefined,
[],
[],
undefined,
)
})
})

View File

@ -1,5 +1,8 @@
import type {
UpdateWorkflowNodesMapPayload,
} from './index'
import type { WorkflowNodesMap } from './node'
import type { ValueSelector, Var } from '@/app/components/workflow/types'
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { mergeRegister } from '@lexical/utils'
import {
@ -15,7 +18,7 @@ import {
import { useTranslation } from 'react-i18next'
import { useReactFlow, useStoreApi } from 'reactflow'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import { isRagVariableVar, isSpecialVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import VarFullPathPanel from '@/app/components/workflow/nodes/_base/components/variable/var-full-path-panel'
import {
VariableLabelInEditor,
@ -34,6 +37,7 @@ type WorkflowVariableBlockComponentProps = {
nodeKey: string
variables: string[]
workflowNodesMap: WorkflowNodesMap
availableVariables?: NodeOutPutVar[]
environmentVariables?: Var[]
conversationVariables?: Var[]
ragVariables?: Var[]
@ -47,10 +51,8 @@ const WorkflowVariableBlockComponent = ({
nodeKey,
variables,
workflowNodesMap = {},
availableVariables,
getVarType,
environmentVariables,
conversationVariables,
ragVariables,
}: WorkflowVariableBlockComponentProps) => {
const { t } = useTranslation()
const [editor] = useLexicalComposerContext()
@ -66,36 +68,25 @@ const WorkflowVariableBlockComponent = ({
}
)()
const [localWorkflowNodesMap, setLocalWorkflowNodesMap] = useState<WorkflowNodesMap>(workflowNodesMap)
const [localAvailableVariables, setLocalAvailableVariables] = useState<NodeOutPutVar[]>(availableVariables || [])
const node = localWorkflowNodesMap![variables[isRagVar ? 1 : 0]]
const isException = isExceptionVariable(varName, node?.type)
const sourceNodeId = variables[isRagVar ? 1 : 0]
const isLlmModelInstalled = useLlmModelPluginInstalled(sourceNodeId, localWorkflowNodesMap)
const variableValid = useMemo(() => {
let variableValid = true
const isEnv = isENV(variables)
const isChatVar = isConversationVar(variables)
const isGlobal = isGlobalVar(variables)
if (isGlobal)
if (isSpecialVar(variables[0] ?? ''))
return true
if (isEnv) {
if (environmentVariables)
variableValid = environmentVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`)
}
else if (isChatVar) {
if (conversationVariables)
variableValid = conversationVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`)
}
else if (isRagVar) {
if (ragVariables)
variableValid = ragVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}.${variables?.[2] ?? ''}`)
}
else {
variableValid = !!node
}
return variableValid
}, [variables, node, environmentVariables, conversationVariables, isRagVar, ragVariables])
if (!variables[1])
return false
const sourceNode = localAvailableVariables.find(v => v.nodeId === variables[0])
if (!sourceNode)
return false
return sourceNode.vars.some(v => v.variable === variables[1])
}, [localAvailableVariables, variables])
const reactflow = useReactFlow()
const store = useStoreApi()
@ -107,9 +98,9 @@ const WorkflowVariableBlockComponent = ({
return mergeRegister(
editor.registerCommand(
UPDATE_WORKFLOW_NODES_MAP,
(workflowNodesMap: WorkflowNodesMap) => {
setLocalWorkflowNodesMap(workflowNodesMap)
(payload: UpdateWorkflowNodesMapPayload) => {
setLocalWorkflowNodesMap(payload.workflowNodesMap)
setLocalAvailableVariables(payload.availableVariables)
return true
},
COMMAND_PRIORITY_EDITOR,

View File

@ -17,9 +17,14 @@ import {
export const INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND')
export const DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND')
export const UPDATE_WORKFLOW_NODES_MAP = createCommand('UPDATE_WORKFLOW_NODES_MAP')
export type UpdateWorkflowNodesMapPayload = {
workflowNodesMap: NonNullable<WorkflowVariableBlockType['workflowNodesMap']>
availableVariables: NonNullable<WorkflowVariableBlockType['variables']>
}
export const UPDATE_WORKFLOW_NODES_MAP = createCommand<UpdateWorkflowNodesMapPayload>('UPDATE_WORKFLOW_NODES_MAP')
const WorkflowVariableBlock = memo(({
workflowNodesMap,
workflowNodesMap = {},
variables: workflowAvailableVariables,
onInsert,
onDelete,
getVarType,
@ -28,9 +33,12 @@ const WorkflowVariableBlock = memo(({
useEffect(() => {
editor.update(() => {
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap)
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, {
workflowNodesMap: workflowNodesMap || {},
availableVariables: workflowAvailableVariables || [],
})
})
}, [editor, workflowNodesMap])
}, [editor, workflowNodesMap, workflowAvailableVariables])
useEffect(() => {
if (!editor.hasNodes([WorkflowVariableBlockNode]))
@ -40,7 +48,12 @@ const WorkflowVariableBlock = memo(({
editor.registerCommand(
INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND,
(variables: string[]) => {
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, workflowNodesMap, getVarType)
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(
variables,
workflowNodesMap,
getVarType,
workflowAvailableVariables || [],
)
$insertNodes([workflowVariableBlockNode])
if (onInsert)
@ -61,7 +74,7 @@ const WorkflowVariableBlock = memo(({
COMMAND_PRIORITY_EDITOR,
),
)
}, [editor, onInsert, onDelete, workflowNodesMap, getVarType])
}, [editor, onInsert, onDelete, workflowNodesMap, getVarType, workflowAvailableVariables])
return null
})

View File

@ -1,49 +1,55 @@
import type { LexicalNode, NodeKey, SerializedLexicalNode } from 'lexical'
import type { GetVarType, WorkflowVariableBlockType } from '../../types'
import type { Var } from '@/app/components/workflow/types'
import type { NodeOutPutVar } from '@/app/components/workflow/types'
import { DecoratorNode } from 'lexical'
import WorkflowVariableBlockComponent from './component'
export type WorkflowNodesMap = WorkflowVariableBlockType['workflowNodesMap']
export type WorkflowNodesMap = NonNullable<WorkflowVariableBlockType['workflowNodesMap']>
type SerializedNode = SerializedLexicalNode & {
variables: string[]
workflowNodesMap: WorkflowNodesMap
getVarType?: GetVarType
environmentVariables?: Var[]
conversationVariables?: Var[]
ragVariables?: Var[]
availableVariables?: NodeOutPutVar[]
}
export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element> {
__variables: string[]
__workflowNodesMap: WorkflowNodesMap
__getVarType?: GetVarType
__environmentVariables?: Var[]
__conversationVariables?: Var[]
__ragVariables?: Var[]
__availableVariables?: NodeOutPutVar[]
static getType(): string {
return 'workflow-variable-block'
}
static clone(node: WorkflowVariableBlockNode): WorkflowVariableBlockNode {
return new WorkflowVariableBlockNode(node.__variables, node.__workflowNodesMap, node.__getVarType, node.__key, node.__environmentVariables, node.__conversationVariables, node.__ragVariables)
return new WorkflowVariableBlockNode(
node.__variables,
node.__workflowNodesMap,
node.__getVarType,
node.__key,
node.__availableVariables,
)
}
isInline(): boolean {
return true
}
constructor(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType: any, key?: NodeKey, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[]) {
constructor(
variables: string[],
workflowNodesMap: WorkflowNodesMap,
getVarType: any,
key?: NodeKey,
availableVariables?: NodeOutPutVar[],
) {
super(key)
this.__variables = variables
this.__workflowNodesMap = workflowNodesMap
this.__getVarType = getVarType
this.__environmentVariables = environmentVariables
this.__conversationVariables = conversationVariables
this.__ragVariables = ragVariables
this.__availableVariables = availableVariables
}
createDOM(): HTMLElement {
@ -63,30 +69,34 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
variables={this.__variables}
workflowNodesMap={this.__workflowNodesMap}
getVarType={this.__getVarType!}
environmentVariables={this.__environmentVariables}
conversationVariables={this.__conversationVariables}
ragVariables={this.__ragVariables}
availableVariables={this.__availableVariables}
/>
)
}
static importJSON(serializedNode: SerializedNode): WorkflowVariableBlockNode {
const node = $createWorkflowVariableBlockNode(serializedNode.variables, serializedNode.workflowNodesMap, serializedNode.getVarType, serializedNode.environmentVariables, serializedNode.conversationVariables, serializedNode.ragVariables)
const node = $createWorkflowVariableBlockNode(
serializedNode.variables,
serializedNode.workflowNodesMap,
serializedNode.getVarType,
serializedNode.availableVariables,
)
return node
}
exportJSON(): SerializedNode {
return {
const json: SerializedNode = {
type: 'workflow-variable-block',
version: 1,
variables: this.getVariables(),
workflowNodesMap: this.getWorkflowNodesMap(),
getVarType: this.getVarType(),
environmentVariables: this.getEnvironmentVariables(),
conversationVariables: this.getConversationVariables(),
ragVariables: this.getRagVariables(),
}
if (this.getAvailableVariables())
json.availableVariables = this.getAvailableVariables()
return json
}
getVariables(): string[] {
@ -104,27 +114,28 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
return self.__getVarType
}
getEnvironmentVariables(): any {
getAvailableVariables(): NodeOutPutVar[] | undefined {
const self = this.getLatest()
return self.__environmentVariables
}
getConversationVariables(): any {
const self = this.getLatest()
return self.__conversationVariables
}
getRagVariables(): any {
const self = this.getLatest()
return self.__ragVariables
return self.__availableVariables
}
getTextContent(): string {
return `{{#${this.getVariables().join('.')}#}}`
}
}
export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[]): WorkflowVariableBlockNode {
return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, ragVariables)
export function $createWorkflowVariableBlockNode(
variables: string[],
workflowNodesMap: WorkflowNodesMap,
getVarType?: GetVarType,
availableVariables?: NodeOutPutVar[],
): WorkflowVariableBlockNode {
return new WorkflowVariableBlockNode(
variables,
workflowNodesMap,
getVarType,
undefined,
availableVariables,
)
}
export function $isWorkflowVariableBlockNode(

View File

@ -15,19 +15,12 @@ import { WorkflowVariableBlockNode } from './index'
import { $createWorkflowVariableBlockNode } from './node'
const WorkflowVariableBlockReplacementBlock = ({
workflowNodesMap,
workflowNodesMap = {},
getVarType,
onInsert,
variables,
}: WorkflowVariableBlockType) => {
const [editor] = useLexicalComposerContext()
const ragVariables = variables?.reduce<any[]>((acc, curr) => {
if (curr.nodeId === 'rag')
acc.push(...curr.vars)
else
acc.push(...curr.vars.filter(v => v.isRagVariable))
return acc
}, [])
useEffect(() => {
if (!editor.hasNodes([WorkflowVariableBlockNode]))
@ -39,8 +32,13 @@ const WorkflowVariableBlockReplacementBlock = ({
onInsert()
const nodePathString = textNode.getTextContent().slice(3, -3)
return $applyNodeReplacement($createWorkflowVariableBlockNode(nodePathString.split('.'), workflowNodesMap, getVarType, variables?.find(o => o.nodeId === 'env')?.vars || [], variables?.find(o => o.nodeId === 'conversation')?.vars || [], ragVariables))
}, [onInsert, workflowNodesMap, getVarType, variables, ragVariables])
return $applyNodeReplacement($createWorkflowVariableBlockNode(
nodePathString.split('.'),
workflowNodesMap,
getVarType,
variables || [],
))
}, [onInsert, workflowNodesMap, getVarType, variables])
const getMatch = useCallback((text: string) => {
const matchArr = REGEX.exec(text)

View File

@ -3385,7 +3385,7 @@
"count": 3
},
"react-refresh/only-export-components": {
"count": 3
"count": 2
}
},
"app/components/base/prompt-editor/plugins/hitl-input-block/input-field.tsx": {
@ -3485,12 +3485,7 @@
},
"app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx": {
"ts/no-explicit-any": {
"count": 5
}
},
"app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx": {
"ts/no-explicit-any": {
"count": 1
"count": 2
}
},
"app/components/base/prompt-log-modal/card.tsx": {