-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into ai-226-when-adding-an-llm-model-to-the-can…
…vas-without-a-root-node
- Loading branch information
Showing
315 changed files
with
6,930 additions
and
3,256 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
import type { ExecutionError } from 'n8n-workflow/src'; | ||
import { NDV, WorkflowPage as WorkflowPageClass } from '../pages'; | ||
import { | ||
addLanguageModelNodeToParent, | ||
addMemoryNodeToParent, | ||
addNodeToCanvas, | ||
addToolNodeToParent, | ||
navigateToNewWorkflowPage, | ||
openNode, | ||
} from '../composables/workflow'; | ||
import { | ||
AGENT_NODE_NAME, | ||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, | ||
AI_MEMORY_POSTGRES_NODE_NAME, | ||
AI_TOOL_CALCULATOR_NODE_NAME, | ||
CHAT_TRIGGER_NODE_DISPLAY_NAME, | ||
MANUAL_CHAT_TRIGGER_NODE_NAME, | ||
MANUAL_TRIGGER_NODE_DISPLAY_NAME, | ||
MANUAL_TRIGGER_NODE_NAME, | ||
} from '../constants'; | ||
import { | ||
clickCreateNewCredential, | ||
clickExecuteNode, | ||
clickGetBackToCanvas, | ||
} from '../composables/ndv'; | ||
import { setCredentialValues } from '../composables/modals/credential-modal'; | ||
import { | ||
closeManualChatModal, | ||
getManualChatMessages, | ||
getManualChatModalLogs, | ||
getManualChatModalLogsEntries, | ||
sendManualChatMessage, | ||
} from '../composables/modals/chat-modal'; | ||
import { createMockNodeExecutionData, getVisibleSelect, runMockWorkflowExecution } from '../utils'; | ||
|
||
const ndv = new NDV(); | ||
const WorkflowPage = new WorkflowPageClass(); | ||
|
||
function createRunDataWithError(inputMessage: string) { | ||
return [ | ||
createMockNodeExecutionData(MANUAL_CHAT_TRIGGER_NODE_NAME, { | ||
jsonData: { | ||
main: { input: inputMessage }, | ||
}, | ||
}), | ||
createMockNodeExecutionData(AI_MEMORY_POSTGRES_NODE_NAME, { | ||
jsonData: { | ||
ai_memory: { | ||
json: { | ||
action: 'loadMemoryVariables', | ||
values: { | ||
input: inputMessage, | ||
system_message: 'You are a helpful assistant', | ||
formatting_instructions: | ||
'IMPORTANT: Always call `format_final_response` to format your final response!', | ||
}, | ||
}, | ||
}, | ||
}, | ||
inputOverride: { | ||
ai_memory: [ | ||
[ | ||
{ | ||
json: { | ||
action: 'loadMemoryVariables', | ||
values: { | ||
input: inputMessage, | ||
system_message: 'You are a helpful assistant', | ||
formatting_instructions: | ||
'IMPORTANT: Always call `format_final_response` to format your final response!', | ||
}, | ||
}, | ||
}, | ||
], | ||
], | ||
}, | ||
error: { | ||
message: 'Internal error', | ||
timestamp: 1722591723244, | ||
name: 'NodeOperationError', | ||
description: 'Internal error', | ||
context: {}, | ||
cause: { | ||
name: 'error', | ||
severity: 'FATAL', | ||
code: '3D000', | ||
file: 'postinit.c', | ||
line: '885', | ||
routine: 'InitPostgres', | ||
} as unknown as Error, | ||
} as ExecutionError, | ||
}), | ||
createMockNodeExecutionData(AGENT_NODE_NAME, { | ||
executionStatus: 'error', | ||
error: { | ||
level: 'error', | ||
tags: { | ||
packageName: 'workflow', | ||
}, | ||
context: {}, | ||
functionality: 'configuration-node', | ||
name: 'NodeOperationError', | ||
timestamp: 1722591723244, | ||
node: { | ||
parameters: { | ||
notice: '', | ||
sessionIdType: 'fromInput', | ||
tableName: 'n8n_chat_histories', | ||
}, | ||
id: '6b9141da-0135-4e9d-94d1-2d658cbf48b5', | ||
name: 'Postgres Chat Memory', | ||
type: '@n8n/n8n-nodes-langchain.memoryPostgresChat', | ||
typeVersion: 1, | ||
position: [1140, 500], | ||
credentials: { | ||
postgres: { | ||
id: 'RkyZetVpGsSfEAhQ', | ||
name: 'Postgres account', | ||
}, | ||
}, | ||
}, | ||
messages: ['database "chat11" does not exist'], | ||
description: 'Internal error', | ||
message: 'Internal error', | ||
} as unknown as ExecutionError, | ||
metadata: { | ||
subRun: [ | ||
{ | ||
node: 'Postgres Chat Memory', | ||
runIndex: 0, | ||
}, | ||
], | ||
}, | ||
}), | ||
]; | ||
} | ||
|
||
function setupTestWorkflow(chatTrigger: boolean = false) { | ||
// Setup test workflow with AI Agent, Postgres Memory Node (source of error), Calculator Tool, and OpenAI Chat Model | ||
if (chatTrigger) { | ||
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); | ||
} else { | ||
addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true); | ||
} | ||
|
||
addNodeToCanvas(AGENT_NODE_NAME, true); | ||
|
||
if (!chatTrigger) { | ||
// Remove chat trigger | ||
WorkflowPage.getters | ||
.canvasNodeByName(CHAT_TRIGGER_NODE_DISPLAY_NAME) | ||
.find('[data-test-id="delete-node-button"]') | ||
.click({ force: true }); | ||
|
||
// Set manual trigger to output standard pinned data | ||
openNode(MANUAL_TRIGGER_NODE_DISPLAY_NAME); | ||
ndv.actions.editPinnedData(); | ||
ndv.actions.savePinnedData(); | ||
ndv.actions.close(); | ||
} | ||
|
||
// Calculator is added just to make OpenAI Chat Model work (tools can not be empty with OpenAI model) | ||
addToolNodeToParent(AI_TOOL_CALCULATOR_NODE_NAME, AGENT_NODE_NAME); | ||
clickGetBackToCanvas(); | ||
|
||
addMemoryNodeToParent(AI_MEMORY_POSTGRES_NODE_NAME, AGENT_NODE_NAME); | ||
|
||
clickCreateNewCredential(); | ||
setCredentialValues({ | ||
password: 'testtesttest', | ||
}); | ||
|
||
ndv.getters.parameterInput('sessionIdType').click(); | ||
getVisibleSelect().contains('Define below').click(); | ||
ndv.getters.parameterInput('sessionKey').type('asdasd'); | ||
|
||
clickGetBackToCanvas(); | ||
|
||
addLanguageModelNodeToParent( | ||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, | ||
AGENT_NODE_NAME, | ||
true, | ||
); | ||
|
||
clickCreateNewCredential(); | ||
setCredentialValues({ | ||
apiKey: 'sk_test_123', | ||
}); | ||
clickGetBackToCanvas(); | ||
|
||
WorkflowPage.actions.zoomToFit(); | ||
} | ||
|
||
function checkMessages(inputMessage: string, outputMessage: string) { | ||
const messages = getManualChatMessages(); | ||
messages.should('have.length', 2); | ||
messages.should('contain', inputMessage); | ||
messages.should('contain', outputMessage); | ||
|
||
getManualChatModalLogs().should('exist'); | ||
getManualChatModalLogsEntries() | ||
.should('have.length', 1) | ||
.should('contain', AI_MEMORY_POSTGRES_NODE_NAME); | ||
} | ||
|
||
describe("AI-233 Make root node's logs pane active in case of an error in sub-nodes", () => { | ||
beforeEach(() => { | ||
navigateToNewWorkflowPage(); | ||
}); | ||
|
||
it('should open logs tab by default when there was an error', () => { | ||
setupTestWorkflow(true); | ||
|
||
openNode(AGENT_NODE_NAME); | ||
|
||
const inputMessage = 'Test the code tool'; | ||
|
||
clickExecuteNode(); | ||
runMockWorkflowExecution({ | ||
trigger: () => sendManualChatMessage(inputMessage), | ||
runData: createRunDataWithError(inputMessage), | ||
lastNodeExecuted: AGENT_NODE_NAME, | ||
}); | ||
|
||
checkMessages(inputMessage, '[ERROR: Internal error]'); | ||
closeManualChatModal(); | ||
|
||
// Open the AI Agent node to see the logs | ||
openNode(AGENT_NODE_NAME); | ||
|
||
// Finally check that logs pane is opened by default | ||
ndv.getters.outputDataContainer().should('be.visible'); | ||
|
||
ndv.getters.aiOutputModeToggle().should('be.visible'); | ||
ndv.getters | ||
.aiOutputModeToggle() | ||
.find('[role="radio"]') | ||
.should('have.length', 2) | ||
.eq(1) | ||
.should('have.attr', 'aria-checked', 'true'); | ||
|
||
ndv.getters | ||
.outputPanel() | ||
.findChildByTestId('node-error-message') | ||
.should('be.visible') | ||
.should('contain', 'Error in sub-node'); | ||
}); | ||
|
||
it('should switch to logs tab on error, when NDV is already opened', () => { | ||
setupTestWorkflow(false); | ||
|
||
openNode(AGENT_NODE_NAME); | ||
|
||
const inputMessage = 'Test the code tool'; | ||
|
||
runMockWorkflowExecution({ | ||
trigger: () => clickExecuteNode(), | ||
runData: createRunDataWithError(inputMessage), | ||
lastNodeExecuted: AGENT_NODE_NAME, | ||
}); | ||
|
||
// Check that logs pane is opened by default | ||
ndv.getters.outputDataContainer().should('be.visible'); | ||
|
||
ndv.getters.aiOutputModeToggle().should('be.visible'); | ||
ndv.getters | ||
.aiOutputModeToggle() | ||
.find('[role="radio"]') | ||
.should('have.length', 2) | ||
.eq(1) | ||
.should('have.attr', 'aria-checked', 'true'); | ||
|
||
ndv.getters | ||
.outputPanel() | ||
.findChildByTestId('node-error-message') | ||
.should('be.visible') | ||
.should('contain', 'Error in sub-node'); | ||
}); | ||
}); |
Oops, something went wrong.