n8n-nodes-pollinations-ai 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -16,7 +16,16 @@ Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes
|
|
|
16
16
|
2. Select **Install**
|
|
17
17
|
3. Enter `n8n-nodes-pollinations-ai` and confirm
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Nodes
|
|
20
|
+
|
|
21
|
+
This package includes two nodes:
|
|
22
|
+
|
|
23
|
+
| Node | Description |
|
|
24
|
+
|------|-------------|
|
|
25
|
+
| **Pollinations** | Main node for image and text generation |
|
|
26
|
+
| **Pollinations Chat Model** | Sub-node for AI Agent integration |
|
|
27
|
+
|
|
28
|
+
## Operations (Pollinations Node)
|
|
20
29
|
|
|
21
30
|
### Generate Image
|
|
22
31
|
|
|
@@ -40,6 +49,37 @@ Generate an image from a text prompt using Pollinations AI.
|
|
|
40
49
|
| Enhance Prompt | false | Automatically enhance the prompt |
|
|
41
50
|
| Safe Mode | false | Enable content safety filter |
|
|
42
51
|
|
|
52
|
+
### Generate with Reference
|
|
53
|
+
|
|
54
|
+
Generate an image using a reference image (image-to-image). Only models supporting image input are shown.
|
|
55
|
+
|
|
56
|
+
**Parameters:**
|
|
57
|
+
|
|
58
|
+
| Parameter | Required | Description |
|
|
59
|
+
|-----------|----------|-------------|
|
|
60
|
+
| Prompt | Yes | Text describing how to transform or use the reference image |
|
|
61
|
+
| Reference Image URL | Yes | URL of the reference image (must be publicly accessible) |
|
|
62
|
+
| Model | Yes | The model to use (only models with image input support) |
|
|
63
|
+
|
|
64
|
+
**Advanced Options:**
|
|
65
|
+
|
|
66
|
+
| Option | Default | Description |
|
|
67
|
+
|--------|---------|-------------|
|
|
68
|
+
| Width | 1024 | Width of the generated image (64-2048) |
|
|
69
|
+
| Height | 1024 | Height of the generated image (64-2048) |
|
|
70
|
+
| Seed | 0 | Seed for reproducible generation (0 = random) |
|
|
71
|
+
| No Logo | false | Remove the Pollinations watermark |
|
|
72
|
+
| Enhance Prompt | false | Automatically enhance the prompt |
|
|
73
|
+
| Safe Mode | false | Enable content safety filter |
|
|
74
|
+
|
|
75
|
+
**Supported Models:**
|
|
76
|
+
|
|
77
|
+
Models are filtered dynamically from the API. Common models supporting reference images include:
|
|
78
|
+
- FLUX.1 Kontext
|
|
79
|
+
- NanoBanana / NanoBanana Pro
|
|
80
|
+
- Seedream 4.0
|
|
81
|
+
- GPT Image
|
|
82
|
+
|
|
43
83
|
### Generate Text
|
|
44
84
|
|
|
45
85
|
Generate text from a prompt using AI language models.
|
|
@@ -60,6 +100,41 @@ Generate text from a prompt using AI language models.
|
|
|
60
100
|
| JSON Response | false | Force the response in JSON format (not supported by all models) |
|
|
61
101
|
| Seed | -1 | Seed for reproducible results (-1 = random) |
|
|
62
102
|
|
|
103
|
+
## Pollinations Chat Model (Sub-node)
|
|
104
|
+
|
|
105
|
+
A sub-node designed for use with **AI Agents** and **LLM Chains** in n8n. This node provides a language model that can be connected to AI workflow nodes.
|
|
106
|
+
|
|
107
|
+
### Use Cases
|
|
108
|
+
|
|
109
|
+
- Connect to **AI Agent** nodes for conversational AI workflows
|
|
110
|
+
- Use with **LLM Chain** nodes for text processing pipelines
|
|
111
|
+
- Build custom AI assistants using n8n's AI capabilities
|
|
112
|
+
|
|
113
|
+
### Parameters
|
|
114
|
+
|
|
115
|
+
| Parameter | Required | Description |
|
|
116
|
+
|-----------|----------|-------------|
|
|
117
|
+
| Model | Yes | The chat model to use (loaded dynamically from API) |
|
|
118
|
+
| Temperature | Yes | Controls randomness: 0 = deterministic, 2 = very creative (default: 1) |
|
|
119
|
+
|
|
120
|
+
### Advanced Options
|
|
121
|
+
|
|
122
|
+
| Option | Default | Description |
|
|
123
|
+
|--------|---------|-------------|
|
|
124
|
+
| Max Tokens | 0 | Maximum tokens in response (0 = model default) |
|
|
125
|
+
| Top P | 1 | Nucleus sampling parameter (0-1) |
|
|
126
|
+
| Frequency Penalty | 0 | Reduce repetition of token sequences (-2 to 2) |
|
|
127
|
+
| Presence Penalty | 0 | Increase likelihood of new topics (-2 to 2) |
|
|
128
|
+
| Timeout | 60000 | Request timeout in milliseconds |
|
|
129
|
+
|
|
130
|
+
### How to Use
|
|
131
|
+
|
|
132
|
+
1. Add an **AI Agent** or **LLM Chain** node to your workflow
|
|
133
|
+
2. Add a **Pollinations Chat Model** node
|
|
134
|
+
3. Connect the Chat Model output to the Agent/Chain model input
|
|
135
|
+
4. Configure your Pollinations API credentials
|
|
136
|
+
5. Select a model and adjust parameters as needed
|
|
137
|
+
|
|
63
138
|
## Credentials
|
|
64
139
|
|
|
65
140
|
To use this node, you need a Pollinations API key.
|
|
@@ -132,6 +207,22 @@ The output will be a binary image that you can:
|
|
|
132
207
|
- Send via email or messaging platforms
|
|
133
208
|
- Process with other image manipulation nodes
|
|
134
209
|
|
|
210
|
+
### Image-to-Image with Reference
|
|
211
|
+
|
|
212
|
+
1. Add a **Pollinations** node to your workflow
|
|
213
|
+
2. Select **Generate with Reference** operation
|
|
214
|
+
3. Select your Pollinations API credentials
|
|
215
|
+
4. Enter a prompt like "Transform into a watercolor painting"
|
|
216
|
+
5. Enter the URL of your reference image
|
|
217
|
+
6. Select a model (e.g., Kontext)
|
|
218
|
+
7. Execute the node
|
|
219
|
+
|
|
220
|
+
Use cases for reference images:
|
|
221
|
+
- **Style transfer**: Apply artistic styles to existing images
|
|
222
|
+
- **Consistent characters**: Generate variations of a character for tutorials
|
|
223
|
+
- **Image editing**: Modify specific aspects of an image with text prompts
|
|
224
|
+
- **Product variations**: Create different versions of product images
|
|
225
|
+
|
|
135
226
|
### Basic Text Generation
|
|
136
227
|
|
|
137
228
|
1. Add a **Pollinations** node to your workflow
|
|
@@ -144,6 +235,18 @@ The output will be a binary image that you can:
|
|
|
144
235
|
|
|
145
236
|
The output will be a JSON object with the generated text and metadata.
|
|
146
237
|
|
|
238
|
+
### AI Agent Integration
|
|
239
|
+
|
|
240
|
+
1. Add an **AI Agent** node to your workflow
|
|
241
|
+
2. Add a **Pollinations Chat Model** node
|
|
242
|
+
3. Connect the Chat Model to the Agent's model input
|
|
243
|
+
4. Configure your Pollinations API credentials on the Chat Model
|
|
244
|
+
5. Select a model (e.g., OpenAI, Claude, or DeepSeek)
|
|
245
|
+
6. Add tools to your Agent as needed
|
|
246
|
+
7. Execute the workflow
|
|
247
|
+
|
|
248
|
+
This setup allows you to build conversational AI assistants powered by Pollinations models.
|
|
249
|
+
|
|
147
250
|
## Compatibility
|
|
148
251
|
|
|
149
252
|
- n8n version: 1.0.0 or later
|
|
@@ -5,6 +5,7 @@ export declare class Pollinations implements INodeType {
|
|
|
5
5
|
loadOptions: {
|
|
6
6
|
getImageModels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
7
7
|
getTextModels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
8
|
+
getImageModelsWithReferenceSupport(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
8
9
|
};
|
|
9
10
|
};
|
|
10
11
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Pollinations = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
4
5
|
class Pollinations {
|
|
5
6
|
constructor() {
|
|
6
7
|
this.description = {
|
|
@@ -36,6 +37,12 @@ class Pollinations {
|
|
|
36
37
|
description: 'Generate an image from a text prompt',
|
|
37
38
|
action: 'Generate an image from a text prompt',
|
|
38
39
|
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Generate with Reference',
|
|
42
|
+
value: 'generateImageWithReference',
|
|
43
|
+
description: 'Generate an image using a reference image',
|
|
44
|
+
action: 'Generate an image using a reference image',
|
|
45
|
+
},
|
|
39
46
|
{
|
|
40
47
|
name: 'Generate Text',
|
|
41
48
|
value: 'generateText',
|
|
@@ -144,6 +151,120 @@ class Pollinations {
|
|
|
144
151
|
},
|
|
145
152
|
],
|
|
146
153
|
},
|
|
154
|
+
// ==================== GENERATE IMAGE WITH REFERENCE ====================
|
|
155
|
+
// Prompt (Reference)
|
|
156
|
+
{
|
|
157
|
+
displayName: 'Prompt',
|
|
158
|
+
name: 'referencePrompt',
|
|
159
|
+
type: 'string',
|
|
160
|
+
default: '',
|
|
161
|
+
required: true,
|
|
162
|
+
displayOptions: {
|
|
163
|
+
show: {
|
|
164
|
+
operation: ['generateImageWithReference'],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
description: 'The text prompt describing how to transform or use the reference image',
|
|
168
|
+
typeOptions: {
|
|
169
|
+
rows: 4,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
// Reference Image URL (Required)
|
|
173
|
+
{
|
|
174
|
+
displayName: 'Reference Image URL',
|
|
175
|
+
name: 'referenceImage',
|
|
176
|
+
type: 'string',
|
|
177
|
+
default: '',
|
|
178
|
+
required: true,
|
|
179
|
+
displayOptions: {
|
|
180
|
+
show: {
|
|
181
|
+
operation: ['generateImageWithReference'],
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
placeholder: 'https://example.com/image.jpg',
|
|
185
|
+
description: 'URL of the reference image. Must be publicly accessible.',
|
|
186
|
+
},
|
|
187
|
+
// Model (Reference) - Dynamic loading with image input support
|
|
188
|
+
{
|
|
189
|
+
displayName: 'Model',
|
|
190
|
+
name: 'referenceModel',
|
|
191
|
+
type: 'options',
|
|
192
|
+
default: 'kontext',
|
|
193
|
+
displayOptions: {
|
|
194
|
+
show: {
|
|
195
|
+
operation: ['generateImageWithReference'],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
typeOptions: {
|
|
199
|
+
loadOptionsMethod: 'getImageModelsWithReferenceSupport',
|
|
200
|
+
},
|
|
201
|
+
description: 'The model to use. Only models supporting image input are shown.',
|
|
202
|
+
},
|
|
203
|
+
// Advanced Options (Reference)
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Options',
|
|
206
|
+
name: 'referenceOptions',
|
|
207
|
+
type: 'collection',
|
|
208
|
+
placeholder: 'Add Option',
|
|
209
|
+
default: {},
|
|
210
|
+
displayOptions: {
|
|
211
|
+
show: {
|
|
212
|
+
operation: ['generateImageWithReference'],
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
options: [
|
|
216
|
+
{
|
|
217
|
+
displayName: 'Width',
|
|
218
|
+
name: 'width',
|
|
219
|
+
type: 'number',
|
|
220
|
+
default: 1024,
|
|
221
|
+
description: 'Width of the generated image in pixels',
|
|
222
|
+
typeOptions: {
|
|
223
|
+
minValue: 64,
|
|
224
|
+
maxValue: 2048,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
displayName: 'Height',
|
|
229
|
+
name: 'height',
|
|
230
|
+
type: 'number',
|
|
231
|
+
default: 1024,
|
|
232
|
+
description: 'Height of the generated image in pixels',
|
|
233
|
+
typeOptions: {
|
|
234
|
+
minValue: 64,
|
|
235
|
+
maxValue: 2048,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
displayName: 'Seed',
|
|
240
|
+
name: 'seed',
|
|
241
|
+
type: 'number',
|
|
242
|
+
default: 0,
|
|
243
|
+
description: 'Seed for reproducible generation. Use 0 for random.',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
displayName: 'No Logo',
|
|
247
|
+
name: 'nologo',
|
|
248
|
+
type: 'boolean',
|
|
249
|
+
default: false,
|
|
250
|
+
description: 'Whether to remove the Pollinations watermark',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
displayName: 'Enhance Prompt',
|
|
254
|
+
name: 'enhance',
|
|
255
|
+
type: 'boolean',
|
|
256
|
+
default: false,
|
|
257
|
+
description: 'Whether to automatically enhance the prompt for better results',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
displayName: 'Safe Mode',
|
|
261
|
+
name: 'safe',
|
|
262
|
+
type: 'boolean',
|
|
263
|
+
default: false,
|
|
264
|
+
description: 'Whether to enable content safety filter',
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
},
|
|
147
268
|
// ==================== GENERATE TEXT ====================
|
|
148
269
|
// Prompt (Text)
|
|
149
270
|
{
|
|
@@ -353,6 +474,55 @@ class Pollinations {
|
|
|
353
474
|
];
|
|
354
475
|
}
|
|
355
476
|
},
|
|
477
|
+
async getImageModelsWithReferenceSupport() {
|
|
478
|
+
try {
|
|
479
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
480
|
+
const apiKey = credentials.apiKey;
|
|
481
|
+
const response = await this.helpers.httpRequest({
|
|
482
|
+
method: 'GET',
|
|
483
|
+
url: 'https://gen.pollinations.ai/image/models',
|
|
484
|
+
headers: {
|
|
485
|
+
Authorization: `Bearer ${apiKey}`,
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
if (Array.isArray(response)) {
|
|
489
|
+
// Filter only image models that support image input (for reference/transformation)
|
|
490
|
+
const imageModels = response.filter((model) => model.output_modalities?.includes('image') &&
|
|
491
|
+
!model.output_modalities?.includes('video') &&
|
|
492
|
+
model.input_modalities?.includes('image'));
|
|
493
|
+
return imageModels.map((model) => {
|
|
494
|
+
let displayName = model.description || model.name;
|
|
495
|
+
// Add pricing info if available
|
|
496
|
+
if (model.pricing?.completionImageTokens) {
|
|
497
|
+
const imagesPerPollen = Math.floor(1 / model.pricing.completionImageTokens);
|
|
498
|
+
displayName += ` (~${imagesPerPollen.toLocaleString()} img/$)`;
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
name: displayName,
|
|
502
|
+
value: model.name,
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
// Fallback if API fails
|
|
507
|
+
return [
|
|
508
|
+
{ name: 'FLUX.1 Kontext', value: 'kontext' },
|
|
509
|
+
{ name: 'NanoBanana', value: 'nanobanana' },
|
|
510
|
+
{ name: 'NanoBanana Pro', value: 'nanobanana-pro' },
|
|
511
|
+
{ name: 'Seedream 4.0', value: 'seedream' },
|
|
512
|
+
{ name: 'GPT Image 1 Mini', value: 'gptimage' },
|
|
513
|
+
];
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
// Fallback if API fails
|
|
517
|
+
return [
|
|
518
|
+
{ name: 'FLUX.1 Kontext', value: 'kontext' },
|
|
519
|
+
{ name: 'NanoBanana', value: 'nanobanana' },
|
|
520
|
+
{ name: 'NanoBanana Pro', value: 'nanobanana-pro' },
|
|
521
|
+
{ name: 'Seedream 4.0', value: 'seedream' },
|
|
522
|
+
{ name: 'GPT Image 1 Mini', value: 'gptimage' },
|
|
523
|
+
];
|
|
524
|
+
}
|
|
525
|
+
},
|
|
356
526
|
},
|
|
357
527
|
};
|
|
358
528
|
}
|
|
@@ -516,6 +686,94 @@ class Pollinations {
|
|
|
516
686
|
json: metadata,
|
|
517
687
|
});
|
|
518
688
|
}
|
|
689
|
+
if (operation === 'generateImageWithReference') {
|
|
690
|
+
const prompt = this.getNodeParameter('referencePrompt', i);
|
|
691
|
+
const referenceImage = this.getNodeParameter('referenceImage', i);
|
|
692
|
+
const model = this.getNodeParameter('referenceModel', i);
|
|
693
|
+
const options = this.getNodeParameter('referenceOptions', i, {});
|
|
694
|
+
// Validate reference image URL
|
|
695
|
+
try {
|
|
696
|
+
new URL(referenceImage);
|
|
697
|
+
}
|
|
698
|
+
catch {
|
|
699
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid reference image URL: "${referenceImage}"`, { itemIndex: i });
|
|
700
|
+
}
|
|
701
|
+
// Get credentials
|
|
702
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
703
|
+
const apiKey = credentials.apiKey;
|
|
704
|
+
// Build query parameters
|
|
705
|
+
const queryParams = {
|
|
706
|
+
model,
|
|
707
|
+
image: referenceImage,
|
|
708
|
+
};
|
|
709
|
+
if (options.width) {
|
|
710
|
+
queryParams.width = options.width.toString();
|
|
711
|
+
}
|
|
712
|
+
if (options.height) {
|
|
713
|
+
queryParams.height = options.height.toString();
|
|
714
|
+
}
|
|
715
|
+
if (options.seed) {
|
|
716
|
+
queryParams.seed = options.seed.toString();
|
|
717
|
+
}
|
|
718
|
+
if (options.nologo) {
|
|
719
|
+
queryParams.nologo = 'true';
|
|
720
|
+
}
|
|
721
|
+
if (options.enhance) {
|
|
722
|
+
queryParams.enhance = 'true';
|
|
723
|
+
}
|
|
724
|
+
if (options.safe) {
|
|
725
|
+
queryParams.safe = 'true';
|
|
726
|
+
}
|
|
727
|
+
// Build the URL
|
|
728
|
+
const baseUrl = 'https://gen.pollinations.ai/image';
|
|
729
|
+
const encodedPrompt = encodeURIComponent(prompt);
|
|
730
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
|
731
|
+
const fullUrl = `${baseUrl}/${encodedPrompt}?${queryString}`;
|
|
732
|
+
// Record start time
|
|
733
|
+
const startTime = Date.now();
|
|
734
|
+
// Make the request with authentication
|
|
735
|
+
const response = await this.helpers.httpRequest({
|
|
736
|
+
method: 'GET',
|
|
737
|
+
url: fullUrl,
|
|
738
|
+
headers: {
|
|
739
|
+
Authorization: `Bearer ${apiKey}`,
|
|
740
|
+
},
|
|
741
|
+
encoding: 'arraybuffer',
|
|
742
|
+
returnFullResponse: true,
|
|
743
|
+
});
|
|
744
|
+
// Calculate duration
|
|
745
|
+
const duration = Date.now() - startTime;
|
|
746
|
+
// Prepare binary data
|
|
747
|
+
const binaryData = await this.helpers.prepareBinaryData(Buffer.from(response.body), 'image.png', 'image/png');
|
|
748
|
+
// Build metadata for debugging
|
|
749
|
+
const metadata = {
|
|
750
|
+
request: {
|
|
751
|
+
url: fullUrl,
|
|
752
|
+
prompt,
|
|
753
|
+
model,
|
|
754
|
+
referenceImage,
|
|
755
|
+
width: options.width || 1024,
|
|
756
|
+
height: options.height || 1024,
|
|
757
|
+
seed: options.seed || null,
|
|
758
|
+
nologo: options.nologo || false,
|
|
759
|
+
enhance: options.enhance || false,
|
|
760
|
+
safe: options.safe || false,
|
|
761
|
+
},
|
|
762
|
+
response: {
|
|
763
|
+
statusCode: response.statusCode,
|
|
764
|
+
contentType: response.headers?.['content-type'] || 'image/png',
|
|
765
|
+
contentLength: response.headers?.['content-length'] || null,
|
|
766
|
+
duration: `${duration}ms`,
|
|
767
|
+
},
|
|
768
|
+
timestamp: new Date().toISOString(),
|
|
769
|
+
};
|
|
770
|
+
returnData.push({
|
|
771
|
+
json: metadata,
|
|
772
|
+
binary: {
|
|
773
|
+
data: binaryData,
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
}
|
|
519
777
|
}
|
|
520
778
|
return [returnData];
|
|
521
779
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
INodePropertyOptions,
|
|
6
6
|
INodeType,
|
|
7
7
|
INodeTypeDescription,
|
|
8
|
+
NodeOperationError,
|
|
8
9
|
} from 'n8n-workflow';
|
|
9
10
|
|
|
10
11
|
export class Pollinations implements INodeType {
|
|
@@ -41,6 +42,12 @@ export class Pollinations implements INodeType {
|
|
|
41
42
|
description: 'Generate an image from a text prompt',
|
|
42
43
|
action: 'Generate an image from a text prompt',
|
|
43
44
|
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Generate with Reference',
|
|
47
|
+
value: 'generateImageWithReference',
|
|
48
|
+
description: 'Generate an image using a reference image',
|
|
49
|
+
action: 'Generate an image using a reference image',
|
|
50
|
+
},
|
|
44
51
|
{
|
|
45
52
|
name: 'Generate Text',
|
|
46
53
|
value: 'generateText',
|
|
@@ -154,6 +161,125 @@ export class Pollinations implements INodeType {
|
|
|
154
161
|
],
|
|
155
162
|
},
|
|
156
163
|
|
|
164
|
+
// ==================== GENERATE IMAGE WITH REFERENCE ====================
|
|
165
|
+
|
|
166
|
+
// Prompt (Reference)
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Prompt',
|
|
169
|
+
name: 'referencePrompt',
|
|
170
|
+
type: 'string',
|
|
171
|
+
default: '',
|
|
172
|
+
required: true,
|
|
173
|
+
displayOptions: {
|
|
174
|
+
show: {
|
|
175
|
+
operation: ['generateImageWithReference'],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
description: 'The text prompt describing how to transform or use the reference image',
|
|
179
|
+
typeOptions: {
|
|
180
|
+
rows: 4,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// Reference Image URL (Required)
|
|
185
|
+
{
|
|
186
|
+
displayName: 'Reference Image URL',
|
|
187
|
+
name: 'referenceImage',
|
|
188
|
+
type: 'string',
|
|
189
|
+
default: '',
|
|
190
|
+
required: true,
|
|
191
|
+
displayOptions: {
|
|
192
|
+
show: {
|
|
193
|
+
operation: ['generateImageWithReference'],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
placeholder: 'https://example.com/image.jpg',
|
|
197
|
+
description: 'URL of the reference image. Must be publicly accessible.',
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
// Model (Reference) - Dynamic loading with image input support
|
|
201
|
+
{
|
|
202
|
+
displayName: 'Model',
|
|
203
|
+
name: 'referenceModel',
|
|
204
|
+
type: 'options',
|
|
205
|
+
default: 'kontext',
|
|
206
|
+
displayOptions: {
|
|
207
|
+
show: {
|
|
208
|
+
operation: ['generateImageWithReference'],
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
typeOptions: {
|
|
212
|
+
loadOptionsMethod: 'getImageModelsWithReferenceSupport',
|
|
213
|
+
},
|
|
214
|
+
description: 'The model to use. Only models supporting image input are shown.',
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
// Advanced Options (Reference)
|
|
218
|
+
{
|
|
219
|
+
displayName: 'Options',
|
|
220
|
+
name: 'referenceOptions',
|
|
221
|
+
type: 'collection',
|
|
222
|
+
placeholder: 'Add Option',
|
|
223
|
+
default: {},
|
|
224
|
+
displayOptions: {
|
|
225
|
+
show: {
|
|
226
|
+
operation: ['generateImageWithReference'],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
options: [
|
|
230
|
+
{
|
|
231
|
+
displayName: 'Width',
|
|
232
|
+
name: 'width',
|
|
233
|
+
type: 'number',
|
|
234
|
+
default: 1024,
|
|
235
|
+
description: 'Width of the generated image in pixels',
|
|
236
|
+
typeOptions: {
|
|
237
|
+
minValue: 64,
|
|
238
|
+
maxValue: 2048,
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
displayName: 'Height',
|
|
243
|
+
name: 'height',
|
|
244
|
+
type: 'number',
|
|
245
|
+
default: 1024,
|
|
246
|
+
description: 'Height of the generated image in pixels',
|
|
247
|
+
typeOptions: {
|
|
248
|
+
minValue: 64,
|
|
249
|
+
maxValue: 2048,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
displayName: 'Seed',
|
|
254
|
+
name: 'seed',
|
|
255
|
+
type: 'number',
|
|
256
|
+
default: 0,
|
|
257
|
+
description: 'Seed for reproducible generation. Use 0 for random.',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
displayName: 'No Logo',
|
|
261
|
+
name: 'nologo',
|
|
262
|
+
type: 'boolean',
|
|
263
|
+
default: false,
|
|
264
|
+
description: 'Whether to remove the Pollinations watermark',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
displayName: 'Enhance Prompt',
|
|
268
|
+
name: 'enhance',
|
|
269
|
+
type: 'boolean',
|
|
270
|
+
default: false,
|
|
271
|
+
description: 'Whether to automatically enhance the prompt for better results',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
displayName: 'Safe Mode',
|
|
275
|
+
name: 'safe',
|
|
276
|
+
type: 'boolean',
|
|
277
|
+
default: false,
|
|
278
|
+
description: 'Whether to enable content safety filter',
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
|
|
157
283
|
// ==================== GENERATE TEXT ====================
|
|
158
284
|
|
|
159
285
|
// Prompt (Text)
|
|
@@ -399,6 +525,72 @@ export class Pollinations implements INodeType {
|
|
|
399
525
|
];
|
|
400
526
|
}
|
|
401
527
|
},
|
|
528
|
+
|
|
529
|
+
async getImageModelsWithReferenceSupport(
|
|
530
|
+
this: ILoadOptionsFunctions,
|
|
531
|
+
): Promise<INodePropertyOptions[]> {
|
|
532
|
+
try {
|
|
533
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
534
|
+
const apiKey = credentials.apiKey as string;
|
|
535
|
+
|
|
536
|
+
const response = await this.helpers.httpRequest({
|
|
537
|
+
method: 'GET',
|
|
538
|
+
url: 'https://gen.pollinations.ai/image/models',
|
|
539
|
+
headers: {
|
|
540
|
+
Authorization: `Bearer ${apiKey}`,
|
|
541
|
+
},
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
if (Array.isArray(response)) {
|
|
545
|
+
// Filter only image models that support image input (for reference/transformation)
|
|
546
|
+
const imageModels = response.filter(
|
|
547
|
+
(model: { output_modalities?: string[]; input_modalities?: string[] }) =>
|
|
548
|
+
model.output_modalities?.includes('image') &&
|
|
549
|
+
!model.output_modalities?.includes('video') &&
|
|
550
|
+
model.input_modalities?.includes('image'),
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
return imageModels.map(
|
|
554
|
+
(model: {
|
|
555
|
+
name: string;
|
|
556
|
+
description: string;
|
|
557
|
+
pricing?: { completionImageTokens?: number };
|
|
558
|
+
}) => {
|
|
559
|
+
let displayName = model.description || model.name;
|
|
560
|
+
|
|
561
|
+
// Add pricing info if available
|
|
562
|
+
if (model.pricing?.completionImageTokens) {
|
|
563
|
+
const imagesPerPollen = Math.floor(1 / model.pricing.completionImageTokens);
|
|
564
|
+
displayName += ` (~${imagesPerPollen.toLocaleString()} img/$)`;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return {
|
|
568
|
+
name: displayName,
|
|
569
|
+
value: model.name,
|
|
570
|
+
};
|
|
571
|
+
},
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Fallback if API fails
|
|
576
|
+
return [
|
|
577
|
+
{ name: 'FLUX.1 Kontext', value: 'kontext' },
|
|
578
|
+
{ name: 'NanoBanana', value: 'nanobanana' },
|
|
579
|
+
{ name: 'NanoBanana Pro', value: 'nanobanana-pro' },
|
|
580
|
+
{ name: 'Seedream 4.0', value: 'seedream' },
|
|
581
|
+
{ name: 'GPT Image 1 Mini', value: 'gptimage' },
|
|
582
|
+
];
|
|
583
|
+
} catch {
|
|
584
|
+
// Fallback if API fails
|
|
585
|
+
return [
|
|
586
|
+
{ name: 'FLUX.1 Kontext', value: 'kontext' },
|
|
587
|
+
{ name: 'NanoBanana', value: 'nanobanana' },
|
|
588
|
+
{ name: 'NanoBanana Pro', value: 'nanobanana-pro' },
|
|
589
|
+
{ name: 'Seedream 4.0', value: 'seedream' },
|
|
590
|
+
{ name: 'GPT Image 1 Mini', value: 'gptimage' },
|
|
591
|
+
];
|
|
592
|
+
}
|
|
593
|
+
},
|
|
402
594
|
},
|
|
403
595
|
};
|
|
404
596
|
|
|
@@ -599,6 +791,120 @@ export class Pollinations implements INodeType {
|
|
|
599
791
|
json: metadata,
|
|
600
792
|
});
|
|
601
793
|
}
|
|
794
|
+
|
|
795
|
+
if (operation === 'generateImageWithReference') {
|
|
796
|
+
const prompt = this.getNodeParameter('referencePrompt', i) as string;
|
|
797
|
+
const referenceImage = this.getNodeParameter('referenceImage', i) as string;
|
|
798
|
+
const model = this.getNodeParameter('referenceModel', i) as string;
|
|
799
|
+
const options = this.getNodeParameter('referenceOptions', i, {}) as {
|
|
800
|
+
width?: number;
|
|
801
|
+
height?: number;
|
|
802
|
+
seed?: number;
|
|
803
|
+
nologo?: boolean;
|
|
804
|
+
enhance?: boolean;
|
|
805
|
+
safe?: boolean;
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
// Validate reference image URL
|
|
809
|
+
try {
|
|
810
|
+
new URL(referenceImage);
|
|
811
|
+
} catch {
|
|
812
|
+
throw new NodeOperationError(
|
|
813
|
+
this.getNode(),
|
|
814
|
+
`Invalid reference image URL: "${referenceImage}"`,
|
|
815
|
+
{ itemIndex: i },
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Get credentials
|
|
820
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
821
|
+
const apiKey = credentials.apiKey as string;
|
|
822
|
+
|
|
823
|
+
// Build query parameters
|
|
824
|
+
const queryParams: Record<string, string> = {
|
|
825
|
+
model,
|
|
826
|
+
image: referenceImage,
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
if (options.width) {
|
|
830
|
+
queryParams.width = options.width.toString();
|
|
831
|
+
}
|
|
832
|
+
if (options.height) {
|
|
833
|
+
queryParams.height = options.height.toString();
|
|
834
|
+
}
|
|
835
|
+
if (options.seed) {
|
|
836
|
+
queryParams.seed = options.seed.toString();
|
|
837
|
+
}
|
|
838
|
+
if (options.nologo) {
|
|
839
|
+
queryParams.nologo = 'true';
|
|
840
|
+
}
|
|
841
|
+
if (options.enhance) {
|
|
842
|
+
queryParams.enhance = 'true';
|
|
843
|
+
}
|
|
844
|
+
if (options.safe) {
|
|
845
|
+
queryParams.safe = 'true';
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Build the URL
|
|
849
|
+
const baseUrl = 'https://gen.pollinations.ai/image';
|
|
850
|
+
const encodedPrompt = encodeURIComponent(prompt);
|
|
851
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
|
852
|
+
const fullUrl = `${baseUrl}/${encodedPrompt}?${queryString}`;
|
|
853
|
+
|
|
854
|
+
// Record start time
|
|
855
|
+
const startTime = Date.now();
|
|
856
|
+
|
|
857
|
+
// Make the request with authentication
|
|
858
|
+
const response = await this.helpers.httpRequest({
|
|
859
|
+
method: 'GET',
|
|
860
|
+
url: fullUrl,
|
|
861
|
+
headers: {
|
|
862
|
+
Authorization: `Bearer ${apiKey}`,
|
|
863
|
+
},
|
|
864
|
+
encoding: 'arraybuffer',
|
|
865
|
+
returnFullResponse: true,
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// Calculate duration
|
|
869
|
+
const duration = Date.now() - startTime;
|
|
870
|
+
|
|
871
|
+
// Prepare binary data
|
|
872
|
+
const binaryData = await this.helpers.prepareBinaryData(
|
|
873
|
+
Buffer.from(response.body as ArrayBuffer),
|
|
874
|
+
'image.png',
|
|
875
|
+
'image/png',
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
// Build metadata for debugging
|
|
879
|
+
const metadata = {
|
|
880
|
+
request: {
|
|
881
|
+
url: fullUrl,
|
|
882
|
+
prompt,
|
|
883
|
+
model,
|
|
884
|
+
referenceImage,
|
|
885
|
+
width: options.width || 1024,
|
|
886
|
+
height: options.height || 1024,
|
|
887
|
+
seed: options.seed || null,
|
|
888
|
+
nologo: options.nologo || false,
|
|
889
|
+
enhance: options.enhance || false,
|
|
890
|
+
safe: options.safe || false,
|
|
891
|
+
},
|
|
892
|
+
response: {
|
|
893
|
+
statusCode: response.statusCode,
|
|
894
|
+
contentType: response.headers?.['content-type'] || 'image/png',
|
|
895
|
+
contentLength: response.headers?.['content-length'] || null,
|
|
896
|
+
duration: `${duration}ms`,
|
|
897
|
+
},
|
|
898
|
+
timestamp: new Date().toISOString(),
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
returnData.push({
|
|
902
|
+
json: metadata,
|
|
903
|
+
binary: {
|
|
904
|
+
data: binaryData,
|
|
905
|
+
},
|
|
906
|
+
});
|
|
907
|
+
}
|
|
602
908
|
}
|
|
603
909
|
|
|
604
910
|
return [returnData];
|