UNPKG

@n8n/n8n-nodes-langchain

Version:

![Banner image](https://user-images.githubusercontent.com/10284570/173569848-c624317f-42b1-45a6-ab09-f0ea3c247648.png)

773 lines 39.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LmChatOpenAi = void 0; const openai_1 = require("@langchain/openai"); const pick_1 = __importDefault(require("lodash/pick")); const n8n_workflow_1 = require("n8n-workflow"); const checkDomainRestrictions_1 = require("../../../utils/checkDomainRestrictions"); const helpers_1 = require("../../../utils/helpers"); const error_handling_1 = require("../../vendors/OpenAi/helpers/error-handling"); const ai_utilities_1 = require("@n8n/ai-utilities"); const common_1 = require("./common"); const loadModels_1 = require("./methods/loadModels"); const di_1 = require("@n8n/di"); const config_1 = require("@n8n/config"); const INCLUDE_JSON_WARNING = { displayName: 'If using JSON response format, you must include word "json" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.', name: 'notice', type: 'notice', default: '', }; const OPENAI_MODEL_BUILDER_HINT = { message: 'Prefer the GPT-5.4 family: the flagship variant (e.g. `gpt-5.4`) for general use, a `-mini` / `-nano` variant when the task explicitly calls for cost-efficiency, or `-pro` only when the user asks for maximum capability. Never use gpt-4o, gpt-4-turbo, gpt-4, gpt-3.5, or earlier — those are superseded by the GPT-5 family and are not valid choices.', }; const completionsResponseFormat = { displayName: 'Response Format', name: 'responseFormat', default: 'text', type: 'options', options: [ { name: 'Text', value: 'text', description: 'Regular text response', }, { name: 'JSON', value: 'json_object', description: 'Enables JSON mode, which should guarantee the message the model generates is valid JSON', }, ], }; const jsonSchemaExample = `{ "type": "object", "properties": { "message": { "type": "string" } }, "additionalProperties": false, "required": ["message"] }`; class LmChatOpenAi { constructor() { this.methods = { listSearch: { searchModels: loadModels_1.searchModels, }, }; this.description = { displayName: 'OpenAI Chat Model', name: 'lmChatOpenAi', icon: { light: 'file:openAiLight.svg', dark: 'file:openAiLight.dark.svg' }, group: ['transform'], version: [1, 1.1, 1.2, 1.3], description: 'For advanced usage with an AI chain', defaults: { name: 'OpenAI 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.lmchatopenai/', }, ], }, }, inputs: [], outputs: [n8n_workflow_1.NodeConnectionTypes.AiLanguageModel], outputNames: ['Model'], credentials: [ { name: 'openAiApi', required: true, }, ], requestDefaults: { ignoreHttpStatusErrors: true, baseURL: '={{ $parameter.options?.baseURL?.split("/").slice(0,-1).join("/") || $credentials?.url?.split("/").slice(0,-1).join("/") || "https://api.openai.com" }}', }, properties: [ (0, ai_utilities_1.getConnectionHintNoticeField)([n8n_workflow_1.NodeConnectionTypes.AiChain, n8n_workflow_1.NodeConnectionTypes.AiAgent]), { ...INCLUDE_JSON_WARNING, displayOptions: { show: { '/options.responseFormat': ['json_object'], }, }, }, { ...INCLUDE_JSON_WARNING, displayOptions: { show: { '/options.textFormat.textOptions.type': ['json_object'], }, }, }, { displayName: 'Model', name: 'model', type: 'options', description: 'The model which will generate the completion. <a href="https://beta.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: { pass: `={{ ($parameter.options?.baseURL && !$parameter.options?.baseURL?.startsWith('https://api.openai.com/')) || ($credentials?.url && !$credentials.url.startsWith('https://api.openai.com/')) || $responseItem.id.startsWith('ft:') || $responseItem.id.startsWith('o1') || $responseItem.id.startsWith('o3') || ($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-5-mini', builderHint: OPENAI_MODEL_BUILDER_HINT, displayOptions: { hide: { '@version': [{ _cnd: { gte: 1.2 } }], }, }, }, { displayName: 'Model', name: 'model', type: 'resourceLocator', default: { mode: 'list', value: 'gpt-5-mini' }, builderHint: OPENAI_MODEL_BUILDER_HINT, required: true, modes: [ { displayName: 'From List', name: 'list', type: 'list', placeholder: 'Select a model...', typeOptions: { searchListMethod: 'searchModels', searchable: true, }, }, { displayName: 'ID', name: 'id', type: 'string', placeholder: 'gpt-5-mini', }, ], description: 'The model. Choose from the list, or specify an ID.', displayOptions: { hide: { '@version': [{ _cnd: { lte: 1.1 } }], }, }, }, { displayName: 'When using non-OpenAI models via "Base URL" override, not all models might be chat-compatible or support other features, like tools calling or JSON response format', name: 'notice', type: 'notice', default: '', displayOptions: { show: { '/options.baseURL': [{ _cnd: { exists: true } }], }, }, }, { displayName: 'Use Responses API', name: 'responsesApiEnabled', type: 'boolean', default: true, description: 'Whether to use the Responses API to generate the response. <a href="https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenai/#use-responses-api">Learn more</a>.', displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], }, }, }, { displayName: 'Built-in Tools', name: 'builtInTools', placeholder: 'Add Built-in Tool', type: 'collection', default: {}, options: [ { displayName: 'Web Search', name: 'webSearch', type: 'collection', default: { searchContextSize: 'medium' }, options: [ { displayName: 'Search Context Size', name: 'searchContextSize', type: 'options', default: 'medium', description: 'High level guidance for the amount of context window space to use for the search', options: [ { name: 'Low', value: 'low' }, { name: 'Medium', value: 'medium' }, { name: 'High', value: 'high' }, ], }, { displayName: 'Web Search Allowed Domains', name: 'allowedDomains', type: 'string', default: '', description: 'Comma-separated list of domains to search. Only domains in this list will be searched.', placeholder: 'e.g. google.com, wikipedia.org', }, { displayName: 'Country', name: 'country', type: 'string', default: '', placeholder: 'e.g. US, GB', }, { displayName: 'City', name: 'city', type: 'string', default: '', placeholder: 'e.g. New York, London', }, { displayName: 'Region', name: 'region', type: 'string', default: '', placeholder: 'e.g. New York, London', }, ], }, { displayName: 'File Search', name: 'fileSearch', type: 'collection', default: { vectorStoreIds: '[]' }, options: [ { displayName: 'Vector Store IDs', name: 'vectorStoreIds', description: 'The vector store IDs to use for the file search. Vector stores are managed via OpenAI Dashboard. <a href="https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenai/#built-in-tools">Learn more</a>.', type: 'json', default: '[]', required: true, }, { displayName: 'Filters', name: 'filters', type: 'json', default: '{}', }, { displayName: 'Max Results', name: 'maxResults', type: 'number', default: 1, typeOptions: { minValue: 1, maxValue: 50 }, }, ], }, { displayName: 'Code Interpreter', name: 'codeInterpreter', type: 'boolean', default: true, description: 'Whether to allow the model to execute code in a sandboxed environment', }, ], displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { displayName: 'Options', name: 'options', placeholder: 'Add Option', description: 'Additional options to add', type: 'collection', default: {}, options: [ { 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.1 } }], }, }, }, { 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, }, }, { ...completionsResponseFormat, displayOptions: { show: { '@version': [{ _cnd: { lt: 1.3 } }], }, }, }, { ...completionsResponseFormat, displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [false], }, }, }, { displayName: 'Response Format', name: 'textFormat', type: 'fixedCollection', default: { textOptions: [{ type: 'text' }] }, options: [ { displayName: 'Text', name: 'textOptions', values: [ { displayName: 'Type', name: 'type', type: 'options', default: '', options: [ { name: 'Text', value: 'text' }, { name: 'JSON Schema (recommended)', value: 'json_schema' }, { name: 'JSON Object', value: 'json_object' }, ], }, { displayName: 'Verbosity', name: 'verbosity', type: 'options', default: 'medium', options: [ { name: 'Low', value: 'low' }, { name: 'Medium', value: 'medium' }, { name: 'High', value: 'high' }, ], }, { displayName: 'Name', name: 'name', type: 'string', default: 'my_schema', description: 'The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.', displayOptions: { show: { type: ['json_schema'], }, }, }, { displayName: 'All properties in the schema must be set to "required", when using "strict" mode.', name: 'requiredNotice', type: 'notice', default: '', displayOptions: { show: { strict: [true], }, }, }, { displayName: 'Schema', name: 'schema', type: 'json', default: jsonSchemaExample, description: 'The schema of the response format', displayOptions: { show: { type: ['json_schema'], }, }, }, { displayName: 'Description', name: 'description', type: 'string', default: '', description: 'The description of the response format', displayOptions: { show: { type: ['json_schema'], }, }, }, { displayName: 'Strict', name: 'strict', type: 'boolean', default: false, description: 'Whether to require that the AI will always generate responses that match the provided JSON Schema', displayOptions: { show: { type: ['json_schema'], }, }, }, ], }, ], displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { 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: 2, 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: 'Reasoning Effort', name: 'reasoningEffort', default: 'medium', description: 'Controls the amount of reasoning tokens to use. A value of "low" will favor speed and economical token usage, "high" will favor more complete reasoning at the cost of more tokens generated and slower responses.', type: 'options', options: [ { name: 'Low', value: 'low', description: 'Favors speed and economical token usage', }, { name: 'Medium', value: 'medium', description: 'Balance between speed and reasoning accuracy', }, { name: 'High', value: 'high', description: 'Favors more complete reasoning at the cost of more tokens generated and slower responses', }, ], displayOptions: { show: { '/model': [{ _cnd: { regex: '(^o1([-\\d]+)?$)|(^o[3-9].*)|(^gpt-5.*)' } }], }, }, }, { 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', }, { displayName: 'Conversation ID', name: 'conversationId', default: '', description: 'The conversation that this response belongs to. Input items and output items from this response are automatically added to this conversation after this response completes.', type: 'string', displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { displayName: 'Prompt Cache Key', name: 'promptCacheKey', type: 'string', default: '', description: 'Used by OpenAI to cache responses for similar requests to optimize your cache hit rates', displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { displayName: 'Safety Identifier', name: 'safetyIdentifier', type: 'string', default: '', description: "A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely identifies each user.", displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { displayName: 'Service Tier', name: 'serviceTier', type: 'options', default: 'auto', description: 'The service tier to use for the request', options: [ { name: 'Auto', value: 'auto' }, { name: 'Flex', value: 'flex' }, { name: 'Default', value: 'default' }, { name: 'Priority', value: 'priority' }, ], displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { displayName: 'Metadata', name: 'metadata', type: 'json', description: 'Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard. Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.', default: '{}', displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { displayName: 'Top Logprobs', name: 'topLogprobs', type: 'number', default: 0, description: 'An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability', typeOptions: { minValue: 0, maxValue: 20, }, displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, { displayName: 'Prompt', name: 'promptConfig', type: 'fixedCollection', description: 'Configure the reusable prompt template configured via OpenAI Dashboard. <a href="https://platform.openai.com/docs/guides/prompt-engineering#reusable-prompts">Learn more</a>.', default: { promptOptions: [{ promptId: '' }] }, options: [ { displayName: 'Prompt', name: 'promptOptions', values: [ { displayName: 'Prompt ID', name: 'promptId', type: 'string', default: '', description: 'The unique identifier of the prompt template to use', }, { displayName: 'Version', name: 'version', type: 'string', default: '', description: 'Optional version of the prompt template', }, { displayName: 'Variables', name: 'variables', type: 'json', default: '{}', description: 'Variables to be substituted into the prompt template', }, ], }, ], displayOptions: { show: { '@version': [{ _cnd: { gte: 1.3 } }], '/responsesApiEnabled': [true], }, }, }, ], }, ], }; } async supplyData(itemIndex) { const credentials = await this.getCredentials('openAiApi'); const version = this.getNode().typeVersion; const modelName = version >= 1.2 ? this.getNodeParameter('model.value', itemIndex) : this.getNodeParameter('model', itemIndex); const responsesApiEnabled = this.getNodeParameter('responsesApiEnabled', itemIndex, false); const options = this.getNodeParameter('options', itemIndex, {}); const { openAiDefaultHeaders: defaultHeaders } = di_1.Container.get(config_1.AiConfig); const configuration = { defaultHeaders, }; if (options.baseURL) { (0, checkDomainRestrictions_1.checkDomainRestrictions)(this, credentials, options.baseURL); configuration.baseURL = options.baseURL; } else if (credentials.url) { configuration.baseURL = credentials.url; } const timeout = options.timeout; configuration.fetchOptions = { dispatcher: (0, ai_utilities_1.getProxyAgent)(configuration.baseURL ?? 'https://api.openai.com/v1', { headersTimeout: timeout, bodyTimeout: timeout, }), }; configuration.defaultHeaders = (0, helpers_1.mergeCustomHeaders)(credentials, (configuration.defaultHeaders ?? {})); const modelKwargs = {}; if (responsesApiEnabled) { const kwargs = (0, common_1.prepareAdditionalResponsesParams)(options); Object.assign(modelKwargs, kwargs); } else { if (options.responseFormat) modelKwargs.response_format = { type: options.responseFormat }; if (options.reasoningEffort && ['low', 'medium', 'high'].includes(options.reasoningEffort)) { modelKwargs.reasoning_effort = options.reasoningEffort; } } const includedOptions = (0, pick_1.default)(options, [ 'frequencyPenalty', 'maxTokens', 'presencePenalty', 'temperature', 'topP', 'baseURL', ]); const fields = { apiKey: credentials.apiKey, model: modelName, ...includedOptions, timeout, maxRetries: options.maxRetries ?? 2, configuration, callbacks: [new ai_utilities_1.N8nLlmTracing(this)], modelKwargs, onFailedAttempt: (0, ai_utilities_1.makeN8nLlmFailedAttemptHandler)(this, error_handling_1.openAiFailedAttemptHandler), supportsStrictToolCalling: false, }; if (responsesApiEnabled) { fields.useResponsesApi = true; } const model = new openai_1.ChatOpenAI(fields); if (responsesApiEnabled) { const tools = (0, common_1.formatBuiltInTools)(this.getNodeParameter('builtInTools', itemIndex, {})); if (tools.length) { model.metadata = { ...model.metadata, tools, }; } } return { response: model, }; } } exports.LmChatOpenAi = LmChatOpenAi; //# sourceMappingURL=LmChatOpenAi.node.js.map