UNPKG

@n8n/n8n-nodes-langchain

Version:

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

390 lines 15 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.description = void 0; exports.execute = execute; const n8n_workflow_1 = require("n8n-workflow"); const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema")); const helpers_1 = require("../../../../../utils/helpers"); const utils_1 = require("../../helpers/utils"); const transport_1 = require("../../transport"); const descriptions_1 = require("../descriptions"); const properties = [ descriptions_1.modelRLC, { displayName: 'Messages', name: 'messages', type: 'fixedCollection', typeOptions: { sortable: true, multipleValues: true, }, placeholder: 'Add Message', default: { values: [{ content: '', role: 'user' }] }, options: [ { displayName: 'Values', name: 'values', values: [ { displayName: 'Prompt', name: 'content', type: 'string', description: 'The content of the message to be sent', default: '', placeholder: 'e.g. Hello, how can you help me?', typeOptions: { rows: 2, }, }, { displayName: 'Role', name: 'role', type: 'options', description: "Role in shaping the model's response, it tells the model how it should behave and interact with the user", options: [ { name: 'User', value: 'user', description: 'Send a message as a user and get a response from the model', }, { name: 'Assistant', value: 'assistant', description: 'Tell the model to adopt a specific tone or personality', }, ], default: 'user', }, ], }, ], }, { displayName: 'Add Attachments', name: 'addAttachments', type: 'boolean', default: false, description: 'Whether to add image attachments to the message', }, { displayName: 'Attachment Input Data Field Name(s)', name: 'binaryPropertyName', type: 'string', default: 'data', placeholder: 'e.g. data', description: 'Name of the binary field(s) which contains the image(s) to attach, separate multiple field names with commas', typeOptions: { binaryDataProperty: true, }, displayOptions: { show: { addAttachments: [true], }, }, }, { displayName: 'Simplify Output', name: 'simplify', type: 'boolean', default: true, description: 'Whether to return a simplified version of the response instead of the raw data', }, { displayName: 'Options', name: 'options', placeholder: 'Add Option', 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: 'Include Merged Response', name: 'includeMergedResponse', type: 'boolean', default: false, description: 'Whether to include a single output string merging all text parts of the response', }, { displayName: 'Maximum Number of Tokens', name: 'maxTokens', default: 1024, description: 'The maximum number of tokens to generate in the completion', type: 'number', typeOptions: { minValue: 1, numberPrecision: 0, }, }, { displayName: 'Max Tool Calls Iterations', name: 'maxToolsIterations', type: 'number', default: 15, description: 'The maximum number of tool iteration cycles the LLM will run before stopping. A single iteration can contain multiple tool calls. Set to 0 for no limit.', typeOptions: { minValue: 0, numberPrecision: 0, }, }, { displayName: 'Output Randomness (Temperature)', name: 'temperature', default: 0.7, description: 'Controls the randomness of the output. Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.', type: 'number', typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 1, }, }, { displayName: 'Output Randomness (Top P)', name: 'topP', default: 1, description: 'The maximum cumulative probability of tokens to consider when sampling', type: 'number', typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 1, }, }, { 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: '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', }, ], }, { displayName: 'System Message', name: 'system', type: 'string', default: '', placeholder: 'e.g. You are a helpful assistant', }, { displayName: 'Thinking Mode', name: 'thinkingMode', type: 'boolean', default: false, description: 'Whether to enable thinking mode for deep reasoning. The model will include reasoning steps in the response. Cannot be used together with Web Search.', }, { displayName: 'Web Search', name: 'webSearch', type: 'boolean', default: false, description: 'Whether to enable built-in web search. The model will search the web for relevant information. Cannot be used together with Thinking Mode.', }, ], }, ]; const displayOptions = { show: { operation: ['message'], resource: ['text'], }, }; exports.description = (0, n8n_workflow_1.updateDisplayOptions)(displayOptions, properties); async function execute(i) { const model = this.getNodeParameter('modelId', i, '', { extractValue: true }); const rawMessages = this.getNodeParameter('messages.values', i, []); const addAttachments = this.getNodeParameter('addAttachments', i, false); const simplify = this.getNodeParameter('simplify', i, true); const options = this.getNodeParameter('options', i, {}); const messages = []; if (options.system) { messages.push({ role: 'system', content: options.system }); } for (const msg of rawMessages) { messages.push({ role: msg.role, content: msg.content }); } if (addAttachments) { await addAttachmentsToMessages.call(this, i, messages); } const { tools, connectedTools } = await getToolDefinitions.call(this, options); const body = { model, messages, max_tokens: options.maxTokens ?? 1024, }; if (options.temperature !== undefined) body.temperature = options.temperature; if (options.topP !== undefined) body.top_p = options.topP; if (options.frequencyPenalty !== undefined) body.frequency_penalty = options.frequencyPenalty; if (options.presencePenalty !== undefined) body.presence_penalty = options.presencePenalty; if (tools.length > 0) { body.tools = tools; } if (options.responseFormat && options.responseFormat !== 'text') { body.response_format = { type: options.responseFormat }; } if (options.thinkingMode && !options.webSearch) { body.thinking = { type: 'enabled' }; } else { body.thinking = { type: 'disabled' }; } let response = (await transport_1.apiRequest.call(this, 'POST', '/chat/completions', { body, })); const captureUsage = () => { const usage = response.usage; if (usage) { (0, n8n_workflow_1.accumulateTokenUsage)(this, usage.prompt_tokens, usage.completion_tokens); } }; captureUsage(); const maxToolsIterations = this.getNodeParameter('options.maxToolsIterations', i, 15); const abortSignal = this.getExecutionCancelSignal(); let currentIteration = 0; while (true) { if (abortSignal?.aborted) { break; } const choice = response.choices?.[0]; if (choice?.finish_reason !== 'tool_calls' || !choice.message.tool_calls?.length) { break; } if (maxToolsIterations > 0 && currentIteration >= maxToolsIterations) { break; } const assistantMsg = { role: 'assistant', content: choice.message.content ?? '', tool_calls: choice.message.tool_calls, }; if (choice.message.reasoning_content) { assistantMsg.reasoning_content = choice.message.reasoning_content; } messages.push(assistantMsg); await handleToolUse.call(this, choice.message.tool_calls, messages, connectedTools); currentIteration++; response = (await transport_1.apiRequest.call(this, 'POST', '/chat/completions', { body, })); captureUsage(); } const finalMessage = response.choices?.[0]?.message; const mergedResponse = options.includeMergedResponse ? (finalMessage?.content ?? '') : undefined; if (simplify) { const result = { content: finalMessage?.content ?? '', }; if (options.thinkingMode && finalMessage?.reasoning_content) { result.reasoning_content = finalMessage.reasoning_content; } if (mergedResponse !== undefined) { result.merged_response = mergedResponse; } return [ { json: result, pairedItem: { item: i }, }, ]; } return [ { json: { ...response, merged_response: mergedResponse }, pairedItem: { item: i }, }, ]; } async function getToolDefinitions(options) { let connectedTools = []; const nodeInputs = this.getNodeInputs(); if (nodeInputs.some((input) => input.type === 'ai_tool')) { connectedTools = await (0, helpers_1.getConnectedTools)(this, true); } const tools = connectedTools.map((t) => ({ type: 'function', function: { name: t.name, description: t.description, parameters: (0, zod_to_json_schema_1.default)(t.schema), }, })); if (options.webSearch) { tools.push({ type: 'builtin_function', function: { name: '$web_search' }, }); } return { tools, connectedTools }; } async function addAttachmentsToMessages(i, messages) { const binaryPropertyNames = this.getNodeParameter('binaryPropertyName', i, 'data'); const content = []; for (const binaryPropertyName of (0, utils_1.prepareBinaryPropertyList)(binaryPropertyNames)) { const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName); const buffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName); const base64 = buffer.toString('base64'); const dataUrl = `data:${binaryData.mimeType};base64,${base64}`; content.push({ type: 'image_url', image_url: { url: dataUrl } }); } if (content.length === 0) { return; } const lastUserMessage = [...messages].reverse().find((m) => m.role === 'user'); if (lastUserMessage && typeof lastUserMessage.content === 'string') { content.push({ type: 'text', text: lastUserMessage.content }); lastUserMessage.content = content; } else { messages.push({ role: 'user', content }); } } async function handleToolUse(toolCalls, messages, connectedTools) { for (const toolCall of toolCalls) { let toolResponse; for (const connectedTool of connectedTools) { if (connectedTool.name === toolCall.function.name) { const args = (0, n8n_workflow_1.jsonParse)(toolCall.function.arguments); toolResponse = await connectedTool.invoke(args); } } messages.push({ role: 'tool', content: typeof toolResponse === 'object' ? JSON.stringify(toolResponse) : (toolResponse ?? ''), tool_call_id: toolCall.id, }); } } //# sourceMappingURL=message.operation.js.map