From 60a06081ad6b4d0d92c77b2f03784fde8799db8e Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 25 Jun 2026 13:45:51 +0100 Subject: [PATCH] Share one file-input control between code and agent workflow nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code node's Input files and the agent node's Documents now render the same NodeDocumentsControl: an All/None/Choose toggle, a multiselect of upstream document/artifact refs, and a free-text add-ref escape (a typed A1/id appears as a removable chip). The control owns its toggle state so Choose is reachable from an empty selection — retiring the per-node parent flag. File passing stays agent-only; the code node keeps its inputs field name and stages the selected refs as files in the sandbox. --- .../src/agents/workflow/WorkflowBuilder.tsx | 238 ++++++------------ .../components/NodeDocumentsControl.tsx | 128 ++++++++++ .../agents/workflow/documentConfig.test.ts | 21 ++ .../src/agents/workflow/documentConfig.ts | 13 + 4 files changed, 233 insertions(+), 167 deletions(-) create mode 100644 frontend/src/agents/workflow/components/NodeDocumentsControl.tsx diff --git a/frontend/src/agents/workflow/WorkflowBuilder.tsx b/frontend/src/agents/workflow/WorkflowBuilder.tsx index 5f458e78..0b7b7f41 100644 --- a/frontend/src/agents/workflow/WorkflowBuilder.tsx +++ b/frontend/src/agents/workflow/WorkflowBuilder.tsx @@ -68,24 +68,20 @@ import { ConditionCase, WorkflowNode } from '../types/workflow'; import { createDefaultCodeConfig, normalizeCodeConfig, - parseCodeInputs, parseCodeJsonSchemaDraft, serializeCodeConfig, - stringifyCodeInputs, validateCodeJsonSchema, } from './codeNodeConfig'; import MobileBlocker from './components/MobileBlocker'; +import NodeDocumentsControl from './components/NodeDocumentsControl'; import PromptTextArea, { extractUpstreamVariables, } from './components/PromptTextArea'; import { - documentsModeToInputDocuments, FILE_PASSING_OPTIONS, FilePassing, - getDocumentsMode, normalizeFilePassing, toDocumentVariableOptions, - withChosenDocumentOptions, } from './documentConfig'; import { AgentNode, @@ -359,9 +355,6 @@ function WorkflowBuilderInner() { const reactFlowWrapper = useRef(null); const [selectedNode, setSelectedNode] = useState(null); - // Node id for which the author explicitly entered "Choose…" documents mode, - // so the picker shows even before any document is selected (empty reads as None). - const [docChooseNodeId, setDocChooseNodeId] = useState(null); const [workflowName, setWorkflowName] = useState('New Workflow'); const [workflowDescription, setWorkflowDescription] = useState(''); const [showWorkflowSettings, setShowWorkflowSettings] = useState(false); @@ -1478,6 +1471,13 @@ function WorkflowBuilderInner() { ); }, [selectedNode, nodes, edges]); + const selectedCodeDocumentOptions = useMemo(() => { + if (!selectedNode || selectedNode.type !== 'code') return []; + return toDocumentVariableOptions( + extractUpstreamVariables(nodes, edges, selectedNode.id), + ); + }, [selectedNode, nodes, edges]); + const selectedCodeJsonSchemaText = useMemo(() => { if (!selectedNode || selectedNode.type !== 'code') return ''; @@ -2214,144 +2214,60 @@ function WorkflowBuilderInner() { emptyText="No sources available" /> - {(() => { - const inputDocuments = - selectedNode.data.config?.input_documents; - const documentsMode = - getDocumentsMode(inputDocuments); - const showChoose = - documentsMode === 'choose' || - docChooseNodeId === selectedNode.id; - const chosenDocuments = showChoose - ? documentsModeToInputDocuments( - 'choose', - inputDocuments ?? [], - ) - : []; - const filePassing = normalizeFilePassing( - selectedNode.data.config?.file_passing, - ); - const setInputDocuments = ( - nextInputDocuments: string[], - ) => + handleUpdateNodeData({ config: { ...(selectedNode.data.config || {}), input_documents: nextInputDocuments, }, - }); - return ( - <> -
- -
- {( - [ - ['all', 'All input docs'], - ['none', 'None'], - ['choose', 'Choose…'], - ] as const - ).map(([mode, modeLabel]) => ( - - ))} -
- {showChoose && ( -
- - setInputDocuments( - documentsModeToInputDocuments( - 'choose', - nextDocuments, - ), - ) - } - placeholder="Select documents..." - searchPlaceholder="Search variables..." - emptyText="No upstream documents" - /> -
- )} -

- Documents passed to this agent from - uploads or upstream nodes. -

-
-
- - + handleUpdateNodeData({ + config: { + ...(selectedNode.data.config || {}), + file_passing: value as FilePassing, + }, + }) + } + > + + + + + {FILE_PASSING_OPTIONS.map((option) => ( + - - - - - {FILE_PASSING_OPTIONS.map( - (option) => ( - - {option.label} - - ), - )} - - -

- Auto: send native when the model - supports it, otherwise extract text. -

-
- - ); - })()} + {option.label} + + ))} + + +

+ Auto: send native when the model supports + it, otherwise extract text. +

+
-
- -