mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
feat(editor): Send workflow context to assistant store (#11135)
This commit is contained in:
parent
8e6ddfe028
commit
fade9e43c8
|
@ -59,7 +59,7 @@ export function setCredentialByName(name: string) {
|
|||
|
||||
export function clickCreateNewCredential() {
|
||||
openCredentialSelect();
|
||||
getCreateNewCredentialOption().click();
|
||||
getCreateNewCredentialOption().click({ force: true });
|
||||
}
|
||||
|
||||
export function clickGetBackToCanvas() {
|
||||
|
|
|
@ -80,9 +80,9 @@ describe('AI Assistant::enabled', () => {
|
|||
it('should start chat session from node error view', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/simple_message_response.json',
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
cy.createFixtureWorkflow('aiAssistant/test_workflow.json');
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json');
|
||||
wf.actions.openNode('Stop and Error');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
aiAssistant.getters.nodeErrorViewAssistantButton().click();
|
||||
|
@ -98,9 +98,9 @@ describe('AI Assistant::enabled', () => {
|
|||
it('should render chat input correctly', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/simple_message_response.json',
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
cy.createFixtureWorkflow('aiAssistant/test_workflow.json');
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json');
|
||||
wf.actions.openNode('Stop and Error');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
aiAssistant.getters.nodeErrorViewAssistantButton().click();
|
||||
|
@ -131,9 +131,9 @@ describe('AI Assistant::enabled', () => {
|
|||
it('should render and handle quick replies', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/quick_reply_message_response.json',
|
||||
fixture: 'aiAssistant/responses/quick_reply_message_response.json',
|
||||
}).as('chatRequest');
|
||||
cy.createFixtureWorkflow('aiAssistant/test_workflow.json');
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json');
|
||||
wf.actions.openNode('Stop and Error');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
aiAssistant.getters.nodeErrorViewAssistantButton().click();
|
||||
|
@ -149,15 +149,21 @@ describe('AI Assistant::enabled', () => {
|
|||
cy.intercept('POST', '/rest/ai/chat', (req) => {
|
||||
req.reply((res) => {
|
||||
if (['init-error-helper', 'message'].includes(req.body.payload.type)) {
|
||||
res.send({ statusCode: 200, fixture: 'aiAssistant/simple_message_response.json' });
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
});
|
||||
} else if (req.body.payload.type === 'event') {
|
||||
res.send({ statusCode: 200, fixture: 'aiAssistant/node_execution_error_response.json' });
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/responses/node_execution_error_response.json',
|
||||
});
|
||||
} else {
|
||||
res.send({ statusCode: 500 });
|
||||
}
|
||||
});
|
||||
}).as('chatRequest');
|
||||
cy.createFixtureWorkflow('aiAssistant/test_workflow.json');
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json');
|
||||
wf.actions.openNode('Edit Fields');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
aiAssistant.getters.nodeErrorViewAssistantButton().click();
|
||||
|
@ -172,16 +178,15 @@ describe('AI Assistant::enabled', () => {
|
|||
aiAssistant.getters.quickReplies().should('not.exist');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
// But after executing the node again, quick replies should be shown
|
||||
aiAssistant.getters.chatMessagesAssistant().should('have.length', 4);
|
||||
aiAssistant.getters.quickReplies().should('have.length', 2);
|
||||
});
|
||||
|
||||
it('should warn before starting a new session', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/simple_message_response.json',
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
cy.createFixtureWorkflow('aiAssistant/test_workflow.json');
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json');
|
||||
wf.actions.openNode('Edit Fields');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
aiAssistant.getters.nodeErrorViewAssistantButton().click({ force: true });
|
||||
|
@ -206,13 +211,13 @@ describe('AI Assistant::enabled', () => {
|
|||
it('should apply code diff to code node', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/code_diff_suggestion_response.json',
|
||||
fixture: 'aiAssistant/responses/code_diff_suggestion_response.json',
|
||||
}).as('chatRequest');
|
||||
cy.intercept('POST', '/rest/ai/chat/apply-suggestion', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/apply_code_diff_response.json',
|
||||
fixture: 'aiAssistant/responses/apply_code_diff_response.json',
|
||||
}).as('applySuggestion');
|
||||
cy.createFixtureWorkflow('aiAssistant/test_workflow.json');
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json');
|
||||
wf.actions.openNode('Code');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
aiAssistant.getters.nodeErrorViewAssistantButton().click({ force: true });
|
||||
|
@ -256,9 +261,9 @@ describe('AI Assistant::enabled', () => {
|
|||
it('should end chat session when `end_session` event is received', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/end_session_response.json',
|
||||
fixture: 'aiAssistant/responses/end_session_response.json',
|
||||
}).as('chatRequest');
|
||||
cy.createFixtureWorkflow('aiAssistant/test_workflow.json');
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json');
|
||||
wf.actions.openNode('Stop and Error');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
aiAssistant.getters.nodeErrorViewAssistantButton().click();
|
||||
|
@ -271,9 +276,12 @@ describe('AI Assistant::enabled', () => {
|
|||
cy.intercept('POST', '/rest/ai/chat', (req) => {
|
||||
req.reply((res) => {
|
||||
if (['init-support-chat'].includes(req.body.payload.type)) {
|
||||
res.send({ statusCode: 200, fixture: 'aiAssistant/simple_message_response.json' });
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
});
|
||||
} else {
|
||||
res.send({ statusCode: 200, fixture: 'aiAssistant/end_session_response.json' });
|
||||
res.send({ statusCode: 200, fixture: 'aiAssistant/responses/end_session_response.json' });
|
||||
}
|
||||
});
|
||||
}).as('chatRequest');
|
||||
|
@ -298,7 +306,7 @@ describe('AI Assistant::enabled', () => {
|
|||
it('Should not reset assistant session when workflow is saved', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/simple_message_response.json',
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
wf.actions.addInitialNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
aiAssistant.actions.openChat();
|
||||
|
@ -323,7 +331,7 @@ describe('AI Assistant Credential Help', () => {
|
|||
it('should start credential help from node credential', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/simple_message_response.json',
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
wf.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
wf.actions.addNodeToCanvas(GMAIL_NODE_NAME);
|
||||
|
@ -349,7 +357,7 @@ describe('AI Assistant Credential Help', () => {
|
|||
it('should start credential help from credential list', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/simple_message_response.json',
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
|
||||
cy.visit(credentialsPage.url);
|
||||
|
@ -448,7 +456,7 @@ describe('General help', () => {
|
|||
it('assistant returns code snippet', () => {
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/code_snippet_response.json',
|
||||
fixture: 'aiAssistant/responses/code_snippet_response.json',
|
||||
}).as('chatRequest');
|
||||
|
||||
aiAssistant.getters.askAssistantFloatingButton().should('be.visible');
|
||||
|
@ -492,4 +500,65 @@ describe('General help', () => {
|
|||
);
|
||||
aiAssistant.getters.codeSnippet().should('have.text', '{{$json.body.city}}');
|
||||
});
|
||||
|
||||
it('should send current context to support chat', () => {
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/simple_http_request_workflow.json');
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
|
||||
aiAssistant.getters.askAssistantFloatingButton().click();
|
||||
aiAssistant.actions.sendMessage('What is wrong with this workflow?');
|
||||
|
||||
cy.wait('@chatRequest').then((interception) => {
|
||||
const { body } = interception.request;
|
||||
// Body should contain the current workflow context
|
||||
expect(body.payload).to.have.property('context');
|
||||
expect(body.payload.context).to.have.property('currentView');
|
||||
expect(body.payload.context.currentView.name).to.equal('NodeViewExisting');
|
||||
expect(body.payload.context).to.have.property('currentWorkflow');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not send workflow context if nothing changed', () => {
|
||||
cy.createFixtureWorkflow('aiAssistant/workflows/simple_http_request_workflow.json');
|
||||
cy.intercept('POST', '/rest/ai/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/responses/simple_message_response.json',
|
||||
}).as('chatRequest');
|
||||
|
||||
aiAssistant.getters.askAssistantFloatingButton().click();
|
||||
aiAssistant.actions.sendMessage('What is wrong with this workflow?');
|
||||
cy.wait('@chatRequest');
|
||||
|
||||
// Send another message without changing workflow or executing any node
|
||||
aiAssistant.actions.sendMessage('And now?');
|
||||
|
||||
cy.wait('@chatRequest').then((interception) => {
|
||||
const { body } = interception.request;
|
||||
// Workflow context should be empty
|
||||
expect(body.payload).to.have.property('context');
|
||||
expect(body.payload.context).not.to.have.property('currentWorkflow');
|
||||
});
|
||||
|
||||
// Update http request node url
|
||||
wf.actions.openNode('HTTP Request');
|
||||
ndv.actions.typeIntoParameterInput('url', 'https://example.com');
|
||||
ndv.actions.close();
|
||||
// Also execute the workflow
|
||||
wf.actions.executeWorkflow();
|
||||
|
||||
// Send another message
|
||||
aiAssistant.actions.sendMessage('What about now?');
|
||||
cy.wait('@chatRequest').then((interception) => {
|
||||
const { body } = interception.request;
|
||||
// Both workflow and execution context should be sent
|
||||
expect(body.payload).to.have.property('context');
|
||||
expect(body.payload.context).to.have.property('currentWorkflow');
|
||||
expect(body.payload.context.currentWorkflow).not.to.be.empty;
|
||||
expect(body.payload.context).to.have.property('executionData');
|
||||
expect(body.payload.context.executionData).not.to.be.empty;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "298d3dc9-5e99-4b3f-919e-05fdcdfbe2d0",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [360, 220]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "65c32346-e939-4ec7-88a9-1f9184e2258d",
|
||||
"name": "HTTP Request",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [580, 220]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "HTTP Request",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -156,7 +156,7 @@ export class NDV extends BasePage {
|
|||
this.getters.nodeExecuteButton().first().click();
|
||||
},
|
||||
close: () => {
|
||||
this.getters.backToCanvas().click();
|
||||
this.getters.backToCanvas().click({ force: true });
|
||||
},
|
||||
openInlineExpressionEditor: () => {
|
||||
cy.contains('Expression').invoke('show').click();
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,4 +1,10 @@
|
|||
import type { IDataObject, NodeApiError, NodeError, NodeOperationError } from 'n8n-workflow';
|
||||
import type {
|
||||
IDataObject,
|
||||
IRunExecutionData,
|
||||
NodeApiError,
|
||||
NodeError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
import { deepCopy, type INode } from 'n8n-workflow';
|
||||
import { useWorkflowHelpers } from './useWorkflowHelpers';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
@ -203,6 +209,39 @@ export const useAIAssistantHelpers = () => {
|
|||
return undefined;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Prepare workflow execution result data for the AI assistant
|
||||
* by removing data from nodes
|
||||
**/
|
||||
function simplifyResultData(
|
||||
data: IRunExecutionData['resultData'],
|
||||
): ChatRequest.ExecutionResultData {
|
||||
const simplifiedResultData: ChatRequest.ExecutionResultData = {
|
||||
runData: {},
|
||||
};
|
||||
|
||||
// Handle optional error
|
||||
if (data.error) {
|
||||
simplifiedResultData.error = data.error;
|
||||
}
|
||||
// Map runData, excluding the `data` field from ITaskData
|
||||
Object.keys(data.runData).forEach((key) => {
|
||||
const taskDataArray = data.runData[key];
|
||||
simplifiedResultData.runData[key] = taskDataArray.map((taskData) => {
|
||||
const { data: taskDataContent, ...taskDataWithoutData } = taskData;
|
||||
return taskDataWithoutData;
|
||||
});
|
||||
});
|
||||
// Handle lastNodeExecuted if it exists
|
||||
if (data.lastNodeExecuted) {
|
||||
simplifiedResultData.lastNodeExecuted = data.lastNodeExecuted;
|
||||
}
|
||||
// Handle metadata if it exists
|
||||
if (data.metadata) {
|
||||
simplifiedResultData.metadata = data.metadata;
|
||||
}
|
||||
return simplifiedResultData;
|
||||
}
|
||||
|
||||
return {
|
||||
processNodeForAssistant,
|
||||
|
@ -212,5 +251,6 @@ export const useAIAssistantHelpers = () => {
|
|||
getNodesSchemas,
|
||||
getCurrentViewDescription,
|
||||
getReferencedNodes,
|
||||
simplifyResultData,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -83,6 +83,9 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
// We use streaming for assistants that support it, and this for agents
|
||||
const assistantThinkingMessage = ref<string | undefined>();
|
||||
const chatSessionTask = ref<'error' | 'support' | 'credentials' | undefined>();
|
||||
// Indicate if last sent workflow and execution data is stale
|
||||
const workflowDataStale = ref<boolean>(true);
|
||||
const workflowExecutionDataStale = ref<boolean>(true);
|
||||
|
||||
const isExperimentEnabled = computed(
|
||||
() => getVariant(AI_ASSISTANT_EXPERIMENT.name) === AI_ASSISTANT_EXPERIMENT.variant,
|
||||
|
@ -124,18 +127,6 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
).length,
|
||||
);
|
||||
|
||||
watch(route, () => {
|
||||
const activeWorkflowId = workflowsStore.workflowId;
|
||||
if (
|
||||
!currentSessionId.value ||
|
||||
currentSessionWorkflowId.value === PLACEHOLDER_EMPTY_WORKFLOW_ID ||
|
||||
currentSessionWorkflowId.value === activeWorkflowId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
resetAssistantChat();
|
||||
});
|
||||
|
||||
function resetAssistantChat() {
|
||||
clearMessages();
|
||||
currentSessionId.value = undefined;
|
||||
|
@ -305,11 +296,7 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
{ withPostHog: true },
|
||||
);
|
||||
// Track first user message in support chat now that we have a session id
|
||||
if (
|
||||
usersMessages.value.length === 1 &&
|
||||
!currentSessionId.value &&
|
||||
chatSessionTask.value === 'support'
|
||||
) {
|
||||
if (usersMessages.value.length === 1 && chatSessionTask.value === 'support') {
|
||||
const firstUserMessage = usersMessages.value[0] as ChatUI.TextMessage;
|
||||
trackUserMessage(firstUserMessage.content, false);
|
||||
}
|
||||
|
@ -325,6 +312,8 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
|
||||
function onDoneStreaming(id: string) {
|
||||
stopStreaming();
|
||||
workflowDataStale.value = false;
|
||||
workflowExecutionDataStale.value = false;
|
||||
lastUnread.value = chatMessages.value.find(
|
||||
(msg) =>
|
||||
msg.id === id && !msg.read && msg.role === 'assistant' && READABLE_TYPES.includes(msg.type),
|
||||
|
@ -353,14 +342,16 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
/**
|
||||
* Gets information about the current view and active node to provide context to the assistant
|
||||
*/
|
||||
function getVisualContext(nodeInfo?: ChatRequest.NodeInfo): ChatRequest.UserContext | undefined {
|
||||
function getVisualContext(
|
||||
nodeInfo?: ChatRequest.NodeInfo,
|
||||
): ChatRequest.AssistantContext | undefined {
|
||||
if (chatSessionTask.value === 'error') {
|
||||
return undefined;
|
||||
}
|
||||
const currentView = route.name as VIEWS;
|
||||
const activeNode = workflowsStore.activeNode();
|
||||
const activeNodeForLLM = activeNode
|
||||
? assistantHelpers.processNodeForAssistant(activeNode, ['position'])
|
||||
? assistantHelpers.processNodeForAssistant(activeNode, ['position', 'parameters.notice'])
|
||||
: null;
|
||||
const activeModals = uiStore.activeModals;
|
||||
const isCredentialModalActive = activeModals.includes(CREDENTIAL_EDIT_MODAL_KEY);
|
||||
|
@ -401,6 +392,11 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
authType: nodeInfo?.authType?.name,
|
||||
}
|
||||
: undefined,
|
||||
currentWorkflow: workflowDataStale.value ? workflowsStore.workflow : undefined,
|
||||
executionData:
|
||||
workflowExecutionDataStale.value && executionResult
|
||||
? assistantHelpers.simplifyResultData(executionResult)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -491,7 +487,10 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
firstName: usersStore.currentUser?.firstName ?? '',
|
||||
},
|
||||
error: context.error,
|
||||
node: assistantHelpers.processNodeForAssistant(context.node, ['position']),
|
||||
node: assistantHelpers.processNodeForAssistant(context.node, [
|
||||
'position',
|
||||
'parameters.notice',
|
||||
]),
|
||||
nodeInputData,
|
||||
executionSchema: schemas,
|
||||
authType,
|
||||
|
@ -577,7 +576,10 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
) {
|
||||
nodeExecutionStatus.value = 'not_executed';
|
||||
}
|
||||
const userContext = getVisualContext();
|
||||
const activeNode = workflowsStore.activeNode() as INode;
|
||||
const nodeInfo = assistantHelpers.getNodeInfoForAssistant(activeNode);
|
||||
const userContext = getVisualContext(nodeInfo);
|
||||
|
||||
chatWithAssistant(
|
||||
rootStore.restApiContext,
|
||||
{
|
||||
|
@ -764,6 +766,33 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
}
|
||||
}
|
||||
|
||||
watch(route, () => {
|
||||
const activeWorkflowId = workflowsStore.workflowId;
|
||||
if (
|
||||
!currentSessionId.value ||
|
||||
currentSessionWorkflowId.value === PLACEHOLDER_EMPTY_WORKFLOW_ID ||
|
||||
currentSessionWorkflowId.value === activeWorkflowId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
resetAssistantChat();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uiStore.stateIsDirty,
|
||||
() => {
|
||||
workflowDataStale.value = true;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => workflowsStore.workflowExecutionData?.data?.resultData ?? {},
|
||||
() => {
|
||||
workflowExecutionDataStale.value = true;
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
return {
|
||||
isAssistantEnabled,
|
||||
canShowAssistantButtonsOnCanvas,
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import type { VIEWS } from '@/constants';
|
||||
import type { NodeAuthenticationOption, Schema } from '@/Interface';
|
||||
import type { IWorkflowDb, NodeAuthenticationOption, Schema } from '@/Interface';
|
||||
import type {
|
||||
ExecutionError,
|
||||
ICredentialType,
|
||||
IDataObject,
|
||||
INode,
|
||||
INodeIssues,
|
||||
INodeParameters,
|
||||
IRunExecutionData,
|
||||
ITaskData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export namespace ChatRequest {
|
||||
|
@ -16,6 +19,15 @@ export namespace ChatRequest {
|
|||
|
||||
export interface WorkflowContext {
|
||||
executionSchema?: NodeExecutionSchema[];
|
||||
currentWorkflow?: IWorkflowDb;
|
||||
executionData?: IRunExecutionData['resultData'];
|
||||
}
|
||||
|
||||
export interface ExecutionResultData {
|
||||
error?: ExecutionError;
|
||||
runData: Record<string, Array<Omit<ITaskData, 'data'>>>;
|
||||
lastNodeExecuted?: string;
|
||||
metadata?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ErrorContext {
|
||||
|
@ -47,6 +59,7 @@ export namespace ChatRequest {
|
|||
firstName: string;
|
||||
};
|
||||
context?: UserContext;
|
||||
workflowContext?: WorkflowContext;
|
||||
question: string;
|
||||
}
|
||||
|
||||
|
@ -78,6 +91,7 @@ export namespace ChatRequest {
|
|||
text: string;
|
||||
quickReplyType?: string;
|
||||
context?: UserContext;
|
||||
workflowContext?: WorkflowContext;
|
||||
}
|
||||
|
||||
export interface UserContext {
|
||||
|
@ -98,6 +112,8 @@ export namespace ChatRequest {
|
|||
};
|
||||
}
|
||||
|
||||
export type AssistantContext = UserContext & WorkflowContext;
|
||||
|
||||
export type RequestPayload =
|
||||
| {
|
||||
payload: InitErrorHelper | InitSupportChat | InitCredHelp;
|
||||
|
|
Loading…
Reference in a new issue