diff --git a/web/app/components/sub-graph/index.tsx b/web/app/components/sub-graph/index.tsx index 91d70fab7d..c7ec066108 100644 --- a/web/app/components/sub-graph/index.tsx +++ b/web/app/components/sub-graph/index.tsx @@ -48,6 +48,8 @@ const SubGraph: FC = (props) => { desc: '', _connectedSourceHandleIds: ['source'], _connectedTargetHandleIds: [], + _subGraphEntry: true, + _iconTypeOverride: BlockEnum.Agent, variables: [], }, selectable: false, @@ -62,9 +64,7 @@ const SubGraph: FC = (props) => { if (!extractorNode) return null - const updateSystemPrompt = (item: PromptItem) => { - if (item.role !== PromptRole.system) - return item + const applyPromptText = (item: PromptItem) => { if (item.edition_type === EditionType.jinja2) { return { ...item, @@ -75,36 +75,45 @@ const SubGraph: FC = (props) => { return { ...item, text: promptText } } - const nextPromptTemplate = Array.isArray(extractorNode.data.prompt_template) - ? extractorNode.data.prompt_template.map(updateSystemPrompt) - : updateSystemPrompt(extractorNode.data.prompt_template as PromptItem) + const nextPromptTemplate = (() => { + const template = extractorNode.data.prompt_template + if (!Array.isArray(template)) + return applyPromptText(template as PromptItem) - const hasSystemPrompt = Array.isArray(nextPromptTemplate) - && nextPromptTemplate.some((item: PromptItem) => item.role === PromptRole.system) - const defaultSystemPrompt: PromptItem = (() => { - const useJinja = Array.isArray(nextPromptTemplate) - && nextPromptTemplate.some((item: PromptItem) => item.edition_type === EditionType.jinja2) - if (useJinja) { - return { - role: PromptRole.system, - text: promptText, - jinja2_text: promptText, - edition_type: EditionType.jinja2, - } + const userIndex = template.findIndex(item => item.role === PromptRole.user) + if (userIndex >= 0) { + return template.map((item, index) => { + if (index !== userIndex) + return item + return applyPromptText(item) + }) } - return { role: PromptRole.system, text: promptText } + + const useJinja = template.some((item: PromptItem) => item.edition_type === EditionType.jinja2) + const defaultUserPrompt: PromptItem = useJinja + ? { + role: PromptRole.user, + text: promptText, + jinja2_text: promptText, + edition_type: EditionType.jinja2, + } + : { role: PromptRole.user, text: promptText } + const systemIndex = template.findIndex(item => item.role === PromptRole.system) + const nextTemplate = [...template] + if (systemIndex >= 0) + nextTemplate.splice(systemIndex + 1, 0, defaultUserPrompt) + else + nextTemplate.unshift(defaultUserPrompt) + return nextTemplate })() - const normalizedPromptTemplate = Array.isArray(nextPromptTemplate) - ? (hasSystemPrompt ? nextPromptTemplate : [defaultSystemPrompt, ...nextPromptTemplate]) - : nextPromptTemplate return { ...extractorNode, hidden: false, - position: { x: 450, y: 150 }, + position: { x: 320, y: 150 }, data: { ...extractorNode.data, - prompt_template: normalizedPromptTemplate, + prompt_template: nextPromptTemplate, }, } }, [extractorNode, promptText]) diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx index 607a29f098..c484ff48f2 100644 --- a/web/app/components/workflow/nodes/_base/node.tsx +++ b/web/app/components/workflow/nodes/_base/node.tsx @@ -63,6 +63,11 @@ const BaseNode: FC = ({ const { t } = useTranslation() const nodeRef = useRef(null) const { nodesReadOnly } = useNodesReadOnly() + const { _subGraphEntry, _iconTypeOverride } = data as { + _subGraphEntry?: boolean + _iconTypeOverride?: BlockEnum + } + const iconType = _iconTypeOverride ?? data.type const { handleNodeIterationChildSizeChange } = useNodeIterationInteractions() const { handleNodeLoopChildSizeChange } = useNodeLoopInteractions() @@ -138,6 +143,48 @@ const BaseNode: FC = ({ return null }, [data._loopIndex, data._runningStatus, t]) + if (_subGraphEntry) { + return ( +
+ +
+
+ +
+ {data.title} +
+
+
+
+ ) + } + const nodeContent = (
= ({ > @@ -344,8 +391,9 @@ const BaseNode: FC = ({ const isStartNode = data.type === BlockEnum.Start const isEntryNode = isTriggerNode(data.type as any) || isStartNode + const shouldWrapEntryNode = isEntryNode && !(isStartNode && _subGraphEntry) - return isEntryNode + return shouldWrapEntryNode ? ( = ({ }, [toolNodeId, workflowNodes]) const toolParamValue = (toolNode?.data as ToolNodeType | undefined)?.tool_parameters?.[paramKey]?.value as string | undefined - const getSystemPromptText = useCallback((promptTemplate?: PromptItem[] | PromptItem) => { + const getUserPromptText = useCallback((promptTemplate?: PromptItem[] | PromptItem) => { if (!promptTemplate) return '' const resolveText = (item?: PromptItem) => { @@ -51,6 +51,9 @@ const SubGraphModal: FC = ({ return item.text || '' } if (Array.isArray(promptTemplate)) { + const userPrompt = promptTemplate.find(item => item.role === PromptRole.user) + if (userPrompt) + return resolveText(userPrompt) const systemPrompt = promptTemplate.find(item => item.role === PromptRole.system) return resolveText(systemPrompt) } @@ -62,9 +65,9 @@ const SubGraphModal: FC = ({ if (!extractorNodeData) return - const systemPromptText = getSystemPromptText(extractorNodeData.data?.prompt_template) + const userPromptText = getUserPromptText(extractorNodeData.data?.prompt_template) const placeholder = `{{@${agentNodeId}.context@}}` - const nextValue = `${placeholder}${systemPromptText}` + const nextValue = `${placeholder}${userPromptText}` const { getNodes, setNodes } = reactflowStore.getState() const nextNodes = getNodes().map((node) => { @@ -104,7 +107,7 @@ const SubGraphModal: FC = ({ // Trigger main graph draft sync to persist changes to backend handleSyncWorkflowDraft(true) setControlPromptEditorRerenderKey(Date.now()) - }, [agentNodeId, extractorNodeId, getSystemPromptText, handleSyncWorkflowDraft, paramKey, reactflowStore, setControlPromptEditorRerenderKey, toolNodeId]) + }, [agentNodeId, extractorNodeId, getUserPromptText, handleSyncWorkflowDraft, paramKey, reactflowStore, setControlPromptEditorRerenderKey, toolNodeId]) return (