fix(editor): Fix run index input for RunData view in sub-nodes (#11538)

This commit is contained in:
oleg 2024-11-05 16:47:45 +01:00 committed by GitHub
parent dfd785bc08
commit 434d31ce92
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 516 additions and 90 deletions

View file

@ -44,6 +44,7 @@ import {
openNode,
getConnectionBySourceAndTarget,
} from '../composables/workflow';
import { NDV, WorkflowPage } from '../pages';
import { createMockNodeExecutionData, runMockWorkflowExecution } from '../utils';
describe('Langchain Integration', () => {
@ -232,12 +233,7 @@ describe('Langchain Integration', () => {
const inputMessage = 'Hello!';
const outputMessage = 'Hi there! How can I assist you today?';
runMockWorkflowExecution({
trigger: () => {
sendManualChatMessage(inputMessage);
},
runData: [
const runData = [
createMockNodeExecutionData(MANUAL_CHAT_TRIGGER_NODE_NAME, {
jsonData: {
main: { input: inputMessage },
@ -320,7 +316,13 @@ describe('Langchain Integration', () => {
main: { output: 'Hi there! How can I assist you today?' },
},
}),
],
];
runMockWorkflowExecution({
trigger: () => {
sendManualChatMessage(inputMessage);
},
runData,
lastNodeExecuted: AGENT_NODE_NAME,
});
@ -357,4 +359,56 @@ describe('Langchain Integration', () => {
getConnectionBySourceAndTarget(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME).should('exist');
getNodes().should('have.length', 3);
});
it('should render runItems for sub-nodes and allow switching between them', () => {
const workflowPage = new WorkflowPage();
const ndv = new NDV();
cy.visit(workflowPage.url);
cy.createFixtureWorkflow('In_memory_vector_store_fake_embeddings.json');
workflowPage.actions.zoomToFit();
workflowPage.actions.executeNode('Populate VS');
cy.get('[data-label="25 items"]').should('exist');
const assertInputOutputText = (text: string, assertion: 'exist' | 'not.exist') => {
ndv.getters.outputPanel().contains(text).should(assertion);
ndv.getters.inputPanel().contains(text).should(assertion);
};
workflowPage.actions.openNode('Character Text Splitter');
ndv.getters.outputRunSelector().should('exist');
ndv.getters.inputRunSelector().should('exist');
ndv.getters.inputRunSelector().find('input').should('include.value', '3 of 3');
ndv.getters.outputRunSelector().find('input').should('include.value', '3 of 3');
assertInputOutputText('Kyiv', 'exist');
assertInputOutputText('Berlin', 'not.exist');
assertInputOutputText('Prague', 'not.exist');
ndv.actions.changeOutputRunSelector('2 of 3');
assertInputOutputText('Berlin', 'exist');
assertInputOutputText('Kyiv', 'not.exist');
assertInputOutputText('Prague', 'not.exist');
ndv.actions.changeOutputRunSelector('1 of 3');
assertInputOutputText('Prague', 'exist');
assertInputOutputText('Berlin', 'not.exist');
assertInputOutputText('Kyiv', 'not.exist');
ndv.actions.toggleInputRunLinking();
ndv.actions.changeOutputRunSelector('2 of 3');
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 3');
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 3');
ndv.getters.inputPanel().contains('Prague').should('exist');
ndv.getters.inputPanel().contains('Berlin').should('not.exist');
ndv.getters.outputPanel().contains('Berlin').should('exist');
ndv.getters.outputPanel().contains('Prague').should('not.exist');
ndv.actions.toggleInputRunLinking();
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 3');
ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 3');
assertInputOutputText('Prague', 'exist');
assertInputOutputText('Berlin', 'not.exist');
assertInputOutputText('Kyiv', 'not.exist');
});
});

File diff suppressed because one or more lines are too long

View file

@ -16,7 +16,7 @@ export function createMockNodeExecutionData(
return {
[name]: {
startTime: new Date().getTime(),
executionTime: 0,
executionTime: 1,
executionStatus,
data: jsonData
? Object.keys(jsonData).reduce((acc, key) => {
@ -33,6 +33,7 @@ export function createMockNodeExecutionData(
}, {} as ITaskDataConnections)
: data,
source: [null],
inputOverride,
...rest,
},
};

View file

@ -179,7 +179,7 @@ export default defineComponent({
rootNode(): string {
const workflow = this.workflow;
const rootNodes = workflow.getChildNodes(this.activeNode?.name ?? '', 'ALL_NON_MAIN');
const rootNodes = workflow.getChildNodes(this.activeNode?.name ?? '', 'ALL');
return rootNodes[0];
},
@ -342,7 +342,7 @@ export default defineComponent({
:node="currentNode"
:nodes="isMappingMode ? rootNodesParents : parentNodes"
:workflow="workflow"
:run-index="runIndex"
:run-index="isMappingMode ? 0 : runIndex"
:linked-runs="linkedRuns"
:can-link-runs="!mappedNode && canLinkRuns"
:too-much-data-title="$locale.baseText('ndv.input.tooMuchData.title')"

View file

@ -154,6 +154,29 @@ const parentNode = computed(() => {
});
const inputNodeName = computed<string | undefined>(() => {
const nodeOutputs =
activeNode.value && activeNodeType.value
? NodeHelpers.getNodeOutputs(props.workflowObject, activeNode.value, activeNodeType.value)
: [];
const nonMainOutputs = nodeOutputs.filter((output) => {
if (typeof output === 'string') return output !== NodeConnectionType.Main;
return output.type !== NodeConnectionType.Main;
});
const isSubNode = nonMainOutputs.length > 0;
if (isSubNode && activeNode.value) {
// For sub-nodes, we need to get their connected output node to determine the input
// because sub-nodes use specialized outputs (e.g. NodeConnectionType.AiTool)
// instead of the standard Main output type
const connectedOutputNode = props.workflowObject.getChildNodes(
activeNode.value.name,
'ALL_NON_MAIN',
)?.[0];
return connectedOutputNode;
}
return selectedInput.value || parentNode.value;
});

View file

@ -164,8 +164,9 @@ const aiData = computed<AIResult[]>(() => {
const result: AIResult[] = [];
const connectedSubNodes = props.workflow.getParentNodes(props.node.name, 'ALL_NON_MAIN');
const rootNodeResult = workflowsStore.getWorkflowResultDataByNodeName(props.node.name);
const rootNodeStartTime = rootNodeResult?.[0]?.startTime ?? 0;
const rootNodeEndTime = rootNodeStartTime + (rootNodeResult?.[0]?.executionTime ?? 0);
const rootNodeStartTime = rootNodeResult?.[props.runIndex ?? 0]?.startTime ?? 0;
const rootNodeEndTime =
rootNodeStartTime + (rootNodeResult?.[props.runIndex ?? 0]?.executionTime ?? 0);
connectedSubNodes.forEach((nodeName) => {
const nodeRunData = workflowsStore.getWorkflowResultDataByNodeName(nodeName) ?? [];
@ -193,7 +194,7 @@ const aiData = computed<AIResult[]>(() => {
const currentNodeResult = result.filter((r) => {
const startTime = r.data?.metadata?.startTime ?? 0;
return startTime >= rootNodeStartTime && startTime <= rootNodeEndTime;
return startTime >= rootNodeStartTime && startTime < rootNodeEndTime;
});
return currentNodeResult;