mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 05:17:28 -08:00
feat(editor): Chat Trigger tweaks (#9618)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
parent
42ceec6879
commit
5322802992
|
@ -52,7 +52,7 @@ const markdownOptions = {
|
|||
},
|
||||
};
|
||||
|
||||
const messageComponents = options.messageComponents ?? {};
|
||||
const messageComponents = options?.messageComponents ?? {};
|
||||
</script>
|
||||
<template>
|
||||
<div class="chat-message" :class="classes">
|
||||
|
|
|
@ -23,7 +23,7 @@ export class ChatTrigger implements INodeType {
|
|||
version: 1,
|
||||
description: 'Runs the workflow when an n8n generated webchat is submitted',
|
||||
defaults: {
|
||||
name: 'Chat Trigger',
|
||||
name: 'When chat message received',
|
||||
},
|
||||
codex: {
|
||||
categories: ['Core Nodes'],
|
||||
|
|
|
@ -18,7 +18,7 @@ export class ManualChatTrigger implements INodeType {
|
|||
maxNodes: 1,
|
||||
hidden: true,
|
||||
defaults: {
|
||||
name: 'On new manual Chat Message',
|
||||
name: 'When chat message received',
|
||||
color: '#909298',
|
||||
},
|
||||
codex: {
|
||||
|
|
|
@ -76,7 +76,7 @@ export const versionDescription: INodeTypeDescription = {
|
|||
name: 'OpenAI',
|
||||
},
|
||||
codex: {
|
||||
alias: ['LangChain', 'ChatGPT', 'DallE'],
|
||||
alias: ['LangChain', 'ChatGPT', 'DallE', 'whisper', 'audio', 'transcribe', 'tts', 'assistant'],
|
||||
categories: ['AI'],
|
||||
subcategories: {
|
||||
AI: ['Agents', 'Miscellaneous', 'Root Nodes'],
|
||||
|
|
|
@ -181,7 +181,7 @@ export class ActiveWorkflows {
|
|||
const cronTimeParts = cronTime.split(' ');
|
||||
if (cronTimeParts.length > 0 && cronTimeParts[0].includes('*')) {
|
||||
throw new ApplicationError(
|
||||
'The polling interval is too short. It has to be at least a minute!',
|
||||
'The polling interval is too short. It has to be at least a minute.',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ export abstract class DirectoryLoader {
|
|||
|
||||
if (currentVersionNode.hasOwnProperty('executeSingle')) {
|
||||
throw new ApplicationError(
|
||||
'"executeSingle" has been removed. Please update the code of this node to use "execute" instead!',
|
||||
'"executeSingle" has been removed. Please update the code of this node to use "execute" instead.',
|
||||
{ extra: { nodeName: `${this.packageName}.${nodeName}` } },
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1547,11 +1547,11 @@ export async function requestOAuth1(
|
|||
const credentials = await this.getCredentials(credentialsType);
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new ApplicationError('No credentials were returned!');
|
||||
throw new ApplicationError('No credentials were returned');
|
||||
}
|
||||
|
||||
if (credentials.oauthTokenData === undefined) {
|
||||
throw new ApplicationError('OAuth credentials not connected!');
|
||||
throw new ApplicationError('OAuth credentials not connected');
|
||||
}
|
||||
|
||||
const oauth = new clientOAuth1({
|
||||
|
@ -1647,7 +1647,7 @@ export async function httpRequestWithAuthentication(
|
|||
if (credentialsDecrypted === undefined) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Node "${node.name}" does not have any credentials of type "${credentialsType}" set!`,
|
||||
`Node "${node.name}" does not have any credentials of type "${credentialsType}" set`,
|
||||
{ level: 'warning' },
|
||||
);
|
||||
}
|
||||
|
@ -1844,7 +1844,7 @@ export async function requestWithAuthentication(
|
|||
if (credentialsDecrypted === undefined) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Node "${node.name}" does not have any credentials of type "${credentialsType}" set!`,
|
||||
`Node "${node.name}" does not have any credentials of type "${credentialsType}" set`,
|
||||
{ level: 'warning' },
|
||||
);
|
||||
}
|
||||
|
@ -1990,7 +1990,7 @@ export async function getCredentials(
|
|||
if (nodeType === undefined) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Node type "${node.type}" is not known so can not get credentials!`,
|
||||
`Node type "${node.type}" is not known so can not get credentials`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2003,7 +2003,7 @@ export async function getCredentials(
|
|||
if (nodeType.description.credentials === undefined) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Node type "${node.type}" does not have any credentials defined!`,
|
||||
`Node type "${node.type}" does not have any credentials defined`,
|
||||
{ level: 'warning' },
|
||||
);
|
||||
}
|
||||
|
@ -2014,7 +2014,7 @@ export async function getCredentials(
|
|||
if (nodeCredentialDescription === undefined) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Node type "${node.type}" does not have any credentials of type "${type}" defined!`,
|
||||
`Node type "${node.type}" does not have any credentials of type "${type}" defined`,
|
||||
{ level: 'warning' },
|
||||
);
|
||||
}
|
||||
|
@ -2039,16 +2039,14 @@ export async function getCredentials(
|
|||
if (nodeCredentialDescription?.required === true) {
|
||||
// Credentials are required so error
|
||||
if (!node.credentials) {
|
||||
throw new NodeOperationError(node, 'Node does not have any credentials set!', {
|
||||
throw new NodeOperationError(node, 'Node does not have any credentials set', {
|
||||
level: 'warning',
|
||||
});
|
||||
}
|
||||
if (!node.credentials[type]) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Node does not have any credentials set for "${type}"!`,
|
||||
{ level: 'warning' },
|
||||
);
|
||||
throw new NodeOperationError(node, `Node does not have any credentials set for "${type}"`, {
|
||||
level: 'warning',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Credentials are not required
|
||||
|
@ -2782,7 +2780,7 @@ async function getInputConnectionData(
|
|||
const nodes = await Promise.all(constParentNodes);
|
||||
|
||||
if (inputConfiguration.required && nodes.length === 0) {
|
||||
throw new NodeOperationError(node, `A ${inputName} processor node must be connected!`);
|
||||
throw new NodeOperationError(node, `A ${inputName} processor node must be connected`);
|
||||
}
|
||||
if (
|
||||
inputConfiguration.maxConnections !== undefined &&
|
||||
|
@ -2790,7 +2788,7 @@ async function getInputConnectionData(
|
|||
) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Only ${inputConfiguration.maxConnections} ${inputName} processor nodes are/is allowed to be connected!`,
|
||||
`Only ${inputConfiguration.maxConnections} ${inputName} processor nodes are/is allowed to be connected`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3335,12 +3333,12 @@ export function getExecutePollFunctions(
|
|||
...getCommonWorkflowFunctions(workflow, node, additionalData),
|
||||
__emit: (): void => {
|
||||
throw new ApplicationError(
|
||||
'Overwrite NodeExecuteFunctions.getExecutePollFunctions.__emit function!',
|
||||
'Overwrite NodeExecuteFunctions.getExecutePollFunctions.__emit function',
|
||||
);
|
||||
},
|
||||
__emitError() {
|
||||
throw new ApplicationError(
|
||||
'Overwrite NodeExecuteFunctions.getExecutePollFunctions.__emitError function!',
|
||||
'Overwrite NodeExecuteFunctions.getExecutePollFunctions.__emitError function',
|
||||
);
|
||||
},
|
||||
getMode: () => mode,
|
||||
|
@ -3398,12 +3396,12 @@ export function getExecuteTriggerFunctions(
|
|||
...getCommonWorkflowFunctions(workflow, node, additionalData),
|
||||
emit: (): void => {
|
||||
throw new ApplicationError(
|
||||
'Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!',
|
||||
'Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function',
|
||||
);
|
||||
},
|
||||
emitError: (): void => {
|
||||
throw new ApplicationError(
|
||||
'Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!',
|
||||
'Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function',
|
||||
);
|
||||
},
|
||||
getMode: () => mode,
|
||||
|
|
|
@ -1707,7 +1707,7 @@ export class WorkflowExecute {
|
|||
return await this.processSuccessExecution(
|
||||
startedAt,
|
||||
workflow,
|
||||
new WorkflowOperationError('Workflow has been canceled or timed out!'),
|
||||
new WorkflowOperationError('Workflow has been canceled or timed out'),
|
||||
closeFunction,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -156,6 +156,7 @@ import { useRouter } from 'vue-router';
|
|||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useRunWorkflow } from '@/composables/useRunWorkflow';
|
||||
import { usePinnedData } from '@/composables/usePinnedData';
|
||||
import { isEmpty } from '@/utils/typesUtils';
|
||||
|
||||
const RunDataAi = defineAsyncComponent(
|
||||
async () => await import('@/components/RunDataAi/RunDataAi.vue'),
|
||||
|
@ -495,7 +496,9 @@ export default defineComponent({
|
|||
this.waitForExecution(response.executionId);
|
||||
},
|
||||
extractResponseMessage(responseData?: IDataObject) {
|
||||
if (!responseData) return '<NO RESPONSE FOUND>';
|
||||
if (!responseData || isEmpty(responseData)) {
|
||||
return this.$locale.baseText('chat.window.chat.response.empty');
|
||||
}
|
||||
|
||||
// Paths where the response message might be located
|
||||
const paths = ['output', 'text', 'response.text'];
|
||||
|
|
|
@ -170,6 +170,7 @@
|
|||
"chat.window.chat.unpinAndExecute.title": "Unpin chat output data?",
|
||||
"chat.window.chat.unpinAndExecute.confirm": "Unpin and send",
|
||||
"chat.window.chat.unpinAndExecute.cancel": "Cancel",
|
||||
"chat.window.chat.response.empty": "[No response. Make sure the last executed node outputs the content to display here]",
|
||||
"chatEmbed.infoTip.description": "Add chat to external applications using the n8n chat package.",
|
||||
"chatEmbed.infoTip.link": "More info",
|
||||
"chatEmbed.title": "Embed Chat in your website",
|
||||
|
|
|
@ -725,12 +725,23 @@ export default defineComponent({
|
|||
);
|
||||
},
|
||||
isManualChatOnly(): boolean {
|
||||
return this.containsChatNodes && this.triggerNodes.length === 1;
|
||||
if (!this.canvasChatNode) return false;
|
||||
|
||||
return this.containsChatNodes && this.triggerNodes.length === 1 && !this.pinnedChatNodeData;
|
||||
},
|
||||
canvasChatNode() {
|
||||
return this.nodes.find((node) => node.type === CHAT_TRIGGER_NODE_TYPE);
|
||||
},
|
||||
pinnedChatNodeData() {
|
||||
if (!this.canvasChatNode) return null;
|
||||
|
||||
return this.workflowsStore.pinDataByNodeName(this.canvasChatNode.name);
|
||||
},
|
||||
isExecutionDisabled(): boolean {
|
||||
if (
|
||||
this.containsChatNodes &&
|
||||
this.triggerNodes.every((node) => node.disabled || node.type === CHAT_TRIGGER_NODE_TYPE)
|
||||
this.triggerNodes.every((node) => node.disabled || node.type === CHAT_TRIGGER_NODE_TYPE) &&
|
||||
!this.pinnedChatNodeData
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue