mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(editor): Add Info Note to NDV Output Panel if no existing Tools were used during Execution (#11672)
This commit is contained in:
@@ -72,6 +72,10 @@ export function getOutputPanelTable() {
|
|||||||
return getOutputPanelDataContainer().get('table');
|
return getOutputPanelDataContainer().get('table');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRunDataInfoCallout() {
|
||||||
|
return cy.getByTestId('run-data-callout');
|
||||||
|
}
|
||||||
|
|
||||||
export function getOutputPanelItemsCount() {
|
export function getOutputPanelItemsCount() {
|
||||||
return getOutputPanel().getByTestId('ndv-items-count');
|
return getOutputPanel().getByTestId('ndv-items-count');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
clickCreateNewCredential,
|
clickCreateNewCredential,
|
||||||
clickExecuteNode,
|
clickExecuteNode,
|
||||||
clickGetBackToCanvas,
|
clickGetBackToCanvas,
|
||||||
|
getRunDataInfoCallout,
|
||||||
|
getOutputPanelTable,
|
||||||
toggleParameterCheckboxInputByName,
|
toggleParameterCheckboxInputByName,
|
||||||
} from '../composables/ndv';
|
} from '../composables/ndv';
|
||||||
import {
|
import {
|
||||||
@@ -418,4 +420,102 @@ describe('Langchain Integration', () => {
|
|||||||
assertInputOutputText('Berlin', 'not.exist');
|
assertInputOutputText('Berlin', 'not.exist');
|
||||||
assertInputOutputText('Kyiv', 'not.exist');
|
assertInputOutputText('Kyiv', 'not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show tool info notice if no existing tools were used during execution', () => {
|
||||||
|
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
|
||||||
|
addNodeToCanvas(AGENT_NODE_NAME, true);
|
||||||
|
|
||||||
|
addLanguageModelNodeToParent(
|
||||||
|
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||||
|
AGENT_NODE_NAME,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
clickCreateNewCredential();
|
||||||
|
setCredentialValues({
|
||||||
|
apiKey: 'sk_test_123',
|
||||||
|
});
|
||||||
|
clickGetBackToCanvas();
|
||||||
|
|
||||||
|
addToolNodeToParent(AI_TOOL_CALCULATOR_NODE_NAME, AGENT_NODE_NAME);
|
||||||
|
clickGetBackToCanvas();
|
||||||
|
openNode(AGENT_NODE_NAME);
|
||||||
|
|
||||||
|
const inputMessage = 'Hello!';
|
||||||
|
const outputMessage = 'Hi there! How can I assist you today?';
|
||||||
|
|
||||||
|
clickExecuteNode();
|
||||||
|
|
||||||
|
runMockWorkflowExecution({
|
||||||
|
trigger: () => sendManualChatMessage(inputMessage),
|
||||||
|
runData: [
|
||||||
|
createMockNodeExecutionData(AGENT_NODE_NAME, {
|
||||||
|
jsonData: {
|
||||||
|
main: { output: outputMessage },
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
lastNodeExecuted: AGENT_NODE_NAME,
|
||||||
|
});
|
||||||
|
closeManualChatModal();
|
||||||
|
openNode(AGENT_NODE_NAME);
|
||||||
|
|
||||||
|
getRunDataInfoCallout().should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show tool info notice if tools were used during execution', () => {
|
||||||
|
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
|
||||||
|
addNodeToCanvas(AGENT_NODE_NAME, true, true);
|
||||||
|
getRunDataInfoCallout().should('not.exist');
|
||||||
|
clickGetBackToCanvas();
|
||||||
|
|
||||||
|
addLanguageModelNodeToParent(
|
||||||
|
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||||
|
AGENT_NODE_NAME,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
clickCreateNewCredential();
|
||||||
|
setCredentialValues({
|
||||||
|
apiKey: 'sk_test_123',
|
||||||
|
});
|
||||||
|
clickGetBackToCanvas();
|
||||||
|
|
||||||
|
addToolNodeToParent(AI_TOOL_CALCULATOR_NODE_NAME, AGENT_NODE_NAME);
|
||||||
|
clickGetBackToCanvas();
|
||||||
|
openNode(AGENT_NODE_NAME);
|
||||||
|
|
||||||
|
getRunDataInfoCallout().should('not.exist');
|
||||||
|
|
||||||
|
const inputMessage = 'Hello!';
|
||||||
|
const outputMessage = 'Hi there! How can I assist you today?';
|
||||||
|
|
||||||
|
clickExecuteNode();
|
||||||
|
|
||||||
|
runMockWorkflowExecution({
|
||||||
|
trigger: () => sendManualChatMessage(inputMessage),
|
||||||
|
runData: [
|
||||||
|
createMockNodeExecutionData(AGENT_NODE_NAME, {
|
||||||
|
jsonData: {
|
||||||
|
main: { output: outputMessage },
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
createMockNodeExecutionData(AI_TOOL_CALCULATOR_NODE_NAME, {}),
|
||||||
|
],
|
||||||
|
lastNodeExecuted: AGENT_NODE_NAME,
|
||||||
|
});
|
||||||
|
|
||||||
|
closeManualChatModal();
|
||||||
|
openNode(AGENT_NODE_NAME);
|
||||||
|
// This waits to ensure the output panel is rendered
|
||||||
|
getOutputPanelTable();
|
||||||
|
|
||||||
|
getRunDataInfoCallout().should('not.exist');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, watch } from 'vue';
|
import { ref, computed, onMounted, watch } from 'vue';
|
||||||
import type { IRunData, IRunExecutionData, NodeError, Workflow } from 'n8n-workflow';
|
import {
|
||||||
|
NodeConnectionType,
|
||||||
|
type IRunData,
|
||||||
|
type IRunExecutionData,
|
||||||
|
type NodeError,
|
||||||
|
type Workflow,
|
||||||
|
} from 'n8n-workflow';
|
||||||
import RunData from './RunData.vue';
|
import RunData from './RunData.vue';
|
||||||
import RunInfo from './RunInfo.vue';
|
import RunInfo from './RunInfo.vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
@@ -209,6 +215,29 @@ const canPinData = computed(() => {
|
|||||||
return pinnedData.isValidNodeType.value && !props.isReadOnly;
|
return pinnedData.isValidNodeType.value && !props.isReadOnly;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const allToolsWereUnusedNotice = computed(() => {
|
||||||
|
if (!node.value || runsCount.value === 0) return undefined;
|
||||||
|
|
||||||
|
// With pinned data there's no clear correct answer for whether
|
||||||
|
// we should use historic or current parents, so we don't show the notice,
|
||||||
|
// as it likely ends up unactionable noise to the user
|
||||||
|
if (pinnedData.hasData.value) return undefined;
|
||||||
|
|
||||||
|
const toolsAvailable = props.workflow.getParentNodes(
|
||||||
|
node.value.name,
|
||||||
|
NodeConnectionType.AiTool,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
const toolsUsedInLatestRun = toolsAvailable.filter(
|
||||||
|
(tool) => !!workflowRunData.value?.[tool]?.[props.runIndex],
|
||||||
|
);
|
||||||
|
if (toolsAvailable.length > 0 && toolsUsedInLatestRun.length === 0) {
|
||||||
|
return i18n.baseText('ndv.output.noToolUsedInfo');
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
|
||||||
const insertTestData = () => {
|
const insertTestData = () => {
|
||||||
@@ -298,6 +327,7 @@ const activatePane = () => {
|
|||||||
:hide-pagination="outputMode === 'logs'"
|
:hide-pagination="outputMode === 'logs'"
|
||||||
pane-type="output"
|
pane-type="output"
|
||||||
:data-output-type="outputMode"
|
:data-output-type="outputMode"
|
||||||
|
:callout-message="allToolsWereUnusedNotice"
|
||||||
@activate-pane="activatePane"
|
@activate-pane="activatePane"
|
||||||
@run-change="onRunIndexChange"
|
@run-change="onRunIndexChange"
|
||||||
@link-run="onLinkRun"
|
@link-run="onLinkRun"
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ type Props = {
|
|||||||
isProductionExecutionPreview?: boolean;
|
isProductionExecutionPreview?: boolean;
|
||||||
isPaneActive?: boolean;
|
isPaneActive?: boolean;
|
||||||
hidePagination?: boolean;
|
hidePagination?: boolean;
|
||||||
|
calloutMessage?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -133,6 +134,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
mappingEnabled: false,
|
mappingEnabled: false,
|
||||||
isExecuting: false,
|
isExecuting: false,
|
||||||
hidePagination: false,
|
hidePagination: false,
|
||||||
|
calloutMessage: undefined,
|
||||||
});
|
});
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
search: [search: string];
|
search: [search: string];
|
||||||
@@ -1426,6 +1428,12 @@ defineExpose({ enterEditMode });
|
|||||||
|
|
||||||
<slot v-if="!displaysMultipleNodes" name="before-data" />
|
<slot v-if="!displaysMultipleNodes" name="before-data" />
|
||||||
|
|
||||||
|
<div v-if="props.calloutMessage" :class="$style.hintCallout">
|
||||||
|
<N8nCallout theme="secondary" data-test-id="run-data-callout">
|
||||||
|
<N8nText v-n8n-html="props.calloutMessage" size="small"></N8nText>
|
||||||
|
</N8nCallout>
|
||||||
|
</div>
|
||||||
|
|
||||||
<N8nCallout
|
<N8nCallout
|
||||||
v-for="hint in getNodeHints()"
|
v-for="hint in getNodeHints()"
|
||||||
:key="hint.message"
|
:key="hint.message"
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ exports[`InputPanel > should render 1`] = `
|
|||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
|
||||||
|
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
|
|||||||
@@ -988,6 +988,7 @@
|
|||||||
"ndv.output.tooMuchData.showDataAnyway": "Show data",
|
"ndv.output.tooMuchData.showDataAnyway": "Show data",
|
||||||
"ndv.output.tooMuchData.title": "Display data?",
|
"ndv.output.tooMuchData.title": "Display data?",
|
||||||
"ndv.output.waitingToRun": "Waiting to execute...",
|
"ndv.output.waitingToRun": "Waiting to execute...",
|
||||||
|
"ndv.output.noToolUsedInfo": "None of your tools were used in this run. Try giving your tools clearer names and descriptions to help the AI",
|
||||||
"ndv.title.cancel": "Cancel",
|
"ndv.title.cancel": "Cancel",
|
||||||
"ndv.title.rename": "Rename",
|
"ndv.title.rename": "Rename",
|
||||||
"ndv.title.renameNode": "Rename node",
|
"ndv.title.renameNode": "Rename node",
|
||||||
|
|||||||
Reference in New Issue
Block a user