@n8n/n8n-nodes-langchain
Version:
440 lines • 15.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.description = void 0;
exports.execute = execute;
const n8n_workflow_1 = require("n8n-workflow");
const wanImage_1 = require("../../helpers/wanImage");
const transport_1 = require("../../transport");
const descriptions_1 = require("../descriptions");
const WAN_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
const properties = [
{
displayName: 'Model',
name: 'modelId',
type: 'options',
options: [
{
name: 'Qwen Image',
value: 'qwen-image',
description: 'Qwen image generation model',
},
{
name: 'Qwen Image Max',
value: 'qwen-image-max',
description: 'Most capable Qwen image generation model',
},
{
name: 'Qwen Image Plus',
value: 'qwen-image-plus',
description: 'Enhanced Qwen image generation model',
},
{
name: 'Wan 2.6 Image',
value: 'wan2.6-image',
description: 'Wan image editing model. Requires 1 to 4 reference images.',
},
{
name: 'Wan 2.6 T2I',
value: 'wan2.6-t2i',
description: 'Wanx image generation model',
},
{
name: 'Z-Image Turbo',
value: 'z-image-turbo',
description: 'Fast image generation model',
},
],
default: 'z-image-turbo',
description: 'The model to use for image generation',
displayOptions: {
show: { '@version': [1] },
},
},
{
...(0, descriptions_1.modelRLC)('imageGenerationModelSearch'),
displayOptions: {
show: { '@version': [{ _cnd: { gte: 1.1 } }] },
},
},
{
displayName: 'Prompt',
name: 'prompt',
type: 'string',
typeOptions: {
rows: 4,
},
default: '',
description: 'The text prompt describing the image to generate',
required: true,
},
{
displayName: 'Attach reference images for Wan image editing. Use a Wan t2i model for text-only generate.',
name: 'wanImageEditNotice',
type: 'notice',
default: '',
displayOptions: {
show: {
'/modelId': [{ _cnd: { includes: 'wan' } }],
},
hide: {
'/modelId': [{ _cnd: { includes: '-t2i' } }],
},
},
},
{
displayName: 'Reference Images',
name: 'referenceImages',
type: 'fixedCollection',
placeholder: 'Add Image',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add Image',
},
default: {
values: [{ inputType: 'url', imageUrl: '', binaryPropertyName: 'data' }],
},
description: '1 to 4 reference images for Wan image editing',
displayOptions: {
show: {
'/modelId': [{ _cnd: { includes: 'wan' } }],
},
hide: {
'/modelId': [{ _cnd: { includes: '-t2i' } }],
},
},
options: [
{
displayName: 'Image',
name: 'values',
values: [
{
displayName: 'Input Type',
name: 'inputType',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
},
{
name: 'Binary Data',
value: 'binary',
},
],
default: 'url',
},
{
displayName: 'Image URL',
name: 'imageUrl',
type: 'string',
default: '',
placeholder: 'https://example.com/image.png',
displayOptions: {
show: {
inputType: ['url'],
},
},
},
{
displayName: 'Input Data Field Name',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
placeholder: 'e.g. data',
hint: 'The name of the input field containing the binary image data',
typeOptions: {
binaryDataProperty: true,
},
displayOptions: {
show: {
inputType: ['binary'],
},
},
},
],
},
],
},
{
displayName: 'Download Image',
name: 'downloadImage',
type: 'boolean',
default: true,
description: 'Whether to download the generated image as binary data. When disabled, only the image URL is returned.',
},
{
displayName: 'Options',
name: 'imageOptions',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Size',
name: 'size',
type: 'options',
displayOptions: {
show: {
'/modelId': ['z-image-turbo'],
},
},
options: [
{
name: '1024x1024',
value: '1024*1024',
},
{
name: '720x1280',
value: '720*1280',
},
{
name: '1280x720',
value: '1280*720',
},
],
default: '1024*1024',
description: 'The size of the generated image',
},
{
displayName: 'Size',
name: 'size',
type: 'options',
displayOptions: {
show: {
'/modelId': ['wan2.6-t2i', 'wan2.6-image'],
},
},
options: [
{
name: '1104x1472 (3:4)',
value: '1104*1472',
},
{
name: '1280x1280 (1:1)',
value: '1280*1280',
},
{
name: '1472x1104 (4:3)',
value: '1472*1104',
},
{
name: '1696x960 (16:9)',
value: '1696*960',
},
{
name: '960x1696 (9:16)',
value: '960*1696',
},
],
default: '1280*1280',
description: 'The size of the generated image',
},
{
displayName: 'Size',
name: 'size',
type: 'options',
displayOptions: {
show: {
'/modelId': ['qwen-image', 'qwen-image-plus', 'qwen-image-max'],
},
},
options: [
{
name: '1104x1472 (3:4)',
value: '1104*1472',
},
{
name: '1328x1328 (1:1)',
value: '1328*1328',
},
{
name: '1472x1104 (4:3)',
value: '1472*1104',
},
{
name: '1664x928 (16:9)',
value: '1664*928',
},
{
name: '928x1664 (9:16)',
value: '928*1664',
},
],
default: '1664*928',
description: 'The size of the generated image',
},
{
displayName: 'Prompt Extend',
name: 'promptExtend',
type: 'boolean',
default: false,
description: 'Whether to automatically extend and enhance the prompt',
},
],
},
];
const displayOptions = {
show: {
operation: ['generate'],
resource: ['image'],
},
};
exports.description = (0, n8n_workflow_1.updateDisplayOptions)(displayOptions, properties);
function toImageDataUri(buffer, mimeType, imageUrl, ctx, itemIndex) {
if (buffer.length > WAN_IMAGE_MAX_BYTES) {
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Reference image exceeds 10 MB: ${imageUrl}`, {
itemIndex,
});
}
return `data:${mimeType};base64,${buffer.toString('base64')}`;
}
async function downloadImageAsDataUri(ctx, imageUrl, itemIndex) {
if (imageUrl.startsWith('data:')) {
return imageUrl;
}
const imageResponse = await ctx.helpers.httpRequest({
method: 'GET',
url: imageUrl,
encoding: 'arraybuffer',
returnFullResponse: true,
headers: {
'User-Agent': 'n8n (https://n8n.io)',
},
});
const contentType = imageResponse.headers?.['content-type'] ?? 'image/png';
const mimeType = contentType.split(';')[0]?.trim() || 'image/png';
const buffer = Buffer.from(imageResponse.body);
return toImageDataUri(buffer, mimeType, imageUrl, ctx, itemIndex);
}
async function resolveReferenceImages(ctx, itemIndex) {
const collection = ctx.getNodeParameter('referenceImages', itemIndex, {});
const uris = [];
for (const entry of collection.values ?? []) {
if (entry.inputType === 'binary') {
const propertyName = entry.binaryPropertyName ?? 'data';
const binaryData = ctx.helpers.assertBinaryData(itemIndex, propertyName);
const buffer = await ctx.helpers.getBinaryDataBuffer(itemIndex, propertyName);
const mimeType = binaryData.mimeType ?? 'image/png';
uris.push(toImageDataUri(buffer, mimeType, propertyName, ctx, itemIndex));
}
else if (entry.imageUrl) {
uris.push(await downloadImageAsDataUri(ctx, entry.imageUrl, itemIndex));
}
}
return uris;
}
function extractGeneratedImageUrl(response) {
const output = response.output;
const choices = output?.choices;
const content = choices?.[0]?.message?.content;
if (Array.isArray(content)) {
const imagePart = content.find((part) => part?.image);
if (imagePart?.image) {
return imagePart.image;
}
}
const results = output?.results;
return results?.[0]?.url ?? '';
}
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 prompt = this.getNodeParameter('prompt', itemIndex);
const imageOptions = this.getNodeParameter('imageOptions', itemIndex, {});
const downloadImage = this.getNodeParameter('downloadImage', itemIndex, true);
const plan = (0, wanImage_1.planWanImage)(model);
let referenceImages = [];
if (plan.refs) {
referenceImages = await resolveReferenceImages(this, itemIndex);
if (plan.refs.required && referenceImages.length === 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `This Wan image-edit model needs 1 to ${plan.refs.max} reference images. Use a Wan t2i model for text-only generate.`, { itemIndex });
}
if (referenceImages.length > plan.refs.max) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `This Wan image-edit model accepts at most ${plan.refs.max} reference images.`, { itemIndex });
}
}
const parameters = {
prompt_extend: imageOptions.promptExtend ?? false,
};
if (plan.n !== null) {
parameters.n = plan.n;
}
if (plan.defaultSize !== null) {
parameters.size = imageOptions.size ?? plan.defaultSize;
}
else if (imageOptions.size) {
parameters.size = imageOptions.size;
}
const body = {
model,
input: plan.input === 'prompt'
? {
prompt,
...(plan.attachImages === 'input' ? { images: referenceImages } : {}),
}
: {
messages: [
{
role: 'user',
content: [
{ text: prompt },
...(plan.attachImages === 'content'
? referenceImages.map((image) => ({ image }))
: []),
],
},
],
},
parameters,
};
let response;
if (!plan.async) {
response = await transport_1.apiRequest.call(this, 'POST', plan.endpoint, { body });
}
else {
const createResponse = await transport_1.apiRequest.call(this, 'POST', plan.endpoint, {
headers: {
'X-DashScope-Async': 'enable',
},
body,
});
const taskId = createResponse?.output?.task_id;
if (!taskId) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to create image generation task: ${createResponse?.message || 'No task_id returned'}`);
}
response = await transport_1.pollTaskResult.call(this, taskId);
}
const imageUrl = extractGeneratedImageUrl(response);
if (downloadImage && imageUrl) {
const imageResponse = await this.helpers.httpRequest({
method: 'GET',
url: imageUrl,
encoding: 'arraybuffer',
returnFullResponse: true,
});
const contentType = imageResponse.headers?.['content-type'] || 'image/png';
const fileContent = Buffer.from(imageResponse.body);
const ext = contentType.includes('jpeg') || contentType.includes('jpg') ? 'jpg' : 'png';
const binaryData = await this.helpers.prepareBinaryData(fileContent, `image.${ext}`, contentType);
return {
binary: { data: binaryData },
json: {
model,
imageUrl,
usage: response.usage,
},
pairedItem: itemIndex,
};
}
return {
json: {
model,
imageUrl,
usage: response.usage,
},
pairedItem: itemIndex,
};
}
//# sourceMappingURL=generate.operation.js.map