@n8n/n8n-nodes-langchain
Version:

409 lines • 14.3 kB
JavaScript
;
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 transport_1 = require("../../transport");
const descriptions_1 = require("../descriptions");
const properties = [
{
displayName: 'Model',
name: 'modelId',
type: 'options',
options: [
{
name: 'Qwen3 Max',
value: 'qwen3-max',
description: 'Most capable model with best performance',
},
{
name: 'Qwen3 Max (2026-01-23)',
value: 'qwen3-max-2026-01-23',
description: 'Qwen Max snapshot from 2026-01-23',
},
{
name: 'Qwen3.5 122B-A10B',
value: 'qwen3.5-122b-a10b',
description: 'MoE model with 122B total / 10B active parameters',
},
{
name: 'Qwen3.5 27B',
value: 'qwen3.5-27b',
description: 'Dense 27B parameter model',
},
{
name: 'Qwen3.5 35B-A3B',
value: 'qwen3.5-35b-a3b',
description: 'Small MoE model with 35B total / 3B active parameters',
},
{
name: 'Qwen3.5 397B-A17B',
value: 'qwen3.5-397b-a17b',
description: 'Large MoE model with 397B total / 17B active parameters',
},
{
name: 'Qwen3.5 Flash',
value: 'qwen3.5-flash',
description: 'Faster, more cost-effective model',
},
{
name: 'Qwen3.5 Flash (2026-02-23)',
value: 'qwen3.5-flash-2026-02-23',
description: 'Qwen Flash snapshot from 2026-02-23',
},
{
name: 'Qwen3.5 Plus',
value: 'qwen3.5-plus',
description: 'Balanced model with good performance and cost',
},
{
name: 'Qwen3.5 Plus (2026-02-15)',
value: 'qwen3.5-plus-2026-02-15',
description: 'Qwen Plus snapshot from 2026-02-15',
},
],
default: 'qwen3.5-flash',
description: 'The model to use for generation',
displayOptions: {
show: { '@version': [1] },
},
},
{
...(0, descriptions_1.modelRLC)('textModelSearch'),
displayOptions: {
show: { '@version': [{ _cnd: { gte: 1.1 } }] },
},
},
{
displayName: 'Messages',
name: 'messages',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {
messageValues: [
{
content: '',
role: 'user',
},
],
},
placeholder: 'Add Message',
options: [
{
name: 'messageValues',
displayName: 'Message',
values: [
{
displayName: 'Content',
name: 'content',
type: 'string',
typeOptions: {
rows: 4,
},
default: '',
description: 'The content of the message',
},
{
displayName: 'Role',
name: 'role',
type: 'options',
options: [
{
name: 'User',
value: 'user',
},
{
name: 'Assistant',
value: 'assistant',
},
],
default: 'user',
description: 'The role of the message sender',
},
],
},
],
},
{
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',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Enable Search',
name: 'enableSearch',
type: 'boolean',
default: false,
description: 'Whether to enable web search for up-to-date information',
},
{
displayName: 'Max Tokens',
name: 'maxTokens',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 2000,
description: 'Maximum number of tokens to generate',
},
{
displayName: 'Max Tools Iterations',
name: 'maxToolsIterations',
type: 'number',
default: 15,
description: 'Maximum number of tool-calling iterations before stopping. Set to 0 for unlimited.',
},
{
displayName: 'Repetition Penalty',
name: 'repetitionPenalty',
type: 'number',
typeOptions: {
minValue: 1,
maxValue: 2,
numberPrecision: 2,
},
default: 1.1,
description: 'Penalty for token repetition. Higher values reduce repetition.',
},
{
displayName: 'Seed',
name: 'seed',
type: 'number',
default: 1234,
description: 'Random seed for reproducible outputs',
},
{
displayName: 'Stop Sequences',
name: 'stop',
type: 'string',
default: '',
description: 'Comma-separated list of sequences where the API will stop generating',
},
{
displayName: 'System Message',
name: 'system',
type: 'string',
default: '',
placeholder: 'e.g. You are a helpful assistant',
},
{
displayName: 'Temperature',
name: 'temperature',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 2,
numberPrecision: 2,
},
default: 1,
description: 'Controls randomness in the output. Lower values make output more focused and deterministic.',
},
{
displayName: 'Top K',
name: 'topK',
type: 'number',
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Limits the sampling pool to top K tokens',
},
{
displayName: 'Top P',
name: 'topP',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 1,
numberPrecision: 2,
},
default: 0.9,
description: 'Nucleus sampling parameter. Lower values make output more focused.',
},
],
},
];
const displayOptions = {
show: {
operation: ['message'],
resource: ['text'],
},
};
exports.description = (0, n8n_workflow_1.updateDisplayOptions)(displayOptions, properties);
const TEXT_ONLY_PATTERNS = ['coder', 'math'];
function isMultimodalModel(model) {
const lower = model.toLowerCase();
return !TEXT_ONLY_PATTERNS.some((pattern) => lower.includes(pattern));
}
async function execute(itemIndex) {
const nodeVersion = this.getNode().typeVersion;
const model = nodeVersion >= 1.1
? this.getNodeParameter('modelId', itemIndex, '', { extractValue: true })
: this.getNodeParameter('modelId', itemIndex);
const messagesParam = this.getNodeParameter('messages', itemIndex);
const messages = messagesParam.messageValues || [];
const options = this.getNodeParameter('options', itemIndex, {});
const simplify = this.getNodeParameter('simplify', itemIndex, true);
const isMultimodal = isMultimodalModel(model);
const { tools, connectedTools } = await getTools.call(this);
const formattedMessages = isMultimodal
? messages.map((msg) => ({
role: msg.role,
content: typeof msg.content === 'string' ? [{ text: msg.content }] : msg.content,
}))
: messages;
if (options.system) {
formattedMessages.unshift({
role: 'system',
content: isMultimodal ? [{ text: options.system }] : options.system,
});
}
const body = {
model,
input: {
messages: formattedMessages,
},
parameters: {},
};
if (options.temperature !== undefined) {
body.parameters.temperature = options.temperature;
}
if (options.topP !== undefined) {
body.parameters.top_p = options.topP;
}
if (options.topK !== undefined) {
body.parameters.top_k = options.topK;
}
if (options.maxTokens !== undefined) {
body.parameters.max_tokens = options.maxTokens;
}
if (options.repetitionPenalty !== undefined) {
body.parameters.repetition_penalty = options.repetitionPenalty;
}
if (options.stop) {
body.parameters.stop = options.stop.split(',').map((s) => s.trim());
}
if (options.enableSearch !== undefined) {
body.parameters.enable_search = options.enableSearch;
}
if (options.seed !== undefined) {
body.parameters.seed = options.seed;
}
if (tools.length) {
body.parameters.tools = tools;
}
const endpoint = isMultimodal
? '/api/v1/services/aigc/multimodal-generation/generation'
: '/api/v1/services/aigc/text-generation/generation';
let response = await transport_1.apiRequest.call(this, 'POST', endpoint, { body });
const captureUsage = () => {
const usage = response?.usage;
if (usage) {
(0, n8n_workflow_1.accumulateTokenUsage)(this, usage.input_tokens, usage.output_tokens);
}
};
captureUsage();
const maxToolsIterations = this.getNodeParameter('options.maxToolsIterations', itemIndex, 15);
const abortSignal = this.getExecutionCancelSignal();
let currentIteration = 0;
while (true) {
if (abortSignal?.aborted) {
break;
}
const choice = response.output?.choices?.[0];
const finishReason = choice?.finish_reason;
if (finishReason === 'tool_calls') {
if (maxToolsIterations > 0 && currentIteration >= maxToolsIterations) {
break;
}
const assistantMessage = choice.message;
formattedMessages.push(assistantMessage);
await handleToolUse.call(this, assistantMessage.tool_calls, formattedMessages, connectedTools);
currentIteration++;
}
else {
break;
}
response = await transport_1.apiRequest.call(this, 'POST', endpoint, { body });
captureUsage();
}
const output = isMultimodal
? response.output?.choices?.[0]?.message?.content?.[0]?.text || ''
: response.output?.text || response.output?.choices?.[0]?.message?.content || '';
return {
json: simplify
? { content: output }
: {
content: output,
model,
usage: response.usage,
fullResponse: response,
},
pairedItem: itemIndex,
};
}
async function getTools() {
let connectedTools = [];
const nodeInputs = this.getNodeInputs();
if (nodeInputs.some((i) => i.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),
},
}));
return { tools, connectedTools };
}
async function handleToolUse(toolCalls, messages, connectedTools) {
if (!toolCalls?.length) {
return;
}
for (const toolCall of toolCalls) {
const toolName = toolCall.function.name;
let toolInput;
try {
toolInput = JSON.parse(toolCall.function.arguments);
}
catch {
toolInput = toolCall.function.arguments;
}
let toolResponse;
for (const connectedTool of connectedTools) {
if (connectedTool.name === toolName) {
toolResponse = await connectedTool.invoke(toolInput);
}
}
if (toolResponse === undefined) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Tool "${toolName}" was called but not found among connected tools`);
}
messages.push({
role: 'tool',
content: typeof toolResponse === 'object'
? JSON.stringify(toolResponse)
: String(toolResponse ?? ''),
name: toolName,
});
}
}
//# sourceMappingURL=message.operation.js.map