This commit is contained in:
zxhlyh 2025-11-06 15:49:50 +08:00
parent 7cef0fff89
commit 896a29b836
16 changed files with 101 additions and 36 deletions

View File

@ -105,7 +105,6 @@ export type PromptEditorProps = {
errorMessageBlock?: ErrorMessageBlockType
lastRunBlock?: LastRunBlockType
isSupportFileVar?: boolean
isMemorySupported?: boolean
}
const PromptEditor: FC<PromptEditorProps> = ({
@ -131,7 +130,6 @@ const PromptEditor: FC<PromptEditorProps> = ({
errorMessageBlock,
lastRunBlock,
isSupportFileVar,
isMemorySupported,
}) => {
const { eventEmitter } = useEventEmitterContextContext()
const initialConfig = {
@ -202,7 +200,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
}
ErrorBoundary={LexicalErrorBoundary}
/>
{isMemorySupported && workflowVariableBlock?.show && (
{workflowVariableBlock?.show && workflowVariableBlock?.isMemorySupported && (
<MemoryPopupPlugin
instanceId={instanceId}
memoryVariables={workflowVariableBlock?.variables?.find(v => v.nodeId === 'memory_block')?.vars || []}
@ -269,7 +267,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
{
workflowVariableBlock?.show && (
<>
<WorkflowVariableBlock {...workflowVariableBlock} />
<WorkflowVariableBlock {...workflowVariableBlock } />
<WorkflowVariableBlockReplacementBlock {...workflowVariableBlock} />
</>
)

View File

@ -283,7 +283,19 @@ export const useOptions = (
const workflowVariableOptions = useMemo(() => {
if (!workflowVariableBlockType?.show)
return []
const res = workflowVariableBlockType.variables || []
let res = workflowVariableBlockType.variables || []
if (!workflowVariableBlockType.isMemorySupported) {
res = res.map((v) => {
if (v.nodeId === 'conversation') {
return {
...v,
vars: v.vars.filter(vv => !vv.variable.startsWith('memory_block.')),
}
}
return v
})
}
if(errorMessageBlockType?.show && res.findIndex(v => v.nodeId === 'error_message') === -1) {
res.unshift({
nodeId: 'error_message',

View File

@ -2,6 +2,7 @@ import {
memo,
useCallback,
useEffect,
useMemo,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
@ -34,12 +35,12 @@ type WorkflowVariableBlockComponentProps = {
workflowNodesMap: WorkflowNodesMap
environmentVariables?: Var[]
conversationVariables?: Var[]
memoryVariables?: Var[]
ragVariables?: Var[]
getVarType?: (payload: {
nodeId: string,
valueSelector: ValueSelector,
}) => Type
isMemorySupported?: boolean
}
const WorkflowVariableBlockComponent = ({
@ -49,8 +50,8 @@ const WorkflowVariableBlockComponent = ({
getVarType,
environmentVariables,
conversationVariables,
memoryVariables,
ragVariables,
isMemorySupported,
}: WorkflowVariableBlockComponentProps) => {
const { t } = useTranslation()
const [editor] = useLexicalComposerContext()
@ -72,6 +73,8 @@ const WorkflowVariableBlockComponent = ({
const isMemoryVar = isMemoryVariable(variables)
const isException = isExceptionVariable(varName, node?.type)
const memoryVariables = conversationVariables?.filter(v => v.variable.startsWith('memory_block.'))
let variableValid = true
if (isEnv) {
if (environmentVariables)
@ -84,6 +87,9 @@ const WorkflowVariableBlockComponent = ({
else if (isMemoryVar) {
if (memoryVariables)
variableValid = memoryVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`)
if (!isMemorySupported)
variableValid = false
}
else if (isRagVar) {
if (ragVariables)
@ -133,11 +139,28 @@ const WorkflowVariableBlockComponent = ({
})
}, [node, reactflow, store])
const memoriedVariables = useMemo(() => {
if (variables[0] === 'memory_block') {
const currentMemoryVariable = memoryVariables?.find(v => v.variable === variables.join('.'))
if (currentMemoryVariable && currentMemoryVariable.memoryVariableName) {
return [
'memory_block',
currentMemoryVariable.memoryVariableName,
]
}
return variables
}
return variables
}, [memoryVariables, variables])
const Item = (
<VariableLabelInEditor
nodeType={node?.type}
nodeTitle={node?.title}
variables={variables}
variables={memoriedVariables}
onClick={(e) => {
e.stopPropagation()
handleVariableJump()
@ -151,7 +174,7 @@ const WorkflowVariableBlockComponent = ({
)
if (!node)
return Item
return <div>{Item}</div>
return (
<Tooltip
@ -159,10 +182,10 @@ const WorkflowVariableBlockComponent = ({
popupContent={
<VarFullPathPanel
nodeName={node.title}
path={variables.slice(1)}
path={memoriedVariables.slice(1)}
varType={getVarType ? getVarType({
nodeId: variables[0],
valueSelector: variables,
nodeId: memoriedVariables[0],
valueSelector: memoriedVariables,
}) : Type.string}
nodeType={node?.type}
/>}

View File

@ -32,9 +32,19 @@ const WorkflowVariableBlock = memo(({
onInsert,
onDelete,
getVarType,
variables: originalVariables,
isMemorySupported,
}: WorkflowVariableBlockType) => {
const [editor] = useLexicalComposerContext()
const ragVariables = originalVariables?.reduce<any[]>((acc, curr) => {
if (curr.nodeId === 'rag')
acc.push(...curr.vars)
else
acc.push(...curr.vars.filter(v => v.isRagVariable))
return acc
}, [])
useEffect(() => {
editor.update(() => {
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap)
@ -50,7 +60,7 @@ const WorkflowVariableBlock = memo(({
INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND,
(variables: string[]) => {
editor.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, workflowNodesMap, getVarType)
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, originalVariables?.find(o => o.nodeId === 'env')?.vars || [], originalVariables?.find(o => o.nodeId === 'conversation')?.vars || [], ragVariables, isMemorySupported)
$insertNodes([workflowVariableBlockNode])
if (onInsert)

View File

@ -13,8 +13,8 @@ export type SerializedNode = SerializedLexicalNode & {
getVarType?: GetVarType
environmentVariables?: Var[]
conversationVariables?: Var[]
memoryVariables?: Var[]
ragVariables?: Var[]
isMemorySupported?: boolean
}
export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element> {
@ -23,8 +23,8 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
__getVarType?: GetVarType
__environmentVariables?: Var[]
__conversationVariables?: Var[]
__memoryVariables?: Var[]
__ragVariables?: Var[]
__isMemorySupported?: boolean
static getType(): string {
return 'workflow-variable-block'
@ -45,8 +45,8 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
key?: NodeKey,
environmentVariables?: Var[],
conversationVariables?: Var[],
memoryVariables?: Var[],
ragVariables?: Var[],
isMemorySupported?: boolean,
) {
super(key)
@ -55,8 +55,8 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
this.__getVarType = getVarType
this.__environmentVariables = environmentVariables
this.__conversationVariables = conversationVariables
this.__memoryVariables = memoryVariables
this.__ragVariables = ragVariables
this.__isMemorySupported = isMemorySupported
}
createDOM(): HTMLElement {
@ -78,14 +78,14 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
getVarType={this.__getVarType!}
environmentVariables={this.__environmentVariables}
conversationVariables={this.__conversationVariables}
memoryVariables={this.__memoryVariables}
ragVariables={this.__ragVariables}
isMemorySupported={this.__isMemorySupported}
/>
)
}
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.environmentVariables, serializedNode.conversationVariables, serializedNode.ragVariables, serializedNode.isMemorySupported)
return node
}
@ -99,7 +99,6 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
getVarType: this.getVarType(),
environmentVariables: this.getEnvironmentVariables(),
conversationVariables: this.getConversationVariables(),
memoryVariables: this.getMemoryVariables(),
ragVariables: this.getRagVariables(),
}
}
@ -129,22 +128,22 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
return self.__conversationVariables
}
getMemoryVariables(): any {
const self = this.getLatest()
return self.__memoryVariables
}
getRagVariables(): any {
const self = this.getLatest()
return self.__ragVariables
}
getIsMemorySupported() {
const self = this.getLatest()
return self.__isMemorySupported
}
getTextContent(): string {
return `{{#${this.getVariables().join('.')}#}}`
}
}
export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, environmentVariables?: Var[], conversationVariables?: Var[], memoryVariables?: Var[], ragVariables?: Var[]): WorkflowVariableBlockNode {
return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, memoryVariables, ragVariables)
export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[], isMemorySupported?: boolean): WorkflowVariableBlockNode {
return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, ragVariables, isMemorySupported)
}
export function $isWorkflowVariableBlockNode(

View File

@ -19,6 +19,7 @@ const WorkflowVariableBlockReplacementBlock = ({
getVarType,
onInsert,
variables,
isMemorySupported,
}: WorkflowVariableBlockType) => {
const [editor] = useLexicalComposerContext()
const ragVariables = variables?.reduce<any[]>((acc, curr) => {
@ -28,7 +29,6 @@ const WorkflowVariableBlockReplacementBlock = ({
acc.push(...curr.vars.filter(v => v.isRagVariable))
return acc
}, [])
const memoryVariables = variables?.find(variable => variable.nodeId === 'memory_block')?.vars || []
useEffect(() => {
if (!editor.hasNodes([WorkflowVariableBlockNode]))
@ -40,7 +40,7 @@ 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 || [], memoryVariables, ragVariables))
return $applyNodeReplacement($createWorkflowVariableBlockNode(nodePathString.split('.'), workflowNodesMap, getVarType, variables?.find(o => o.nodeId === 'env')?.vars || [], variables?.find(o => o.nodeId === 'conversation')?.vars || [], ragVariables, isMemorySupported))
}, [onInsert, workflowNodesMap, getVarType, variables])
const getMatch = useCallback((text: string) => {

View File

@ -71,6 +71,7 @@ export type WorkflowVariableBlockType = {
getVarType?: GetVarType
showManageInputField?: boolean
onManageInputField?: () => void
isMemorySupported?: boolean
}
export type MenuTextMatch = {

View File

@ -297,13 +297,13 @@ const Editor: FC<Props> = ({
}, {} as any),
showManageInputField: !!pipelineId,
onManageInputField: () => setShowInputFieldPanel?.(true),
isMemorySupported,
}}
onChange={onChange}
onBlur={setBlur}
onFocus={setFocus}
editable={!readOnly}
isSupportFileVar={isSupportFileVar}
isMemorySupported
/>
{/* to patch Editor not support dynamic change editable status */}
{readOnly && <div className='absolute inset-0 z-10'></div>}

View File

@ -39,6 +39,7 @@ type Props = {
varList: Variable[]
handleAddVariable: (payload: any) => void
modelConfig?: ModelConfig
isMemorySupported?: boolean
}
const roleOptions = [
@ -81,6 +82,7 @@ const ConfigPromptItem: FC<Props> = ({
varList,
handleAddVariable,
modelConfig,
isMemorySupported,
}) => {
const { t } = useTranslation()
const workflowStore = useWorkflowStore()
@ -156,7 +158,7 @@ const ConfigPromptItem: FC<Props> = ({
</div>
</>
}
isMemorySupported
isMemorySupported={isMemorySupported}
/>
)
}

View File

@ -34,6 +34,7 @@ type Props = {
varList?: Variable[]
handleAddVariable: (payload: any) => void
modelConfig: ModelConfig
isMemorySupported?: boolean
}
const ConfigPrompt: FC<Props> = ({
@ -49,6 +50,7 @@ const ConfigPrompt: FC<Props> = ({
varList = [],
handleAddVariable,
modelConfig,
isMemorySupported,
}) => {
const { t } = useTranslation()
const workflowStore = useWorkflowStore()
@ -205,6 +207,7 @@ const ConfigPrompt: FC<Props> = ({
varList={varList}
handleAddVariable={handleAddVariable}
modelConfig={modelConfig}
isMemorySupported={isMemorySupported}
/>
</div>
)
@ -241,6 +244,7 @@ const ConfigPrompt: FC<Props> = ({
handleAddVariable={handleAddVariable}
onGenerated={handleGenerated}
modelConfig={modelConfig}
isMemorySupported={isMemorySupported}
/>
</div>
)}

View File

@ -155,6 +155,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
varList={inputs.prompt_config?.jinja2_variables || []}
handleAddVariable={handleAddVariable}
modelConfig={model}
isMemorySupported={memoryType === MemoryMode.block}
/>
)}
@ -213,6 +214,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
availableNodes={availableNodesWithParent}
isSupportFileVar
instanceId={`${id}-chat-workflow-llm-prompt-editor-user`}
isMemorySupported={memoryType === MemoryMode.block}
/>
{inputs.memory?.query_prompt_template && !inputs.memory.query_prompt_template.includes('{{#sys.query#}}') && (

View File

@ -1,4 +1,5 @@
import { memo, useState } from 'react'
import { memo, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { capitalize } from 'lodash-es'
import { RiDeleteBinLine, RiEditLine } from '@remixicon/react'
import {
@ -27,7 +28,13 @@ const VariableItem = ({
term,
currentVarId,
}: VariableItemProps) => {
const { t } = useTranslation()
const [destructive, setDestructive] = useState(false)
const valueType = useMemo(() => {
if (item.value_type === ChatVarType.Memory)
return 'memory'
return item.value_type
}, [item.value_type])
return (
<div className={cn(
'radius-md mb-1 border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2.5 py-2 shadow-xs hover:bg-components-panel-on-panel-item-bg-hover',
@ -46,7 +53,7 @@ const VariableItem = ({
)
}
<div className='system-sm-medium text-text-primary'>{item.name}</div>
<div className='system-xs-medium text-text-tertiary'>{capitalize(item.value_type)}</div>
<div className='system-xs-medium text-text-tertiary'>{capitalize(valueType)}</div>
</div>
<div className='flex shrink-0 items-center gap-1 text-text-tertiary'>
<div className={cn('radius-md hidden cursor-pointer p-1 hover:bg-state-base-hover hover:text-text-secondary group-hover:block', currentVarId === item.id && 'block bg-state-base-hover text-text-secondary')}>
@ -60,7 +67,7 @@ const VariableItem = ({
<RiDeleteBinLine className='h-4 w-4' onClick={() => onDelete(item)}/>
</div>
<div className={cn('flex h-6 items-center gap-0.5 group-hover:hidden', currentVarId === item.id && 'hidden')}>
{scope && <Badge text={scope} />}
{scope === 'app' && <Badge text={'conv'} />}
{term && <Badge text={term} />}
</div>
</div>
@ -70,6 +77,11 @@ const VariableItem = ({
<div className='system-xs-regular truncate text-text-tertiary'>{item.description}</div>
)
}
{
scope === 'app' && (
<div className='system-xs-regular truncate text-text-tertiary'>{t('workflow.chatVariable.appScopeText')}</div>
)
}
</div>
)
}

View File

@ -236,7 +236,7 @@ const ChatVariablePanel = () => {
onEdit={handleEdit}
onDelete={deleteCheck}
term={memoryVariable.term}
scope='conv'
scope='app'
currentVarId={currentVar?.id}
/>
))

View File

@ -311,7 +311,7 @@ Thought: {{agent_scratchpad}}
}
export const VAR_REGEX
= /\{\{(#[a-zA-Z0-9_-]{1,50}(\.\d+)?(\.[a-zA-Z_]\w{0,29}){1,10}#)\}\}/gi
= /\{\{(#[\w-]{1,50}(?:\.\w+(?:\.[a-zA-Z_]\w{0,29}){0,10}|\.[a-zA-Z_]\w{0,29}(?:\.[a-zA-Z_]\w{0,29}){0,9})#)\}\}/gi
export const resetReg = () => (VAR_REGEX.lastIndex = 0)

View File

@ -150,6 +150,7 @@ const translation = {
docLink: 'Visit our docs to learn more.',
button: 'Add Variable',
nodeScopeMemory: 'Node Scope Memory',
appScopeText: 'Conversation History',
modal: {
title: 'Add Conversation Variable',
editTitle: 'Edit Conversation Variable',

View File

@ -150,6 +150,7 @@ const translation = {
docLink: '查看文档了解更多。',
button: '添加变量',
nodeScopeMemory: '节点范围记忆',
appScopeText: '会话历史',
modal: {
title: '添加会话变量',
editTitle: '编辑会话变量',