n8n-nodes-pollinations-ai 1.2.1 → 1.3.0

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.
@@ -2,6 +2,7 @@ import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodePro
2
2
  export declare class PollinationsApi implements ICredentialType {
3
3
  name: string;
4
4
  displayName: string;
5
+ icon: "file:pollinations.svg";
5
6
  documentationUrl: string;
6
7
  properties: INodeProperties[];
7
8
  authenticate: IAuthenticateGeneric;
@@ -5,6 +5,7 @@ class PollinationsApi {
5
5
  constructor() {
6
6
  this.name = 'pollinationsApi';
7
7
  this.displayName = 'Pollinations API';
8
+ this.icon = 'file:pollinations.svg';
8
9
  this.documentationUrl = 'https://github.com/new-xmon-df/n8n-nodes-pollinations-ai#credentials';
9
10
  this.properties = [
10
11
  {
@@ -2,6 +2,55 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Pollinations = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
+ /**
6
+ * Handle HTTP errors from Pollinations API with user-friendly messages
7
+ */
8
+ function handlePollinationsError(error, node, itemIndex, context) {
9
+ const httpError = error;
10
+ const status = httpError.response?.status;
11
+ switch (status) {
12
+ case 400:
13
+ throw new n8n_workflow_1.NodeOperationError(node, `Invalid request: ${httpError.message || 'Check your input parameters'}`, { itemIndex });
14
+ case 401:
15
+ throw new n8n_workflow_1.NodeOperationError(node, 'Authentication failed. Please check your API key is valid.', { itemIndex });
16
+ case 403:
17
+ if (context === 'balance') {
18
+ throw new n8n_workflow_1.NodeOperationError(node, 'API key does not have "Balance" permission. Please generate a new API key at https://enter.pollinations.ai with the "Balance" permission enabled.', { itemIndex });
19
+ }
20
+ throw new n8n_workflow_1.NodeOperationError(node, 'Permission denied. Your API key may not have the required permissions for this operation.', { itemIndex });
21
+ case 429:
22
+ throw new n8n_workflow_1.NodeOperationError(node, 'Rate limit exceeded. Please wait a moment before trying again.', { itemIndex });
23
+ case 500:
24
+ case 502:
25
+ case 503:
26
+ throw new n8n_workflow_1.NodeOperationError(node, 'Pollinations API is temporarily unavailable. Please try again later.', { itemIndex });
27
+ default:
28
+ throw error;
29
+ }
30
+ }
31
+ async function checkMinimumBalance(context, minimumBalance, itemIndex) {
32
+ if (minimumBalance <= 0)
33
+ return;
34
+ const credentials = await context.getCredentials('pollinationsApi');
35
+ const apiKey = credentials.apiKey;
36
+ let response;
37
+ try {
38
+ response = await context.helpers.httpRequest({
39
+ method: 'GET',
40
+ url: 'https://gen.pollinations.ai/account/balance',
41
+ headers: {
42
+ Authorization: `Bearer ${apiKey}`,
43
+ },
44
+ });
45
+ }
46
+ catch (error) {
47
+ handlePollinationsError(error, context.getNode(), itemIndex, 'balance');
48
+ }
49
+ const currentBalance = response.balance;
50
+ if (currentBalance < minimumBalance) {
51
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Insufficient balance: ${currentBalance} pollens available, ${minimumBalance} required`, { itemIndex });
52
+ }
53
+ }
5
54
  class Pollinations {
6
55
  constructor() {
7
56
  this.description = {
@@ -49,6 +98,12 @@ class Pollinations {
49
98
  description: 'Generate text from a prompt using AI',
50
99
  action: 'Generate text from a prompt',
51
100
  },
101
+ {
102
+ name: 'Get Balance',
103
+ value: 'getBalance',
104
+ description: 'Get current pollen balance from your account',
105
+ action: 'Get current pollen balance',
106
+ },
52
107
  ],
53
108
  default: 'generateImage',
54
109
  },
@@ -72,10 +127,10 @@ class Pollinations {
72
127
  },
73
128
  // Model (Image) - Dynamic loading
74
129
  {
75
- displayName: 'Model',
130
+ displayName: 'Model Name or ID',
76
131
  name: 'model',
77
132
  type: 'options',
78
- default: 'flux',
133
+ default: '',
79
134
  displayOptions: {
80
135
  show: {
81
136
  operation: ['generateImage'],
@@ -84,7 +139,7 @@ class Pollinations {
84
139
  typeOptions: {
85
140
  loadOptionsMethod: 'getImageModels',
86
141
  },
87
- description: 'The model to use for image generation',
142
+ description: 'The model to use for image generation. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
88
143
  },
89
144
  // Advanced Options (Image)
90
145
  {
@@ -100,15 +155,11 @@ class Pollinations {
100
155
  },
101
156
  options: [
102
157
  {
103
- displayName: 'Width',
104
- name: 'width',
105
- type: 'number',
106
- default: 1024,
107
- description: 'Width of the generated image in pixels',
108
- typeOptions: {
109
- minValue: 64,
110
- maxValue: 2048,
111
- },
158
+ displayName: 'Enhance Prompt',
159
+ name: 'enhance',
160
+ type: 'boolean',
161
+ default: false,
162
+ description: 'Whether to automatically enhance the prompt for better results',
112
163
  },
113
164
  {
114
165
  displayName: 'Height',
@@ -122,11 +173,14 @@ class Pollinations {
122
173
  },
123
174
  },
124
175
  {
125
- displayName: 'Seed',
126
- name: 'seed',
176
+ displayName: 'Minimum Balance',
177
+ name: 'minimumBalance',
127
178
  type: 'number',
128
179
  default: 0,
129
- description: 'Seed for reproducible generation. Use 0 for random.',
180
+ description: 'Minimum pollen balance required to execute. Set to 0 to disable check.',
181
+ typeOptions: {
182
+ minValue: 0,
183
+ },
130
184
  },
131
185
  {
132
186
  displayName: 'No Logo',
@@ -135,13 +189,6 @@ class Pollinations {
135
189
  default: false,
136
190
  description: 'Whether to remove the Pollinations watermark',
137
191
  },
138
- {
139
- displayName: 'Enhance Prompt',
140
- name: 'enhance',
141
- type: 'boolean',
142
- default: false,
143
- description: 'Whether to automatically enhance the prompt for better results',
144
- },
145
192
  {
146
193
  displayName: 'Safe Mode',
147
194
  name: 'safe',
@@ -149,6 +196,24 @@ class Pollinations {
149
196
  default: false,
150
197
  description: 'Whether to enable content safety filter',
151
198
  },
199
+ {
200
+ displayName: 'Seed',
201
+ name: 'seed',
202
+ type: 'number',
203
+ default: 0,
204
+ description: 'Seed for reproducible generation. Use 0 for random.',
205
+ },
206
+ {
207
+ displayName: 'Width',
208
+ name: 'width',
209
+ type: 'number',
210
+ default: 1024,
211
+ description: 'Width of the generated image in pixels',
212
+ typeOptions: {
213
+ minValue: 64,
214
+ maxValue: 2048,
215
+ },
216
+ },
152
217
  ],
153
218
  },
154
219
  // ==================== GENERATE IMAGE WITH REFERENCE ====================
@@ -186,10 +251,10 @@ class Pollinations {
186
251
  },
187
252
  // Model (Reference) - Dynamic loading with image input support
188
253
  {
189
- displayName: 'Model',
254
+ displayName: 'Model Name or ID',
190
255
  name: 'referenceModel',
191
256
  type: 'options',
192
- default: 'kontext',
257
+ default: '',
193
258
  displayOptions: {
194
259
  show: {
195
260
  operation: ['generateImageWithReference'],
@@ -198,7 +263,7 @@ class Pollinations {
198
263
  typeOptions: {
199
264
  loadOptionsMethod: 'getImageModelsWithReferenceSupport',
200
265
  },
201
- description: 'The model to use. Only models supporting image input are shown.',
266
+ description: 'The model to use. Only models supporting image input are shown. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
202
267
  },
203
268
  // Advanced Options (Reference)
204
269
  {
@@ -214,15 +279,11 @@ class Pollinations {
214
279
  },
215
280
  options: [
216
281
  {
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
- },
282
+ displayName: 'Enhance Prompt',
283
+ name: 'enhance',
284
+ type: 'boolean',
285
+ default: false,
286
+ description: 'Whether to automatically enhance the prompt for better results',
226
287
  },
227
288
  {
228
289
  displayName: 'Height',
@@ -236,11 +297,14 @@ class Pollinations {
236
297
  },
237
298
  },
238
299
  {
239
- displayName: 'Seed',
240
- name: 'seed',
300
+ displayName: 'Minimum Balance',
301
+ name: 'minimumBalance',
241
302
  type: 'number',
242
303
  default: 0,
243
- description: 'Seed for reproducible generation. Use 0 for random.',
304
+ description: 'Minimum pollen balance required to execute. Set to 0 to disable check.',
305
+ typeOptions: {
306
+ minValue: 0,
307
+ },
244
308
  },
245
309
  {
246
310
  displayName: 'No Logo',
@@ -249,13 +313,6 @@ class Pollinations {
249
313
  default: false,
250
314
  description: 'Whether to remove the Pollinations watermark',
251
315
  },
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
316
  {
260
317
  displayName: 'Safe Mode',
261
318
  name: 'safe',
@@ -263,6 +320,24 @@ class Pollinations {
263
320
  default: false,
264
321
  description: 'Whether to enable content safety filter',
265
322
  },
323
+ {
324
+ displayName: 'Seed',
325
+ name: 'seed',
326
+ type: 'number',
327
+ default: 0,
328
+ description: 'Seed for reproducible generation. Use 0 for random.',
329
+ },
330
+ {
331
+ displayName: 'Width',
332
+ name: 'width',
333
+ type: 'number',
334
+ default: 1024,
335
+ description: 'Width of the generated image in pixels',
336
+ typeOptions: {
337
+ minValue: 64,
338
+ maxValue: 2048,
339
+ },
340
+ },
266
341
  ],
267
342
  },
268
343
  // ==================== GENERATE TEXT ====================
@@ -285,10 +360,10 @@ class Pollinations {
285
360
  },
286
361
  // Model (Text) - Dynamic loading
287
362
  {
288
- displayName: 'Model',
363
+ displayName: 'Model Name or ID',
289
364
  name: 'textModel',
290
365
  type: 'options',
291
- default: 'openai',
366
+ default: '',
292
367
  displayOptions: {
293
368
  show: {
294
369
  operation: ['generateText'],
@@ -297,7 +372,7 @@ class Pollinations {
297
372
  typeOptions: {
298
373
  loadOptionsMethod: 'getTextModels',
299
374
  },
300
- description: 'The AI model to use for text generation',
375
+ description: 'The AI model to use for text generation. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
301
376
  },
302
377
  // System Prompt (Text)
303
378
  {
@@ -354,6 +429,16 @@ class Pollinations {
354
429
  default: false,
355
430
  description: 'Whether to force the response in JSON format. Not supported by all models.',
356
431
  },
432
+ {
433
+ displayName: 'Minimum Balance',
434
+ name: 'minimumBalance',
435
+ type: 'number',
436
+ default: 0,
437
+ description: 'Minimum pollen balance required to execute. Set to 0 to disable check.',
438
+ typeOptions: {
439
+ minValue: 0,
440
+ },
441
+ },
357
442
  {
358
443
  displayName: 'Seed',
359
444
  name: 'seed',
@@ -364,6 +449,7 @@ class Pollinations {
364
449
  ],
365
450
  },
366
451
  ],
452
+ usableAsTool: true,
367
453
  };
368
454
  this.methods = {
369
455
  loadOptions: {
@@ -529,12 +615,37 @@ class Pollinations {
529
615
  async execute() {
530
616
  const items = this.getInputData();
531
617
  const returnData = [];
618
+ const operation = this.getNodeParameter('operation', 0);
619
+ // Handle account operations outside the loop (they don't depend on input items)
620
+ if (operation === 'getBalance') {
621
+ const credentials = await this.getCredentials('pollinationsApi');
622
+ const apiKey = credentials.apiKey;
623
+ let response;
624
+ try {
625
+ response = await this.helpers.httpRequest({
626
+ method: 'GET',
627
+ url: 'https://gen.pollinations.ai/account/balance',
628
+ headers: {
629
+ Authorization: `Bearer ${apiKey}`,
630
+ },
631
+ });
632
+ }
633
+ catch (error) {
634
+ handlePollinationsError(error, this.getNode(), 0, 'balance');
635
+ }
636
+ returnData.push({
637
+ json: response,
638
+ });
639
+ return [returnData];
640
+ }
532
641
  for (let i = 0; i < items.length; i++) {
533
- const operation = this.getNodeParameter('operation', i);
534
642
  if (operation === 'generateImage') {
535
643
  const prompt = this.getNodeParameter('prompt', i);
536
644
  const model = this.getNodeParameter('model', i);
537
645
  const options = this.getNodeParameter('options', i, {});
646
+ // Check minimum balance if configured
647
+ const minimumBalance = options.minimumBalance || 0;
648
+ await checkMinimumBalance(this, minimumBalance, i);
538
649
  // Get credentials
539
650
  const credentials = await this.getCredentials('pollinationsApi');
540
651
  const apiKey = credentials.apiKey;
@@ -568,15 +679,21 @@ class Pollinations {
568
679
  // Record start time
569
680
  const startTime = Date.now();
570
681
  // Make the request with authentication
571
- const response = await this.helpers.httpRequest({
572
- method: 'GET',
573
- url: fullUrl,
574
- headers: {
575
- Authorization: `Bearer ${apiKey}`,
576
- },
577
- encoding: 'arraybuffer',
578
- returnFullResponse: true,
579
- });
682
+ let response;
683
+ try {
684
+ response = await this.helpers.httpRequest({
685
+ method: 'GET',
686
+ url: fullUrl,
687
+ headers: {
688
+ Authorization: `Bearer ${apiKey}`,
689
+ },
690
+ encoding: 'arraybuffer',
691
+ returnFullResponse: true,
692
+ });
693
+ }
694
+ catch (error) {
695
+ handlePollinationsError(error, this.getNode(), i, 'image');
696
+ }
580
697
  // Calculate duration
581
698
  const duration = Date.now() - startTime;
582
699
  // Prepare binary data
@@ -616,6 +733,9 @@ class Pollinations {
616
733
  const temperature = this.getNodeParameter('temperature', i);
617
734
  const textOptions = this.getNodeParameter('textOptions', i, {});
618
735
  const jsonMode = textOptions.jsonMode || false;
736
+ // Check minimum balance if configured
737
+ const minimumBalance = textOptions.minimumBalance || 0;
738
+ await checkMinimumBalance(this, minimumBalance, i);
619
739
  // Get credentials
620
740
  const credentials = await this.getCredentials('pollinationsApi');
621
741
  const apiKey = credentials.apiKey;
@@ -641,14 +761,20 @@ class Pollinations {
641
761
  // Record start time
642
762
  const startTime = Date.now();
643
763
  // Make the request with authentication
644
- const response = await this.helpers.httpRequest({
645
- method: 'GET',
646
- url: fullUrl,
647
- headers: {
648
- Authorization: `Bearer ${apiKey}`,
649
- },
650
- returnFullResponse: true,
651
- });
764
+ let response;
765
+ try {
766
+ response = await this.helpers.httpRequest({
767
+ method: 'GET',
768
+ url: fullUrl,
769
+ headers: {
770
+ Authorization: `Bearer ${apiKey}`,
771
+ },
772
+ returnFullResponse: true,
773
+ });
774
+ }
775
+ catch (error) {
776
+ handlePollinationsError(error, this.getNode(), i, 'text');
777
+ }
652
778
  // Calculate duration
653
779
  const duration = Date.now() - startTime;
654
780
  // Parse response text
@@ -691,6 +817,9 @@ class Pollinations {
691
817
  const referenceImage = this.getNodeParameter('referenceImage', i);
692
818
  const model = this.getNodeParameter('referenceModel', i);
693
819
  const options = this.getNodeParameter('referenceOptions', i, {});
820
+ // Check minimum balance if configured
821
+ const minimumBalance = options.minimumBalance || 0;
822
+ await checkMinimumBalance(this, minimumBalance, i);
694
823
  // Validate reference image URL
695
824
  try {
696
825
  new URL(referenceImage);
@@ -732,15 +861,21 @@ class Pollinations {
732
861
  // Record start time
733
862
  const startTime = Date.now();
734
863
  // 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
- });
864
+ let response;
865
+ try {
866
+ response = await this.helpers.httpRequest({
867
+ method: 'GET',
868
+ url: fullUrl,
869
+ headers: {
870
+ Authorization: `Bearer ${apiKey}`,
871
+ },
872
+ encoding: 'arraybuffer',
873
+ returnFullResponse: true,
874
+ });
875
+ }
876
+ catch (error) {
877
+ handlePollinationsError(error, this.getNode(), i, 'image');
878
+ }
744
879
  // Calculate duration
745
880
  const duration = Date.now() - startTime;
746
881
  // Prepare binary data
@@ -40,14 +40,14 @@ class PollinationsChatModel {
40
40
  properties: [
41
41
  // Model - dynamic loading
42
42
  {
43
- displayName: 'Model',
43
+ displayName: 'Model Name or ID',
44
44
  name: 'model',
45
45
  type: 'options',
46
- default: 'openai',
46
+ default: '',
47
47
  typeOptions: {
48
48
  loadOptionsMethod: 'getChatModels',
49
49
  },
50
- description: 'The model to use for chat completions',
50
+ description: 'The model to use for chat completions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
51
51
  },
52
52
  // Temperature
53
53
  {
@@ -70,28 +70,6 @@ class PollinationsChatModel {
70
70
  placeholder: 'Add Option',
71
71
  default: {},
72
72
  options: [
73
- {
74
- displayName: 'Max Tokens',
75
- name: 'maxTokens',
76
- type: 'number',
77
- default: 0,
78
- description: 'Maximum tokens in response. 0 uses model default.',
79
- typeOptions: {
80
- minValue: 0,
81
- },
82
- },
83
- {
84
- displayName: 'Top P',
85
- name: 'topP',
86
- type: 'number',
87
- default: 1,
88
- typeOptions: {
89
- minValue: 0,
90
- maxValue: 1,
91
- numberPrecision: 2,
92
- },
93
- description: 'Nucleus sampling: consider tokens with top_p probability mass',
94
- },
95
73
  {
96
74
  displayName: 'Frequency Penalty',
97
75
  name: 'frequencyPenalty',
@@ -104,6 +82,16 @@ class PollinationsChatModel {
104
82
  },
105
83
  description: 'Reduce repetition of token sequences. Higher values decrease repetition.',
106
84
  },
85
+ {
86
+ displayName: 'Max Tokens',
87
+ name: 'maxTokens',
88
+ type: 'number',
89
+ default: 0,
90
+ description: 'Maximum tokens in response. 0 uses model default.',
91
+ typeOptions: {
92
+ minValue: 0,
93
+ },
94
+ },
107
95
  {
108
96
  displayName: 'Presence Penalty',
109
97
  name: 'presencePenalty',
@@ -126,9 +114,22 @@ class PollinationsChatModel {
126
114
  },
127
115
  description: 'Request timeout in milliseconds',
128
116
  },
117
+ {
118
+ displayName: 'Top P',
119
+ name: 'topP',
120
+ type: 'number',
121
+ default: 1,
122
+ typeOptions: {
123
+ minValue: 0,
124
+ maxValue: 1,
125
+ numberPrecision: 2,
126
+ },
127
+ description: 'Nucleus sampling: consider tokens with top_p probability mass',
128
+ },
129
129
  ],
130
130
  },
131
131
  ],
132
+ usableAsTool: true,
132
133
  };
133
134
  this.methods = {
134
135
  loadOptions: {