n8n-nodes-pollinations-ai 1.0.0 → 1.1.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.
- package/README.md +82 -74
- package/credentials/PollinationsApi.credentials.ts +11 -2
- package/dist/credentials/PollinationsApi.credentials.d.ts +2 -1
- package/dist/credentials/PollinationsApi.credentials.js +8 -2
- package/dist/nodes/Pollinations/Pollinations.node.d.ts +7 -1
- package/dist/nodes/Pollinations/Pollinations.node.js +308 -43
- package/dist/nodes/Pollinations/PollinationsChatModel.node.d.ts +10 -0
- package/dist/nodes/Pollinations/PollinationsChatModel.node.js +219 -0
- package/dist/nodes/Pollinations/pollinations.svg +1 -1
- package/nodes/Pollinations/Pollinations.node.json +2 -5
- package/nodes/Pollinations/Pollinations.node.ts +363 -43
- package/nodes/Pollinations/PollinationsChatModel.node.json +17 -0
- package/nodes/Pollinations/PollinationsChatModel.node.ts +247 -0
- package/nodes/Pollinations/pollinations.svg +1 -1
- package/package.json +12 -4
|
@@ -10,7 +10,7 @@ class Pollinations {
|
|
|
10
10
|
group: ['transform'],
|
|
11
11
|
version: 1,
|
|
12
12
|
subtitle: '={{$parameter["operation"]}}',
|
|
13
|
-
description: 'Generate images using Pollinations AI',
|
|
13
|
+
description: 'Generate images and text using Pollinations AI',
|
|
14
14
|
defaults: {
|
|
15
15
|
name: 'Pollinations',
|
|
16
16
|
},
|
|
@@ -36,10 +36,17 @@ class Pollinations {
|
|
|
36
36
|
description: 'Generate an image from a text prompt',
|
|
37
37
|
action: 'Generate an image from a text prompt',
|
|
38
38
|
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Generate Text',
|
|
41
|
+
value: 'generateText',
|
|
42
|
+
description: 'Generate text from a prompt using AI',
|
|
43
|
+
action: 'Generate text from a prompt',
|
|
44
|
+
},
|
|
39
45
|
],
|
|
40
46
|
default: 'generateImage',
|
|
41
47
|
},
|
|
42
|
-
//
|
|
48
|
+
// ==================== GENERATE IMAGE ====================
|
|
49
|
+
// Prompt (Image)
|
|
43
50
|
{
|
|
44
51
|
displayName: 'Prompt',
|
|
45
52
|
name: 'prompt',
|
|
@@ -56,7 +63,7 @@ class Pollinations {
|
|
|
56
63
|
rows: 4,
|
|
57
64
|
},
|
|
58
65
|
},
|
|
59
|
-
// Model (
|
|
66
|
+
// Model (Image) - Dynamic loading
|
|
60
67
|
{
|
|
61
68
|
displayName: 'Model',
|
|
62
69
|
name: 'model',
|
|
@@ -67,46 +74,12 @@ class Pollinations {
|
|
|
67
74
|
operation: ['generateImage'],
|
|
68
75
|
},
|
|
69
76
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
value: 'flux',
|
|
74
|
-
description: 'High quality image generation model',
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
name: 'Turbo',
|
|
78
|
-
value: 'turbo',
|
|
79
|
-
description: 'Faster generation with good quality',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'GPT Image',
|
|
83
|
-
value: 'gptimage',
|
|
84
|
-
description: 'OpenAI DALL-E style generation',
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
name: 'Kontext',
|
|
88
|
-
value: 'kontext',
|
|
89
|
-
description: 'Context-aware image generation (strict content filter)',
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: 'Seedream',
|
|
93
|
-
value: 'seedream',
|
|
94
|
-
description: 'Dream-like artistic images',
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: 'Nanobanana',
|
|
98
|
-
value: 'nanobanana',
|
|
99
|
-
description: 'Lightweight fast model',
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: 'Nanobanana Pro',
|
|
103
|
-
value: 'nanobanana-pro',
|
|
104
|
-
description: 'Enhanced nanobanana model',
|
|
105
|
-
},
|
|
106
|
-
],
|
|
77
|
+
typeOptions: {
|
|
78
|
+
loadOptionsMethod: 'getImageModels',
|
|
79
|
+
},
|
|
107
80
|
description: 'The model to use for image generation',
|
|
108
81
|
},
|
|
109
|
-
// Advanced Options
|
|
82
|
+
// Advanced Options (Image)
|
|
110
83
|
{
|
|
111
84
|
displayName: 'Options',
|
|
112
85
|
name: 'options',
|
|
@@ -171,8 +144,217 @@ class Pollinations {
|
|
|
171
144
|
},
|
|
172
145
|
],
|
|
173
146
|
},
|
|
147
|
+
// ==================== GENERATE TEXT ====================
|
|
148
|
+
// Prompt (Text)
|
|
149
|
+
{
|
|
150
|
+
displayName: 'Prompt',
|
|
151
|
+
name: 'textPrompt',
|
|
152
|
+
type: 'string',
|
|
153
|
+
default: '',
|
|
154
|
+
required: true,
|
|
155
|
+
displayOptions: {
|
|
156
|
+
show: {
|
|
157
|
+
operation: ['generateText'],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
description: 'The text prompt or question for the AI model',
|
|
161
|
+
typeOptions: {
|
|
162
|
+
rows: 4,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
// Model (Text) - Dynamic loading
|
|
166
|
+
{
|
|
167
|
+
displayName: 'Model',
|
|
168
|
+
name: 'textModel',
|
|
169
|
+
type: 'options',
|
|
170
|
+
default: 'openai',
|
|
171
|
+
displayOptions: {
|
|
172
|
+
show: {
|
|
173
|
+
operation: ['generateText'],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
typeOptions: {
|
|
177
|
+
loadOptionsMethod: 'getTextModels',
|
|
178
|
+
},
|
|
179
|
+
description: 'The AI model to use for text generation',
|
|
180
|
+
},
|
|
181
|
+
// System Prompt (Text)
|
|
182
|
+
{
|
|
183
|
+
displayName: 'System Prompt',
|
|
184
|
+
name: 'systemPrompt',
|
|
185
|
+
type: 'string',
|
|
186
|
+
default: '',
|
|
187
|
+
displayOptions: {
|
|
188
|
+
show: {
|
|
189
|
+
operation: ['generateText'],
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
description: 'Instructions that define the AI behavior and context',
|
|
193
|
+
placeholder: 'You are a helpful assistant that responds concisely...',
|
|
194
|
+
typeOptions: {
|
|
195
|
+
rows: 3,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
// Temperature (Text)
|
|
199
|
+
{
|
|
200
|
+
displayName: 'Temperature',
|
|
201
|
+
name: 'temperature',
|
|
202
|
+
type: 'number',
|
|
203
|
+
default: 0.7,
|
|
204
|
+
displayOptions: {
|
|
205
|
+
show: {
|
|
206
|
+
operation: ['generateText'],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
description: 'Controls creativity: 0.0 = strict/deterministic, 2.0 = very creative',
|
|
210
|
+
typeOptions: {
|
|
211
|
+
minValue: 0,
|
|
212
|
+
maxValue: 2,
|
|
213
|
+
numberPrecision: 1,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
// Advanced Options (Text)
|
|
217
|
+
{
|
|
218
|
+
displayName: 'Options',
|
|
219
|
+
name: 'textOptions',
|
|
220
|
+
type: 'collection',
|
|
221
|
+
placeholder: 'Add Option',
|
|
222
|
+
default: {},
|
|
223
|
+
displayOptions: {
|
|
224
|
+
show: {
|
|
225
|
+
operation: ['generateText'],
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
options: [
|
|
229
|
+
{
|
|
230
|
+
displayName: 'JSON Response',
|
|
231
|
+
name: 'jsonMode',
|
|
232
|
+
type: 'boolean',
|
|
233
|
+
default: false,
|
|
234
|
+
description: 'Whether to force the response in JSON format. Not supported by all models.',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
displayName: 'Seed',
|
|
238
|
+
name: 'seed',
|
|
239
|
+
type: 'number',
|
|
240
|
+
default: -1,
|
|
241
|
+
description: 'Seed for reproducible results. Use -1 for random.',
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
},
|
|
174
245
|
],
|
|
175
246
|
};
|
|
247
|
+
this.methods = {
|
|
248
|
+
loadOptions: {
|
|
249
|
+
async getImageModels() {
|
|
250
|
+
try {
|
|
251
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
252
|
+
const apiKey = credentials.apiKey;
|
|
253
|
+
const response = await this.helpers.httpRequest({
|
|
254
|
+
method: 'GET',
|
|
255
|
+
url: 'https://gen.pollinations.ai/image/models',
|
|
256
|
+
headers: {
|
|
257
|
+
Authorization: `Bearer ${apiKey}`,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
if (Array.isArray(response)) {
|
|
261
|
+
// Filter only image models (exclude video models)
|
|
262
|
+
const imageModels = response.filter((model) => model.output_modalities?.includes('image') &&
|
|
263
|
+
!model.output_modalities?.includes('video'));
|
|
264
|
+
return imageModels.map((model) => {
|
|
265
|
+
let displayName = model.description || model.name;
|
|
266
|
+
// Add pricing info if available
|
|
267
|
+
if (model.pricing?.completionImageTokens) {
|
|
268
|
+
const imagesPerPollen = Math.floor(1 / model.pricing.completionImageTokens);
|
|
269
|
+
displayName += ` (~${imagesPerPollen.toLocaleString()} img/$)`;
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
name: displayName,
|
|
273
|
+
value: model.name,
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// Fallback if API fails
|
|
278
|
+
return [
|
|
279
|
+
{ name: 'Flux Schnell', value: 'flux' },
|
|
280
|
+
{ name: 'SDXL Turbo', value: 'turbo' },
|
|
281
|
+
{ name: 'GPT Image 1 Mini', value: 'gptimage' },
|
|
282
|
+
{ name: 'FLUX.1 Kontext', value: 'kontext' },
|
|
283
|
+
{ name: 'Seedream 4.0', value: 'seedream' },
|
|
284
|
+
{ name: 'NanoBanana', value: 'nanobanana' },
|
|
285
|
+
{ name: 'NanoBanana Pro', value: 'nanobanana-pro' },
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Fallback if API fails
|
|
290
|
+
return [
|
|
291
|
+
{ name: 'Flux Schnell', value: 'flux' },
|
|
292
|
+
{ name: 'SDXL Turbo', value: 'turbo' },
|
|
293
|
+
{ name: 'GPT Image 1 Mini', value: 'gptimage' },
|
|
294
|
+
{ name: 'FLUX.1 Kontext', value: 'kontext' },
|
|
295
|
+
{ name: 'Seedream 4.0', value: 'seedream' },
|
|
296
|
+
];
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
async getTextModels() {
|
|
300
|
+
try {
|
|
301
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
302
|
+
const apiKey = credentials.apiKey;
|
|
303
|
+
const response = await this.helpers.httpRequest({
|
|
304
|
+
method: 'GET',
|
|
305
|
+
url: 'https://gen.pollinations.ai/text/models',
|
|
306
|
+
headers: {
|
|
307
|
+
Authorization: `Bearer ${apiKey}`,
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
if (Array.isArray(response)) {
|
|
311
|
+
// Filter only text models (exclude image/video models)
|
|
312
|
+
const textModels = response.filter((model) => model.output_modalities?.includes('text') &&
|
|
313
|
+
!model.output_modalities?.includes('image') &&
|
|
314
|
+
!model.output_modalities?.includes('video'));
|
|
315
|
+
return textModels.map((model) => {
|
|
316
|
+
let displayName = model.description || model.name;
|
|
317
|
+
// Add pricing info if available (responses per pollen)
|
|
318
|
+
if (model.pricing?.completionTextTokens) {
|
|
319
|
+
const responsesPerPollen = Math.floor(1 / model.pricing.completionTextTokens);
|
|
320
|
+
displayName += ` (~${responsesPerPollen.toLocaleString()} resp/$)`;
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
name: displayName,
|
|
324
|
+
value: model.name,
|
|
325
|
+
};
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// Fallback if API fails
|
|
329
|
+
return [
|
|
330
|
+
{ name: 'OpenAI GPT-4o Mini', value: 'openai' },
|
|
331
|
+
{ name: 'OpenAI GPT-4o Mini (Fast)', value: 'openai-fast' },
|
|
332
|
+
{ name: 'OpenAI GPT-4o (Large)', value: 'openai-large' },
|
|
333
|
+
{ name: 'Claude Sonnet 3.5', value: 'claude' },
|
|
334
|
+
{ name: 'Claude (Fast)', value: 'claude-fast' },
|
|
335
|
+
{ name: 'Claude (Large)', value: 'claude-large' },
|
|
336
|
+
{ name: 'Gemini', value: 'gemini' },
|
|
337
|
+
{ name: 'Gemini (Fast)', value: 'gemini-fast' },
|
|
338
|
+
{ name: 'Gemini (Large)', value: 'gemini-large' },
|
|
339
|
+
{ name: 'DeepSeek V3', value: 'deepseek' },
|
|
340
|
+
{ name: 'Mistral', value: 'mistral' },
|
|
341
|
+
{ name: 'Grok', value: 'grok' },
|
|
342
|
+
];
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// Fallback if API fails
|
|
346
|
+
return [
|
|
347
|
+
{ name: 'OpenAI GPT-4o Mini', value: 'openai' },
|
|
348
|
+
{ name: 'OpenAI GPT-4o Mini (Fast)', value: 'openai-fast' },
|
|
349
|
+
{ name: 'OpenAI GPT-4o (Large)', value: 'openai-large' },
|
|
350
|
+
{ name: 'Claude Sonnet 3.5', value: 'claude' },
|
|
351
|
+
{ name: 'Mistral', value: 'mistral' },
|
|
352
|
+
{ name: 'DeepSeek V3', value: 'deepseek' },
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
};
|
|
176
358
|
}
|
|
177
359
|
async execute() {
|
|
178
360
|
const items = this.getInputData();
|
|
@@ -183,6 +365,9 @@ class Pollinations {
|
|
|
183
365
|
const prompt = this.getNodeParameter('prompt', i);
|
|
184
366
|
const model = this.getNodeParameter('model', i);
|
|
185
367
|
const options = this.getNodeParameter('options', i, {});
|
|
368
|
+
// Get credentials
|
|
369
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
370
|
+
const apiKey = credentials.apiKey;
|
|
186
371
|
// Build query parameters
|
|
187
372
|
const queryParams = {
|
|
188
373
|
model,
|
|
@@ -206,16 +391,19 @@ class Pollinations {
|
|
|
206
391
|
queryParams.safe = 'true';
|
|
207
392
|
}
|
|
208
393
|
// Build the URL
|
|
209
|
-
const baseUrl = 'https://
|
|
394
|
+
const baseUrl = 'https://gen.pollinations.ai/image';
|
|
210
395
|
const encodedPrompt = encodeURIComponent(prompt);
|
|
211
396
|
const queryString = new URLSearchParams(queryParams).toString();
|
|
212
397
|
const fullUrl = `${baseUrl}/${encodedPrompt}?${queryString}`;
|
|
213
398
|
// Record start time
|
|
214
399
|
const startTime = Date.now();
|
|
215
|
-
// Make the request
|
|
400
|
+
// Make the request with authentication
|
|
216
401
|
const response = await this.helpers.httpRequest({
|
|
217
402
|
method: 'GET',
|
|
218
403
|
url: fullUrl,
|
|
404
|
+
headers: {
|
|
405
|
+
Authorization: `Bearer ${apiKey}`,
|
|
406
|
+
},
|
|
219
407
|
encoding: 'arraybuffer',
|
|
220
408
|
returnFullResponse: true,
|
|
221
409
|
});
|
|
@@ -251,6 +439,83 @@ class Pollinations {
|
|
|
251
439
|
},
|
|
252
440
|
});
|
|
253
441
|
}
|
|
442
|
+
if (operation === 'generateText') {
|
|
443
|
+
const prompt = this.getNodeParameter('textPrompt', i);
|
|
444
|
+
const model = this.getNodeParameter('textModel', i);
|
|
445
|
+
const systemPrompt = this.getNodeParameter('systemPrompt', i, '');
|
|
446
|
+
const temperature = this.getNodeParameter('temperature', i);
|
|
447
|
+
const textOptions = this.getNodeParameter('textOptions', i, {});
|
|
448
|
+
const jsonMode = textOptions.jsonMode || false;
|
|
449
|
+
// Get credentials
|
|
450
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
451
|
+
const apiKey = credentials.apiKey;
|
|
452
|
+
// Build query parameters
|
|
453
|
+
const queryParams = {
|
|
454
|
+
model,
|
|
455
|
+
temperature: temperature.toString(),
|
|
456
|
+
};
|
|
457
|
+
if (systemPrompt) {
|
|
458
|
+
queryParams.system = systemPrompt;
|
|
459
|
+
}
|
|
460
|
+
if (textOptions.seed !== undefined && textOptions.seed !== -1) {
|
|
461
|
+
queryParams.seed = textOptions.seed.toString();
|
|
462
|
+
}
|
|
463
|
+
if (jsonMode) {
|
|
464
|
+
queryParams.json = 'true';
|
|
465
|
+
}
|
|
466
|
+
// Build the URL
|
|
467
|
+
const baseUrl = 'https://gen.pollinations.ai/text';
|
|
468
|
+
const encodedPrompt = encodeURIComponent(prompt);
|
|
469
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
|
470
|
+
const fullUrl = `${baseUrl}/${encodedPrompt}?${queryString}`;
|
|
471
|
+
// Record start time
|
|
472
|
+
const startTime = Date.now();
|
|
473
|
+
// Make the request with authentication
|
|
474
|
+
const response = await this.helpers.httpRequest({
|
|
475
|
+
method: 'GET',
|
|
476
|
+
url: fullUrl,
|
|
477
|
+
headers: {
|
|
478
|
+
Authorization: `Bearer ${apiKey}`,
|
|
479
|
+
},
|
|
480
|
+
returnFullResponse: true,
|
|
481
|
+
});
|
|
482
|
+
// Calculate duration
|
|
483
|
+
const duration = Date.now() - startTime;
|
|
484
|
+
// Parse response text
|
|
485
|
+
const text = response.body;
|
|
486
|
+
let parsedJson = null;
|
|
487
|
+
// If JSON mode, try to parse the response
|
|
488
|
+
if (jsonMode) {
|
|
489
|
+
try {
|
|
490
|
+
parsedJson = JSON.parse(text);
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
// Keep as string if parsing fails
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// Build metadata for debugging
|
|
497
|
+
const metadata = {
|
|
498
|
+
text: parsedJson || text,
|
|
499
|
+
request: {
|
|
500
|
+
url: fullUrl,
|
|
501
|
+
prompt,
|
|
502
|
+
model,
|
|
503
|
+
system: systemPrompt || null,
|
|
504
|
+
temperature,
|
|
505
|
+
seed: textOptions.seed !== -1 ? textOptions.seed : null,
|
|
506
|
+
jsonMode: jsonMode,
|
|
507
|
+
},
|
|
508
|
+
response: {
|
|
509
|
+
statusCode: response.statusCode,
|
|
510
|
+
contentType: response.headers?.['content-type'] || 'text/plain',
|
|
511
|
+
duration: `${duration}ms`,
|
|
512
|
+
},
|
|
513
|
+
timestamp: new Date().toISOString(),
|
|
514
|
+
};
|
|
515
|
+
returnData.push({
|
|
516
|
+
json: metadata,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
254
519
|
}
|
|
255
520
|
return [returnData];
|
|
256
521
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ILoadOptionsFunctions, INodePropertyOptions, INodeType, INodeTypeDescription, ISupplyDataFunctions, SupplyData } from 'n8n-workflow';
|
|
2
|
+
export declare class PollinationsChatModel implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
methods: {
|
|
5
|
+
loadOptions: {
|
|
6
|
+
getChatModels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
supplyData(this: ISupplyDataFunctions): Promise<SupplyData>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PollinationsChatModel = void 0;
|
|
4
|
+
const openai_1 = require("@langchain/openai");
|
|
5
|
+
class PollinationsChatModel {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'Pollinations Chat Model',
|
|
9
|
+
name: 'pollinationsChatModel',
|
|
10
|
+
icon: 'file:pollinations.svg',
|
|
11
|
+
group: ['transform'],
|
|
12
|
+
version: 1,
|
|
13
|
+
description: 'Use Pollinations AI chat models with AI Agents and LLM Chains',
|
|
14
|
+
defaults: {
|
|
15
|
+
name: 'Pollinations Chat Model',
|
|
16
|
+
},
|
|
17
|
+
codex: {
|
|
18
|
+
categories: ['AI'],
|
|
19
|
+
subcategories: {
|
|
20
|
+
AI: ['Language Models', 'Chat Models'],
|
|
21
|
+
},
|
|
22
|
+
resources: {
|
|
23
|
+
primaryDocumentation: [
|
|
24
|
+
{
|
|
25
|
+
url: 'https://enter.pollinations.ai/api/docs',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
// Sub-node: no main inputs, output is ai_languageModel
|
|
31
|
+
inputs: [],
|
|
32
|
+
outputs: ['ai_languageModel'],
|
|
33
|
+
outputNames: ['Model'],
|
|
34
|
+
credentials: [
|
|
35
|
+
{
|
|
36
|
+
name: 'pollinationsApi',
|
|
37
|
+
required: true,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
properties: [
|
|
41
|
+
// Model - dynamic loading
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Model',
|
|
44
|
+
name: 'model',
|
|
45
|
+
type: 'options',
|
|
46
|
+
default: 'openai',
|
|
47
|
+
typeOptions: {
|
|
48
|
+
loadOptionsMethod: 'getChatModels',
|
|
49
|
+
},
|
|
50
|
+
description: 'The model to use for chat completions',
|
|
51
|
+
},
|
|
52
|
+
// Temperature
|
|
53
|
+
{
|
|
54
|
+
displayName: 'Temperature',
|
|
55
|
+
name: 'temperature',
|
|
56
|
+
type: 'number',
|
|
57
|
+
default: 1,
|
|
58
|
+
typeOptions: {
|
|
59
|
+
minValue: 0,
|
|
60
|
+
maxValue: 2,
|
|
61
|
+
numberPrecision: 1,
|
|
62
|
+
},
|
|
63
|
+
description: 'Controls randomness: 0 = deterministic, 2 = very creative',
|
|
64
|
+
},
|
|
65
|
+
// Options collection
|
|
66
|
+
{
|
|
67
|
+
displayName: 'Options',
|
|
68
|
+
name: 'options',
|
|
69
|
+
type: 'collection',
|
|
70
|
+
placeholder: 'Add Option',
|
|
71
|
+
default: {},
|
|
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
|
+
{
|
|
96
|
+
displayName: 'Frequency Penalty',
|
|
97
|
+
name: 'frequencyPenalty',
|
|
98
|
+
type: 'number',
|
|
99
|
+
default: 0,
|
|
100
|
+
typeOptions: {
|
|
101
|
+
minValue: -2,
|
|
102
|
+
maxValue: 2,
|
|
103
|
+
numberPrecision: 1,
|
|
104
|
+
},
|
|
105
|
+
description: 'Reduce repetition of token sequences. Higher values decrease repetition.',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
displayName: 'Presence Penalty',
|
|
109
|
+
name: 'presencePenalty',
|
|
110
|
+
type: 'number',
|
|
111
|
+
default: 0,
|
|
112
|
+
typeOptions: {
|
|
113
|
+
minValue: -2,
|
|
114
|
+
maxValue: 2,
|
|
115
|
+
numberPrecision: 1,
|
|
116
|
+
},
|
|
117
|
+
description: 'Increase likelihood of new topics. Higher values encourage novelty.',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
displayName: 'Timeout',
|
|
121
|
+
name: 'timeout',
|
|
122
|
+
type: 'number',
|
|
123
|
+
default: 60000,
|
|
124
|
+
typeOptions: {
|
|
125
|
+
minValue: 1000,
|
|
126
|
+
},
|
|
127
|
+
description: 'Request timeout in milliseconds',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
this.methods = {
|
|
134
|
+
loadOptions: {
|
|
135
|
+
async getChatModels() {
|
|
136
|
+
try {
|
|
137
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
138
|
+
const apiKey = credentials.apiKey;
|
|
139
|
+
const response = await this.helpers.httpRequest({
|
|
140
|
+
method: 'GET',
|
|
141
|
+
url: 'https://gen.pollinations.ai/text/models',
|
|
142
|
+
headers: {
|
|
143
|
+
Authorization: `Bearer ${apiKey}`,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
if (Array.isArray(response)) {
|
|
147
|
+
// Filter only text models (exclude image/video models)
|
|
148
|
+
const textModels = response.filter((model) => model.output_modalities?.includes('text') &&
|
|
149
|
+
!model.output_modalities?.includes('image') &&
|
|
150
|
+
!model.output_modalities?.includes('video'));
|
|
151
|
+
return textModels.map((model) => {
|
|
152
|
+
let displayName = model.description || model.name;
|
|
153
|
+
// Add pricing info if available (responses per pollen)
|
|
154
|
+
if (model.pricing?.completionTextTokens) {
|
|
155
|
+
const responsesPerPollen = Math.floor(1 / model.pricing.completionTextTokens);
|
|
156
|
+
displayName += ` (~${responsesPerPollen.toLocaleString()} resp/$)`;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
name: displayName,
|
|
160
|
+
value: model.name,
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// Fallback if API fails
|
|
165
|
+
return [
|
|
166
|
+
{ name: 'OpenAI GPT-4o Mini', value: 'openai' },
|
|
167
|
+
{ name: 'OpenAI GPT-4o Mini (Fast)', value: 'openai-fast' },
|
|
168
|
+
{ name: 'OpenAI GPT-4o (Large)', value: 'openai-large' },
|
|
169
|
+
{ name: 'Claude Sonnet 3.5', value: 'claude' },
|
|
170
|
+
{ name: 'Claude (Fast)', value: 'claude-fast' },
|
|
171
|
+
{ name: 'Claude (Large)', value: 'claude-large' },
|
|
172
|
+
{ name: 'Gemini', value: 'gemini' },
|
|
173
|
+
{ name: 'Gemini (Fast)', value: 'gemini-fast' },
|
|
174
|
+
{ name: 'Gemini (Large)', value: 'gemini-large' },
|
|
175
|
+
{ name: 'DeepSeek V3', value: 'deepseek' },
|
|
176
|
+
{ name: 'Mistral', value: 'mistral' },
|
|
177
|
+
{ name: 'Grok', value: 'grok' },
|
|
178
|
+
];
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Fallback if API fails
|
|
182
|
+
return [
|
|
183
|
+
{ name: 'OpenAI GPT-4o Mini', value: 'openai' },
|
|
184
|
+
{ name: 'OpenAI GPT-4o Mini (Fast)', value: 'openai-fast' },
|
|
185
|
+
{ name: 'OpenAI GPT-4o (Large)', value: 'openai-large' },
|
|
186
|
+
{ name: 'Claude Sonnet 3.5', value: 'claude' },
|
|
187
|
+
{ name: 'Mistral', value: 'mistral' },
|
|
188
|
+
{ name: 'DeepSeek V3', value: 'deepseek' },
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
async supplyData() {
|
|
196
|
+
const credentials = await this.getCredentials('pollinationsApi');
|
|
197
|
+
const apiKey = credentials.apiKey;
|
|
198
|
+
const model = this.getNodeParameter('model', 0);
|
|
199
|
+
const temperature = this.getNodeParameter('temperature', 0);
|
|
200
|
+
const options = this.getNodeParameter('options', 0, {});
|
|
201
|
+
const chatModel = new openai_1.ChatOpenAI({
|
|
202
|
+
model,
|
|
203
|
+
temperature,
|
|
204
|
+
maxTokens: options.maxTokens || undefined,
|
|
205
|
+
topP: options.topP,
|
|
206
|
+
frequencyPenalty: options.frequencyPenalty,
|
|
207
|
+
presencePenalty: options.presencePenalty,
|
|
208
|
+
timeout: options.timeout,
|
|
209
|
+
configuration: {
|
|
210
|
+
baseURL: 'https://gen.pollinations.ai/v1',
|
|
211
|
+
},
|
|
212
|
+
apiKey,
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
response: chatModel,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.PollinationsChatModel = PollinationsChatModel;
|