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

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 normalizedCacheContent = (_a = cacheContent === null || cacheContent === void 0 ? void 0 : cacheContent.trim()) !== null && _a !== void 0 ? _a : '';
133
+ if (!normalizedCacheContent) {
134
+ return null;
209
135
  }
210
- catch (error) {
211
- console.warn(`Failed to cleanup Google cache ${cacheName}:`, error);
136
+ const googleCacheManager = await getGoogleCacheManager(apiKey);
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, Object.keys(cacheKeyData).sort());
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('Cache creation returned no name identifier');
167
+ }
168
+ googleCachedContexts.set(cacheKey, { name: cachedContentName }, (ttlSeconds - 300) * 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: Google cache creation failed:', 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
- sections.push(`Messages:\n${messageSections.join('\n\n')}`);
230
+ sections.push(`Conversation History:\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()) {
325
- sections.push(`Prompt Template:\n${input.prompt.trim()}`);
234
+ if (input.prompt && input.prompt.trim()) {
235
+ sections.push(`Instruction 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
- }
361
+ const batchResults = await Promise.all(batchPromises);
362
+ processedAttachments.push(...batchResults);
532
363
  }
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
- };
364
+ for (const attachment of processedAttachments) {
365
+ if (attachment) {
366
+ parts.push(attachment);
550
367
  }
551
368
  }
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
- }
561
- }
562
- }
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;
@@ -712,152 +539,155 @@ function formatResponse(result) {
712
539
  headers: (_d = result.response) === null || _d === void 0 ? void 0 : _d.headers,
713
540
  };
714
541
  }
715
- async function getProvider(provider, config) {
716
- const headersKey = config.customHeaders
717
- ? generateCacheKey(Object.keys(config.customHeaders)
542
+ async function getProvider(provider, apiKey, baseURL, customHeaders) {
543
+ const headersKey = customHeaders
544
+ ? JSON.stringify(Object.keys(customHeaders)
718
545
  .sort()
719
- .map((key) => [key, config.customHeaders[key]]))
546
+ .map((key) => [key, customHeaders[key]]))
720
547
  : '';
721
- const cacheKey = generateCacheKey(`${provider}:${config.apiKey}:${config.baseURL || ''}:${headersKey}`, 'provider:');
548
+ const cacheKey = `${provider}:${apiKey}:${baseURL || ''}:${headersKey}`;
722
549
  const cached = providerCache.get(cacheKey);
723
550
  if (cached)
724
551
  return cached;
552
+ let providerInstance;
725
553
  try {
726
- let providerInstance;
727
554
  switch (provider) {
728
555
  case 'google':
729
556
  const { createGoogleGenerativeAI } = await Promise.resolve().then(() => __importStar(require('@ai-sdk/google')));
730
557
  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
- }),
558
+ apiKey,
559
+ ...(baseURL && { baseURL }),
560
+ ...(customHeaders && Object.keys(customHeaders).length > 0 && { headers: customHeaders }),
736
561
  });
737
562
  break;
738
563
  case 'deepseek':
739
564
  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
- });
565
+ providerInstance = createDeepSeek({ apiKey, ...(baseURL && { baseURL }) });
744
566
  break;
745
567
  case 'groq':
746
568
  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
- });
569
+ providerInstance = createGroq({ apiKey, ...(baseURL && { baseURL }) });
751
570
  break;
752
571
  case 'openrouter':
753
572
  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
- });
573
+ providerInstance = createOpenRouter({ apiKey, ...(baseURL && { baseURL }) });
758
574
  break;
759
575
  default:
760
- throw new ProviderError(`Unsupported provider: ${provider}`);
576
+ throw new Error(`Unsupported provider: ${provider}`);
761
577
  }
762
578
  providerCache.set(cacheKey, providerInstance);
763
579
  return providerInstance;
764
580
  }
765
581
  catch (error) {
766
- throw new ProviderError(`Failed to initialize ${provider} provider: ${error.message}`, { provider, baseURL: config.baseURL });
582
+ throw new Error(`Failed to initialize ${provider} provider: ${error.message}`);
767
583
  }
768
584
  }
769
585
  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:');
586
+ const cacheKey = `schema:${Buffer.from(rawSchema).toString('base64').substring(0, 50)}`;
777
587
  const cached = schemaCache.get(cacheKey);
778
588
  if (cached)
779
589
  return cached;
590
+ let parsedSchema;
780
591
  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;
592
+ parsedSchema = JSON.parse(rawSchema);
787
593
  }
788
594
  catch (err) {
789
- throw new ValidationError(`Schema is not valid JSON: ${err.message}`);
595
+ throw new n8n_workflow_1.NodeOperationError(exec.getNode(), 'Schema is not valid JSON: ' + err.message);
596
+ }
597
+ if (!ajv.validateSchema(parsedSchema)) {
598
+ throw new n8n_workflow_1.NodeOperationError(exec.getNode(), `Invalid JSON Schema: ${ajv.errorsText(ajv.errors)}`);
790
599
  }
600
+ schemaCache.set(cacheKey, parsedSchema);
601
+ return parsedSchema;
791
602
  }
792
603
  function parseStopSequences(stopSequencesStr) {
793
604
  if (!stopSequencesStr)
794
605
  return undefined;
795
- const sequences = stopSequencesStr
796
- .split(',')
797
- .map(s => s.trim())
798
- .filter(Boolean);
799
- return sequences.length > 0 ? sequences : undefined;
606
+ return stopSequencesStr.split(',').map(s => s.trim()).filter(s => s.length > 0);
800
607
  }
801
608
  function applyNumericOptions(params, options, keys) {
802
609
  for (const key of keys) {
803
610
  const value = options[key];
804
611
  if (value !== undefined && value !== null && value !== '') {
805
- const numValue = Number(value);
806
- if (!isNaN(numValue)) {
807
- params[key] = numValue;
808
- }
612
+ params[key] = value;
809
613
  }
810
614
  }
811
615
  }
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
- }
616
+ class UniversalAI {
617
+ constructor() {
618
+ this.description = descriptions_1.UNIVERSAL_AI_DESCRIPTION;
619
+ this.methods = {
620
+ loadOptions: {
621
+ async getModels() {
622
+ const provider = this.getCurrentNodeParameter('provider');
623
+ const cacheKey = `models:${provider}`;
624
+ const cached = modelCache.get(cacheKey);
625
+ if (cached)
626
+ return cached;
627
+ const { OPENROUTER_MODELS, GOOGLE_GEMINI_MODELS, DEEPSEEK_MODELS, GROQ_MODELS } = await Promise.resolve().then(() => __importStar(require('./model-lists')));
628
+ const models = {
629
+ google: GOOGLE_GEMINI_MODELS,
630
+ deepseek: DEEPSEEK_MODELS,
631
+ groq: GROQ_MODELS,
632
+ openrouter: OPENROUTER_MODELS,
633
+ }[provider] || [];
634
+ modelCache.set(cacheKey, models);
635
+ return models;
636
+ },
637
+ },
638
+ };
825
639
  }
826
- return finalContext;
827
- }
828
- function withErrorHandling(fn, context, exec, itemIndex) {
829
- return fn().catch((error) => {
830
- if (error instanceof UniversalAIError) {
831
- throw error;
640
+ async execute() {
641
+ const items = this.getInputData();
642
+ const returnData = [];
643
+ const provider = this.getNodeParameter('provider', 0);
644
+ const credentialType = {
645
+ google: 'googleGenerativeAIApi',
646
+ deepseek: 'deepSeekApi',
647
+ groq: 'groqApi',
648
+ openrouter: 'openRouterApi',
649
+ }[provider];
650
+ if (!credentialType) {
651
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported provider: ${provider}`);
832
652
  }
833
- if (error instanceof n8n_workflow_1.NodeOperationError) {
834
- throw error;
653
+ const credentials = await this.getCredentials(credentialType);
654
+ if (!(credentials === null || credentials === void 0 ? void 0 : credentials.apiKey)) {
655
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No API key provided in credentials');
835
656
  }
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
657
+ const customHeaders = provider === 'google' ? getGoogleCustomHeaders(this, 0) : undefined;
658
+ const aiProvider = await getProvider(provider, credentials.apiKey, credentials.baseUrl, customHeaders);
659
+ for (let i = 0; i < items.length; i++) {
660
+ if (this.continueOnFail()) {
661
+ try {
662
+ const result = await processItem(this, i, provider, aiProvider, credentials.apiKey);
663
+ returnData.push(...result);
844
664
  }
845
- };
665
+ catch (error) {
666
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
667
+ returnData.push({
668
+ json: { error: errorMessage },
669
+ pairedItem: { item: i },
670
+ });
671
+ }
672
+ }
673
+ else {
674
+ const result = await processItem(this, i, provider, aiProvider, credentials.apiKey);
675
+ returnData.push(...result);
676
+ }
846
677
  }
847
- throw enhancedError;
848
- });
678
+ return [returnData];
679
+ }
849
680
  }
681
+ exports.UniversalAI = UniversalAI;
850
682
  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);
683
+ const operation = exec.getNodeParameter('operation', index);
684
+ const model = exec.getNodeParameter('model', index);
685
+ const options = exec.getNodeParameter('options', index, {});
686
+ const input = await buildInput(exec, index);
687
+ const modelSettings = getModelSettings(exec, index, provider, operation, options);
688
+ return operation === 'generateText'
689
+ ? await generateTextOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey)
690
+ : await generateObjectOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey);
861
691
  }
862
692
  function getModelSettings(exec, index, provider, operation, options) {
863
693
  const settings = {};
@@ -868,14 +698,12 @@ function getModelSettings(exec, index, provider, operation, options) {
868
698
  if (provider === 'google') {
869
699
  const safetySettingsRaw = exec.getNodeParameter('safetySettings.settings', index, []);
870
700
  if (safetySettingsRaw.length > 0) {
871
- settings.safetySettings = safetySettingsRaw.map(s => ({
701
+ settings.safetySettings = safetySettingsRaw.map((s) => ({
872
702
  category: s.category,
873
703
  threshold: s.threshold,
874
704
  }));
875
705
  }
876
- if (operation === 'generateObject') {
877
- settings.structuredOutputs = true;
878
- }
706
+ settings.structuredOutputs = operation === 'generateObject';
879
707
  const responseModalities = exec.getNodeParameter('responseModalities', index, []);
880
708
  if (responseModalities.length > 0) {
881
709
  settings.responseModalities = responseModalities;
@@ -890,7 +718,7 @@ function buildGoogleProviderOptions(exec, index, cachedContentName) {
890
718
  if (!Number.isNaN(thinkingBudgetValue) && thinkingBudgetValue > -1) {
891
719
  options.thinkingConfig = {
892
720
  thinkingBudget: Math.max(0, thinkingBudgetValue),
893
- includeThoughts
721
+ includeThoughts,
894
722
  };
895
723
  }
896
724
  if (cachedContentName) {
@@ -899,16 +727,20 @@ function buildGoogleProviderOptions(exec, index, cachedContentName) {
899
727
  return Object.keys(options).length > 0 ? options : undefined;
900
728
  }
901
729
  function getGoogleCustomHeaders(exec, index) {
902
- var _a, _b, _c, _d;
730
+ var _a, _b;
903
731
  const headersCollection = exec.getNodeParameter('customHeaders', index, {});
904
732
  const entries = (_a = headersCollection === null || headersCollection === void 0 ? void 0 : headersCollection.headers) !== null && _a !== void 0 ? _a : [];
905
- if (!entries.length)
733
+ if (!entries || entries.length === 0) {
906
734
  return undefined;
735
+ }
907
736
  const headers = {};
908
737
  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
- }
738
+ if (!entry)
739
+ continue;
740
+ const name = (entry.name || '').trim();
741
+ if (!name)
742
+ continue;
743
+ headers[name] = (_b = entry.value) !== null && _b !== void 0 ? _b : '';
912
744
  }
913
745
  return Object.keys(headers).length > 0 ? headers : undefined;
914
746
  }
@@ -921,101 +753,59 @@ async function prepareGoogleCache(exec, index, apiKey, input, tools, context) {
921
753
  cachedContentName = await createGoogleCache(exec, index, apiKey, cacheContentInfo.content, tools);
922
754
  }
923
755
  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
- }
756
+ console.warn(`UniversalAI: Cache creation for ${context} generation failed, continuing without cache:`, error);
930
757
  }
931
758
  }
932
759
  const googleProviderOptions = buildGoogleProviderOptions(exec, index, cachedContentName || undefined);
933
760
  return {
934
761
  cachedContentName,
935
762
  cacheContentInfo,
936
- googleProviderOptions
763
+ googleProviderOptions,
937
764
  };
938
765
  }
939
766
  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);
972
- }
973
- const result = await (0, ai_1.generateText)(params);
974
- const formattedResult = formatTextResult(result, includeRequestBody, provider);
975
- return [{ json: formattedResult }];
976
- }, 'generateTextOperation', exec, index);
977
- }
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,
767
+ const enableStreaming = exec.getNodeParameter('enableStreaming', index, false);
768
+ const includeRequestBody = options.includeRequestBody;
769
+ const tools = provider === 'google' ? await buildGoogleTools(exec, index) : undefined;
770
+ let googleProviderOptions;
771
+ if (provider === 'google') {
772
+ const cacheSetup = await prepareGoogleCache(exec, index, apiKey, input, tools, 'text');
773
+ googleProviderOptions = cacheSetup.googleProviderOptions;
774
+ }
775
+ const params = {
776
+ model: aiProvider(model, modelSettings),
777
+ ...input,
778
+ };
779
+ if (tools && Object.keys(tools).length > 0) {
780
+ params.tools = tools;
781
+ }
782
+ if (provider === 'google' && googleProviderOptions) {
783
+ params.providerOptions = {
784
+ google: googleProviderOptions,
1000
785
  };
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);
786
+ }
787
+ const textNumericKeys = [
788
+ 'maxTokens',
789
+ 'temperature',
790
+ 'topP',
791
+ 'topK',
792
+ 'frequencyPenalty',
793
+ 'presencePenalty',
794
+ 'seed',
795
+ ];
796
+ applyNumericOptions(params, options, textNumericKeys);
797
+ if (enableStreaming) {
798
+ return await handleStreaming(params, provider, includeRequestBody);
799
+ }
800
+ const result = await (0, ai_1.generateText)(params);
801
+ const formattedResult = formatTextResult(result, includeRequestBody, provider);
802
+ return [{ json: formattedResult }];
1014
803
  }
1015
804
  async function buildGoogleTools(exec, index) {
1016
805
  const googleTools = exec.getNodeParameter('googleTools', index, []);
1017
- if (!(googleTools === null || googleTools === void 0 ? void 0 : googleTools.length))
806
+ if (!googleTools || googleTools.length === 0) {
1018
807
  return undefined;
808
+ }
1019
809
  const tools = {};
1020
810
  const { google } = await Promise.resolve().then(() => __importStar(require('@ai-sdk/google')));
1021
811
  const toolSet = new Set(googleTools);
@@ -1031,129 +821,77 @@ async function buildGoogleTools(exec, index) {
1031
821
  return tools;
1032
822
  }
1033
823
  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 = '';
824
+ const stream = await (0, ai_1.streamText)(params);
825
+ const chunks = [];
826
+ let fullText = '';
827
+ for await (const textPart of stream.textStream) {
828
+ fullText += textPart;
829
+ chunks.push({ json: { chunk: textPart, isStreaming: true } });
830
+ }
831
+ let finalUsage;
832
+ try {
833
+ finalUsage = await stream.usage;
834
+ }
835
+ catch (error) {
836
+ console.warn('UniversalAI: Failed to get usage from stream:', error);
837
+ finalUsage = undefined;
838
+ }
839
+ const finalJson = {
840
+ text: fullText,
841
+ toolCalls: stream.toolCalls || [],
842
+ toolResults: stream.toolResults || [],
843
+ finishReason: stream.finishReason,
844
+ usage: finalUsage ? formatUsage({ usage: finalUsage }, provider) : undefined,
845
+ isStreaming: false,
846
+ isFinal: true,
847
+ };
848
+ if (includeRequestBody) {
1038
849
  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
- });
850
+ const requestMetadata = stream.request ? await stream.request : undefined;
851
+ if ((requestMetadata === null || requestMetadata === void 0 ? void 0 : requestMetadata.body) !== undefined) {
852
+ finalJson.request = { body: requestMetadata.body };
1047
853
  }
1048
854
  }
1049
855
  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;
856
+ console.warn('UniversalAI: Failed to get request metadata from stream:', error);
1056
857
  }
1057
- catch (error) {
1058
- console.warn('Could not get usage from stream:', error);
1059
- }
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
858
  }
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,
859
+ chunks.push({ json: finalJson });
860
+ return chunks;
861
+ }
862
+ async function generateObjectOperation(exec, index, provider, aiProvider, model, modelSettings, input, options, apiKey) {
863
+ const schemaName = exec.getNodeParameter('schemaName', index, '');
864
+ const schemaDescription = exec.getNodeParameter('schemaDescription', index, '');
865
+ const rawSchema = exec.getNodeParameter('schema', index);
866
+ const parsedSchema = parseAndValidateSchema(rawSchema, exec);
867
+ let googleProviderOptions;
868
+ if (provider === 'google') {
869
+ const cacheSetup = await prepareGoogleCache(exec, index, apiKey, input, undefined, 'object');
870
+ googleProviderOptions = cacheSetup.googleProviderOptions;
871
+ }
872
+ const params = {
873
+ model: aiProvider(model, modelSettings),
874
+ schema: (0, ai_1.jsonSchema)(parsedSchema),
875
+ schemaName,
876
+ schemaDescription,
877
+ ...input,
878
+ };
879
+ if (provider === 'google' && googleProviderOptions) {
880
+ params.providerOptions = {
881
+ google: googleProviderOptions,
1129
882
  };
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
883
  }
884
+ const objectNumericKeys = [
885
+ 'temperature',
886
+ 'topP',
887
+ 'topK',
888
+ 'frequencyPenalty',
889
+ 'presencePenalty',
890
+ 'seed',
891
+ ];
892
+ applyNumericOptions(params, options, objectNumericKeys);
893
+ const result = await (0, ai_1.generateObject)(params);
894
+ const formattedResult = formatObjectResult(result, options.includeRequestBody, provider);
895
+ return [{ json: formattedResult }];
1157
896
  }
1158
- exports.UniversalAI = UniversalAI;
1159
897
  //# sourceMappingURL=UniversalAI.node.js.map