n8n-nodes-vercel-ai-sdk-universal-temp 0.1.54 → 0.1.55

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.
@@ -41,42 +41,15 @@ const n8n_workflow_1 = require("n8n-workflow");
41
41
  const ai_1 = require("ai");
42
42
  const zod_1 = require("zod");
43
43
  const ajv_1 = __importDefault(require("ajv"));
44
- const crypto_1 = require("crypto");
45
44
  const descriptions_1 = require("../shared/descriptions");
46
- class UniversalAIError extends Error {
47
- constructor(message, code, context) {
48
- super(message);
49
- this.code = code;
50
- this.context = context;
51
- this.name = 'UniversalAIError';
52
- }
53
- }
54
- class CacheError extends UniversalAIError {
55
- constructor(message, context) {
56
- super(message, 'CACHE_ERROR', context);
57
- }
58
- }
59
- class ProviderError extends UniversalAIError {
60
- constructor(message, context) {
61
- super(message, 'PROVIDER_ERROR', context);
62
- }
63
- }
64
- class ValidationError extends UniversalAIError {
65
- constructor(message, context) {
66
- super(message, 'VALIDATION_ERROR', context);
67
- }
68
- }
69
45
  class Cache {
70
- constructor(_name, maxSize = 100, ttl = 5 * 60 * 1000) {
46
+ constructor(maxSize = 100, ttl = 5 * 60 * 1000) {
71
47
  this.cache = new Map();
72
48
  this.totalHits = 0;
73
49
  this.totalMisses = 0;
74
50
  this.totalEvictions = 0;
75
51
  this.maxSize = maxSize;
76
52
  this.ttl = ttl;
77
- if (ttl > 0) {
78
- setInterval(() => this.cleanupExpired(), Math.min(ttl, 60000));
79
- }
80
53
  }
81
54
  get(key) {
82
55
  const item = this.cache.get(key);
@@ -92,7 +65,6 @@ class Cache {
92
65
  return undefined;
93
66
  }
94
67
  item.hits++;
95
- item.lastAccessed = now;
96
68
  this.totalHits++;
97
69
  return item.value;
98
70
  }
@@ -100,42 +72,20 @@ class Cache {
100
72
  const now = Date.now();
101
73
  const expiresAt = customTTL ? now + customTTL : (this.ttl > 0 ? now + this.ttl : undefined);
102
74
  if (this.cache.size >= this.maxSize) {
103
- this.evictLRU();
104
- }
105
- this.cache.set(key, {
106
- value,
107
- timestamp: now,
108
- hits: 0,
109
- expiresAt,
110
- lastAccessed: now
111
- });
112
- }
113
- evictLRU() {
114
- let lruKey;
115
- let oldestAccess = Date.now();
116
- for (const [key, item] of this.cache.entries()) {
117
- if (item.lastAccessed < oldestAccess) {
118
- oldestAccess = item.lastAccessed;
119
- lruKey = key;
75
+ let oldestKey;
76
+ let oldestTime = now;
77
+ for (const [k, v] of this.cache.entries()) {
78
+ if (v.timestamp < oldestTime) {
79
+ oldestTime = v.timestamp;
80
+ oldestKey = k;
81
+ }
120
82
  }
121
- }
122
- if (lruKey) {
123
- this.cache.delete(lruKey);
124
- this.totalEvictions++;
125
- }
126
- }
127
- cleanupExpired() {
128
- const now = Date.now();
129
- let cleaned = 0;
130
- for (const [key, item] of this.cache.entries()) {
131
- if (item.expiresAt && now > item.expiresAt) {
132
- this.cache.delete(key);
133
- cleaned++;
83
+ if (oldestKey) {
84
+ this.cache.delete(oldestKey);
85
+ this.totalEvictions++;
134
86
  }
135
87
  }
136
- if (cleaned > 0) {
137
- this.totalEvictions += cleaned;
138
- }
88
+ this.cache.set(key, { value, timestamp: now, hits: 0, expiresAt });
139
89
  }
140
90
  delete(key) {
141
91
  return this.cache.delete(key);
@@ -146,128 +96,93 @@ class Cache {
146
96
  this.totalMisses = 0;
147
97
  this.totalEvictions = 0;
148
98
  }
149
- *entries() {
150
- for (const [key, value] of this.cache.entries()) {
151
- yield [key, value];
152
- }
153
- }
154
- getMetadata(key) {
155
- return this.cache.get(key);
156
- }
157
99
  getStats() {
158
- const totalRequests = this.totalHits + this.totalMisses;
159
- let totalSize = 0;
160
- for (const item of this.cache.values()) {
161
- totalSize += this.estimateSize(item.value);
162
- }
163
100
  return {
164
101
  size: this.cache.size,
165
102
  maxSize: this.maxSize,
166
- hitRate: totalRequests > 0 ? this.totalHits / totalRequests : 0,
103
+ hitRate: this.totalHits / (this.totalHits + this.totalMisses) || 0,
167
104
  totalHits: this.totalHits,
168
105
  totalMisses: this.totalMisses,
169
106
  totalEvictions: this.totalEvictions,
170
107
  ttl: this.ttl,
171
- averageItemSize: this.cache.size > 0 ? totalSize / this.cache.size : 0,
172
108
  };
173
109
  }
174
- estimateSize(value) {
175
- try {
176
- return JSON.stringify(value).length;
177
- }
178
- catch {
179
- return 1024;
180
- }
181
- }
182
110
  }
183
- function generateCacheKey(data, prefix = '') {
184
- const dataStr = typeof data === 'string' ? data : JSON.stringify(data);
185
- const hash = (0, crypto_1.createHash)('sha256').update(dataStr).digest('hex').substring(0, 16);
186
- return `${prefix}${hash}`;
111
+ const modelCache = new Cache(50);
112
+ const providerCache = new Cache(20);
113
+ const schemaCache = new Cache(30);
114
+ const googleCacheClients = new Cache(10, 60 * 60 * 1000);
115
+ const googleCachedContexts = new Cache(50, 55 * 60 * 1000);
116
+ async function getGoogleCacheManager(apiKey) {
117
+ let client = googleCacheClients.get(apiKey);
118
+ if (!client) {
119
+ const { GoogleGenAI } = await Promise.resolve().then(() => __importStar(require('@google/genai')));
120
+ client = new GoogleGenAI({ apiKey });
121
+ googleCacheClients.set(apiKey, client);
122
+ }
123
+ return client;
187
124
  }
188
- const modelCache = new Cache('models', 50, 10 * 60 * 1000);
189
- const providerCache = new Cache('providers', 20, 30 * 60 * 1000);
190
- const schemaCache = new Cache('schemas', 30, 60 * 60 * 1000);
191
- const googleCacheClients = new Cache('google_clients', 10, 60 * 60 * 1000);
192
- const googleCachedContexts = new Cache('google_contexts', 50, 55 * 60 * 1000);
193
- async function cleanupExpiredGoogleCaches() {
194
- const now = Date.now();
195
- const entriesToDelete = [];
196
- for (const [key, cacheItem] of googleCachedContexts.entries()) {
197
- if (cacheItem.expiresAt && now > cacheItem.expiresAt) {
198
- entriesToDelete.push({
199
- key,
200
- cacheName: cacheItem.value.name,
201
- apiKey: cacheItem.value.apiKey
202
- });
125
+ async function createGoogleCache(exec, index, apiKey, cacheContent, tools) {
126
+ var _a;
127
+ try {
128
+ const useGoogleCache = exec.getNodeParameter('useGoogleCache', index, false);
129
+ if (!useGoogleCache) {
130
+ return null;
203
131
  }
204
- }
205
- for (const { key, cacheName, apiKey } of entriesToDelete) {
206
- try {
207
- const client = await getGoogleCacheManager(apiKey);
208
- await client.caches.delete(cacheName);
132
+ const googleCacheManager = await getGoogleCacheManager(apiKey);
133
+ const normalizedCacheContent = (_a = cacheContent === null || cacheContent === void 0 ? void 0 : cacheContent.trim()) !== null && _a !== void 0 ? _a : '';
134
+ if (!normalizedCacheContent) {
135
+ return null;
209
136
  }
210
- catch (error) {
211
- console.warn(`Failed to cleanup Google cache ${cacheName}:`, error);
137
+ const cacheKeyData = {
138
+ content: normalizedCacheContent,
139
+ tools: tools ? Object.keys(tools).sort() : [],
140
+ model: 'gemini-2.0-flash-001',
141
+ };
142
+ const cacheKey = JSON.stringify(cacheKeyData);
143
+ const existingCache = googleCachedContexts.get(cacheKey);
144
+ if (existingCache) {
145
+ return existingCache.name;
212
146
  }
213
- finally {
214
- googleCachedContexts.delete(key);
147
+ const ttlSeconds = 3600;
148
+ const displayName = `universal_ai_cache_${Date.now()}`;
149
+ const cacheConfig = {
150
+ model: 'gemini-2.0-flash-001',
151
+ config: {
152
+ displayName,
153
+ ttl: `${ttlSeconds}s`,
154
+ contents: [{
155
+ role: 'user',
156
+ parts: [{ text: normalizedCacheContent }],
157
+ }],
158
+ },
159
+ };
160
+ if (tools && Object.keys(tools).length > 0) {
161
+ cacheConfig.config.tools = Object.values(tools);
215
162
  }
216
- }
217
- }
218
- let cleanupInterval = null;
219
- function startCacheCleanup() {
220
- if (cleanupInterval) {
221
- clearInterval(cleanupInterval);
222
- }
223
- cleanupInterval = setInterval(() => {
224
- cleanupExpiredGoogleCaches().catch(console.error);
225
- }, 5 * 60 * 1000);
226
- }
227
- startCacheCleanup();
228
- async function getGoogleCacheManager(apiKey) {
229
- const cacheKey = generateCacheKey(apiKey, 'google_client:');
230
- const cachedClient = googleCacheClients.get(cacheKey);
231
- if (cachedClient) {
232
- return cachedClient;
233
- }
234
- try {
235
- const { GoogleGenAI } = await Promise.resolve().then(() => __importStar(require('@google/genai')));
236
- const client = new GoogleGenAI({ apiKey });
237
- googleCacheClients.set(cacheKey, client);
238
- return client;
163
+ const result = await googleCacheManager.caches.create(cacheConfig);
164
+ const cachedContentName = result === null || result === void 0 ? void 0 : result.name;
165
+ if (!cachedContentName) {
166
+ throw new Error('Failed to get cached content name from creation response');
167
+ }
168
+ googleCachedContexts.set(cacheKey, { name: cachedContentName }, ttlSeconds * 1000);
169
+ return cachedContentName;
239
170
  }
240
171
  catch (error) {
241
- throw new CacheError(`Failed to initialize Google cache client: ${error.message}`, { apiKey: apiKey.substring(0, 8) + '...' });
242
- }
243
- }
244
- function isUrl(str) {
245
- if (typeof str !== 'string')
246
- return false;
247
- try {
248
- const url = new URL(str);
249
- return url.protocol === 'http:' || url.protocol === 'https:';
250
- }
251
- catch {
252
- return str.startsWith('data:');
172
+ console.error('UniversalAI: Failed to create Google cache. Falling back to non-cached execution:', error);
173
+ return null;
253
174
  }
254
175
  }
255
- function isLikelyBase64(str) {
256
- if (typeof str !== 'string')
257
- return false;
258
- if (str.length % 4 !== 0)
259
- return false;
260
- if (!/^[A-Za-z0-9+/]*={0,2}$/.test(str))
261
- return false;
262
- if (str.length > 10000)
263
- return true;
264
- return true;
176
+ function canUseCache(cacheContent) {
177
+ return Boolean(cacheContent && cacheContent.trim().length > 0);
265
178
  }
266
179
  function extractTextFromMessageContent(content) {
267
- if (!content)
180
+ if (!content) {
268
181
  return '';
269
- if (typeof content === 'string')
182
+ }
183
+ if (typeof content === 'string') {
270
184
  return content;
185
+ }
271
186
  if (Array.isArray(content)) {
272
187
  return content
273
188
  .map((part) => {
@@ -287,20 +202,16 @@ function extractTextFromMessageContent(content) {
287
202
  }
288
203
  return '';
289
204
  }
290
- function canUseCache(cacheContent) {
291
- return Boolean(cacheContent && cacheContent.trim().length > 0);
292
- }
293
205
  function resolveCacheContent(input) {
294
- var _a, _b, _c;
295
206
  const sections = [];
296
207
  let hasSystem = false;
297
208
  let hasMessages = false;
298
209
  let hasPrompt = false;
299
- if ((_a = input.system) === null || _a === void 0 ? void 0 : _a.trim()) {
210
+ if (input.system && input.system.trim()) {
300
211
  sections.push(`System Instruction:\n${input.system.trim()}`);
301
212
  hasSystem = true;
302
213
  }
303
- if ((_b = input.messages) === null || _b === void 0 ? void 0 : _b.length) {
214
+ if (input.messages && input.messages.length > 0) {
304
215
  const messageSections = [];
305
216
  for (const message of input.messages) {
306
217
  const text = extractTextFromMessageContent(message.content);
@@ -311,31 +222,34 @@ function resolveCacheContent(input) {
311
222
  sections.push(`System Instruction (from messages):\n${text.trim()}`);
312
223
  hasSystem = true;
313
224
  }
225
+ continue;
314
226
  }
315
- else {
316
- messageSections.push(`${message.role.toUpperCase()}:\n${text.trim()}`);
317
- }
227
+ messageSections.push(`${message.role.toUpperCase()}:\n${text.trim()}`);
318
228
  }
319
229
  if (messageSections.length > 0) {
320
230
  sections.push(`Messages:\n${messageSections.join('\n\n')}`);
321
231
  hasMessages = true;
322
232
  }
323
233
  }
324
- if ((_c = input.prompt) === null || _c === void 0 ? void 0 : _c.trim()) {
234
+ if (input.prompt && input.prompt.trim()) {
325
235
  sections.push(`Prompt Template:\n${input.prompt.trim()}`);
326
236
  hasPrompt = true;
327
237
  }
328
238
  const content = sections.join('\n\n').trim();
329
239
  let source;
330
240
  const sourceCount = [hasSystem, hasMessages, hasPrompt].filter(Boolean).length;
331
- if (sourceCount > 1)
241
+ if (sourceCount > 1) {
332
242
  source = 'combined';
333
- else if (hasSystem)
243
+ }
244
+ else if (hasSystem) {
334
245
  source = 'system';
335
- else if (hasMessages)
246
+ }
247
+ else if (hasMessages) {
336
248
  source = 'messages';
337
- else if (hasPrompt)
249
+ }
250
+ else if (hasPrompt) {
338
251
  source = 'prompt';
252
+ }
339
253
  return {
340
254
  content: content || undefined,
341
255
  hasSystem,
@@ -344,90 +258,8 @@ function resolveCacheContent(input) {
344
258
  source,
345
259
  };
346
260
  }
347
- async function createGoogleCache(exec, index, apiKey, cacheContent, tools) {
348
- try {
349
- const useGoogleCache = exec.getNodeParameter('useGoogleCache', index, false);
350
- if (!useGoogleCache || !(cacheContent === null || cacheContent === void 0 ? void 0 : cacheContent.trim())) {
351
- return null;
352
- }
353
- const googleCacheManager = await getGoogleCacheManager(apiKey);
354
- const normalizedCacheContent = cacheContent.trim();
355
- const cacheKeyData = {
356
- content: normalizedCacheContent,
357
- tools: tools ? Object.keys(tools).sort() : [],
358
- model: 'gemini-2.0-flash-001',
359
- };
360
- const cacheKey = generateCacheKey(cacheKeyData, 'google_cache:');
361
- const existingCache = googleCachedContexts.get(cacheKey);
362
- if (existingCache) {
363
- return existingCache.name;
364
- }
365
- const ttlSeconds = 3600;
366
- const displayName = `n8n_cache_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
367
- const cacheConfig = {
368
- model: 'gemini-2.0-flash-001',
369
- config: {
370
- displayName,
371
- ttl: `${ttlSeconds}s`,
372
- contents: [{
373
- role: 'user',
374
- parts: [{ text: normalizedCacheContent }],
375
- }],
376
- },
377
- };
378
- if (tools && Object.keys(tools).length > 0) {
379
- cacheConfig.config.tools = Object.values(tools);
380
- }
381
- const result = await googleCacheManager.caches.create(cacheConfig);
382
- const cachedContentName = result === null || result === void 0 ? void 0 : result.name;
383
- if (!cachedContentName) {
384
- throw new CacheError('Failed to create cache: No name in response', { displayName });
385
- }
386
- googleCachedContexts.set(cacheKey, {
387
- name: cachedContentName,
388
- apiKey
389
- }, ttlSeconds * 1000);
390
- return cachedContentName;
391
- }
392
- catch (error) {
393
- if (error instanceof CacheError)
394
- throw error;
395
- throw new CacheError(`Google cache creation failed: ${error.message}`, {
396
- cacheContentLength: cacheContent === null || cacheContent === void 0 ? void 0 : cacheContent.length,
397
- hasTools: !!tools && Object.keys(tools).length > 0
398
- });
399
- }
400
- }
401
- async function buildInput(exec, itemIndex) {
402
- const inputType = exec.getNodeParameter('inputType', itemIndex);
403
- if (inputType === 'prompt') {
404
- return buildPromptInput(exec, itemIndex);
405
- }
406
- const messageAsJson = exec.getNodeParameter('messageAsJson', itemIndex, false);
407
- return messageAsJson
408
- ? buildMessagesFromJson(exec, itemIndex)
409
- : buildMessagesFromUI(exec, itemIndex);
410
- }
411
- function buildPromptInput(exec, itemIndex) {
412
- const result = {};
413
- const promptValue = exec.getNodeParameter('prompt', itemIndex, '').trim();
414
- if (promptValue) {
415
- if (promptValue.length > 100000) {
416
- throw new ValidationError('Prompt is too long (max 100,000 characters)');
417
- }
418
- result.prompt = promptValue;
419
- }
420
- const systemValue = exec.getNodeParameter('system', itemIndex, '').trim();
421
- if (systemValue) {
422
- if (systemValue.length > 50000) {
423
- throw new ValidationError('System instruction is too long (max 50,000 characters)');
424
- }
425
- result.system = systemValue;
426
- }
427
- return result;
428
- }
429
261
  const messageSchema = zod_1.z.object({
430
- role: zod_1.z.enum(['system', 'user', 'assistant', 'tool']),
262
+ role: zod_1.z.enum(['system', 'user', 'assistant']),
431
263
  content: zod_1.z.any(),
432
264
  });
433
265
  const messagesArraySchema = zod_1.z.array(messageSchema);
@@ -435,143 +267,111 @@ const ajv = new ajv_1.default({
435
267
  allErrors: true,
436
268
  verbose: true,
437
269
  strict: false,
438
- useDefaults: true,
439
- removeAdditional: true,
440
270
  });
271
+ const isUrl = (str) => {
272
+ if (typeof str !== 'string')
273
+ return false;
274
+ return str.startsWith('http://') ||
275
+ str.startsWith('https://') ||
276
+ str.startsWith('data:');
277
+ };
278
+ const isLikelyBase64 = (str) => {
279
+ if (str.length % 4 !== 0)
280
+ return false;
281
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(str))
282
+ return false;
283
+ if (str.length > 10000)
284
+ return true;
285
+ return true;
286
+ };
287
+ async function buildInput(exec, itemIndex) {
288
+ const inputType = exec.getNodeParameter('inputType', itemIndex);
289
+ if (inputType === 'prompt') {
290
+ const promptValue = exec.getNodeParameter('prompt', itemIndex, '');
291
+ const systemValue = exec.getNodeParameter('system', itemIndex, '');
292
+ const result = {};
293
+ const trimmedPrompt = typeof promptValue === 'string' ? promptValue.trim() : '';
294
+ if (trimmedPrompt) {
295
+ result.prompt = trimmedPrompt;
296
+ }
297
+ const trimmedSystem = typeof systemValue === 'string' ? systemValue.trim() : '';
298
+ if (trimmedSystem) {
299
+ result.system = trimmedSystem;
300
+ }
301
+ return result;
302
+ }
303
+ const messageAsJson = exec.getNodeParameter('messageAsJson', itemIndex, false);
304
+ return messageAsJson
305
+ ? buildMessagesFromJson(exec, itemIndex)
306
+ : buildMessagesFromUI(exec, itemIndex);
307
+ }
441
308
  async function buildMessagesFromJson(exec, itemIndex) {
442
309
  const rawJson = exec.getNodeParameter('messagesJson', itemIndex);
443
- if (!rawJson.trim()) {
444
- throw new ValidationError('Messages JSON field is empty');
445
- }
446
- if (rawJson.length > 200000) {
447
- throw new ValidationError('Messages JSON is too large (max 200,000 characters)');
448
- }
449
310
  try {
450
311
  const parsed = JSON.parse(rawJson);
451
312
  const result = messagesArraySchema.safeParse(parsed);
452
313
  if (!result.success) {
453
- const errorDetails = result.error.issues
454
- .map((issue) => {
455
- const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';
456
- return `${path}: ${issue.message}`;
457
- })
458
- .join('; ');
459
- throw new ValidationError(`Invalid messages format: ${errorDetails}`);
460
- }
461
- if (result.data.length > 100) {
462
- throw new ValidationError('Too many messages (max 100)');
314
+ throw new n8n_workflow_1.NodeOperationError(exec.getNode(), 'Messages must be an array of objects with role and content.');
463
315
  }
464
316
  return { messages: result.data };
465
317
  }
466
318
  catch (error) {
467
- if (error instanceof ValidationError)
468
- throw error;
469
- throw new ValidationError(`Invalid JSON in messages field: ${error.message}`);
319
+ throw new n8n_workflow_1.NodeOperationError(exec.getNode(), `Invalid JSON in "Messages (JSON)" field: ${error.message}`);
470
320
  }
471
321
  }
472
322
  async function buildMessagesFromUI(exec, itemIndex) {
473
- var _a, _b, _c;
323
+ var _a;
474
324
  const items = exec.getInputData();
475
325
  const messagesUi = exec.getNodeParameter('messages.messagesUi', itemIndex, []);
476
- if (messagesUi.length > 100) {
477
- throw new ValidationError('Too many messages (max 100)');
478
- }
479
326
  const builtMessages = [];
480
327
  const itemBinary = items[itemIndex].binary;
481
328
  for (const msg of messagesUi) {
482
329
  const role = msg.role;
483
330
  if (role === 'system') {
484
- if ((_a = msg.systemContent) === null || _a === void 0 ? void 0 : _a.trim()) {
485
- builtMessages.push({ role, content: msg.systemContent.trim() });
486
- }
331
+ builtMessages.push({ role, content: msg.systemContent || '' });
487
332
  continue;
488
333
  }
489
- const attachments = ((_b = msg.attachments) === null || _b === void 0 ? void 0 : _b.attachment) || [];
490
- const content = ((_c = msg.content) === null || _c === void 0 ? void 0 : _c.trim()) || '';
334
+ const attachments = ((_a = msg.attachments) === null || _a === void 0 ? void 0 : _a.attachment) || [];
491
335
  if (attachments.length === 0) {
492
- if (content) {
493
- builtMessages.push({ role, content });
494
- }
336
+ builtMessages.push({ role, content: msg.content || '' });
495
337
  }
496
338
  else {
497
- const messageWithAttachments = await buildMessageWithAttachments(role, content, attachments, itemBinary, exec, itemIndex);
339
+ const messageWithAttachments = await buildMessageWithAttachments(role, msg.content, attachments, itemBinary, exec, itemIndex);
498
340
  if (messageWithAttachments) {
499
341
  builtMessages.push(messageWithAttachments);
500
342
  }
501
343
  }
502
344
  }
503
345
  const convertMessagesToModel = exec.getNodeParameter('convertMessagesToModel', itemIndex, false);
504
- return {
505
- messages: convertMessagesToModel ? (0, ai_1.convertToModelMessages)(builtMessages) : builtMessages
506
- };
346
+ if (convertMessagesToModel) {
347
+ return { messages: (0, ai_1.convertToModelMessages)(builtMessages) };
348
+ }
349
+ return { messages: builtMessages };
507
350
  }
508
- const MAX_ATTACHMENT_SIZE = 50 * 1024 * 1024;
509
- const MAX_TOTAL_ATTACHMENTS_SIZE = 100 * 1024 * 1024;
510
351
  async function buildMessageWithAttachments(role, content, attachments, itemBinary, exec, itemIndex) {
511
352
  const parts = [];
512
353
  if (content) {
513
354
  parts.push({ type: 'text', text: content });
514
355
  }
515
- let totalSize = 0;
516
356
  const MAX_CONCURRENT_ATTACHMENTS = 3;
357
+ const processedAttachments = [];
517
358
  for (let i = 0; i < attachments.length; i += MAX_CONCURRENT_ATTACHMENTS) {
518
359
  const batch = attachments.slice(i, i + MAX_CONCURRENT_ATTACHMENTS);
519
360
  const batchPromises = batch.map(attachment => processAttachment(attachment, itemBinary, exec, itemIndex));
520
- const processedAttachments = await Promise.all(batchPromises);
521
- for (const attachment of processedAttachments) {
522
- if (attachment) {
523
- if (attachment.data instanceof Buffer) {
524
- totalSize += attachment.data.length;
525
- if (totalSize > MAX_TOTAL_ATTACHMENTS_SIZE) {
526
- throw new ValidationError(`Total attachments size exceeds limit of ${MAX_TOTAL_ATTACHMENTS_SIZE / 1024 / 1024}MB`);
527
- }
528
- }
529
- parts.push(attachment);
530
- }
531
- }
532
- }
533
- return parts.length > 0 ? { role, content: parts } : null;
534
- }
535
- function getMimeType(attachment) {
536
- return attachment.mimeType === 'other' ? attachment.mimeTypeOther : attachment.mimeType;
537
- }
538
- async function getBinaryData(fileContentInput, itemBinary, exec, itemIndex) {
539
- if (itemBinary === null || itemBinary === void 0 ? void 0 : itemBinary[fileContentInput]) {
540
- const binaryData = itemBinary[fileContentInput];
541
- if (binaryData.data) {
542
- const buffer = Buffer.from(binaryData.data, 'base64');
543
- if (buffer.length > MAX_ATTACHMENT_SIZE) {
544
- throw new ValidationError(`Attachment too large: ${buffer.length / 1024 / 1024}MB (max ${MAX_ATTACHMENT_SIZE / 1024 / 1024}MB)`);
545
- }
546
- return {
547
- data: buffer,
548
- mimeType: binaryData.mimeType
549
- };
550
- }
361
+ const batchResults = await Promise.all(batchPromises);
362
+ processedAttachments.push(...batchResults);
551
363
  }
552
- try {
553
- if (isLikelyBase64(fileContentInput)) {
554
- const buffer = Buffer.from(fileContentInput, 'base64');
555
- if (buffer.length > MAX_ATTACHMENT_SIZE) {
556
- throw new ValidationError(`Attachment too large: ${buffer.length / 1024 / 1024}MB (max ${MAX_ATTACHMENT_SIZE / 1024 / 1024}MB)`);
557
- }
558
- if (buffer.length > 0) {
559
- return { data: buffer, mimeType: undefined };
560
- }
364
+ for (const attachment of processedAttachments) {
365
+ if (attachment) {
366
+ parts.push(attachment);
561
367
  }
562
368
  }
563
- catch (error) {
564
- if (error instanceof ValidationError)
565
- throw error;
566
- throw new ValidationError(`Invalid file content for attachment: ${error.message}`);
567
- }
568
- return { data: null, mimeType: undefined };
369
+ return parts.length > 0 ? { role, content: parts } : null;
569
370
  }
570
371
  async function processAttachment(attachment, itemBinary, exec, itemIndex) {
571
372
  const fileContentInput = attachment.fileContent;
572
- if (!fileContentInput || typeof fileContentInput !== 'string') {
373
+ if (!fileContentInput || typeof fileContentInput !== 'string')
573
374
  return null;
574
- }
575
375
  let mimeType = getMimeType(attachment);
576
376
  let fileData;
577
377
  if (isUrl(fileContentInput)) {
@@ -579,19 +379,46 @@ async function processAttachment(attachment, itemBinary, exec, itemIndex) {
579
379
  }
580
380
  else {
581
381
  const result = await getBinaryData(fileContentInput, itemBinary, exec, itemIndex);
582
- if (!result.data)
583
- return null;
584
382
  fileData = result.data;
585
383
  if (!mimeType && result.mimeType) {
586
384
  mimeType = result.mimeType;
587
385
  }
588
386
  }
387
+ if (!fileData || (Buffer.isBuffer(fileData) && fileData.length === 0)) {
388
+ return null;
389
+ }
589
390
  return {
590
391
  type: 'file',
591
392
  data: fileData,
592
- mediaType: mimeType || 'application/octet-stream'
393
+ mediaType: mimeType || 'application/octet-stream',
593
394
  };
594
395
  }
396
+ function getMimeType(attachment) {
397
+ return attachment.mimeType === 'other'
398
+ ? attachment.mimeTypeOther
399
+ : attachment.mimeType;
400
+ }
401
+ async function getBinaryData(fileContentInput, itemBinary, exec, itemIndex) {
402
+ if (itemBinary === null || itemBinary === void 0 ? void 0 : itemBinary[fileContentInput]) {
403
+ const binaryData = itemBinary[fileContentInput];
404
+ return {
405
+ data: Buffer.from(binaryData.data, 'base64'),
406
+ mimeType: binaryData.mimeType,
407
+ };
408
+ }
409
+ try {
410
+ if (isLikelyBase64(fileContentInput)) {
411
+ const buffer = Buffer.from(fileContentInput, 'base64');
412
+ if (buffer.length > 0 && buffer.length < 50 * 1024 * 1024) {
413
+ return { data: buffer, mimeType: undefined };
414
+ }
415
+ }
416
+ }
417
+ catch (error) {
418
+ throw new n8n_workflow_1.NodeOperationError(exec.getNode(), `Invalid file content for attachment: ${error.message}`);
419
+ }
420
+ return { data: null, mimeType: undefined };
421
+ }
595
422
  function formatTextResult(result, includeRequestBody, provider) {
596
423
  var _a, _b, _c, _d, _e;
597
424
  let { text, reasoning } = result;
@@ -705,159 +532,163 @@ function getCacheMetrics(result, provider, metadata) {
705
532
  }
706
533
  function formatResponse(result) {
707
534
  var _a, _b, _c, _d;
708
- return {
535
+ const response = {
709
536
  id: (_a = result.response) === null || _a === void 0 ? void 0 : _a.id,
710
537
  modelId: (_b = result.response) === null || _b === void 0 ? void 0 : _b.modelId,
711
538
  timestamp: (_c = result.response) === null || _c === void 0 ? void 0 : _c.timestamp,
712
539
  headers: (_d = result.response) === null || _d === void 0 ? void 0 : _d.headers,
713
540
  };
541
+ return response;
714
542
  }
715
- async function getProvider(provider, config) {
716
- const headersKey = config.customHeaders
717
- ? generateCacheKey(Object.keys(config.customHeaders)
543
+ async function getProvider(provider, apiKey, baseURL, customHeaders) {
544
+ const headersKey = customHeaders
545
+ ? JSON.stringify(Object.keys(customHeaders)
718
546
  .sort()
719
- .map((key) => [key, config.customHeaders[key]]))
547
+ .map((key) => [key, customHeaders[key]]))
720
548
  : '';
721
- const cacheKey = generateCacheKey(`${provider}:${config.apiKey}:${config.baseURL || ''}:${headersKey}`, 'provider:');
549
+ const cacheKey = `${provider}:${apiKey}:${baseURL || ''}:${headersKey}`;
722
550
  const cached = providerCache.get(cacheKey);
723
551
  if (cached)
724
552
  return cached;
553
+ let providerInstance;
725
554
  try {
726
- let providerInstance;
727
555
  switch (provider) {
728
556
  case 'google':
729
557
  const { createGoogleGenerativeAI } = await Promise.resolve().then(() => __importStar(require('@ai-sdk/google')));
730
558
  providerInstance = createGoogleGenerativeAI({
731
- apiKey: config.apiKey,
732
- ...(config.baseURL && { baseURL: config.baseURL }),
733
- ...(config.customHeaders && Object.keys(config.customHeaders).length > 0 && {
734
- headers: config.customHeaders
735
- }),
559
+ apiKey,
560
+ ...(baseURL && { baseURL }),
561
+ ...(customHeaders && Object.keys(customHeaders).length > 0 && { headers: customHeaders }),
736
562
  });
737
563
  break;
738
564
  case 'deepseek':
739
565
  const { createDeepSeek } = await Promise.resolve().then(() => __importStar(require('@ai-sdk/deepseek')));
740
- providerInstance = createDeepSeek({
741
- apiKey: config.apiKey,
742
- ...(config.baseURL && { baseURL: config.baseURL })
743
- });
566
+ providerInstance = createDeepSeek({ apiKey, ...(baseURL && { baseURL }) });
744
567
  break;
745
568
  case 'groq':
746
569
  const { createGroq } = await Promise.resolve().then(() => __importStar(require('@ai-sdk/groq')));
747
- providerInstance = createGroq({
748
- apiKey: config.apiKey,
749
- ...(config.baseURL && { baseURL: config.baseURL })
750
- });
570
+ providerInstance = createGroq({ apiKey, ...(baseURL && { baseURL }) });
751
571
  break;
752
572
  case 'openrouter':
753
573
  const { createOpenRouter } = await Promise.resolve().then(() => __importStar(require('@openrouter/ai-sdk-provider')));
754
- providerInstance = createOpenRouter({
755
- apiKey: config.apiKey,
756
- ...(config.baseURL && { baseURL: config.baseURL })
757
- });
574
+ providerInstance = createOpenRouter({ apiKey, ...(baseURL && { baseURL }) });
758
575
  break;
759
576
  default:
760
- throw new ProviderError(`Unsupported provider: ${provider}`);
577
+ throw new Error(`Unsupported provider: ${provider}`);
761
578
  }
762
579
  providerCache.set(cacheKey, providerInstance);
763
580
  return providerInstance;
764
581
  }
765
582
  catch (error) {
766
- throw new ProviderError(`Failed to initialize ${provider} provider: ${error.message}`, { provider, baseURL: config.baseURL });
583
+ throw new Error(`Failed to initialize ${provider} provider: ${error.message}`);
767
584
  }
768
585
  }
769
586
  function parseAndValidateSchema(rawSchema, exec) {
770
- if (!rawSchema.trim()) {
771
- throw new ValidationError('Schema field is empty');
772
- }
773
- if (rawSchema.length > 100000) {
774
- throw new ValidationError('Schema is too large (max 100,000 characters)');
775
- }
776
- const cacheKey = generateCacheKey(rawSchema, 'schema:');
587
+ const cacheKey = `schema:${Buffer.from(rawSchema).toString('base64').substring(0, 50)}`;
777
588
  const cached = schemaCache.get(cacheKey);
778
589
  if (cached)
779
590
  return cached;
591
+ let parsedSchema;
780
592
  try {
781
- const parsedSchema = JSON.parse(rawSchema);
782
- if (!ajv.validateSchema(parsedSchema)) {
783
- throw new ValidationError(`Invalid JSON Schema: ${ajv.errorsText(ajv.errors)}`);
784
- }
785
- schemaCache.set(cacheKey, parsedSchema);
786
- return parsedSchema;
593
+ parsedSchema = JSON.parse(rawSchema);
787
594
  }
788
595
  catch (err) {
789
- throw new ValidationError(`Schema is not valid JSON: ${err.message}`);
596
+ throw new n8n_workflow_1.NodeOperationError(exec.getNode(), 'Schema is not valid JSON: ' + err.message);
597
+ }
598
+ if (!ajv.validateSchema(parsedSchema)) {
599
+ throw new n8n_workflow_1.NodeOperationError(exec.getNode(), `Invalid JSON Schema: ${ajv.errorsText(ajv.errors)}`);
790
600
  }
601
+ schemaCache.set(cacheKey, parsedSchema);
602
+ return parsedSchema;
791
603
  }
792
604
  function parseStopSequences(stopSequencesStr) {
793
605
  if (!stopSequencesStr)
794
606
  return undefined;
795
- const sequences = stopSequencesStr
796
- .split(',')
797
- .map(s => s.trim())
798
- .filter(Boolean);
799
- return sequences.length > 0 ? sequences : undefined;
607
+ return stopSequencesStr.split(',').map(s => s.trim()).filter(s => s.length > 0);
800
608
  }
801
609
  function applyNumericOptions(params, options, keys) {
802
610
  for (const key of keys) {
803
611
  const value = options[key];
804
612
  if (value !== undefined && value !== null && value !== '') {
805
- const numValue = Number(value);
806
- if (!isNaN(numValue)) {
807
- params[key] = numValue;
808
- }
613
+ params[key] = value;
809
614
  }
810
615
  }
811
616
  }
812
- function resolveFinalContext(input, cachedContentName, cacheContentInfo) {
813
- const finalContext = {};
814
- if (!cachedContentName) {
815
- return input;
816
- }
817
- if (input.prompt) {
818
- finalContext.prompt = input.prompt;
819
- }
820
- else if (input.messages) {
821
- const filteredMessages = input.messages.filter(msg => msg.role !== 'system');
822
- if (filteredMessages.length > 0) {
823
- finalContext.messages = filteredMessages;
824
- }
617
+ class UniversalAI {
618
+ constructor() {
619
+ this.description = descriptions_1.UNIVERSAL_AI_DESCRIPTION;
620
+ this.methods = {
621
+ loadOptions: {
622
+ async getModels() {
623
+ const provider = this.getCurrentNodeParameter('provider');
624
+ const cacheKey = `models:${provider}`;
625
+ const cached = modelCache.get(cacheKey);
626
+ if (cached)
627
+ return cached;
628
+ const { OPENROUTER_MODELS, GOOGLE_GEMINI_MODELS, DEEPSEEK_MODELS, GROQ_MODELS } = await Promise.resolve().then(() => __importStar(require('./model-lists')));
629
+ const models = {
630
+ google: GOOGLE_GEMINI_MODELS,
631
+ deepseek: DEEPSEEK_MODELS,
632
+ groq: GROQ_MODELS,
633
+ openrouter: OPENROUTER_MODELS,
634
+ }[provider] || [];
635
+ modelCache.set(cacheKey, models);
636
+ return models;
637
+ },
638
+ },
639
+ };
825
640
  }
826
- return finalContext;
827
- }
828
- function withErrorHandling(fn, context, exec, itemIndex) {
829
- return fn().catch((error) => {
830
- if (error instanceof UniversalAIError) {
831
- throw error;
641
+ async execute() {
642
+ const items = this.getInputData();
643
+ const returnData = [];
644
+ const provider = this.getNodeParameter('provider', 0);
645
+ const credentialType = {
646
+ google: 'googleGenerativeAIApi',
647
+ deepseek: 'deepSeekApi',
648
+ groq: 'groqApi',
649
+ openrouter: 'openRouterApi',
650
+ }[provider];
651
+ if (!credentialType) {
652
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported provider: ${provider}`);
832
653
  }
833
- if (error instanceof n8n_workflow_1.NodeOperationError) {
834
- throw error;
654
+ const credentials = await this.getCredentials(credentialType);
655
+ if (!(credentials === null || credentials === void 0 ? void 0 : credentials.apiKey)) {
656
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No API key provided in credentials');
835
657
  }
836
- const errorMessage = error instanceof Error ? error.message : String(error);
837
- const enhancedError = new UniversalAIError(`${context}: ${errorMessage}`, 'UNKNOWN_ERROR', { originalError: error });
838
- if (exec && itemIndex !== undefined && exec.continueOnFail()) {
839
- return {
840
- json: {
841
- error: enhancedError.message,
842
- errorCode: enhancedError.code,
843
- success: false
658
+ const customHeaders = provider === 'google' ? getGoogleCustomHeaders(this, 0) : undefined;
659
+ const aiProvider = await getProvider(provider, credentials.apiKey, credentials.baseUrl, customHeaders);
660
+ for (let i = 0; i < items.length; i++) {
661
+ if (this.continueOnFail()) {
662
+ try {
663
+ const result = await processItem(this, i, provider, aiProvider, credentials.apiKey);
664
+ returnData.push(...result);
844
665
  }
845
- };
666
+ catch (error) {
667
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
668
+ returnData.push({
669
+ json: { error: errorMessage },
670
+ pairedItem: { item: i },
671
+ });
672
+ }
673
+ }
674
+ else {
675
+ const result = await processItem(this, i, provider, aiProvider, credentials.apiKey);
676
+ returnData.push(...result);
677
+ }
846
678
  }
847
- throw enhancedError;
848
- });
679
+ return [returnData];
680
+ }
849
681
  }
682
+ exports.UniversalAI = UniversalAI;
850
683
  async function processItem(exec, index, provider, aiProvider, apiKey) {
851
- return withErrorHandling(async () => {
852
- const operation = exec.getNodeParameter('operation', index);
853
- const model = exec.getNodeParameter('model', index);
854
- const options = exec.getNodeParameter('options', index, {});
855
- const input = await buildInput(exec, index);
856
- const modelSettings = getModelSettings(exec, index, provider, operation, options);
857
- return operation === 'generateText'
858
- ? await generateTextOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey)
859
- : await generateObjectOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey);
860
- }, 'processItem', exec, index);
684
+ const operation = exec.getNodeParameter('operation', index);
685
+ const model = exec.getNodeParameter('model', index);
686
+ const options = exec.getNodeParameter('options', index, {});
687
+ const input = await buildInput(exec, index);
688
+ const modelSettings = getModelSettings(exec, index, provider, operation, options);
689
+ return operation === 'generateText'
690
+ ? await generateTextOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey)
691
+ : await generateObjectOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey);
861
692
  }
862
693
  function getModelSettings(exec, index, provider, operation, options) {
863
694
  const settings = {};
@@ -868,14 +699,12 @@ function getModelSettings(exec, index, provider, operation, options) {
868
699
  if (provider === 'google') {
869
700
  const safetySettingsRaw = exec.getNodeParameter('safetySettings.settings', index, []);
870
701
  if (safetySettingsRaw.length > 0) {
871
- settings.safetySettings = safetySettingsRaw.map(s => ({
702
+ settings.safetySettings = safetySettingsRaw.map((s) => ({
872
703
  category: s.category,
873
704
  threshold: s.threshold,
874
705
  }));
875
706
  }
876
- if (operation === 'generateObject') {
877
- settings.structuredOutputs = true;
878
- }
707
+ settings.structuredOutputs = operation === 'generateObject';
879
708
  const responseModalities = exec.getNodeParameter('responseModalities', index, []);
880
709
  if (responseModalities.length > 0) {
881
710
  settings.responseModalities = responseModalities;
@@ -890,7 +719,7 @@ function buildGoogleProviderOptions(exec, index, cachedContentName) {
890
719
  if (!Number.isNaN(thinkingBudgetValue) && thinkingBudgetValue > -1) {
891
720
  options.thinkingConfig = {
892
721
  thinkingBudget: Math.max(0, thinkingBudgetValue),
893
- includeThoughts
722
+ includeThoughts,
894
723
  };
895
724
  }
896
725
  if (cachedContentName) {
@@ -899,16 +728,20 @@ function buildGoogleProviderOptions(exec, index, cachedContentName) {
899
728
  return Object.keys(options).length > 0 ? options : undefined;
900
729
  }
901
730
  function getGoogleCustomHeaders(exec, index) {
902
- var _a, _b, _c, _d;
731
+ var _a, _b;
903
732
  const headersCollection = exec.getNodeParameter('customHeaders', index, {});
904
733
  const entries = (_a = headersCollection === null || headersCollection === void 0 ? void 0 : headersCollection.headers) !== null && _a !== void 0 ? _a : [];
905
- if (!entries.length)
734
+ if (!entries || entries.length === 0) {
906
735
  return undefined;
736
+ }
907
737
  const headers = {};
908
738
  for (const entry of entries) {
909
- if ((_b = entry === null || entry === void 0 ? void 0 : entry.name) === null || _b === void 0 ? void 0 : _b.trim()) {
910
- headers[entry.name.trim()] = (_d = (_c = entry.value) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : '';
911
- }
739
+ if (!entry)
740
+ continue;
741
+ const name = (entry.name || '').trim();
742
+ if (!name)
743
+ continue;
744
+ headers[name] = (_b = entry.value) !== null && _b !== void 0 ? _b : '';
912
745
  }
913
746
  return Object.keys(headers).length > 0 ? headers : undefined;
914
747
  }
@@ -921,101 +754,82 @@ async function prepareGoogleCache(exec, index, apiKey, input, tools, context) {
921
754
  cachedContentName = await createGoogleCache(exec, index, apiKey, cacheContentInfo.content, tools);
922
755
  }
923
756
  catch (error) {
924
- if (error instanceof CacheError) {
925
- console.warn(`Cache creation failed for ${context}:`, error.message);
926
- }
927
- else {
928
- console.warn(`Unexpected cache error for ${context}:`, error);
929
- }
757
+ console.warn(`UniversalAI: Cache creation for ${context} generation failed, continuing without cache:`, error);
930
758
  }
931
759
  }
932
760
  const googleProviderOptions = buildGoogleProviderOptions(exec, index, cachedContentName || undefined);
933
761
  return {
934
762
  cachedContentName,
935
763
  cacheContentInfo,
936
- googleProviderOptions
764
+ googleProviderOptions,
937
765
  };
938
766
  }
939
- async function generateTextOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey) {
940
- return withErrorHandling(async () => {
941
- const enableStreaming = exec.getNodeParameter('enableStreaming', index, false);
942
- const includeRequestBody = options.includeRequestBody;
943
- const tools = provider === 'google' ? await buildGoogleTools(exec, index) : undefined;
944
- let cachedContentName = null;
945
- let googleProviderOptions;
946
- let cacheContentInfo;
947
- if (provider === 'google') {
948
- const cacheSetup = await prepareGoogleCache(exec, index, apiKey, input, tools, 'text');
949
- cachedContentName = cacheSetup.cachedContentName;
950
- cacheContentInfo = cacheSetup.cacheContentInfo;
951
- googleProviderOptions = cacheSetup.googleProviderOptions;
952
- }
953
- const finalContext = resolveFinalContext(input, cachedContentName, cacheContentInfo);
954
- const params = {
955
- model: aiProvider(model, modelSettings),
956
- ...finalContext,
957
- };
958
- if (tools && !cachedContentName) {
959
- params.tools = tools;
960
- }
961
- if (provider === 'google' && googleProviderOptions) {
962
- params.providerOptions = {
963
- google: googleProviderOptions,
964
- };
965
- }
966
- applyNumericOptions(params, options, [
967
- 'maxTokens', 'temperature', 'topP', 'topK',
968
- 'frequencyPenalty', 'presencePenalty', 'seed'
969
- ]);
970
- if (enableStreaming) {
971
- return await handleStreaming(params, provider, includeRequestBody);
767
+ function resolveFinalContext(input, cachedContentName, cacheContentInfo) {
768
+ const finalContext = {};
769
+ if (!cachedContentName) {
770
+ return input;
771
+ }
772
+ if (input.system) {
773
+ }
774
+ if (input.prompt) {
775
+ finalContext.prompt = input.prompt;
776
+ }
777
+ else if (input.messages) {
778
+ const filteredMessages = input.messages.filter(msg => msg.role !== 'system');
779
+ if (filteredMessages.length > 0) {
780
+ finalContext.messages = filteredMessages;
972
781
  }
973
- const result = await (0, ai_1.generateText)(params);
974
- const formattedResult = formatTextResult(result, includeRequestBody, provider);
975
- return [{ json: formattedResult }];
976
- }, 'generateTextOperation', exec, index);
782
+ }
783
+ return finalContext;
977
784
  }
978
- async function generateObjectOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey) {
979
- return withErrorHandling(async () => {
980
- const schemaName = exec.getNodeParameter('schemaName', index, '').trim();
981
- const schemaDescription = exec.getNodeParameter('schemaDescription', index, '').trim();
982
- const rawSchema = exec.getNodeParameter('schema', index);
983
- const parsedSchema = parseAndValidateSchema(rawSchema, exec);
984
- let cachedContentName = null;
985
- let googleProviderOptions;
986
- let cacheContentInfo;
987
- if (provider === 'google') {
988
- const cacheSetup = await prepareGoogleCache(exec, index, apiKey, input, undefined, 'object');
989
- cachedContentName = cacheSetup.cachedContentName;
990
- cacheContentInfo = cacheSetup.cacheContentInfo;
991
- googleProviderOptions = cacheSetup.googleProviderOptions;
992
- }
993
- const finalContext = resolveFinalContext(input, cachedContentName, cacheContentInfo);
994
- const params = {
995
- model: aiProvider(model, modelSettings),
996
- schema: (0, ai_1.jsonSchema)(parsedSchema),
997
- schemaName,
998
- schemaDescription,
999
- ...finalContext,
785
+ async function generateTextOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey) {
786
+ const enableStreaming = exec.getNodeParameter('enableStreaming', index, false);
787
+ const includeRequestBody = options.includeRequestBody;
788
+ const tools = provider === 'google' ? await buildGoogleTools(exec, index) : undefined;
789
+ let cachedContentName = null;
790
+ let googleProviderOptions;
791
+ let cacheContentInfo;
792
+ if (provider === 'google') {
793
+ const cacheSetup = await prepareGoogleCache(exec, index, apiKey, input, tools, 'text');
794
+ cachedContentName = cacheSetup.cachedContentName;
795
+ cacheContentInfo = cacheSetup.cacheContentInfo;
796
+ googleProviderOptions = cacheSetup.googleProviderOptions;
797
+ }
798
+ const finalContext = resolveFinalContext(input, cachedContentName, cacheContentInfo);
799
+ const params = {
800
+ model: aiProvider(model, modelSettings),
801
+ ...finalContext,
802
+ };
803
+ if (tools && !cachedContentName) {
804
+ params.tools = tools;
805
+ }
806
+ if (provider === 'google' && googleProviderOptions) {
807
+ params.providerOptions = {
808
+ google: googleProviderOptions,
1000
809
  };
1001
- if (provider === 'google' && googleProviderOptions) {
1002
- params.providerOptions = {
1003
- google: googleProviderOptions,
1004
- };
1005
- }
1006
- applyNumericOptions(params, options, [
1007
- 'temperature', 'topP', 'topK',
1008
- 'frequencyPenalty', 'presencePenalty', 'seed'
1009
- ]);
1010
- const result = await (0, ai_1.generateObject)(params);
1011
- const formattedResult = formatObjectResult(result, options.includeRequestBody, provider);
1012
- return [{ json: formattedResult }];
1013
- }, 'generateObjectOperation', exec, index);
810
+ }
811
+ const textNumericKeys = [
812
+ 'maxTokens',
813
+ 'temperature',
814
+ 'topP',
815
+ 'topK',
816
+ 'frequencyPenalty',
817
+ 'presencePenalty',
818
+ 'seed',
819
+ ];
820
+ applyNumericOptions(params, options, textNumericKeys);
821
+ if (enableStreaming) {
822
+ return await handleStreaming(params, provider, includeRequestBody);
823
+ }
824
+ const result = await (0, ai_1.generateText)(params);
825
+ const formattedResult = formatTextResult(result, includeRequestBody, provider);
826
+ return [{ json: formattedResult }];
1014
827
  }
1015
828
  async function buildGoogleTools(exec, index) {
1016
829
  const googleTools = exec.getNodeParameter('googleTools', index, []);
1017
- if (!(googleTools === null || googleTools === void 0 ? void 0 : googleTools.length))
830
+ if (!googleTools || googleTools.length === 0) {
1018
831
  return undefined;
832
+ }
1019
833
  const tools = {};
1020
834
  const { google } = await Promise.resolve().then(() => __importStar(require('@ai-sdk/google')));
1021
835
  const toolSet = new Set(googleTools);
@@ -1031,129 +845,82 @@ async function buildGoogleTools(exec, index) {
1031
845
  return tools;
1032
846
  }
1033
847
  async function handleStreaming(params, provider, includeRequestBody) {
1034
- return withErrorHandling(async () => {
1035
- const stream = await (0, ai_1.streamText)(params);
1036
- const chunks = [];
1037
- let fullText = '';
848
+ const stream = await (0, ai_1.streamText)(params);
849
+ const chunks = [];
850
+ let fullText = '';
851
+ for await (const textPart of stream.textStream) {
852
+ fullText += textPart;
853
+ chunks.push({ json: { chunk: textPart, isStreaming: true } });
854
+ }
855
+ let finalUsage;
856
+ try {
857
+ finalUsage = await stream.usage;
858
+ }
859
+ catch (error) {
860
+ console.warn('UniversalAI: Failed to get usage from stream:', error);
861
+ finalUsage = undefined;
862
+ }
863
+ const finalJson = {
864
+ text: fullText,
865
+ toolCalls: stream.toolCalls || [],
866
+ toolResults: stream.toolResults || [],
867
+ finishReason: stream.finishReason,
868
+ usage: finalUsage ? formatUsage({ usage: finalUsage }, provider) : undefined,
869
+ isStreaming: false,
870
+ isFinal: true,
871
+ };
872
+ if (includeRequestBody) {
1038
873
  try {
1039
- for await (const textPart of stream.textStream) {
1040
- fullText += textPart;
1041
- chunks.push({
1042
- json: {
1043
- chunk: textPart,
1044
- isStreaming: true
1045
- }
1046
- });
874
+ const requestMetadata = stream.request ? await stream.request : undefined;
875
+ if ((requestMetadata === null || requestMetadata === void 0 ? void 0 : requestMetadata.body) !== undefined) {
876
+ finalJson.request = { body: requestMetadata.body };
1047
877
  }
1048
878
  }
1049
879
  catch (error) {
1050
- throw new UniversalAIError(`Stream processing failed: ${error.message}`, 'STREAM_ERROR', { provider });
1051
- }
1052
- let finalUsage;
1053
- let requestMetadata;
1054
- try {
1055
- finalUsage = await stream.usage;
1056
- }
1057
- catch (error) {
1058
- console.warn('Could not get usage from stream:', error);
880
+ console.warn('UniversalAI: Failed to get request metadata from stream:', error);
1059
881
  }
1060
- try {
1061
- requestMetadata = stream.request ? await stream.request : undefined;
1062
- }
1063
- catch (error) {
1064
- console.warn('Could not get request metadata from stream:', error);
1065
- }
1066
- const finalJson = {
1067
- text: fullText,
1068
- toolCalls: stream.toolCalls || [],
1069
- toolResults: stream.toolResults || [],
1070
- finishReason: stream.finishReason,
1071
- usage: finalUsage ? formatUsage({ usage: finalUsage }, provider) : undefined,
1072
- isStreaming: false,
1073
- isFinal: true,
1074
- };
1075
- if (includeRequestBody && (requestMetadata === null || requestMetadata === void 0 ? void 0 : requestMetadata.body) !== undefined) {
1076
- finalJson.request = { body: requestMetadata.body };
1077
- }
1078
- chunks.push({ json: finalJson });
1079
- return chunks;
1080
- }, 'handleStreaming');
1081
- }
1082
- class UniversalAI {
1083
- constructor() {
1084
- this.description = descriptions_1.UNIVERSAL_AI_DESCRIPTION;
1085
- this.methods = {
1086
- loadOptions: {
1087
- async getModels() {
1088
- return withErrorHandling(async () => {
1089
- const provider = this.getCurrentNodeParameter('provider');
1090
- const cacheKey = generateCacheKey(provider, 'models:');
1091
- const cached = modelCache.get(cacheKey);
1092
- if (cached)
1093
- return cached;
1094
- const { OPENROUTER_MODELS, GOOGLE_GEMINI_MODELS, DEEPSEEK_MODELS, GROQ_MODELS } = await Promise.resolve().then(() => __importStar(require('./model-lists')));
1095
- const models = {
1096
- google: GOOGLE_GEMINI_MODELS,
1097
- deepseek: DEEPSEEK_MODELS,
1098
- groq: GROQ_MODELS,
1099
- openrouter: OPENROUTER_MODELS
1100
- }[provider] || [];
1101
- modelCache.set(cacheKey, models);
1102
- return models;
1103
- }, 'getModels');
1104
- },
1105
- },
1106
- };
1107
882
  }
1108
- async execute() {
1109
- const items = this.getInputData();
1110
- const returnData = [];
1111
- const provider = this.getNodeParameter('provider', 0);
1112
- const credentialType = {
1113
- google: 'googleGenerativeAIApi',
1114
- deepseek: 'deepSeekApi',
1115
- groq: 'groqApi',
1116
- openrouter: 'openRouterApi'
1117
- }[provider];
1118
- if (!credentialType) {
1119
- throw new ValidationError(`Unsupported provider: ${provider}`);
1120
- }
1121
- const credentials = await this.getCredentials(credentialType);
1122
- if (!(credentials === null || credentials === void 0 ? void 0 : credentials.apiKey)) {
1123
- throw new ValidationError('No API key provided in credentials');
1124
- }
1125
- const customHeaders = provider === 'google' ? getGoogleCustomHeaders(this, 0) : undefined;
1126
- const providerConfig = {
1127
- apiKey: credentials.apiKey,
1128
- customHeaders,
883
+ chunks.push({ json: finalJson });
884
+ return chunks;
885
+ }
886
+ async function generateObjectOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey) {
887
+ const schemaName = exec.getNodeParameter('schemaName', index, '');
888
+ const schemaDescription = exec.getNodeParameter('schemaDescription', index, '');
889
+ const rawSchema = exec.getNodeParameter('schema', index);
890
+ const parsedSchema = parseAndValidateSchema(rawSchema, exec);
891
+ let cachedContentName = null;
892
+ let googleProviderOptions;
893
+ let cacheContentInfo;
894
+ if (provider === 'google') {
895
+ const cacheSetup = await prepareGoogleCache(exec, index, apiKey, input, undefined, 'object');
896
+ cachedContentName = cacheSetup.cachedContentName;
897
+ cacheContentInfo = cacheSetup.cacheContentInfo;
898
+ googleProviderOptions = cacheSetup.googleProviderOptions;
899
+ }
900
+ const finalContext = resolveFinalContext(input, cachedContentName, cacheContentInfo);
901
+ const params = {
902
+ model: aiProvider(model, modelSettings),
903
+ schema: (0, ai_1.jsonSchema)(parsedSchema),
904
+ schemaName,
905
+ schemaDescription,
906
+ ...finalContext,
907
+ };
908
+ if (provider === 'google' && googleProviderOptions) {
909
+ params.providerOptions = {
910
+ google: googleProviderOptions,
1129
911
  };
1130
- if (credentials.baseUrl && String(credentials.baseUrl).trim()) {
1131
- providerConfig.baseURL = credentials.baseUrl;
1132
- }
1133
- const aiProvider = await getProvider(provider, providerConfig);
1134
- for (let i = 0; i < items.length; i++) {
1135
- try {
1136
- const result = await processItem(this, i, provider, aiProvider, credentials.apiKey);
1137
- returnData.push(...result);
1138
- }
1139
- catch (error) {
1140
- if (this.continueOnFail()) {
1141
- returnData.push({
1142
- json: {
1143
- error: error instanceof Error ? error.message : 'Unknown error',
1144
- errorCode: error instanceof UniversalAIError ? error.code : 'UNKNOWN_ERROR',
1145
- itemIndex: i,
1146
- success: false
1147
- },
1148
- pairedItem: { item: i }
1149
- });
1150
- continue;
1151
- }
1152
- throw error;
1153
- }
1154
- }
1155
- return [returnData];
1156
912
  }
913
+ const objectNumericKeys = [
914
+ 'temperature',
915
+ 'topP',
916
+ 'topK',
917
+ 'frequencyPenalty',
918
+ 'presencePenalty',
919
+ 'seed',
920
+ ];
921
+ applyNumericOptions(params, options, objectNumericKeys);
922
+ const result = await (0, ai_1.generateObject)(params);
923
+ const formattedResult = formatObjectResult(result, options.includeRequestBody, provider);
924
+ return [{ json: formattedResult }];
1157
925
  }
1158
- exports.UniversalAI = UniversalAI;
1159
926
  //# sourceMappingURL=UniversalAI.node.js.map