From 359aaab9c7994e6939cf068b5f1742b7d7d708ac Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 31 Jan 2024 11:40:03 +0100 Subject: [PATCH 1/5] feat: Add Azure Open AI LM Chat node Signed-off-by: Oleg Ivaniv --- .../credentials/AzureOpenAiApi.credentials.ts | 48 ++++ .../LmChatAzureOpenAi.node.ts | 226 ++++++++++++++++++ .../nodes/llms/LmChatAzureOpenAi/azure.svg | 1 + packages/@n8n/nodes-langchain/package.json | 2 + 4 files changed, 277 insertions(+) create mode 100644 packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts create mode 100644 packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts create mode 100644 packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/azure.svg diff --git a/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts b/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts new file mode 100644 index 0000000000000..59d6ef63d4e1a --- /dev/null +++ b/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts @@ -0,0 +1,48 @@ +import type { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class AzureOpenAiApi implements ICredentialType { + name = 'azureOpenAiApi'; + + displayName = 'Azure Open AI'; + + documentationUrl = 'azureOpenAi'; + + properties: INodeProperties[] = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string', + typeOptions: { password: true }, + required: true, + default: '', + }, + { + displayName: 'Resource Name', + name: 'resourceName', + type: 'string', + required: true, + default: '', + }, + { + displayName: 'API Version', + name: 'apiVersion', + type: 'string', + required: true, + default: '2023-05-15', + }, + ]; + + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + 'api-key': '={{$credentials.apiKey}}', + }, + }, + }; +} diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts new file mode 100644 index 0000000000000..79787b21032a6 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts @@ -0,0 +1,226 @@ +/* eslint-disable n8n-nodes-base/node-dirname-against-convention */ +import { + NodeConnectionType, + type IExecuteFunctions, + type INodeType, + type INodeTypeDescription, + type SupplyData, +} from 'n8n-workflow'; + +import type { ClientOptions } from 'openai'; +import { ChatOpenAI } from 'langchain/chat_models/openai'; +import { logWrapper } from '../../../utils/logWrapper'; +import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; + +export class LmChatAzureOpenAi implements INodeType { + description: INodeTypeDescription = { + displayName: 'Azure OpenAI Chat Model', + // eslint-disable-next-line n8n-nodes-base/node-class-description-name-miscased + name: 'lmChatAzureOpenAi', + icon: 'file:azure.svg', + group: ['transform'], + version: 1, + description: 'For advanced usage with an AI chain', + defaults: { + name: 'Azure OpenAI Chat Model', + }, + codex: { + categories: ['AI'], + subcategories: { + AI: ['Language Models'], + }, + resources: { + primaryDocumentation: [ + { + url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatazureopenai/', + }, + ], + }, + }, + // 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: 'azureOpenAiApi', + required: true, + }, + ], + properties: [ + getConnectionHintNoticeField([NodeConnectionType.AiChain, NodeConnectionType.AiAgent]), + // { + // displayName: 'Model', + // name: 'model', + // type: 'options', + // description: + // 'The model which will generate the completion. Learn more.', + // typeOptions: { + // loadOptions: { + // routing: { + // request: { + // method: 'GET', + // url: '={{ $parameter.options?.baseURL?.split("/").slice(-1).pop() || "v1" }}/models', + // }, + // output: { + // postReceive: [ + // { + // type: 'rootProperty', + // properties: { + // property: 'data', + // }, + // }, + // { + // type: 'filter', + // properties: { + // pass: "={{ $responseItem.id.startsWith('gpt-') && !$responseItem.id.includes('instruct') }}", + // }, + // }, + // { + // type: 'setKeyValue', + // properties: { + // name: '={{$responseItem.id}}', + // value: '={{$responseItem.id}}', + // }, + // }, + // { + // type: 'sort', + // properties: { + // key: 'name', + // }, + // }, + // ], + // }, + // }, + // }, + // }, + // routing: { + // send: { + // type: 'body', + // property: 'model', + // }, + // }, + // default: 'gpt-3.5-turbo', + // }, + { + 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: 'Frequency Penalty', + name: 'frequencyPenalty', + default: 0, + typeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 }, + description: + "Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim", + type: 'number', + }, + { + displayName: 'Maximum Number of Tokens', + name: 'maxTokens', + default: -1, + description: + 'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).', + type: 'number', + typeOptions: { + maxValue: 32768, + }, + }, + { + displayName: 'Presence Penalty', + name: 'presencePenalty', + default: 0, + typeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 }, + description: + "Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics", + 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: 'Timeout', + name: 'timeout', + default: 60000, + description: 'Maximum amount of time a request is allowed to take in milliseconds', + type: 'number', + }, + { + displayName: 'Max Retries', + name: 'maxRetries', + default: 2, + description: 'Maximum number of retries to attempt', + type: 'number', + }, + { + displayName: 'Top P', + name: 'topP', + default: 1, + 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 { + const credentials = await this.getCredentials('azureOpenAiApi') as { + apiKey: string; + resourceName: string; + apiVersion: string; + }; + + const modelName = this.getNodeParameter('model', itemIndex) as string; + const options = this.getNodeParameter('options', itemIndex, {}) as { + baseURL?: string; + frequencyPenalty?: number; + maxTokens?: number; + maxRetries: number; + timeout: number; + presencePenalty?: number; + temperature?: number; + topP?: number; + }; + + const configuration: ClientOptions = {}; + if (options.baseURL) { + configuration.baseURL = options.baseURL; + } + + const model = new ChatOpenAI({ + azureOpenAIApiDeploymentName: modelName, + azureOpenAIApiInstanceName: credentials.resourceName, + azureOpenAIApiKey: credentials.apiKey as string, + azureOpenAIApiVersion: credentials.apiVersion, + ...options, + timeout: options.timeout ?? 60000, + maxRetries: options.maxRetries ?? 2, + configuration, + }); + + return { + response: logWrapper(model, this), + }; + } +} diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/azure.svg b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/azure.svg new file mode 100644 index 0000000000000..bbbc6c33b3bcd --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/azure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index b61f0735eb0d4..313c8faecd802 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -27,6 +27,7 @@ "n8nNodesApiVersion": 1, "credentials": [ "dist/credentials/AnthropicApi.credentials.js", + "dist/credentials/AzureOpenAiApi.credentials.js", "dist/credentials/CohereApi.credentials.js", "dist/credentials/GooglePalmApi.credentials.js", "dist/credentials/HuggingFaceApi.credentials.js", @@ -58,6 +59,7 @@ "dist/nodes/embeddings/EmbeddingsMistralCloud/EmbeddingsMistralCloud.node.js", "dist/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.js", "dist/nodes/llms/LMChatAnthropic/LmChatAnthropic.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/LmChatGooglePalm/LmChatGooglePalm.node.js", From bf3dd43c88ae0fdf9f08eb26dad4da2d9e8629e4 Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Thu, 1 Feb 2024 13:37:09 +0100 Subject: [PATCH 2/5] Add Azure OpenAI embeddings Signed-off-by: Oleg Ivaniv --- .../credentials/AzureOpenAiApi.credentials.ts | 2 +- .../EmbeddingsAzureOpenAI.node.ts | 128 ++++++++++++++++++ .../EmbeddingsAzureOpenAI/azure.svg | 1 + .../LmChatAzureOpenAi.node.ts | 57 -------- packages/@n8n/nodes-langchain/package.json | 1 + 5 files changed, 131 insertions(+), 58 deletions(-) create mode 100644 packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts create mode 100644 packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/azure.svg diff --git a/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts b/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts index 59d6ef63d4e1a..8ca9676b1df9a 100644 --- a/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts +++ b/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts @@ -10,7 +10,7 @@ export class AzureOpenAiApi implements ICredentialType { displayName = 'Azure Open AI'; - documentationUrl = 'azureOpenAi'; + documentationUrl = 'azureopenai'; properties: INodeProperties[] = [ { diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts new file mode 100644 index 0000000000000..5d34710d95833 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts @@ -0,0 +1,128 @@ +/* eslint-disable n8n-nodes-base/node-dirname-against-convention */ +import { + NodeConnectionType, + type IExecuteFunctions, + type INodeType, + type INodeTypeDescription, + type SupplyData, +} from 'n8n-workflow'; + +import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; +import { logWrapper } from '../../../utils/logWrapper'; +import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; + +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/', + }, + ], + }, + }, + // 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: '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', + }, + ], + }, + ], + }; + + async supplyData(this: IExecuteFunctions, itemIndex: number): Promise { + this.logger.verbose('Supply data for embeddings'); + const credentials = await this.getCredentials('azureOpenAiApi') as { + apiKey: string; + resourceName: string; + apiVersion: string; + }; + const modelName = this.getNodeParameter('model', itemIndex) as string; + + const options = this.getNodeParameter('options', itemIndex, {}) as { + batchSize?: number; + stripNewLines?: boolean; + timeout?: number; + }; + + if (options.timeout === -1) { + options.timeout = undefined; + } + + const embeddings = new OpenAIEmbeddings( + { + azureOpenAIApiDeploymentName: modelName, + azureOpenAIApiInstanceName: credentials.resourceName, + azureOpenAIApiKey: credentials.apiKey as string, + azureOpenAIApiVersion: credentials.apiVersion, + ...options, + }, + ); + + return { + response: logWrapper(embeddings, this), + }; + } +} diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/azure.svg b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/azure.svg new file mode 100644 index 0000000000000..bbbc6c33b3bcd --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/azure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts index 79787b21032a6..cd1af15f8bf50 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts @@ -50,59 +50,6 @@ export class LmChatAzureOpenAi implements INodeType { ], properties: [ getConnectionHintNoticeField([NodeConnectionType.AiChain, NodeConnectionType.AiAgent]), - // { - // displayName: 'Model', - // name: 'model', - // type: 'options', - // description: - // 'The model which will generate the completion. Learn more.', - // typeOptions: { - // loadOptions: { - // routing: { - // request: { - // method: 'GET', - // url: '={{ $parameter.options?.baseURL?.split("/").slice(-1).pop() || "v1" }}/models', - // }, - // output: { - // postReceive: [ - // { - // type: 'rootProperty', - // properties: { - // property: 'data', - // }, - // }, - // { - // type: 'filter', - // properties: { - // pass: "={{ $responseItem.id.startsWith('gpt-') && !$responseItem.id.includes('instruct') }}", - // }, - // }, - // { - // type: 'setKeyValue', - // properties: { - // name: '={{$responseItem.id}}', - // value: '={{$responseItem.id}}', - // }, - // }, - // { - // type: 'sort', - // properties: { - // key: 'name', - // }, - // }, - // ], - // }, - // }, - // }, - // }, - // routing: { - // send: { - // type: 'body', - // property: 'model', - // }, - // }, - // default: 'gpt-3.5-turbo', - // }, { displayName: 'Model (Deployment) Name', name: 'model', @@ -193,7 +140,6 @@ export class LmChatAzureOpenAi implements INodeType { const modelName = this.getNodeParameter('model', itemIndex) as string; const options = this.getNodeParameter('options', itemIndex, {}) as { - baseURL?: string; frequencyPenalty?: number; maxTokens?: number; maxRetries: number; @@ -204,9 +150,6 @@ export class LmChatAzureOpenAi implements INodeType { }; const configuration: ClientOptions = {}; - if (options.baseURL) { - configuration.baseURL = options.baseURL; - } const model = new ChatOpenAI({ azureOpenAIApiDeploymentName: modelName, diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 313c8faecd802..874687827dfee 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -54,6 +54,7 @@ "dist/nodes/document_loaders/DocumentJSONInputLoader/DocumentJsonInputLoader.node.js", "dist/nodes/embeddings/EmbeddingsCohere/EmbeddingsCohere.node.js", "dist/nodes/embeddings/EmbeddingsAwsBedrock/EmbeddingsAwsBedrock.node.js", + "dist/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.js", "dist/nodes/embeddings/EmbeddingsGooglePalm/EmbeddingsGooglePalm.node.js", "dist/nodes/embeddings/EmbeddingsHuggingFaceInference/EmbeddingsHuggingFaceInference.node.js", "dist/nodes/embeddings/EmbeddingsMistralCloud/EmbeddingsMistralCloud.node.js", From 533d66e05e6b7849c6a5fcde284390356eaac96b Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Thu, 1 Feb 2024 15:09:23 +0100 Subject: [PATCH 3/5] Linting fixes Signed-off-by: Oleg Ivaniv --- .../credentials/AzureOpenAiApi.credentials.ts | 7 +------ .../EmbeddingsAzureOpenAI.node.ts | 20 +++++++++---------- .../LmChatAzureOpenAi.node.ts | 4 ++-- packages/@n8n/nodes-langchain/package.json | 2 +- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts b/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts index 8ca9676b1df9a..0828a3e204d61 100644 --- a/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts +++ b/packages/@n8n/nodes-langchain/credentials/AzureOpenAiApi.credentials.ts @@ -1,9 +1,4 @@ -import type { - IAuthenticateGeneric, - ICredentialTestRequest, - ICredentialType, - INodeProperties, -} from 'n8n-workflow'; +import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow'; export class AzureOpenAiApi implements ICredentialType { name = 'azureOpenAiApi'; diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts index 5d34710d95833..e4503665a6c0b 100644 --- a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts @@ -11,7 +11,7 @@ import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; import { logWrapper } from '../../../utils/logWrapper'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; -export class EmbeddingsAzureOpenAI implements INodeType { +export class EmbeddingsAzureOpenAi implements INodeType { description: INodeTypeDescription = { displayName: 'Embeddings Azure OpenAI', name: 'embeddingsAzureOpenAi', @@ -94,7 +94,7 @@ export class EmbeddingsAzureOpenAI implements INodeType { async supplyData(this: IExecuteFunctions, itemIndex: number): Promise { this.logger.verbose('Supply data for embeddings'); - const credentials = await this.getCredentials('azureOpenAiApi') as { + const credentials = (await this.getCredentials('azureOpenAiApi')) as { apiKey: string; resourceName: string; apiVersion: string; @@ -111,15 +111,13 @@ export class EmbeddingsAzureOpenAI implements INodeType { options.timeout = undefined; } - const embeddings = new OpenAIEmbeddings( - { - azureOpenAIApiDeploymentName: modelName, - azureOpenAIApiInstanceName: credentials.resourceName, - azureOpenAIApiKey: credentials.apiKey as string, - azureOpenAIApiVersion: credentials.apiVersion, - ...options, - }, - ); + const embeddings = new OpenAIEmbeddings({ + azureOpenAIApiDeploymentName: modelName, + azureOpenAIApiInstanceName: credentials.resourceName, + azureOpenAIApiKey: credentials.apiKey, + azureOpenAIApiVersion: credentials.apiVersion, + ...options, + }); return { response: logWrapper(embeddings, this), diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts index cd1af15f8bf50..764b92312136e 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts @@ -132,7 +132,7 @@ export class LmChatAzureOpenAi implements INodeType { }; async supplyData(this: IExecuteFunctions, itemIndex: number): Promise { - const credentials = await this.getCredentials('azureOpenAiApi') as { + const credentials = (await this.getCredentials('azureOpenAiApi')) as { apiKey: string; resourceName: string; apiVersion: string; @@ -154,7 +154,7 @@ export class LmChatAzureOpenAi implements INodeType { const model = new ChatOpenAI({ azureOpenAIApiDeploymentName: modelName, azureOpenAIApiInstanceName: credentials.resourceName, - azureOpenAIApiKey: credentials.apiKey as string, + azureOpenAIApiKey: credentials.apiKey, azureOpenAIApiVersion: credentials.apiVersion, ...options, timeout: options.timeout ?? 60000, diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 874687827dfee..11a174584d15d 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -54,7 +54,7 @@ "dist/nodes/document_loaders/DocumentJSONInputLoader/DocumentJsonInputLoader.node.js", "dist/nodes/embeddings/EmbeddingsCohere/EmbeddingsCohere.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/EmbeddingsHuggingFaceInference/EmbeddingsHuggingFaceInference.node.js", "dist/nodes/embeddings/EmbeddingsMistralCloud/EmbeddingsMistralCloud.node.js", From 244e63b6d39bbe3eef76e29962573aacbb26f2fd Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Thu, 1 Feb 2024 15:10:10 +0100 Subject: [PATCH 4/5] Linting rename Signed-off-by: Oleg Ivaniv --- .../EmbeddingsAzureOpenAi.node.ts} | 0 .../{EmbeddingsAzureOpenAI => EmbeddingsAzureOpenAi}/azure.svg | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename packages/@n8n/nodes-langchain/nodes/embeddings/{EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts => EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts} (100%) rename packages/@n8n/nodes-langchain/nodes/embeddings/{EmbeddingsAzureOpenAI => EmbeddingsAzureOpenAi}/azure.svg (100%) diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts similarity index 100% rename from packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/EmbeddingsAzureOpenAI.node.ts rename to packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/azure.svg b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/azure.svg similarity index 100% rename from packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAI/azure.svg rename to packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/azure.svg From 1b6b7a74be1e5c3c421d4b339f7bddd7d374a355 Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Thu, 1 Feb 2024 15:47:00 +0100 Subject: [PATCH 5/5] Fix 30-langchain e2e spec Signed-off-by: Oleg Ivaniv --- cypress/composables/workflow.ts | 11 ++++++++--- cypress/e2e/30-langchain.cy.ts | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cypress/composables/workflow.ts b/cypress/composables/workflow.ts index b1810943a3f25..1518805c6be41 100644 --- a/cypress/composables/workflow.ts +++ b/cypress/composables/workflow.ts @@ -106,14 +106,19 @@ export function addSupplementalNodeToParent( nodeName: string, endpointType: EndpointType, parentNodeName: string, + exactMatch = false, ) { getAddInputEndpointByType(parentNodeName, endpointType).click({ force: true }); - getNodeCreatorItems().contains(nodeName).click(); + if (exactMatch) { + getNodeCreatorItems().contains(new RegExp("^" + nodeName + "$", "g")).click(); + } else { + getNodeCreatorItems().contains(nodeName).click(); + } getConnectionBySourceAndTarget(parentNodeName, nodeName).should('exist'); } -export function addLanguageModelNodeToParent(nodeName: string, parentNodeName: string) { - addSupplementalNodeToParent(nodeName, 'ai_languageModel', parentNodeName); +export function addLanguageModelNodeToParent(nodeName: string, parentNodeName: string, exactMatch = false) { + addSupplementalNodeToParent(nodeName, 'ai_languageModel', parentNodeName, exactMatch); } export function addMemoryNodeToParent(nodeName: string, parentNodeName: string) { diff --git a/cypress/e2e/30-langchain.cy.ts b/cypress/e2e/30-langchain.cy.ts index 9140acdef2541..7c74be35b477d 100644 --- a/cypress/e2e/30-langchain.cy.ts +++ b/cypress/e2e/30-langchain.cy.ts @@ -83,6 +83,7 @@ describe('Langchain Integration', () => { addLanguageModelNodeToParent( AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, BASIC_LLM_CHAIN_NODE_NAME, + true ); clickCreateNewCredential(); @@ -121,7 +122,7 @@ describe('Langchain Integration', () => { addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); addNodeToCanvas(AGENT_NODE_NAME, true); - addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME); + addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME, true); clickCreateNewCredential(); setCredentialValues({ @@ -159,7 +160,7 @@ describe('Langchain Integration', () => { addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); addNodeToCanvas(AGENT_NODE_NAME, true); - addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME); + addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME, true); clickCreateNewCredential(); setCredentialValues({