first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,146 @@
import type { BedrockRuntimeClientConfig } from '@aws-sdk/client-bedrock-runtime';
import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime';
import { BedrockEmbeddings } from '@langchain/aws';
import { NodeHttpHandler } from '@smithy/node-http-handler';
import { getNodeProxyAgent, logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
export class EmbeddingsAwsBedrock implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings AWS Bedrock',
name: 'embeddingsAwsBedrock',
icon: 'file:bedrock.svg',
credentials: [
{
name: 'aws',
required: true,
},
],
group: ['transform'],
version: 1,
description: 'Use Embeddings AWS Bedrock',
defaults: {
name: 'Embeddings AWS Bedrock',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingsawsbedrock/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '=https://bedrock.{{$credentials?.region ?? "eu-central-1"}}.amazonaws.com',
},
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.AiVectorStore]),
{
displayName: 'Model',
name: 'model',
type: 'options',
description:
'The model which will generate the completion. <a href="https://docs.aws.amazon.com/bedrock/latest/userguide/foundation-models.html">Learn more</a>.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/foundation-models?byInferenceType=ON_DEMAND&byOutputModality=EMBEDDING',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'modelSummaries',
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.modelName}}',
description: '={{$responseItem.modelArn}}',
value: '={{$responseItem.modelId}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: {
type: 'body',
property: 'model',
},
},
default: '',
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = await this.getCredentials<{
region: string;
secretAccessKey: string;
accessKeyId: string;
sessionToken: string;
}>('aws');
const modelName = this.getNodeParameter('model', itemIndex) as string;
const clientConfig: BedrockRuntimeClientConfig = {
region: credentials.region,
credentials: {
secretAccessKey: credentials.secretAccessKey,
accessKeyId: credentials.accessKeyId,
sessionToken: credentials.sessionToken,
},
};
const proxyAgent = getNodeProxyAgent();
if (proxyAgent) {
clientConfig.requestHandler = new NodeHttpHandler({
httpAgent: proxyAgent,
httpsAgent: proxyAgent,
});
}
const client = new BedrockRuntimeClient(clientConfig);
const embeddings = new BedrockEmbeddings({
client,
model: modelName,
maxRetries: 3,
region: credentials.region,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><defs><linearGradient id="a" x1="0%" x2="100%" y1="100%" y2="0%"><stop offset="0%" stop-color="#055F4E"/><stop offset="100%" stop-color="#56C0A7"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><path fill="url(#a)" d="M0 0h24v24H0z"/><path fill="#FFF" d="m12 18.14-2.426.809-.946-.631 1.03-.344-.316-.948-1.768.589L7 17.233V14.5a.5.5 0 0 0-.276-.447L5 13.19v-2.382l1.5-.75 1.5.75v1.69c0 .19.107.364.276.449l2 1 .448-.895L9 12.19v-1.382l1.724-.861A.5.5 0 0 0 11 9.5V8h-1v1.19l-1.5.75L7 9.19V6.769L8 6.1V8h1V5.435l.574-.383L12 5.86zM17.5 17a.5.5 0 1 1-.002 1 .5.5 0 0 1 .002-1m-1-11a.5.5 0 1 1-.002 1 .5.5 0 0 1 .002-1m2 6a.5.5 0 1 1-.002 1 .5.5 0 0 1 .002-1m-1.408 1c.207.58.757 1 1.408 1a1.501 1.501 0 0 0 0-3c-.651 0-1.201.42-1.408 1H13v-2h3.5a.5.5 0 0 0 .5-.5V7.908c.581-.207 1-.757 1-1.408 0-.827-.673-1.5-1.5-1.5S15 5.673 15 6.5c0 .65.419 1.2 1 1.408V9h-3V5.5a.5.5 0 0 0-.342-.474l-3-1a.5.5 0 0 0-.435.058l-3 2A.5.5 0 0 0 6 6.5v2.69l-1.724.863A.5.5 0 0 0 4 10.5v3c0 .19.107.363.276.448l1.724.86V17.5a.5.5 0 0 0 .223.416l3 2a.5.5 0 0 0 .435.058l3-1A.5.5 0 0 0 13 18.5V16h2.293l.853.854.013-.013c-.098.2-.159.422-.159.659 0 .827.673 1.5 1.5 1.5s1.5-.673 1.5-1.5-.673-1.5-1.5-1.5c-.238 0-.46.06-.659.16l.013-.013-1-1A.5.5 0 0 0 15.5 15H13v-2z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,169 @@
import { AzureOpenAIEmbeddings } from '@langchain/openai';
import { getProxyAgent, logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
export class EmbeddingsAzureOpenAi implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Azure OpenAI',
name: 'embeddingsAzureOpenAi',
icon: 'file:azure.svg',
credentials: [
{
name: 'azureOpenAiApi',
required: true,
},
],
group: ['transform'],
version: 1,
description: 'Use Embeddings Azure OpenAI',
defaults: {
name: 'Embeddings Azure OpenAI',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingsazureopenai/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.AiVectorStore]),
{
displayName: 'Model (Deployment) Name',
name: 'model',
type: 'string',
description: 'The name of the model(deployment) to use',
default: '',
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
options: [
{
displayName: 'Batch Size',
name: 'batchSize',
default: 512,
typeOptions: { maxValue: 2048 },
description: 'Maximum number of documents to send in each request',
type: 'number',
},
{
displayName: 'Strip New Lines',
name: 'stripNewLines',
default: true,
description: 'Whether to strip new lines from the input text',
type: 'boolean',
},
{
displayName: 'Timeout',
name: 'timeout',
default: -1,
description:
'Maximum amount of time a request is allowed to take in seconds. Set to -1 for no timeout.',
type: 'number',
},
{
displayName: 'Dimensions',
name: 'dimensions',
default: 1536,
description:
'The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models.',
type: 'options',
options: [
{
name: '256',
value: 256,
},
{
name: '512',
value: 512,
},
{
name: '1024',
value: 1024,
},
{
name: '1536',
value: 1536,
},
{
name: '3072',
value: 3072,
},
],
},
],
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
this.logger.debug('Supply data for embeddings');
const credentials = await this.getCredentials<{
apiKey: string;
resourceName: string;
apiVersion: string;
endpoint?: string;
}>('azureOpenAiApi');
const modelName = this.getNodeParameter('model', itemIndex) as string;
const options = this.getNodeParameter('options', itemIndex, {}) as {
batchSize?: number;
stripNewLines?: boolean;
timeout?: number;
dimensions?: number | undefined;
};
if (options.timeout === -1) {
options.timeout = undefined;
}
const embeddings = new AzureOpenAIEmbeddings({
azureOpenAIApiDeploymentName: modelName,
// instance name only needed to set base url
azureOpenAIApiInstanceName: !credentials.endpoint ? credentials.resourceName : undefined,
azureOpenAIApiKey: credentials.apiKey,
azureOpenAIApiVersion: credentials.apiVersion,
// azureOpenAIEndpoint and configuration.baseURL are both ignored here
// only setting azureOpenAIBasePath worked
azureOpenAIBasePath: credentials.endpoint
? `${credentials.endpoint}/openai/deployments`
: undefined,
configuration: {
fetchOptions: {
dispatcher: getProxyAgent(
credentials.endpoint ?? `https://${credentials.resourceName}.openai.azure.com`,
{},
),
},
},
...options,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="242" preserveAspectRatio="xMidYMid"><defs><linearGradient id="a" x1="58.972%" x2="37.191%" y1="7.411%" y2="103.762%"><stop offset="0%" stop-color="#114A8B"/><stop offset="100%" stop-color="#0669BC"/></linearGradient><linearGradient id="b" x1="59.719%" x2="52.691%" y1="52.313%" y2="54.864%"><stop offset="0%" stop-opacity=".3"/><stop offset="7.1%" stop-opacity=".2"/><stop offset="32.1%" stop-opacity=".1"/><stop offset="62.3%" stop-opacity=".05"/><stop offset="100%" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="37.279%" x2="62.473%" y1="4.6%" y2="99.979%"><stop offset="0%" stop-color="#3CCBF4"/><stop offset="100%" stop-color="#2892DF"/></linearGradient></defs><path fill="url(#a)" d="M85.343.003h75.753L82.457 233a12.08 12.08 0 0 1-11.442 8.216H12.06A12.06 12.06 0 0 1 .633 225.303L73.898 8.219A12.08 12.08 0 0 1 85.343 0z"/><path fill="#0078D4" d="M195.423 156.282H75.297a5.56 5.56 0 0 0-3.796 9.627l77.19 72.047a12.14 12.14 0 0 0 8.28 3.26h68.02z"/><path fill="url(#b)" d="M85.343.003a11.98 11.98 0 0 0-11.471 8.376L.723 225.105a12.045 12.045 0 0 0 11.37 16.112h60.475a12.93 12.93 0 0 0 9.921-8.437l14.588-42.991 52.105 48.6a12.33 12.33 0 0 0 7.757 2.828h67.766l-29.721-84.935-86.643.02L161.37.003z"/><path fill="url(#c)" d="M182.098 8.207A12.06 12.06 0 0 0 170.67.003H86.245c5.175 0 9.773 3.301 11.428 8.204L170.94 225.3a12.062 12.062 0 0 1-11.428 15.92h84.429a12.062 12.062 0 0 0 11.425-15.92z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,114 @@
import { CohereEmbeddings } from '@langchain/cohere';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
export class EmbeddingsCohere implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Cohere',
name: 'embeddingsCohere',
icon: { light: 'file:cohere.svg', dark: 'file:cohere.dark.svg' },
group: ['transform'],
version: 1,
description: 'Use Cohere Embeddings',
defaults: {
name: 'Embeddings Cohere',
},
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '={{ $credentials.host }}',
},
credentials: [
{
name: 'cohereApi',
required: true,
},
],
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingscohere/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.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://docs.cohere.com/docs/models">Learn more</a>.',
default: 'embed-english-v2.0',
options: [
{
name: 'Embed-English-Light-v2.0 (1024 Dimensions)',
value: 'embed-english-light-v2.0',
},
{
name: 'Embed-English-Light-v3.0 (384 Dimensions)',
value: 'embed-english-light-v3.0',
},
{
name: 'Embed-English-v2.0 (4096 Dimensions)',
value: 'embed-english-v2.0',
},
{
name: 'Embed-English-v3.0 (1024 Dimensions)',
value: 'embed-english-v3.0',
},
{
name: 'Embed-Multilingual-Light-v3.0 (384 Dimensions)',
value: 'embed-multilingual-light-v3.0',
},
{
name: 'Embed-Multilingual-v2.0 (768 Dimensions)',
value: 'embed-multilingual-v2.0',
},
{
name: 'Embed-Multilingual-v3.0 (1024 Dimensions)',
value: 'embed-multilingual-v3.0',
},
],
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
this.logger.debug('Supply data for embeddings Cohere');
const modelName = this.getNodeParameter('modelName', itemIndex, 'embed-english-v2.0') as string;
const credentials = await this.getCredentials<{ apiKey: string }>('cohereApi');
const embeddings = new CohereEmbeddings({
apiKey: credentials.apiKey,
model: modelName,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.96 23.84C14.0267 23.84 16.16 23.7867 19.1467 22.56C22.6133 21.12 29.44 18.56 34.4 15.8933C37.8667 14.0267 39.36 11.5733 39.36 8.26667C39.36 3.73333 35.68 0 31.0933 0H11.8933C5.33333 0 0 5.33333 0 11.8933C0 18.4533 5.01333 23.84 12.96 23.84Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.2134 31.9999C16.2134 28.7999 18.1334 25.8666 21.12 24.6399L27.1467 22.1333C33.28 19.6266 40 24.1066 40 30.7199C40 35.8399 35.84 39.9999 30.72 39.9999H24.16C19.7867 39.9999 16.2134 36.4266 16.2134 31.9999Z" fill="white"/>
<path d="M6.88 25.3867C3.09333 25.3867 0 28.4801 0 32.2667V33.1734C0 36.9067 3.09333 40.0001 6.88 40.0001C10.6667 40.0001 13.76 36.9067 13.76 33.1201V32.2134C13.7067 28.4801 10.6667 25.3867 6.88 25.3867Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 907 B

@@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.96 23.84C14.0267 23.84 16.16 23.7867 19.1467 22.56C22.6133 21.12 29.44 18.56 34.4 15.8933C37.8667 14.0267 39.36 11.5733 39.36 8.26667C39.36 3.73333 35.68 0 31.0933 0H11.8933C5.33333 0 0 5.33333 0 11.8933C0 18.4533 5.01333 23.84 12.96 23.84Z" fill="#39594D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.2134 31.9999C16.2134 28.7999 18.1334 25.8666 21.12 24.6399L27.1467 22.1333C33.28 19.6266 40 24.1066 40 30.7199C40 35.8399 35.84 39.9999 30.72 39.9999H24.16C19.7867 39.9999 16.2134 36.4266 16.2134 31.9999Z" fill="#D18EE2"/>
<path d="M6.88 25.3867C3.09333 25.3867 0 28.4801 0 32.2667V33.1734C0 36.9067 3.09333 40.0001 6.88 40.0001C10.6667 40.0001 13.76 36.9067 13.76 33.1201V32.2134C13.7067 28.4801 10.6667 25.3867 6.88 25.3867Z" fill="#FF7759"/>
</svg>

After

Width:  |  Height:  |  Size: 913 B

@@ -0,0 +1,135 @@
import { GoogleGenerativeAIEmbeddings } from '@langchain/google-genai';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
export class EmbeddingsGoogleGemini implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Google Gemini',
name: 'embeddingsGoogleGemini',
icon: 'file:google.svg',
group: ['transform'],
version: 1,
description: 'Use Google Gemini Embeddings',
defaults: {
name: 'Embeddings Google Gemini',
},
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.embeddingsgooglegemini/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.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: '/v1beta/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'models',
},
},
{
type: 'filter',
properties: {
pass: "={{ $responseItem.name.includes('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/text-embedding-004',
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
this.logger.debug('Supply data for embeddings Google Gemini');
const modelName = this.getNodeParameter(
'modelName',
itemIndex,
'models/text-embedding-004',
) as string;
const credentials = await this.getCredentials('googlePalmApi');
const embeddings = new GoogleGenerativeAIEmbeddings({
apiKey: credentials.apiKey as string,
baseUrl: credentials.host as string,
model: modelName,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 687 B

@@ -0,0 +1,158 @@
import { ProjectsClient } from '@google-cloud/resource-manager';
import { VertexAIEmbeddings } from '@langchain/google-vertexai';
import { formatPrivateKey } from 'n8n-nodes-base/dist/utils/utilities';
import { NodeConnectionTypes } from 'n8n-workflow';
import type {
ILoadOptionsFunctions,
INodeType,
INodeTypeDescription,
ISupplyDataFunctions,
SupplyData,
} from 'n8n-workflow';
import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
export class EmbeddingsGoogleVertex implements INodeType {
methods = {
listSearch: {
async gcpProjectsList(this: ILoadOptionsFunctions) {
const results: Array<{ name: string; value: string }> = [];
const credentials = await this.getCredentials('googleApi');
const privateKey = formatPrivateKey(credentials.privateKey as string);
const email = (credentials.email as string).trim();
const client = new ProjectsClient({
credentials: {
client_email: email,
private_key: privateKey,
},
});
const [projects] = await client.searchProjects();
for (const project of projects) {
if (project.projectId) {
results.push({
name: project.displayName ?? project.projectId,
value: project.projectId,
});
}
}
return { results };
},
},
};
description: INodeTypeDescription = {
displayName: 'Embeddings Google Vertex',
name: 'embeddingsGoogleVertex',
icon: 'file:google.svg',
group: ['transform'],
version: 1,
description: 'Use Google Vertex Embeddings',
defaults: {
name: 'Embeddings Google Vertex',
},
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '={{ $credentials.host }}',
},
credentials: [
{
name: 'googleApi',
required: true,
},
],
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingsgooglevertex/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.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. You can find available models <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api">here</a>.',
name: 'notice',
type: 'notice',
default: '',
},
{
displayName: 'Project ID',
name: 'projectId',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
description: 'Select or enter your Google Cloud project ID',
modes: [
{
displayName: 'From List',
name: 'list',
type: 'list',
typeOptions: {
searchListMethod: 'gcpProjectsList',
},
},
{
displayName: 'ID',
name: 'id',
type: 'string',
},
],
},
{
displayName: 'Model Name',
name: 'modelName',
type: 'string',
description:
'The model which will generate the embeddings. <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api">Learn more</a>.',
default: 'text-embedding-005',
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = await this.getCredentials('googleApi');
const privateKey = formatPrivateKey(credentials.privateKey as string);
const email = (credentials.email as string).trim();
const region = credentials.region as string;
const modelName = this.getNodeParameter('modelName', itemIndex) as string;
const projectId = this.getNodeParameter('projectId', itemIndex, '', {
extractValue: true,
}) as string;
const embeddings = new VertexAIEmbeddings({
authOptions: {
projectId,
credentials: {
client_email: email,
private_key: privateKey,
},
},
location: region,
model: modelName,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 687 B

@@ -0,0 +1,123 @@
import type { InferenceProviderOrPolicy } from '@huggingface/inference';
import { PROVIDERS_OR_POLICIES } from '@huggingface/inference';
import { HuggingFaceInferenceEmbeddings } from '@langchain/community/embeddings/hf';
import {
NodeConnectionTypes,
NodeOperationError,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
export class EmbeddingsHuggingFaceInference implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Hugging Face Inference',
name: 'embeddingsHuggingFaceInference',
icon: 'file:huggingface.svg',
group: ['transform'],
version: 1,
description: 'Use HuggingFace Inference Embeddings',
defaults: {
name: 'Embeddings HuggingFace Inference',
},
credentials: [
{
name: 'huggingFaceApi',
required: true,
},
],
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingshuggingfaceinference/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.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',
name: 'modelName',
type: 'string',
default: 'sentence-transformers/distilbert-base-nli-mean-tokens',
description: 'The model name to use from HuggingFace library',
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
options: [
{
displayName: 'Custom Inference Endpoint',
name: 'endpointUrl',
default: '',
description: 'Custom endpoint URL',
type: 'string',
},
{
displayName: 'Provider',
name: 'provider',
type: 'options',
options: PROVIDERS_OR_POLICIES.map((value) => ({ value, name: value })),
default: 'auto',
},
],
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
this.logger.debug('Supply data for embeddings HF Inference');
const model = this.getNodeParameter(
'modelName',
itemIndex,
'sentence-transformers/distilbert-base-nli-mean-tokens',
) as string;
const credentials = await this.getCredentials('huggingFaceApi');
const options = this.getNodeParameter('options', itemIndex, {}) as object;
if ('provider' in options && !isValidHFProviderOrPolicy(options.provider)) {
throw new NodeOperationError(this.getNode(), 'Unsupported provider');
}
const embeddings = new HuggingFaceInferenceEmbeddings({
apiKey: credentials.apiKey as string,
model,
...options,
});
return {
response: logWrapper(embeddings, this),
};
}
}
function isValidHFProviderOrPolicy(provider: unknown): provider is InferenceProviderOrPolicy {
return (
typeof provider === 'string' && (PROVIDERS_OR_POLICIES as readonly string[]).includes(provider)
);
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

@@ -0,0 +1,78 @@
import { OpenAIEmbeddings } from '@langchain/openai';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import type { LemonadeApiCredentialsType } from '../../../credentials/LemonadeApi.credentials';
import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
import { lemonadeDescription, lemonadeModel } from '../../llms/LMLemonade/description';
export class EmbeddingsLemonade implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Lemonade',
name: 'embeddingsLemonade',
icon: 'file:lemonade.svg',
group: ['transform'],
version: 1,
description: 'Use Lemonade Embeddings',
defaults: {
name: 'Embeddings Lemonade',
},
...lemonadeDescription,
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingslemonade/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
properties: [getConnectionHintNoticeField([NodeConnectionTypes.AiVectorStore]), lemonadeModel],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const modelName = this.getNodeParameter('model', itemIndex) as string;
const credentials = (await this.getCredentials('lemonadeApi')) as LemonadeApiCredentialsType;
// Ensure we have an API key for OpenAI client validation
const apiKey = credentials.apiKey || 'lemonade-placeholder-key';
// Build configuration object separately like official OpenAI nodes
const configuration: any = {
baseURL: credentials.baseUrl,
};
// Add custom headers if API key is provided
if (credentials.apiKey) {
configuration.defaultHeaders = {
Authorization: `Bearer ${credentials.apiKey}`,
};
}
const embeddings = new OpenAIEmbeddings({
apiKey,
model: modelName,
configuration,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1,53 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.03604 2.49169L2 5.00962C2.82591 7.34634 5.52523 8.53484 8.04325 7.52763L14.0865 5.00967C13.2606 2.66287 9.85018 1.17686 7.03604 2.49169Z" fill="url(#paint0_linear_18_30102)"/>
<g filter="url(#filter0_f_18_30102)">
<path d="M2.64575 5.26035L7.22767 2.96947C8.5803 2.39118 10.05 2.55851 11.2129 2.97773C12.2111 3.33758 12.9907 3.9608 13.418 4.75328L7.85198 7.0724C5.7348 7.91746 3.52324 7.0363 2.64575 5.26035Z" fill="url(#paint1_linear_18_30102)"/>
</g>
<g filter="url(#filter1_f_18_30102)">
<path d="M5.00464 4.17246L7.43032 2.76879C8.78294 2.1905 10.2527 2.35783 11.4155 2.77705C12.4137 3.13689 13.1933 3.76012 13.6206 4.5526C10.4511 2.99575 9.9351 2.43024 5.00464 4.17246Z" fill="url(#paint2_linear_18_30102)"/>
</g>
<path d="M14.9236 4.5997C9.51985 6.50701 6.6904 12.4499 8.59216 17.8694L9.84454 21.4282C11.2477 25.4172 14.8309 28.0107 18.7736 28.3363C19.5157 28.3945 20.2115 28.7201 20.7681 29.2202C21.5682 29.9413 22.7162 30.2087 23.7947 29.8249C24.8731 29.4412 25.6037 28.5108 25.7776 27.4525C25.8936 26.7082 26.2299 26.0336 26.7749 25.5103C29.6391 22.7656 30.8103 18.4974 29.4072 14.5084L28.1548 10.9496C26.2531 5.51849 20.3274 2.68077 14.9236 4.5997Z" fill="url(#paint3_radial_18_30102)"/>
<path d="M14.9236 4.5997C9.51985 6.50701 6.6904 12.4499 8.59216 17.8694L9.84454 21.4282C11.2477 25.4172 14.8309 28.0107 18.7736 28.3363C19.5157 28.3945 20.2115 28.7201 20.7681 29.2202C21.5682 29.9413 22.7162 30.2087 23.7947 29.8249C24.8731 29.4412 25.6037 28.5108 25.7776 27.4525C25.8936 26.7082 26.2299 26.0336 26.7749 25.5103C29.6391 22.7656 30.8103 18.4974 29.4072 14.5084L28.1548 10.9496C26.2531 5.51849 20.3274 2.68077 14.9236 4.5997Z" fill="url(#paint4_radial_18_30102)"/>
<path d="M14.9236 4.5997C9.51985 6.50701 6.6904 12.4499 8.59216 17.8694L9.84454 21.4282C11.2477 25.4172 14.8309 28.0107 18.7736 28.3363C19.5157 28.3945 20.2115 28.7201 20.7681 29.2202C21.5682 29.9413 22.7162 30.2087 23.7947 29.8249C24.8731 29.4412 25.6037 28.5108 25.7776 27.4525C25.8936 26.7082 26.2299 26.0336 26.7749 25.5103C29.6391 22.7656 30.8103 18.4974 29.4072 14.5084L28.1548 10.9496C26.2531 5.51849 20.3274 2.68077 14.9236 4.5997Z" fill="url(#paint5_radial_18_30102)"/>
<defs>
<filter id="filter0_f_18_30102" x="1.89035" y="1.84127" width="12.283" height="6.31025" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.377703" result="effect1_foregroundBlur_18_30102"/>
</filter>
<filter id="filter1_f_18_30102" x="4.24923" y="1.64059" width="10.1268" height="3.66743" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.377703" result="effect1_foregroundBlur_18_30102"/>
</filter>
<linearGradient id="paint0_linear_18_30102" x1="2" y1="5.00899" x2="14.0865" y2="5.00899" gradientUnits="userSpaceOnUse">
<stop stop-color="#80A338"/>
<stop offset="1" stop-color="#B3D745"/>
</linearGradient>
<linearGradient id="paint1_linear_18_30102" x1="1.99902" y1="5.02002" x2="14.0855" y2="5.02002" gradientUnits="userSpaceOnUse">
<stop stop-color="#95BD27"/>
<stop offset="1" stop-color="#BAE038"/>
</linearGradient>
<linearGradient id="paint2_linear_18_30102" x1="13.6206" y1="4.2397" x2="6.38307" y2="3.29833" gradientUnits="userSpaceOnUse">
<stop stop-color="#D1F56E" stop-opacity="0"/>
<stop offset="0.286062" stop-color="#D1F56E"/>
<stop offset="1" stop-color="#D1F56E" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint3_radial_18_30102" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(21.2025 10.5724) rotate(115.148) scale(17.6305 14.9181)">
<stop stop-color="#FFFB98"/>
<stop offset="0.505208" stop-color="#FFD84C"/>
<stop offset="1" stop-color="#E6B534"/>
</radialGradient>
<radialGradient id="paint4_radial_18_30102" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(14.6238 4.96834) rotate(69.3343) scale(26.7531 22.6372)">
<stop offset="0.521583" stop-color="#FFDE67" stop-opacity="0"/>
<stop offset="0.736095" stop-color="#FFA457" stop-opacity="0.2"/>
<stop offset="0.886173" stop-color="#D5676D" stop-opacity="0.75"/>
<stop offset="0.917885" stop-color="#E88257"/>
<stop offset="1" stop-color="#F49754"/>
</radialGradient>
<radialGradient id="paint5_radial_18_30102" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-10.5942 -28.473) rotate(56.1215) scale(51.3589 43.4576)">
<stop offset="0.707976" stop-color="#D5B638"/>
<stop offset="0.873737" stop-color="#D5B638" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

@@ -0,0 +1,155 @@
import type { MistralAIEmbeddingsParams } from '@langchain/mistralai';
import { MistralAIEmbeddings } from '@langchain/mistralai';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
export class EmbeddingsMistralCloud implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Mistral Cloud',
name: 'embeddingsMistralCloud',
icon: 'file:mistral.svg',
credentials: [
{
name: 'mistralCloudApi',
required: true,
},
],
group: ['transform'],
version: 1,
description: 'Use Embeddings Mistral Cloud',
defaults: {
name: 'Embeddings Mistral Cloud',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingsmistralcloud/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: 'https://api.mistral.ai/v1',
},
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.AiVectorStore]),
{
displayName: 'Model',
name: 'model',
type: 'options',
description:
'The model which will compute the embeddings. <a href="https://docs.mistral.ai/platform/endpoints/">Learn more</a>.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'data',
},
},
{
type: 'filter',
properties: {
pass: "={{ $responseItem.id.includes('embed') }}",
},
},
{
type: 'setKeyValue',
properties: {
name: '={{ $responseItem.id }}',
value: '={{ $responseItem.id }}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: {
type: 'body',
property: 'model',
},
},
default: 'mistral-embed',
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
options: [
{
displayName: 'Batch Size',
name: 'batchSize',
default: 512,
typeOptions: { maxValue: 2048 },
description: 'Maximum number of documents to send in each request',
type: 'number',
},
{
displayName: 'Strip New Lines',
name: 'stripNewLines',
default: true,
description: 'Whether to strip new lines from the input text',
type: 'boolean',
},
],
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = await this.getCredentials('mistralCloudApi');
const modelName = this.getNodeParameter('model', itemIndex) as string;
const options = this.getNodeParameter(
'options',
itemIndex,
{},
) as Partial<MistralAIEmbeddingsParams>;
const embeddings = new MistralAIEmbeddings({
apiKey: credentials.apiKey as string,
model: modelName,
...options,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="216"
height="216"
version="1.1"
id="svg41"
sodipodi:docname="mistral.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview41"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.936488"
inkscape:cx="197.78072"
inkscape:cy="79.00901"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg41" />
<style
id="style1"><![CDATA[.I{fill:#ff7000}.J{fill:#ff4900}.K{fill:#ffa300}.L{fill:#1c1c1b icc-color(adobe-rgb-1998, 0.13299561, 0.13299561, 0.1289978)}]]></style>
<defs
id="defs10">
<clipPath
id="A">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-206.251,-140.139)"
id="path1" />
</clipPath>
<clipPath
id="B">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-247.436,-104.865)"
id="path2" />
</clipPath>
<clipPath
id="C">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-285.938,-102.089)"
id="path3" />
</clipPath>
<clipPath
id="D">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-337.769,-131.877)"
id="path4" />
</clipPath>
<clipPath
id="E">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-377.247,-132.319)"
id="path5" />
</clipPath>
<clipPath
id="F">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-418.107,-114.634)"
id="path6" />
</clipPath>
<clipPath
id="G">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-450.023,-140.139)"
id="path7" />
</clipPath>
<clipPath
id="H">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-217.694,-44.794)"
id="path8" />
</clipPath>
<clipPath
id="I">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
transform="translate(-247.436,-35.025)"
id="path9" />
</clipPath>
<clipPath
id="J">
<path
d="M 0,184.252 H 481.89 V 0 H 0 Z"
id="path10" />
</clipPath>
<path
id="K"
d="m 173.987,134.362 h -37.795 l 9.633,-37.776 h 37.796 z" />
</defs>
<g
transform="matrix(1,0,0.254535,1,-51.362792,-7.4725007)"
id="g32">
<g
class="L"
id="g22">
<path
d="M 98.397,134.362 H 60.602 l 9.633,-37.776 h 37.796 z"
id="path11" />
<path
d="M 126.558,172.138 H 88.763 l 9.633,-37.776 h 37.796 z"
id="path12" />
<path
d="M 136.192,134.362 H 98.397 l 9.633,-37.776 h 37.796 z"
id="path13" />
<use
xlink:href="#K"
id="use13" />
<path
d="M 108.031,96.585 H 70.236 l 9.633,-37.776 h 37.796 z"
id="path14" />
<use
xlink:href="#K"
x="9.6339998"
y="-37.777"
id="use14" />
<path
d="M 60.602,134.362 H 22.807 L 32.44,96.586 h 37.796 z"
id="path15" />
<path
d="M 70.236,96.585 H 32.441 L 42.074,58.809 H 79.87 Z"
id="path16" />
<path
d="M 79.87,58.809 H 42.075 l 9.633,-37.776 h 37.796 z"
id="path17" />
<use
xlink:href="#K"
x="57.063"
y="-75.553001"
id="use17" />
<path
d="M 50.968,172.138 H 13.173 l 9.633,-37.776 h 37.796 z"
id="path18" />
<path
d="M 41.334,209.915 H 3.539 l 9.633,-37.776 h 37.796 z"
id="path19" />
<use
xlink:href="#K"
x="37.794998"
id="use19" />
<use
xlink:href="#K"
x="47.429001"
y="-37.777"
id="use20" />
<use
xlink:href="#K"
x="28.160999"
y="37.776001"
id="use21" />
<use
xlink:href="#K"
x="18.527"
y="75.553001"
id="use22" />
</g>
<path
d="M 114.115,134.359 H 76.321 l 9.633,-37.776 h 37.796 z"
class="I"
id="path22" />
<use
xlink:href="#K"
x="-31.709999"
y="37.772999"
class="J"
id="use23" />
<g
class="I"
id="g25">
<use
xlink:href="#K"
x="-22.076"
y="-0.003"
id="use24" />
<use
xlink:href="#K"
x="15.719"
y="-0.003"
id="use25" />
</g>
<g
class="K"
id="g26">
<path
d="M 123.749,96.582 H 85.955 l 9.633,-37.776 h 37.796 z"
id="path25" />
<use
xlink:href="#K"
x="25.353001"
y="-37.779999"
id="use26" />
</g>
<path
d="M 76.32,134.359 H 38.526 l 9.633,-37.776 h 37.796 z"
class="I"
id="path26" />
<path
d="M 85.954,96.582 H 48.16 l 9.633,-37.776 h 37.796 z"
class="K"
id="path27" />
<g
fill="#ffce00"
id="g28">
<path
d="M 95.588,58.806 H 57.794 L 67.427,21.03 h 37.796 z"
id="path28" />
<use
xlink:href="#K"
x="72.781998"
y="-75.556"
id="use28" />
</g>
<path
d="M 66.686,172.135 H 28.892 l 9.633,-37.776 h 37.796 z"
class="J"
id="path29" />
<path
d="M 57.052,209.912 H 19.258 l 9.633,-37.776 h 37.796 z"
fill="#ff0107"
id="path30" />
<use
xlink:href="#K"
x="53.514"
y="-0.003"
class="I"
id="use30" />
<path
d="M 237.135,96.582 H 199.34 l 9.633,-37.776 h 37.796 z"
class="K"
id="path31" />
<use
xlink:href="#K"
x="43.880001"
y="37.772999"
class="J"
id="use31" />
<use
xlink:href="#K"
x="34.245998"
y="75.550003"
fill="#ff0107"
id="use32" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

@@ -0,0 +1,67 @@
import { OllamaEmbeddings } from '@langchain/ollama';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import { logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
import { ollamaDescription, ollamaModel } from '../../llms/LMOllama/description';
export class EmbeddingsOllama implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings Ollama',
name: 'embeddingsOllama',
icon: 'file:ollama.svg',
group: ['transform'],
version: 1,
description: 'Use Ollama Embeddings',
defaults: {
name: 'Embeddings Ollama',
},
...ollamaDescription,
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingsollama/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
properties: [getConnectionHintNoticeField([NodeConnectionTypes.AiVectorStore]), ollamaModel],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
this.logger.debug('Supply data for embeddings Ollama');
const modelName = this.getNodeParameter('model', itemIndex) as string;
const credentials = await this.getCredentials('ollamaApi');
const headers = credentials.apiKey
? {
Authorization: `Bearer ${credentials.apiKey as string}`,
}
: undefined;
const embeddings = new OllamaEmbeddings({
baseUrl: credentials.baseUrl as string,
model: modelName,
headers,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="241.333" height="341.333" version="1.0" viewBox="0 0 181 256"><g fill="#7D7D87"><path d="M37.7 19.5c-5.2 1.8-8.3 4.9-11.7 11.6-4.5 8.9-6.2 19.2-5.8 35.5l.3 14.2-5.8 6.1c-14.8 15.5-18.5 38.7-9.2 57.4l3.4 6.9-2 4.4c-3.4 8.2-5 16.4-5 26.3 0 10.8 1.8 19 5.8 26.2l2.6 4.8-2.1 4.9c-1.2 2.7-2.6 7.1-3.2 9.8-1.4 6.2-1.5 22.1-.1 25.7 1 2.6 1.4 2.7 7.6 2.7 7.3 0 7 .4 5.3-8.6-1.5-8.2.2-18.8 4.2-26.6 3.7-7 3.8-10.4.5-14.8-4.7-6.4-6.8-13.6-6.9-24-.1-10.3 1.4-16 6.6-26.1 3.1-6.1 2.9-8.7-1-12.2-1.1-1-3.1-4.2-4.3-7-1.9-4.2-2.4-6.9-2.3-14.2 0-11.4 2.5-18.3 9.5-26 7-7.6 14.2-11 23.9-11.2 4.1 0 7.8-.2 8.2-.2.4-.1 1.7-2.2 2.9-4.7 3-5.9 9.6-11.9 16.7-15.2 4.9-2.3 7-2.7 14.7-2.7 7.9 0 9.7.4 14.9 2.9 6.8 3.3 13.3 9.4 15.9 14.8 1 2 2.3 4.1 3 4.5.6.4 4.6.8 8.7.8 6.7.1 8.3.5 14 3.6 12.3 6.8 19.3 18.7 19.3 33.4.1 6.7-.4 9-2.7 14.2-1.6 3.5-3.5 6.8-4.3 7.5-3.4 2.8-3.5 5.8-.5 11.7 5.2 10.1 6.7 15.8 6.6 26.1-.1 10.4-2.2 17.6-6.9 24-3.3 4.4-3.2 7.8.5 14.8 4 7.8 5.7 18.4 4.2 26.6-1.7 9-2 8.6 5.3 8.6 6.2 0 6.6-.1 7.6-2.7 1.4-3.6 1.3-19.5-.1-25.7-.6-2.7-2-7.1-3.2-9.8l-2.1-4.9 2.6-4.8c7.6-13.9 7.9-35.9.6-52.8l-2-4.7 2.5-4.6c9.9-18.3 6.4-43.9-8.1-59.1l-5.8-6.1.3-14.2c.4-16.4-1.3-26.6-5.8-35.7-6.4-12.6-17.2-15.9-26.3-7.9-5.4 4.7-9.2 13.8-12.3 29.8-.3 1.4-1 2.2-1.7 1.8-18.2-8-29.7-8.5-44.3-2.1L65 54.9l-.4-2.2C61 34.2 56.1 24.2 49 20.5c-4.3-2.1-7.4-2.4-11.3-1m7.7 16.8c4.2 7.1 8.1 30.1 5.7 33.6-.5.8-3.1 1.6-5.8 1.8-2.6.2-6.2.8-8 1.3l-3.1.8-.7-4.9c-.8-5.9.2-17.2 2.2-24.8C37.1 38.4 40.5 32 42 32c.5 0 2 1.9 3.4 4.3m96.5-1c4 6.5 6.9 23.9 5.6 33.6l-.7 4.9-3.1-.8c-1.8-.5-5.4-1.1-8-1.3-2.7-.2-5.3-1-5.8-1.8-1.2-1.7-.3-14.1 1.7-22.9 1.5-6.4 5.7-15 7.4-15 .4 0 1.8 1.5 2.9 3.3"/><path d="M77.8 119.9c-7.3 2.4-11.6 5.1-16.5 10.4-5.5 6-7.6 12-7.1 20.1.5 7.6 3.5 12.9 10.6 18.3 6.2 4.7 12.7 6.3 25.7 6.3 17.2 0 25.8-3.6 32.9-13.8 4.2-5.9 4.8-15.5 1.6-23-2.9-6.8-11.1-14.3-18.8-17.3-8-3.1-20.7-3.6-28.4-1m25.7 10c16.1 7.1 19.4 23.2 6.6 31.8-4.9 3.3-9.4 4.3-19.6 4.3s-14.7-1-19.6-4.3c-17.8-12-3.2-35.6 21.1-34.3 3.9.2 8.6 1.2 11.5 2.5"/><path d="M83.8 140.1c-2.5 1.4-2.2 4.4.7 6.7 2 1.6 2.4 2.6 1.9 4.9-.7 3.6 1.5 5.8 5.1 4.9 2.1-.5 2.5-1.2 2.5-4.6 0-2.9.5-4.2 2-5 2.7-1.5 2.7-6.6 0-7.5-1-.3-2.8-.1-4 .5-1.4.7-2.6.8-3.9 0-2.3-1.2-2.2-1.2-4.3.1m-44.1-18.9c-.9.7-2.3 3-3.2 5-2.1 5.3-.1 10.3 4.7 11.6 4.3 1.1 6 .6 9.2-2.7 4-4.1 4.3-8.1 1.1-11.9-2.1-2.5-3.4-3.2-6.4-3.2-2 0-4.5.6-5.4 1.2m89.8 2c-3.2 3.8-2.9 7.8 1.1 11.9 3.2 3.3 4.9 3.8 9.2 2.7 4.9-1.3 6.8-6.2 4.6-11.8-1.9-4.7-3.8-6-8.7-6-2.7 0-4.1.7-6.2 3.2"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,281 @@
import { OpenAIEmbeddings } from '@langchain/openai';
import { AiConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import {
NodeConnectionTypes,
type INodeProperties,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import type { ClientOptions } from 'openai';
import { checkDomainRestrictions } from '@utils/checkDomainRestrictions';
import { mergeCustomHeaders } from '@utils/helpers';
import { getProxyAgent, logWrapper, getConnectionHintNoticeField } from '@n8n/ai-utilities';
const modelParameter: INodeProperties = {
displayName: 'Model',
name: 'model',
type: 'options',
description:
'The model which will generate the embeddings. <a href="https://platform.openai.com/docs/models/overview">Learn more</a>.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '={{ $parameter.options?.baseURL?.split("/").slice(-1).pop() || $credentials?.url?.split("/").slice(-1).pop() || "v1" }}/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'data',
},
},
{
type: 'filter',
properties: {
// If the baseURL is not set or is set to api.openai.com, include only embedding models
pass: `={{
($parameter.options?.baseURL && !$parameter.options?.baseURL?.startsWith('https://api.openai.com/')) ||
($credentials?.url && !$credentials.url.startsWith('https://api.openai.com/')) ||
$responseItem.id.includes('embed')
}}`,
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.id}}',
value: '={{$responseItem.id}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: {
type: 'body',
property: 'model',
},
},
default: 'text-embedding-3-small',
};
export class EmbeddingsOpenAi implements INodeType {
description: INodeTypeDescription = {
displayName: 'Embeddings OpenAI',
name: 'embeddingsOpenAi',
icon: { light: 'file:openAiLight.svg', dark: 'file:openAiLight.dark.svg' },
credentials: [
{
name: 'openAiApi',
required: true,
},
],
group: ['transform'],
version: [1, 1.1, 1.2],
description: 'Use Embeddings OpenAI',
defaults: {
name: 'Embeddings OpenAI',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Embeddings'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.embeddingsopenai/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiEmbedding],
outputNames: ['Embeddings'],
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL:
'={{ $parameter.options?.baseURL?.split("/").slice(0,-1).join("/") || $credentials.url?.split("/").slice(0,-1).join("/") || "https://api.openai.com" }}',
},
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.AiVectorStore]),
{
...modelParameter,
default: 'text-embedding-ada-002',
displayOptions: {
show: {
'@version': [1],
},
},
},
{
...modelParameter,
displayOptions: {
hide: {
'@version': [1],
},
},
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
options: [
{
displayName: 'Dimensions',
name: 'dimensions',
default: 1536,
description:
'The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models.',
type: 'options',
options: [
{
name: '256',
value: 256,
},
{
name: '512',
value: 512,
},
{
name: '1024',
value: 1024,
},
{
name: '1536',
value: 1536,
},
{
name: '3072',
value: 3072,
},
],
},
{
displayName: 'Base URL',
name: 'baseURL',
default: 'https://api.openai.com/v1',
description: 'Override the default base URL for the API',
type: 'string',
displayOptions: {
hide: {
'@version': [{ _cnd: { gte: 1.2 } }],
},
},
},
{
displayName: 'Batch Size',
name: 'batchSize',
default: 512,
typeOptions: { maxValue: 2048 },
description: 'Maximum number of documents to send in each request',
type: 'number',
},
{
displayName: 'Strip New Lines',
name: 'stripNewLines',
default: true,
description: 'Whether to strip new lines from the input text',
type: 'boolean',
},
{
displayName: 'Timeout',
name: 'timeout',
default: -1,
description:
'Maximum amount of time a request is allowed to take in seconds. Set to -1 for no timeout.',
type: 'number',
},
{
displayName: 'Encoding Format',
name: 'encodingFormat',
type: 'options',
description: 'The format to return the embeddings in',
default: 'float',
options: [
{
name: 'Float',
value: 'float',
},
{
name: 'Base64',
value: 'base64',
},
],
},
],
},
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
this.logger.debug('Supply data for embeddings');
const credentials = await this.getCredentials('openAiApi');
const options = this.getNodeParameter('options', itemIndex, {}) as {
baseURL?: string;
batchSize?: number;
stripNewLines?: boolean;
timeout?: number;
dimensions?: number | undefined;
encodingFormat?: 'float' | 'base64' | undefined;
};
if (options.timeout === -1) {
options.timeout = undefined;
}
const { openAiDefaultHeaders: defaultHeaders } = Container.get(AiConfig);
const configuration: ClientOptions = {
defaultHeaders,
};
if (options.baseURL) {
checkDomainRestrictions(this, credentials, options.baseURL);
configuration.baseURL = options.baseURL;
} else if (credentials.url) {
configuration.baseURL = credentials.url as string;
}
configuration.fetchOptions = {
dispatcher: getProxyAgent(configuration.baseURL ?? 'https://api.openai.com/v1', {}),
};
configuration.defaultHeaders = mergeCustomHeaders(
credentials,
(configuration.defaultHeaders ?? {}) as Record<string, string>,
);
const embeddings = new OpenAIEmbeddings({
model: this.getNodeParameter('model', itemIndex, 'text-embedding-3-small') as string,
apiKey: credentials.apiKey as string,
...options,
configuration,
});
return {
response: logWrapper(embeddings, this),
};
}
}
@@ -0,0 +1,3 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M36.8671 16.3718C37.7746 13.648 37.4621 10.6642 36.0108 8.18661C33.8282 4.38653 29.4407 2.43149 25.1556 3.35151C23.2493 1.20396 20.5105 -0.0173148 17.6392 0.000185533C13.2591 -0.00981468 9.37273 2.81025 8.0252 6.97783C5.21139 7.5541 2.78258 9.31538 1.3613 11.8117C-0.837493 15.6018 -0.336232 20.3794 2.60133 23.6294C1.69381 26.3532 2.00632 29.3371 3.4576 31.8146C5.64015 35.6147 10.0277 37.5697 14.3128 36.6497C16.2179 38.7973 18.9579 40.0185 21.8292 39.9998C26.2118 40.011 30.0994 37.1885 31.4469 33.0171C34.2608 32.4409 36.6896 30.6796 38.1108 28.1833C40.3071 24.3932 39.8046 19.6194 36.8683 16.3693L36.8671 16.3718ZM21.8317 37.386C20.078 37.3885 18.3792 36.7747 17.0329 35.6509C17.0941 35.6184 17.2004 35.5597 17.2691 35.5172L25.2343 30.9171C25.6418 30.6858 25.8918 30.2521 25.8893 29.7833V18.5543L29.2557 20.4981C29.2919 20.5156 29.3157 20.5506 29.3207 20.5906V29.8896C29.3157 34.0247 25.9668 37.3772 21.8317 37.386ZM5.7264 30.5071C4.84763 28.9896 4.53137 27.2108 4.83263 25.4845C4.89138 25.5195 4.99513 25.5832 5.06888 25.6257L13.0341 30.2258C13.4378 30.4621 13.9378 30.4621 14.3428 30.2258L24.0668 24.6107V28.4983C24.0693 28.5383 24.0505 28.577 24.0193 28.602L15.9679 33.2509C12.3815 35.3159 7.80144 34.0884 5.72765 30.5071H5.7264ZM3.6301 13.1205C4.50512 11.6004 5.8864 10.4379 7.53144 9.83415C7.53144 9.9029 7.52769 10.0242 7.52769 10.1092V19.3106C7.52519 19.7781 7.77519 20.2119 8.18145 20.4431L17.9054 26.057L14.5391 28.0008C14.5053 28.0233 14.4628 28.027 14.4253 28.0108L6.37266 23.3582C2.79383 21.2856 1.56631 16.7068 3.62885 13.1217L3.6301 13.1205ZM31.2882 19.5569L21.5642 13.9417L24.9306 11.9992C24.9643 11.9767 25.0068 11.9729 25.0443 11.9892L33.097 16.638C36.6821 18.7093 37.9108 23.2957 35.8395 26.8808C34.9633 28.3983 33.5832 29.5608 31.9395 30.1658V20.6894C31.9432 20.2219 31.6945 19.7894 31.2894 19.5569H31.2882ZM34.6383 14.5142C34.5795 14.478 34.4758 14.4155 34.402 14.373L26.4368 9.77289C26.0331 9.53664 25.5331 9.53664 25.1281 9.77289L15.4041 15.388V11.5004C15.4016 11.4604 15.4204 11.4217 15.4516 11.3967L23.503 6.75158C27.0894 4.68279 31.6745 5.91406 33.742 9.50164C34.6158 11.0167 34.932 12.7905 34.6358 14.5142H34.6383ZM13.5741 21.4431L10.2065 19.4994C10.1702 19.4819 10.1465 19.4468 10.1415 19.4068V10.1079C10.144 5.96781 13.5028 2.61274 17.6429 2.61524C19.3942 2.61524 21.0892 3.23025 22.4355 4.35028C22.3743 4.38278 22.2693 4.44153 22.1992 4.48403L14.2341 9.08413C13.8266 9.31538 13.5766 9.74789 13.5791 10.2167L13.5741 21.4406V21.4431ZM15.4029 17.5006L19.7342 14.9993L24.0655 17.4993V22.5007L19.7342 25.0007L15.4029 22.5007V17.5006Z" fill="#C3C9D5"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,3 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M36.8671 16.3718C37.7746 13.648 37.4621 10.6642 36.0108 8.18661C33.8282 4.38653 29.4407 2.43149 25.1556 3.35151C23.2493 1.20396 20.5105 -0.0173148 17.6392 0.000185533C13.2591 -0.00981468 9.37273 2.81025 8.0252 6.97783C5.21139 7.5541 2.78258 9.31538 1.3613 11.8117C-0.837493 15.6018 -0.336232 20.3794 2.60133 23.6294C1.69381 26.3532 2.00632 29.3371 3.4576 31.8146C5.64015 35.6147 10.0277 37.5697 14.3128 36.6497C16.2179 38.7973 18.9579 40.0185 21.8292 39.9998C26.2118 40.011 30.0994 37.1885 31.4469 33.0171C34.2608 32.4409 36.6896 30.6796 38.1108 28.1833C40.3071 24.3932 39.8046 19.6194 36.8683 16.3693L36.8671 16.3718ZM21.8317 37.386C20.078 37.3885 18.3792 36.7747 17.0329 35.6509C17.0941 35.6184 17.2004 35.5597 17.2691 35.5172L25.2343 30.9171C25.6418 30.6858 25.8918 30.2521 25.8893 29.7833V18.5543L29.2557 20.4981C29.2919 20.5156 29.3157 20.5506 29.3207 20.5906V29.8896C29.3157 34.0247 25.9668 37.3772 21.8317 37.386ZM5.7264 30.5071C4.84763 28.9896 4.53137 27.2108 4.83263 25.4845C4.89138 25.5195 4.99513 25.5832 5.06888 25.6257L13.0341 30.2258C13.4378 30.4621 13.9378 30.4621 14.3428 30.2258L24.0668 24.6107V28.4983C24.0693 28.5383 24.0505 28.577 24.0193 28.602L15.9679 33.2509C12.3815 35.3159 7.80144 34.0884 5.72765 30.5071H5.7264ZM3.6301 13.1205C4.50512 11.6004 5.8864 10.4379 7.53144 9.83415C7.53144 9.9029 7.52769 10.0242 7.52769 10.1092V19.3106C7.52519 19.7781 7.77519 20.2119 8.18145 20.4431L17.9054 26.057L14.5391 28.0008C14.5053 28.0233 14.4628 28.027 14.4253 28.0108L6.37266 23.3582C2.79383 21.2856 1.56631 16.7068 3.62885 13.1217L3.6301 13.1205ZM31.2882 19.5569L21.5642 13.9417L24.9306 11.9992C24.9643 11.9767 25.0068 11.9729 25.0443 11.9892L33.097 16.638C36.6821 18.7093 37.9108 23.2957 35.8395 26.8808C34.9633 28.3983 33.5832 29.5608 31.9395 30.1658V20.6894C31.9432 20.2219 31.6945 19.7894 31.2894 19.5569H31.2882ZM34.6383 14.5142C34.5795 14.478 34.4758 14.4155 34.402 14.373L26.4368 9.77289C26.0331 9.53664 25.5331 9.53664 25.1281 9.77289L15.4041 15.388V11.5004C15.4016 11.4604 15.4204 11.4217 15.4516 11.3967L23.503 6.75158C27.0894 4.68279 31.6745 5.91406 33.742 9.50164C34.6158 11.0167 34.932 12.7905 34.6358 14.5142H34.6383ZM13.5741 21.4431L10.2065 19.4994C10.1702 19.4819 10.1465 19.4468 10.1415 19.4068V10.1079C10.144 5.96781 13.5028 2.61274 17.6429 2.61524C19.3942 2.61524 21.0892 3.23025 22.4355 4.35028C22.3743 4.38278 22.2693 4.44153 22.1992 4.48403L14.2341 9.08413C13.8266 9.31538 13.5766 9.74789 13.5791 10.2167L13.5741 21.4406V21.4431ZM15.4029 17.5006L19.7342 14.9993L24.0655 17.4993V22.5007L19.7342 25.0007L15.4029 22.5007V17.5006Z" fill="#7D7D87"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,102 @@
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
/* eslint-disable @typescript-eslint/unbound-method */
import { AzureOpenAIEmbeddings } from '@langchain/openai';
import { createMockExecuteFunction } from 'n8n-nodes-base/test/nodes/Helpers';
import type { INode, ISupplyDataFunctions } from 'n8n-workflow';
import { EmbeddingsAzureOpenAi } from '../EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node';
jest.mock('@langchain/openai');
class MockProxyAgent {}
jest.mock('@n8n/ai-utilities', () => {
const actual = jest.requireActual('@n8n/ai-utilities');
return {
...actual,
logWrapper: jest.fn().mockImplementation(() => jest.fn()),
getProxyAgent: jest.fn().mockImplementation(() => new MockProxyAgent()),
};
});
const MockedAzureOpenAIEmbeddings = jest.mocked(AzureOpenAIEmbeddings);
describe('AzureOpenAIEmbeddings', () => {
let embeddingsAzureOpenAi: EmbeddingsAzureOpenAi;
let mockContext: jest.Mocked<ISupplyDataFunctions>;
const mockNode: INode = {
id: '1',
name: 'Embeddings Azure OpenAI',
typeVersion: 1,
type: '@n8n/n8n-nodes-langchain.embeddingsAzureOpenAi',
position: [0, 0],
parameters: {},
};
const setupMockContext = (nodeOverrides: Partial<INode> = {}) => {
const node = { ...mockNode, ...nodeOverrides };
mockContext = createMockExecuteFunction<ISupplyDataFunctions>(
{},
node,
) as jest.Mocked<ISupplyDataFunctions>;
// Setup default mocks
mockContext.getCredentials = jest.fn().mockResolvedValue({
apiKey: 'test-api-key',
});
mockContext.getNode = jest.fn().mockReturnValue(node);
mockContext.getNodeParameter = jest.fn();
mockContext.logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
return mockContext;
};
beforeEach(() => {
embeddingsAzureOpenAi = new EmbeddingsAzureOpenAi();
jest.clearAllMocks();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('supplyData', () => {
it('dispatcher should get proxy agent', async () => {
const mockContext = setupMockContext();
mockContext.getCredentials.mockResolvedValue({
apiKey: 'test-api-key',
endpoint: 'https://test-resource-name.openai.azure.com',
apiVersion: 'v1',
});
mockContext.getNodeParameter = jest.fn().mockImplementation((paramName: string) => {
if (paramName === 'model') return 'text-embedding-3-large';
if (paramName === 'options') return {};
return undefined;
});
await embeddingsAzureOpenAi.supplyData.call(mockContext, 0);
expect(MockedAzureOpenAIEmbeddings).toHaveBeenCalledWith(
expect.objectContaining({
azureOpenAIApiDeploymentName: 'text-embedding-3-large',
azureOpenAIApiInstanceName: undefined,
azureOpenAIApiKey: 'test-api-key',
azureOpenAIApiVersion: 'v1',
azureOpenAIBasePath: 'https://test-resource-name.openai.azure.com/openai/deployments',
configuration: {
fetchOptions: {
dispatcher: expect.any(MockProxyAgent),
},
},
}),
);
});
});
});
@@ -0,0 +1,158 @@
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
/* eslint-disable @typescript-eslint/unbound-method */
import { OpenAIEmbeddings } from '@langchain/openai';
import { AiConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import { createMockExecuteFunction } from 'n8n-nodes-base/test/nodes/Helpers';
import type { INode, ISupplyDataFunctions } from 'n8n-workflow';
import { EmbeddingsOpenAi } from '../EmbeddingsOpenAI/EmbeddingsOpenAi.node';
jest.mock('@langchain/openai');
class MockProxyAgent {}
jest.mock('@n8n/ai-utilities', () => {
const actual = jest.requireActual('@n8n/ai-utilities');
return {
...actual,
logWrapper: jest.fn().mockImplementation(() => jest.fn()),
getProxyAgent: jest.fn().mockImplementation(() => new MockProxyAgent()),
};
});
const MockedOpenAIEmbeddings = jest.mocked(OpenAIEmbeddings);
const { openAiDefaultHeaders: defaultHeaders } = Container.get(AiConfig);
describe('EmbeddingsOpenAi', () => {
let embeddingsOpenAi: EmbeddingsOpenAi;
let mockContext: jest.Mocked<ISupplyDataFunctions>;
const mockNode: INode = {
id: '1',
name: 'Embeddings OpenAI',
typeVersion: 1.2,
type: '@n8n/n8n-nodes-langchain.embeddingsOpenAi',
position: [0, 0],
parameters: {},
};
const setupMockContext = (nodeOverrides: Partial<INode> = {}) => {
const node = { ...mockNode, ...nodeOverrides };
mockContext = createMockExecuteFunction<ISupplyDataFunctions>(
{},
node,
) as jest.Mocked<ISupplyDataFunctions>;
mockContext.getCredentials = jest.fn().mockResolvedValue({
apiKey: 'test-api-key',
});
mockContext.getNode = jest.fn().mockReturnValue(node);
mockContext.getNodeParameter = jest.fn();
mockContext.logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
return mockContext;
};
beforeEach(() => {
embeddingsOpenAi = new EmbeddingsOpenAi();
jest.clearAllMocks();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('supplyData', () => {
it('should create OpenAIEmbeddings with basic configuration', async () => {
const mockContext = setupMockContext();
mockContext.getNodeParameter = jest.fn().mockImplementation((paramName: string) => {
if (paramName === 'model') return 'text-embedding-3-small';
if (paramName === 'options') return {};
return undefined;
});
await embeddingsOpenAi.supplyData.call(mockContext, 0);
expect(MockedOpenAIEmbeddings).toHaveBeenCalledWith(
expect.objectContaining({
model: 'text-embedding-3-small',
apiKey: 'test-api-key',
configuration: expect.objectContaining({
defaultHeaders,
fetchOptions: {
dispatcher: expect.any(MockProxyAgent),
},
}),
}),
);
});
it('should handle custom headers from credentials', async () => {
const mockContext = setupMockContext();
mockContext.getCredentials.mockResolvedValue({
apiKey: 'test-api-key',
header: true,
headerName: 'X-Custom-Header',
headerValue: 'custom-value',
});
mockContext.getNodeParameter = jest.fn().mockImplementation((paramName: string) => {
if (paramName === 'model') return 'text-embedding-3-small';
if (paramName === 'options') return {};
return undefined;
});
await embeddingsOpenAi.supplyData.call(mockContext, 0);
expect(MockedOpenAIEmbeddings).toHaveBeenCalledWith(
expect.objectContaining({
model: 'text-embedding-3-small',
apiKey: 'test-api-key',
configuration: expect.objectContaining({
defaultHeaders: {
...defaultHeaders,
'X-Custom-Header': 'custom-value',
},
fetchOptions: {
dispatcher: expect.any(MockProxyAgent),
},
}),
}),
);
});
it('should not add custom headers when header option is disabled', async () => {
const mockContext = setupMockContext();
mockContext.getCredentials.mockResolvedValue({
apiKey: 'test-api-key',
header: false,
headerName: 'X-Custom-Header',
headerValue: 'custom-value',
});
mockContext.getNodeParameter = jest.fn().mockImplementation((paramName: string) => {
if (paramName === 'model') return 'text-embedding-3-small';
if (paramName === 'options') return {};
return undefined;
});
await embeddingsOpenAi.supplyData.call(mockContext, 0);
expect(MockedOpenAIEmbeddings).toHaveBeenCalledWith(
expect.objectContaining({
configuration: expect.objectContaining({
defaultHeaders,
}),
}),
);
});
});
});