Merge branch 'master' into node-1608-credential-parameters-tech-debt-project

This commit is contained in:
Elias Meire 2024-09-25 12:28:49 +02:00
commit 269c6e70ef
No known key found for this signature in database
18 changed files with 1535 additions and 1251 deletions

View file

@ -85,7 +85,6 @@ function getInputs(
'@n8n/n8n-nodes-langchain.lmChatGroq', '@n8n/n8n-nodes-langchain.lmChatGroq',
'@n8n/n8n-nodes-langchain.lmChatOllama', '@n8n/n8n-nodes-langchain.lmChatOllama',
'@n8n/n8n-nodes-langchain.lmChatOpenAi', '@n8n/n8n-nodes-langchain.lmChatOpenAi',
'@n8n/n8n-nodes-langchain.lmChatGooglePalm',
'@n8n/n8n-nodes-langchain.lmChatGoogleGemini', '@n8n/n8n-nodes-langchain.lmChatGoogleGemini',
'@n8n/n8n-nodes-langchain.lmChatGoogleVertex', '@n8n/n8n-nodes-langchain.lmChatGoogleVertex',
'@n8n/n8n-nodes-langchain.lmChatMistralCloud', '@n8n/n8n-nodes-langchain.lmChatMistralCloud',
@ -111,11 +110,13 @@ function getInputs(
nodes: [ nodes: [
'@n8n/n8n-nodes-langchain.lmChatAnthropic', '@n8n/n8n-nodes-langchain.lmChatAnthropic',
'@n8n/n8n-nodes-langchain.lmChatAzureOpenAi', '@n8n/n8n-nodes-langchain.lmChatAzureOpenAi',
'@n8n/n8n-nodes-langchain.lmChatAwsBedrock',
'@n8n/n8n-nodes-langchain.lmChatMistralCloud', '@n8n/n8n-nodes-langchain.lmChatMistralCloud',
'@n8n/n8n-nodes-langchain.lmChatOllama', '@n8n/n8n-nodes-langchain.lmChatOllama',
'@n8n/n8n-nodes-langchain.lmChatOpenAi', '@n8n/n8n-nodes-langchain.lmChatOpenAi',
'@n8n/n8n-nodes-langchain.lmChatGroq', '@n8n/n8n-nodes-langchain.lmChatGroq',
'@n8n/n8n-nodes-langchain.lmChatGoogleVertex', '@n8n/n8n-nodes-langchain.lmChatGoogleVertex',
'@n8n/n8n-nodes-langchain.lmChatGoogleGemini',
], ],
}, },
}, },

View file

@ -1,4 +1,5 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */ /* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import { BedrockEmbeddings } from '@langchain/aws';
import { import {
NodeConnectionType, NodeConnectionType,
type IExecuteFunctions, type IExecuteFunctions,
@ -6,7 +7,6 @@ import {
type INodeTypeDescription, type INodeTypeDescription,
type SupplyData, type SupplyData,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { BedrockEmbeddings } from '@langchain/community/embeddings/bedrock';
import { logWrapper } from '../../../utils/logWrapper'; import { logWrapper } from '../../../utils/logWrapper';
import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields';

View file

@ -1,135 +0,0 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import {
NodeConnectionType,
type IExecuteFunctions,
type INodeType,
type INodeTypeDescription,
type SupplyData,
} from 'n8n-workflow';
import { GooglePaLMEmbeddings } from '@langchain/community/embeddings/googlepalm';
import { logWrapper } from '../../../utils/logWrapper';
import { getConnectionHintNoticeField } from '../../../utils/sharedFields';
export class EmbeddingsGooglePalm implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Google PaLM',
name: 'embeddingsGooglePalm',
icon: 'file:google.svg',
group: ['transform'],
version: 1,
description: 'Use Google PaLM Embeddings',
defaults: {
name: 'Embeddings Google PaLM',
},
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '={{ $credentials.host }}',
},
credentials: [
{
name: 'googlePalmApi',
required: true,
},
],
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingsgooglepalm/',
},
],
},
},
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
inputs: [],
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
outputs: [NodeConnectionType.AiEmbedding],
outputNames: ['Embeddings'],
properties: [
getConnectionHintNoticeField([NodeConnectionType.AiVectorStore]),
{
displayName:
'Each model is using different dimensional density for embeddings. Please make sure to use the same dimensionality for your vector store. The default model is using 768-dimensional embeddings.',
name: 'notice',
type: 'notice',
default: '',
},
{
displayName: 'Model',
name: 'modelName',
type: 'options',
description:
'The model which will generate the embeddings. <a href="https://developers.generativeai.google/api/rest/generativelanguage/models/list">Learn more</a>.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/v1beta3/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'models',
},
},
{
type: 'filter',
properties: {
pass: "={{ $responseItem.name.startsWith('models/embedding') }}",
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.name}}',
value: '={{$responseItem.name}}',
description: '={{$responseItem.description}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: {
type: 'body',
property: 'model',
},
},
default: 'models/embedding-gecko-001',
},
],
};
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
this.logger.debug('Supply data for embeddings Google PaLM');
const modelName = this.getNodeParameter(
'modelName',
itemIndex,
'models/embedding-gecko-001',
) as string;
const credentials = await this.getCredentials('googlePalmApi');
const embeddings = new GooglePaLMEmbeddings({
apiKey: credentials.apiKey as string,
modelName,
});
return {
response: logWrapper(embeddings, this),
};
}
}

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48"><defs><path id="a" d="M44.5 20H24v8.5h11.8C34.7 33.9 30.1 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4"/></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"/></clipPath><path fill="#FBBC05" d="M0 37V11l17 13z" clip-path="url(#b)"/><path fill="#EA4335" d="m0 11 17 13 7-6.1L48 14V0H0z" clip-path="url(#b)"/><path fill="#34A853" d="m0 37 30-23 7.9 1L48 0v48H0z" clip-path="url(#b)"/><path fill="#4285F4" d="M48 48 17 24l-4-3 35-10z" clip-path="url(#b)"/></svg>

Before

Width:  |  Height:  |  Size: 687 B

View file

@ -1,4 +1,5 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */ /* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import { ChatBedrockConverse } from '@langchain/aws';
import { import {
NodeConnectionType, NodeConnectionType,
type IExecuteFunctions, type IExecuteFunctions,
@ -6,13 +7,8 @@ import {
type INodeTypeDescription, type INodeTypeDescription,
type SupplyData, type SupplyData,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { BedrockChat } from '@langchain/community/chat_models/bedrock';
import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields';
// Dependencies needed underneath the hood. We add them
// here only to track where what dependency is used
import '@aws-sdk/credential-provider-node';
import '@aws-sdk/client-bedrock-runtime';
import '@aws-sdk/client-sso-oidc';
import { N8nLlmTracing } from '../N8nLlmTracing'; import { N8nLlmTracing } from '../N8nLlmTracing';
export class LmChatAwsBedrock implements INodeType { export class LmChatAwsBedrock implements INodeType {
@ -144,7 +140,7 @@ export class LmChatAwsBedrock implements INodeType {
maxTokensToSample: number; maxTokensToSample: number;
}; };
const model = new BedrockChat({ const model = new ChatBedrockConverse({
region: credentials.region as string, region: credentials.region as string,
model: modelName, model: modelName,
temperature: options.temperature, temperature: options.temperature,

View file

@ -1,173 +0,0 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import {
NodeConnectionType,
type IExecuteFunctions,
type INodeType,
type INodeTypeDescription,
type SupplyData,
} from 'n8n-workflow';
import { ChatGooglePaLM } from '@langchain/community/chat_models/googlepalm';
import { N8nLlmTracing } from '../N8nLlmTracing';
export class LmChatGooglePalm implements INodeType {
description: INodeTypeDescription = {
displayName: 'Google PaLM Chat Model',
// eslint-disable-next-line n8n-nodes-base/node-class-description-name-miscased
name: 'lmChatGooglePalm',
icon: 'file:google.svg',
hidden: true,
group: ['transform'],
version: 1,
description: 'Chat Model Google PaLM',
defaults: {
name: 'Google PaLM Chat Model',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Language Models', 'Root Nodes'],
'Language Models': ['Chat Models (Recommended)'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatgooglepalm/',
},
],
},
},
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
inputs: [],
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
outputs: [NodeConnectionType.AiLanguageModel],
outputNames: ['Model'],
credentials: [
{
name: 'googlePalmApi',
required: true,
},
],
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '={{ $credentials.host }}',
},
properties: [
{
displayName:
"Google PaLM API is <a href='https://ai.google.dev/palm_docs/deprecation' target='_blank'>deprecated</a>. Please use Google Vertex or Google Gemini nodes instead.",
name: 'deprecated',
type: 'notice',
default: '',
},
{
displayName: 'Model',
name: 'modelName',
type: 'options',
description:
'The model which will generate the completion. <a href="https://developers.generativeai.google/api/rest/generativelanguage/models/list">Learn more</a>.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/v1beta3/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'models',
},
},
{
type: 'filter',
properties: {
pass: "={{ $responseItem.name.startsWith('models/chat') }}",
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.name}}',
value: '={{$responseItem.name}}',
description: '={{$responseItem.description}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: {
type: 'body',
property: 'model',
},
},
default: 'models/chat-bison-001',
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
options: [
{
displayName: 'Sampling Temperature',
name: 'temperature',
default: 0.7,
typeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },
description:
'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',
type: 'number',
},
{
displayName: 'Top K',
name: 'topK',
default: 40,
typeOptions: { maxValue: 1, minValue: -1, numberPrecision: 1 },
description:
'Used to remove "long tail" low probability responses. Defaults to -1, which disables it.',
type: 'number',
},
{
displayName: 'Top P',
name: 'topP',
default: 0.9,
typeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },
description:
'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',
type: 'number',
},
],
},
],
};
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = await this.getCredentials('googlePalmApi');
const modelName = this.getNodeParameter('modelName', itemIndex) as string;
const options = this.getNodeParameter('options', itemIndex, {}) as object;
const model = new ChatGooglePaLM({
apiKey: credentials.apiKey as string,
modelName,
...options,
callbacks: [new N8nLlmTracing(this)],
});
return {
response: model,
};
}
}

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48"><defs><path id="a" d="M44.5 20H24v8.5h11.8C34.7 33.9 30.1 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4"/></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"/></clipPath><path fill="#FBBC05" d="M0 37V11l17 13z" clip-path="url(#b)"/><path fill="#EA4335" d="m0 11 17 13 7-6.1L48 14V0H0z" clip-path="url(#b)"/><path fill="#34A853" d="m0 37 30-23 7.9 1L48 0v48H0z" clip-path="url(#b)"/><path fill="#4285F4" d="M48 48 17 24l-4-3 35-10z" clip-path="url(#b)"/></svg>

Before

Width:  |  Height:  |  Size: 687 B

View file

@ -1,180 +0,0 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import {
NodeConnectionType,
type IExecuteFunctions,
type INodeType,
type INodeTypeDescription,
type SupplyData,
} from 'n8n-workflow';
import { GooglePaLM } from '@langchain/community/llms/googlepalm';
import { N8nLlmTracing } from '../N8nLlmTracing';
export class LmGooglePalm implements INodeType {
description: INodeTypeDescription = {
displayName: 'Google PaLM Language Model',
// eslint-disable-next-line n8n-nodes-base/node-class-description-name-miscased
name: 'lmGooglePalm',
hidden: true,
icon: 'file:google.svg',
group: ['transform'],
version: 1,
description: 'Language Model Google PaLM',
defaults: {
name: 'Google PaLM Language Model',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Language Models', 'Root Nodes'],
'Language Models': ['Text Completion Models'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmgooglepalm/',
},
],
},
},
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
inputs: [],
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
outputs: [NodeConnectionType.AiLanguageModel],
outputNames: ['Model'],
credentials: [
{
name: 'googlePalmApi',
required: true,
},
],
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '={{ $credentials.host }}',
},
properties: [
{
displayName:
"Google PaLM API is <a href='https://ai.google.dev/palm_docs/deprecation' target='_blank'>deprecated</a>. Please use Google Vertex or Google Gemini nodes instead.",
name: 'deprecated',
type: 'notice',
default: '',
},
{
displayName: 'Model',
name: 'modelName',
type: 'options',
description:
'The model which will generate the completion. <a href="https://developers.generativeai.google/api/rest/generativelanguage/models/list">Learn more</a>.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/v1beta3/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'models',
},
},
{
type: 'filter',
properties: {
pass: "={{ $responseItem.name.startsWith('models/text') }}",
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.name}}',
value: '={{$responseItem.name}}',
description: '={{$responseItem.description}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: {
type: 'body',
property: 'model',
},
},
default: 'models/text-bison-001',
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
options: [
{
displayName: 'Maximum Number of Tokens',
name: 'maxOutputTokens',
default: 1024,
description: 'The maximum number of tokens to generate in the completion',
type: 'number',
},
{
displayName: 'Sampling Temperature',
name: 'temperature',
default: 0.7,
typeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },
description:
'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',
type: 'number',
},
{
displayName: 'Top K',
name: 'topK',
default: 40,
typeOptions: { maxValue: 1, minValue: -1, numberPrecision: 1 },
description:
'Used to remove "long tail" low probability responses. Defaults to -1, which disables it.',
type: 'number',
},
{
displayName: 'Top P',
name: 'topP',
default: 0.9,
typeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },
description:
'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',
type: 'number',
},
],
},
],
};
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = await this.getCredentials('googlePalmApi');
const modelName = this.getNodeParameter('modelName', itemIndex) as string;
const options = this.getNodeParameter('options', itemIndex, {}) as object;
const model = new GooglePaLM({
apiKey: credentials.apiKey as string,
modelName,
...options,
callbacks: [new N8nLlmTracing(this)],
});
return {
response: model,
};
}
}

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48"><defs><path id="a" d="M44.5 20H24v8.5h11.8C34.7 33.9 30.1 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4"/></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"/></clipPath><path fill="#FBBC05" d="M0 37V11l17 13z" clip-path="url(#b)"/><path fill="#EA4335" d="m0 11 17 13 7-6.1L48 14V0H0z" clip-path="url(#b)"/><path fill="#34A853" d="m0 37 30-23 7.9 1L48 0v48H0z" clip-path="url(#b)"/><path fill="#4285F4" d="M48 48 17 24l-4-3 35-10z" clip-path="url(#b)"/></svg>

Before

Width:  |  Height:  |  Size: 687 B

View file

@ -57,7 +57,6 @@
"dist/nodes/embeddings/EmbeddingsCohere/EmbeddingsCohere.node.js", "dist/nodes/embeddings/EmbeddingsCohere/EmbeddingsCohere.node.js",
"dist/nodes/embeddings/EmbeddingsAwsBedrock/EmbeddingsAwsBedrock.node.js", "dist/nodes/embeddings/EmbeddingsAwsBedrock/EmbeddingsAwsBedrock.node.js",
"dist/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.js", "dist/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.js",
"dist/nodes/embeddings/EmbeddingsGooglePalm/EmbeddingsGooglePalm.node.js",
"dist/nodes/embeddings/EmbeddingsGoogleGemini/EmbeddingsGoogleGemini.node.js", "dist/nodes/embeddings/EmbeddingsGoogleGemini/EmbeddingsGoogleGemini.node.js",
"dist/nodes/embeddings/EmbeddingsHuggingFaceInference/EmbeddingsHuggingFaceInference.node.js", "dist/nodes/embeddings/EmbeddingsHuggingFaceInference/EmbeddingsHuggingFaceInference.node.js",
"dist/nodes/embeddings/EmbeddingsMistralCloud/EmbeddingsMistralCloud.node.js", "dist/nodes/embeddings/EmbeddingsMistralCloud/EmbeddingsMistralCloud.node.js",
@ -65,9 +64,7 @@
"dist/nodes/embeddings/EmbeddingsOllama/EmbeddingsOllama.node.js", "dist/nodes/embeddings/EmbeddingsOllama/EmbeddingsOllama.node.js",
"dist/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.js", "dist/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.js",
"dist/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.js", "dist/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.js",
"dist/nodes/llms/LmGooglePalm/LmGooglePalm.node.js",
"dist/nodes/llms/LmChatAwsBedrock/LmChatAwsBedrock.node.js", "dist/nodes/llms/LmChatAwsBedrock/LmChatAwsBedrock.node.js",
"dist/nodes/llms/LmChatGooglePalm/LmChatGooglePalm.node.js",
"dist/nodes/llms/LmChatGoogleGemini/LmChatGoogleGemini.node.js", "dist/nodes/llms/LmChatGoogleGemini/LmChatGoogleGemini.node.js",
"dist/nodes/llms/LmChatGoogleVertex/LmChatGoogleVertex.node.js", "dist/nodes/llms/LmChatGoogleVertex/LmChatGoogleVertex.node.js",
"dist/nodes/llms/LmChatGroq/LmChatGroq.node.js", "dist/nodes/llms/LmChatGroq/LmChatGroq.node.js",
@ -131,40 +128,38 @@
"n8n-core": "workspace:*" "n8n-core": "workspace:*"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-bedrock-runtime": "3.645.0",
"@aws-sdk/client-sso-oidc": "^3.645.0",
"@aws-sdk/credential-provider-node": "3.645.0",
"@getzep/zep-cloud": "1.0.11", "@getzep/zep-cloud": "1.0.11",
"@getzep/zep-js": "0.9.0", "@getzep/zep-js": "0.9.0",
"@google-ai/generativelanguage": "2.5.0", "@google-ai/generativelanguage": "2.6.0",
"@google-cloud/resource-manager": "5.3.0", "@google-cloud/resource-manager": "5.3.0",
"@google/generative-ai": "0.17.1", "@google/generative-ai": "0.19.0",
"@huggingface/inference": "2.8.0", "@huggingface/inference": "2.8.0",
"@langchain/anthropic": "0.2.16", "@langchain/anthropic": "0.3.1",
"@langchain/cohere": "0.2.2", "@langchain/aws": "^0.1.0",
"@langchain/community": "0.2.32", "@langchain/cohere": "0.3.0",
"@langchain/community": "0.3.2",
"@langchain/core": "catalog:", "@langchain/core": "catalog:",
"@langchain/google-genai": "0.0.26", "@langchain/google-genai": "0.1.0",
"@langchain/google-vertexai": "0.0.27", "@langchain/google-vertexai": "0.1.0",
"@langchain/groq": "0.0.17", "@langchain/groq": "0.1.2",
"@langchain/mistralai": "0.0.29", "@langchain/mistralai": "0.1.1",
"@langchain/ollama": "0.0.4", "@langchain/ollama": "0.1.0",
"@langchain/openai": "0.2.10", "@langchain/openai": "0.3.0",
"@langchain/pinecone": "0.0.9", "@langchain/pinecone": "0.1.0",
"@langchain/qdrant": "0.0.5", "@langchain/qdrant": "0.1.0",
"@langchain/redis": "0.0.5", "@langchain/redis": "0.1.0",
"@langchain/textsplitters": "0.0.3", "@langchain/textsplitters": "0.1.0",
"@mozilla/readability": "^0.5.0", "@mozilla/readability": "^0.5.0",
"@n8n/typeorm": "0.3.20-10", "@n8n/typeorm": "0.3.20-10",
"@n8n/vm2": "3.9.25", "@n8n/vm2": "3.9.25",
"@pinecone-database/pinecone": "3.0.0", "@pinecone-database/pinecone": "3.0.3",
"@qdrant/js-client-rest": "1.9.0", "@qdrant/js-client-rest": "1.11.0",
"@supabase/supabase-js": "2.45.3", "@supabase/supabase-js": "2.45.4",
"@types/pg": "^8.11.6", "@types/pg": "^8.11.6",
"@xata.io/client": "0.28.4", "@xata.io/client": "0.30.0",
"basic-auth": "catalog:", "basic-auth": "catalog:",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
"cohere-ai": "7.13.0", "cohere-ai": "7.13.2",
"d3-dsv": "2.0.0", "d3-dsv": "2.0.0",
"epub2": "3.0.2", "epub2": "3.0.2",
"form-data": "catalog:", "form-data": "catalog:",
@ -172,12 +167,12 @@
"html-to-text": "9.0.5", "html-to-text": "9.0.5",
"jsdom": "^23.0.1", "jsdom": "^23.0.1",
"json-schema-to-zod": "2.1.0", "json-schema-to-zod": "2.1.0",
"langchain": "0.2.18", "langchain": "0.3.2",
"lodash": "catalog:", "lodash": "catalog:",
"mammoth": "1.7.2", "mammoth": "1.7.2",
"n8n-nodes-base": "workspace:*", "n8n-nodes-base": "workspace:*",
"n8n-workflow": "workspace:*", "n8n-workflow": "workspace:*",
"openai": "4.58.0", "openai": "4.63.0",
"pdf-parse": "1.1.1", "pdf-parse": "1.1.1",
"pg": "8.12.0", "pg": "8.12.0",
"redis": "4.6.12", "redis": "4.6.12",
@ -185,6 +180,6 @@
"temp": "0.9.4", "temp": "0.9.4",
"tmp-promise": "3.0.3", "tmp-promise": "3.0.3",
"zod": "catalog:", "zod": "catalog:",
"zod-to-json-schema": "3.23.2" "zod-to-json-schema": "3.23.3"
} }
} }

View file

@ -193,6 +193,7 @@ defineExpose({ inputRef });
:label="label" :label="label"
:tooltip-text="tooltipText" :tooltip-text="tooltipText"
:required="required && showRequiredAsterisk" :required="required && showRequiredAsterisk"
:size="labelSize"
> >
<template #content> <template #content>
{{ tooltipText }} {{ tooltipText }}
@ -210,6 +211,7 @@ defineExpose({ inputRef });
:label="label" :label="label"
:tooltip-text="tooltipText" :tooltip-text="tooltipText"
:required="required && showRequiredAsterisk" :required="required && showRequiredAsterisk"
:size="labelSize"
> >
<div :class="showErrors ? $style.errorInput : ''" @keydown.stop @keydown.enter="onEnter"> <div :class="showErrors ? $style.errorInput : ''" @keydown.stop @keydown.enter="onEnter">
<slot v-if="hasDefaultSlot" /> <slot v-if="hasDefaultSlot" />
@ -223,6 +225,7 @@ defineExpose({ inputRef });
:disabled="disabled" :disabled="disabled"
:name="name" :name="name"
:teleported="teleported" :teleported="teleported"
:size="tagSize"
@update:model-value="onUpdateModelValue" @update:model-value="onUpdateModelValue"
@focus="onFocus" @focus="onFocus"
@blur="onBlur" @blur="onBlur"
@ -246,6 +249,7 @@ defineExpose({ inputRef });
:maxlength="maxlength" :maxlength="maxlength"
:autocomplete="autocomplete" :autocomplete="autocomplete"
:disabled="disabled" :disabled="disabled"
:size="tagSize"
@update:model-value="onUpdateModelValue" @update:model-value="onUpdateModelValue"
@blur="onBlur" @blur="onBlur"
@focus="onFocus" @focus="onFocus"

View file

@ -5,7 +5,7 @@ import N8nIcon from '../N8nIcon';
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import N8nTooltip from '../N8nTooltip'; import N8nTooltip from '../N8nTooltip';
const SIZE = ['small', 'medium'] as const; const SIZE = ['small', 'medium', 'large'] as const;
interface InputLabelProps { interface InputLabelProps {
compact?: boolean; compact?: boolean;

View file

@ -313,7 +313,6 @@ onBeforeUnmount(() => {
@open:contextmenu="onOpenContextMenuFromNode" @open:contextmenu="onOpenContextMenuFromNode"
> >
<NodeIcon <NodeIcon
v-if="nodeTypeDescription"
:node-type="nodeTypeDescription" :node-type="nodeTypeDescription"
:size="nodeIconSize" :size="nodeIconSize"
:shrink="false" :shrink="false"

View file

@ -102,15 +102,6 @@ describe('useCanvasOperations', () => {
expect(result).toBe(expectedDescription); expect(result).toBe(expectedDescription);
}); });
it('should throw an error when node type does not exist', () => {
const type = 'nonexistentType';
const { requireNodeTypeDescription } = useCanvasOperations({ router });
expect(() => {
requireNodeTypeDescription(type);
}).toThrow();
});
it('should return node type description when only type is provided and it exists', () => { it('should return node type description when only type is provided and it exists', () => {
const nodeTypesStore = useNodeTypesStore(); const nodeTypesStore = useNodeTypesStore();
const type = 'testTypeWithoutVersion'; const type = 'testTypeWithoutVersion';
@ -123,6 +114,25 @@ describe('useCanvasOperations', () => {
expect(result).toBe(expectedDescription); expect(result).toBe(expectedDescription);
}); });
it("should return placeholder node type description if node type doesn't exist", () => {
const type = 'nonexistentType';
const { requireNodeTypeDescription } = useCanvasOperations({ router });
const result = requireNodeTypeDescription(type);
expect(result).toEqual({
name: type,
displayName: type,
description: '',
defaults: {},
group: [],
inputs: [],
outputs: [],
properties: [],
version: 1,
});
});
}); });
describe('addNode', () => { describe('addNode', () => {
@ -616,7 +626,6 @@ describe('useCanvasOperations', () => {
deleteNode(id, { trackHistory: true }); deleteNode(id, { trackHistory: true });
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeConnectionsById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id);
expect(historyStore.pushCommandToUndo).toHaveBeenCalledWith(new RemoveNodeCommand(node)); expect(historyStore.pushCommandToUndo).toHaveBeenCalledWith(new RemoveNodeCommand(node));
}); });
@ -644,7 +653,6 @@ describe('useCanvasOperations', () => {
deleteNode(id, { trackHistory: false }); deleteNode(id, { trackHistory: false });
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeConnectionsById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id);
expect(historyStore.pushCommandToUndo).not.toHaveBeenCalled(); expect(historyStore.pushCommandToUndo).not.toHaveBeenCalled();
}); });
@ -714,7 +722,6 @@ describe('useCanvasOperations', () => {
deleteNode(nodes[1].id); deleteNode(nodes[1].id);
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id);
expect(workflowsStore.removeNodeConnectionsById).toHaveBeenCalledWith(nodes[1].id);
expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(nodes[1].id); expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(nodes[1].id);
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id);
}); });
@ -1356,6 +1363,62 @@ describe('useCanvasOperations', () => {
}); });
}); });
describe('deleteConnectionsByNodeId', () => {
it('should delete all connections for a given node ID', () => {
const workflowsStore = mockedStore(useWorkflowsStore);
const { deleteConnectionsByNodeId } = useCanvasOperations({ router });
const node1 = createTestNode({ id: 'node1', name: 'Node 1' });
const node2 = createTestNode({ id: 'node2', name: 'Node 1' });
workflowsStore.workflow.connections = {
[node1.name]: {
[NodeConnectionType.Main]: [
[{ node: node2.name, type: NodeConnectionType.Main, index: 0 }],
],
},
node2: {
[NodeConnectionType.Main]: [
[{ node: node1.name, type: NodeConnectionType.Main, index: 0 }],
],
},
};
workflowsStore.getNodeById.mockReturnValue(node1);
workflowsStore.getNodeByName.mockReturnValueOnce(node1).mockReturnValueOnce(node2);
deleteConnectionsByNodeId(node1.id);
expect(workflowsStore.removeConnection).toHaveBeenCalledWith({
connection: [
{ node: node1.name, type: NodeConnectionType.Main, index: 0 },
{ node: node2.name, type: NodeConnectionType.Main, index: 0 },
],
});
expect(workflowsStore.removeConnection).toHaveBeenCalledWith({
connection: [
{ node: node2.name, type: NodeConnectionType.Main, index: 0 },
{ node: node1.name, type: NodeConnectionType.Main, index: 0 },
],
});
expect(workflowsStore.workflow.connections[node1.name]).toBeUndefined();
});
it('should not delete connections if node ID does not exist', () => {
const workflowsStore = mockedStore(useWorkflowsStore);
const { deleteConnectionsByNodeId } = useCanvasOperations({ router });
const nodeId = 'nonexistent';
workflowsStore.getNodeById.mockReturnValue(undefined);
deleteConnectionsByNodeId(nodeId);
expect(workflowsStore.removeConnection).not.toHaveBeenCalled();
});
});
describe('duplicateNodes', () => { describe('duplicateNodes', () => {
it('should duplicate nodes', async () => { it('should duplicate nodes', async () => {
const workflowsStore = mockedStore(useWorkflowsStore); const workflowsStore = mockedStore(useWorkflowsStore);

View file

@ -236,7 +236,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
await renameNode(currentName, previousName); await renameNode(currentName, previousName);
} }
function connectAdjacentNodes(id: string) { function connectAdjacentNodes(id: string, { trackHistory = false } = {}) {
const node = workflowsStore.getNodeById(id); const node = workflowsStore.getNodeById(id);
if (!node) { if (!node) {
@ -262,6 +262,23 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
if (!outgoingNodeId) continue; if (!outgoingNodeId) continue;
if (trackHistory) {
historyStore.pushCommandToUndo(
new AddConnectionCommand([
{
node: incomingConnection.node,
type,
index: 0,
},
{
node: outgoingConnection.node,
type,
index: 0,
},
]),
);
}
createConnection({ createConnection({
source: incomingNodeId, source: incomingNodeId,
sourceHandle: createCanvasConnectionHandleString({ sourceHandle: createCanvasConnectionHandleString({
@ -289,8 +306,13 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
historyStore.startRecordingUndo(); historyStore.startRecordingUndo();
} }
connectAdjacentNodes(id); if (uiStore.lastInteractedWithNodeId === id) {
workflowsStore.removeNodeConnectionsById(id); uiStore.lastInteractedWithNodeId = null;
}
connectAdjacentNodes(id, { trackHistory });
deleteConnectionsByNodeId(id, { trackHistory, trackBulk: false });
workflowsStore.removeNodeExecutionDataById(id); workflowsStore.removeNodeExecutionDataById(id);
workflowsStore.removeNodeById(id); workflowsStore.removeNodeById(id);
@ -423,16 +445,23 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
historyStore.stopRecordingUndo(); historyStore.stopRecordingUndo();
} }
function requireNodeTypeDescription(type: INodeUi['type'], version?: INodeUi['typeVersion']) { function requireNodeTypeDescription(
const nodeTypeDescription = nodeTypesStore.getNodeType(type, version); type: INodeUi['type'],
if (!nodeTypeDescription) { version?: INodeUi['typeVersion'],
throw new Error( ): INodeTypeDescription {
i18n.baseText('nodeView.showMessage.addNodeButton.message', { return (
interpolate: { nodeTypeName: type }, nodeTypesStore.getNodeType(type, version) ?? {
}), properties: [],
); displayName: type,
} name: type,
return nodeTypeDescription; group: [],
description: '',
version: version ?? 1,
defaults: {},
inputs: [],
outputs: [],
}
);
} }
async function addNodes( async function addNodes(
@ -1135,6 +1164,72 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
deleteConnection(mapLegacyConnectionToCanvasConnection(sourceNode, targetNode, connection)); deleteConnection(mapLegacyConnectionToCanvasConnection(sourceNode, targetNode, connection));
} }
function deleteConnectionsByNodeId(
targetNodeId: string,
{ trackHistory = false, trackBulk = true } = {},
) {
const targetNode = workflowsStore.getNodeById(targetNodeId);
if (!targetNode) {
return;
}
if (trackHistory && trackBulk) {
historyStore.startRecordingUndo();
}
const connections = workflowsStore.workflow.connections;
for (const nodeName of Object.keys(connections)) {
const node = workflowsStore.getNodeByName(nodeName);
if (!node) {
continue;
}
for (const type of Object.keys(connections[nodeName])) {
for (const index of Object.keys(connections[nodeName][type])) {
for (const connectionIndex of Object.keys(
connections[nodeName][type][parseInt(index, 10)],
)) {
const connectionData =
connections[nodeName][type][parseInt(index, 10)][parseInt(connectionIndex, 10)];
if (!connectionData) {
continue;
}
const connectionDataNode = workflowsStore.getNodeByName(connectionData.node);
if (
connectionDataNode &&
(connectionDataNode.id === targetNode.id || node.name === targetNode.name)
) {
deleteConnection(
{
source: node.id,
sourceHandle: createCanvasConnectionHandleString({
mode: CanvasConnectionMode.Output,
type: type as NodeConnectionType,
index: parseInt(index, 10),
}),
target: connectionDataNode.id,
targetHandle: createCanvasConnectionHandleString({
mode: CanvasConnectionMode.Input,
type: connectionData.type as NodeConnectionType,
index: connectionData.index,
}),
},
{ trackHistory, trackBulk: false },
);
}
}
}
}
}
delete workflowsStore.workflow.connections[targetNode.name];
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}
function deleteConnection( function deleteConnection(
connection: Connection, connection: Connection,
{ trackHistory = false, trackBulk = true } = {}, { trackHistory = false, trackBulk = true } = {},
@ -1777,6 +1872,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
revertCreateConnection, revertCreateConnection,
deleteConnection, deleteConnection,
revertDeleteConnection, revertDeleteConnection,
deleteConnectionsByNodeId,
isConnectionAllowed, isConnectionAllowed,
importWorkflowData, importWorkflowData,
fetchWorkflowDataFromUrl, fetchWorkflowDataFromUrl,

View file

@ -1525,8 +1525,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
} }
removeNode(node); removeNode(node);
// @TODO When removing node connected between two nodes, create a connection between them
} }
function removeNodeConnectionsById(nodeId: string): void { function removeNodeConnectionsById(nodeId: string): void {

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,7 @@ catalog:
xml2js: 0.6.2 xml2js: 0.6.2
xss: 1.0.15 xss: 1.0.15
zod: 3.23.8 zod: 3.23.8
'@langchain/core': 0.2.31 '@langchain/core': 0.3.3
catalogs: catalogs:
frontend: frontend: