n8n-nodes-github-copilot 4.4.17 โ†’ 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,29 +3,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GitHubCopilotOpenAI = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
5
  const nodeProperties_1 = require("./nodeProperties");
6
- const utils_1 = require("../GitHubCopilotChatAPI/utils");
7
- const GitHubCopilotEndpoints_1 = require("../../shared/utils/GitHubCopilotEndpoints");
6
+ const chatCompletion_1 = require("./execute/chatCompletion");
7
+ const listModels_1 = require("./execute/listModels");
8
8
  const DynamicModelLoader_1 = require("../../shared/models/DynamicModelLoader");
9
- const GitHubCopilotModels_1 = require("../../shared/models/GitHubCopilotModels");
10
- const DynamicModelsManager_1 = require("../../shared/utils/DynamicModelsManager");
11
9
  class GitHubCopilotOpenAI {
12
10
  constructor() {
13
11
  this.description = {
14
- displayName: 'GitHub Copilot OpenAI',
15
- name: 'gitHubCopilotOpenAI',
16
- icon: 'file:../../shared/icons/copilot.svg',
17
- group: ['transform'],
12
+ displayName: "GitHub Copilot OpenAI",
13
+ name: "gitHubCopilotOpenAI",
14
+ icon: "file:../../shared/icons/copilot.svg",
15
+ group: ["transform"],
18
16
  version: 1,
19
- subtitle: '={{$parameter["model"]}}',
20
- description: 'OpenAI-compatible GitHub Copilot Chat API with full support for messages, tools, and all OpenAI parameters',
17
+ subtitle: '={{$parameter["operation"] === "listModels" ? "List Models" : $parameter["model"]}}',
18
+ description: "OpenAI-compatible GitHub Copilot API: Chat Completions and List Models (GET /v1/models)",
21
19
  defaults: {
22
- name: 'GitHub Copilot OpenAI',
20
+ name: "GitHub Copilot OpenAI",
23
21
  },
24
- inputs: ['main'],
25
- outputs: ['main'],
22
+ inputs: ["main"],
23
+ outputs: ["main"],
26
24
  credentials: [
27
25
  {
28
- name: 'githubCopilotApi',
26
+ name: "githubCopilotApi",
29
27
  required: true,
30
28
  },
31
29
  ],
@@ -43,569 +41,96 @@ class GitHubCopilotOpenAI {
43
41
  };
44
42
  }
45
43
  async execute() {
46
- var _a, _b, _c, _d, _e, _f, _g, _h;
44
+ var _a;
47
45
  const items = this.getInputData();
48
46
  const returnData = [];
49
47
  for (let i = 0; i < items.length; i++) {
50
48
  try {
51
- const modelSource = this.getNodeParameter('modelSource', i, 'fromList');
52
- let model;
53
- if (modelSource === 'custom') {
54
- model = this.getNodeParameter('customModel', i);
55
- if (!model || model.trim() === '') {
56
- throw new Error("Custom model name is required when using 'Custom (Manual Entry)' mode");
57
- }
58
- console.log(`๐Ÿ”ง Using custom model: ${model}`);
59
- }
60
- else {
61
- const selectedModel = this.getNodeParameter('model', i);
62
- if (selectedModel === '__manual__') {
63
- model = this.getNodeParameter('customModel', i);
64
- if (!model || model.trim() === '') {
65
- throw new Error("Custom model name is required when selecting 'โœ๏ธ Enter Custom Model Name'");
66
- }
67
- console.log(`โœ๏ธ Using manually entered model: ${model}`);
68
- }
69
- else {
70
- model = selectedModel;
71
- console.log(`๐Ÿ“‹ Using model from list: ${model}`);
72
- }
73
- }
74
- const messagesInputMode = this.getNodeParameter('messagesInputMode', i, 'manual');
75
- let messages = [];
76
- let requestBodyFromJson = undefined;
77
- if (messagesInputMode === 'json') {
78
- const messagesJson = this.getNodeParameter('messagesJson', i, '[]');
79
- try {
80
- let parsed;
81
- if (typeof messagesJson === 'object') {
82
- parsed = messagesJson;
83
- console.log('๐Ÿ“ฅ Received messages as direct object/array (no parsing needed)');
84
- }
85
- else {
86
- parsed = JSON.parse(messagesJson);
87
- console.log('๐Ÿ“ฅ Parsed messages from JSON string');
88
- }
89
- if (Array.isArray(parsed)) {
90
- messages = parsed;
91
- }
92
- else if (parsed.messages && Array.isArray(parsed.messages)) {
93
- messages = parsed.messages;
94
- requestBodyFromJson = parsed;
95
- console.log('๐Ÿ“ฅ Full OpenAI request body received:', JSON.stringify(parsed, null, 2));
96
- }
97
- else {
98
- messages = parsed;
99
- }
100
- }
101
- catch (error) {
102
- throw new Error(`Failed to parse messages JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);
103
- }
104
- }
105
- else {
106
- const messagesParam = this.getNodeParameter('messages', i, {
107
- message: [],
108
- });
109
- console.log('๐Ÿ“ฅ Manual mode - messagesParam:', JSON.stringify(messagesParam, null, 2));
110
- if (messagesParam.message && Array.isArray(messagesParam.message)) {
111
- for (const msg of messagesParam.message) {
112
- const message = {
113
- role: msg.role,
114
- content: msg.content,
115
- };
116
- const msgType = msg.type || 'text';
117
- if (msgType === 'file_binary') {
118
- const binaryKeyData = items[i].binary;
119
- const keyToUse = msg.binaryPropertyName || 'data';
120
- if (binaryKeyData && binaryKeyData[keyToUse]) {
121
- try {
122
- const binaryData = binaryKeyData[keyToUse];
123
- let mimeType = binaryData.mimeType || 'application/octet-stream';
124
- const buffer = await this.helpers.getBinaryDataBuffer(i, keyToUse);
125
- if (!mimeType.startsWith('image/')) {
126
- const detected = (0, utils_1.getImageMimeType)(buffer);
127
- if (detected !== 'application/octet-stream') {
128
- mimeType = detected;
129
- }
130
- else {
131
- console.warn(`โš ๏ธ Could not detect image type for '${keyToUse}', defaulting to 'image/jpeg' or keeping original '${mimeType}'`);
132
- if (mimeType === 'application/octet-stream') {
133
- mimeType = 'image/jpeg';
134
- }
135
- }
136
- }
137
- if (!mimeType.startsWith('image/')) {
138
- console.warn(`โš ๏ธ Forcing mime type '${mimeType}' to 'image/jpeg' to bypass schema validation (allowing backend to validate content)`);
139
- mimeType = 'image/jpeg';
140
- }
141
- const base64 = buffer.toString('base64');
142
- const dataUrl = `data:${mimeType};base64,${base64}`;
143
- const contentArray = [];
144
- const caption = msg.caption;
145
- if (caption && caption.trim() !== '') {
146
- contentArray.push({
147
- type: 'text',
148
- text: caption,
149
- });
150
- }
151
- else if (message.content && message.content.trim() !== '' && message.content !== '[object Object]') {
152
- contentArray.push({
153
- type: 'text',
154
- text: message.content,
155
- });
156
- }
157
- contentArray.push({
158
- type: 'image_url',
159
- image_url: {
160
- url: dataUrl,
161
- detail: 'auto',
162
- },
163
- });
164
- message.role = 'user';
165
- message.content = contentArray;
166
- delete message.type;
167
- console.log(`๐Ÿ“Ž Attached binary file '${keyToUse}' (${mimeType}) as image_url`);
168
- }
169
- catch (err) {
170
- if (err instanceof n8n_workflow_1.NodeOperationError) {
171
- throw err;
172
- }
173
- console.error(`โŒ Failed to read binary data for '${keyToUse}':`, err);
174
- const errorMessage = err instanceof Error ? err.message : String(err);
175
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to read binary file '${keyToUse}': ${errorMessage}`, { itemIndex: i });
176
- }
177
- }
178
- else {
179
- const available = binaryKeyData ? Object.keys(binaryKeyData).join(', ') : 'none';
180
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Binary property '${keyToUse}' not found. Available binary properties: ${available}`, { itemIndex: i });
181
- }
182
- }
183
- else if (msgType === 'file') {
184
- message.type = 'file';
185
- }
186
- messages.push(message);
187
- }
188
- }
189
- console.log('๐Ÿ“ฅ Manual mode - parsed messages:', JSON.stringify(messages, null, 2));
190
- }
191
- if (messages.length === 0) {
192
- messages.push({
193
- role: 'user',
194
- content: 'Hello! How can you help me?',
195
- });
196
- }
197
- for (let msgIndex = 0; msgIndex < messages.length; msgIndex++) {
198
- const msg = messages[msgIndex];
199
- if (Array.isArray(msg.content)) {
200
- for (const contentItem of msg.content) {
201
- if (contentItem.type === 'file') {
202
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `โŒ GitHub Copilot API Error: File attachments cannot be used inside 'content' array.\n\n` +
203
- `๐Ÿ“‹ INCORRECT FORMAT (OpenAI style - doesn't work):\n` +
204
- `{\n` +
205
- ` "role": "user",\n` +
206
- ` "content": [\n` +
207
- ` {"type": "text", "text": "Analyze this"},\n` +
208
- ` {"type": "file", "file": "data:..."} โŒ WRONG\n` +
209
- ` ]\n` +
210
- `}\n\n` +
211
- `โœ… CORRECT FORMAT (GitHub Copilot - message level):\n` +
212
- `[\n` +
213
- ` {"role": "user", "content": "Analyze this file"},\n` +
214
- ` {"role": "user", "content": "data:image/png;base64,...", "type": "file"} โœ… CORRECT\n` +
215
- `]\n\n` +
216
- `๐Ÿ’ก Solution: Use separate messages with 'type' property at message level, not inside content array.`, { itemIndex: i });
217
- }
218
- }
219
- }
220
- }
221
- console.log('๐Ÿ“ค Final messages being sent to API:', JSON.stringify(messages, null, 2));
222
- for (let msgIndex = 0; msgIndex < messages.length; msgIndex++) {
223
- const msg = messages[msgIndex];
224
- if (msg.content !== null &&
225
- msg.content !== undefined &&
226
- typeof msg.content === 'object' &&
227
- !Array.isArray(msg.content)) {
228
- const originalContent = msg.content;
229
- msg.content = JSON.stringify(msg.content, null, 2);
230
- console.log(`๐Ÿ”„ Auto-converted message[${msgIndex}].content from object to JSON string`);
231
- console.log(' Original type:', typeof originalContent);
232
- }
233
- }
234
- const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
235
- let parsedTools = [];
236
- const tools = advancedOptions.tools;
237
- if (tools) {
238
- try {
239
- if (typeof tools === 'object' && Array.isArray(tools) && tools.length > 0) {
240
- parsedTools = tools;
241
- console.log('๐Ÿ“ฅ Received tools as direct array (no parsing needed)');
242
- }
243
- else if (typeof tools === 'string' && tools.trim()) {
244
- const parsed = JSON.parse(tools);
245
- if (Array.isArray(parsed) && parsed.length > 0) {
246
- parsedTools = parsed;
247
- console.log('๐Ÿ“ฅ Parsed tools from JSON string');
248
- }
249
- else {
250
- console.log('๐Ÿ“ฅ Tools string parsed but empty or not an array');
251
- }
252
- }
253
- else {
254
- console.log('๐Ÿ“ฅ Tools field present but empty or invalid');
255
- }
256
- }
257
- catch (error) {
258
- console.log('โš ๏ธ Failed to parse tools, ignoring:', error instanceof Error ? error.message : 'Unknown error');
259
- }
260
- }
261
- else {
262
- console.log('๐Ÿ“ฅ No tools specified');
263
- }
264
- let max_tokens = advancedOptions.max_tokens || 4096;
265
- if (!max_tokens || max_tokens <= 0 || isNaN(max_tokens)) {
266
- max_tokens = 4096;
267
- console.log('โš ๏ธ Invalid max_tokens value, using default: 4096');
268
- }
269
- const temperature = (_a = advancedOptions.temperature) !== null && _a !== void 0 ? _a : 1;
270
- const top_p = (_b = advancedOptions.top_p) !== null && _b !== void 0 ? _b : 1;
271
- const frequency_penalty = (_c = advancedOptions.frequency_penalty) !== null && _c !== void 0 ? _c : 0;
272
- const presence_penalty = (_d = advancedOptions.presence_penalty) !== null && _d !== void 0 ? _d : 0;
273
- const seed = advancedOptions.seed || 0;
274
- const stream = (_e = advancedOptions.stream) !== null && _e !== void 0 ? _e : false;
275
- const user = advancedOptions.user || undefined;
276
- const stop = advancedOptions.stop || undefined;
277
- const response_format_ui = advancedOptions.response_format || 'text';
278
- let response_format = undefined;
279
- if (requestBodyFromJson === null || requestBodyFromJson === void 0 ? void 0 : requestBodyFromJson.response_format) {
280
- response_format = requestBodyFromJson.response_format;
281
- console.log('๐Ÿ“‹ response_format from JSON request body:', JSON.stringify(response_format));
282
- }
283
- else if (response_format_ui && response_format_ui !== 'text') {
284
- response_format = { type: response_format_ui };
285
- console.log('๐Ÿ“‹ response_format from UI field:', JSON.stringify(response_format));
286
- }
287
- else if (advancedOptions.response_format &&
288
- typeof advancedOptions.response_format === 'string') {
289
- try {
290
- response_format = JSON.parse(advancedOptions.response_format);
291
- console.log('๐Ÿ“‹ response_format from advancedOptions:', JSON.stringify(response_format));
292
- }
293
- catch {
294
- console.log('โš ๏ธ Failed to parse response_format from advancedOptions');
295
- }
296
- }
297
- if (response_format) {
298
- console.log('โœ… Final response_format:', JSON.stringify(response_format));
299
- console.log('๐Ÿ” response_format.type:', response_format.type);
300
- }
301
- else {
302
- console.log('โ„น๏ธ No response_format specified - using default text format');
303
- }
304
- const modelMapping = {
305
- 'gpt-4': 'gpt-4o',
306
- 'gpt-4o': 'gpt-4o',
307
- 'gpt-4o-mini': 'gpt-4o-mini',
308
- 'gpt-4-turbo': 'gpt-4o',
309
- 'claude-3-5-sonnet': 'claude-3.5-sonnet',
310
- 'claude-3.5-sonnet-20241022': 'claude-3.5-sonnet',
311
- o1: 'o1',
312
- 'o1-preview': 'o1-preview',
313
- 'o1-mini': 'o1-mini',
314
- };
315
- let copilotModel = modelMapping[model] || model;
316
- let hasVisionContent = false;
317
- for (const msg of messages) {
318
- const content = msg.content;
319
- const type = msg.type;
320
- if (type === 'file' || type === 'image') {
321
- hasVisionContent = true;
322
- break;
323
- }
324
- if (typeof content === 'string') {
325
- const trimmed = content.trim();
326
- const isActualDataUrl = /^data:image\/[a-z]+;base64,[A-Za-z0-9+\/=]{100,}/i.test(trimmed);
327
- const isCopilotFileUrl = trimmed.startsWith('copilot-file://');
328
- if (isActualDataUrl || isCopilotFileUrl) {
329
- hasVisionContent = true;
330
- break;
331
- }
332
- }
333
- else if (Array.isArray(content)) {
334
- for (const part of content) {
335
- if ((part === null || part === void 0 ? void 0 : part.type) === 'image_url' || (part === null || part === void 0 ? void 0 : part.type) === 'image' || (part === null || part === void 0 ? void 0 : part.image_url) || (part === null || part === void 0 ? void 0 : part.type) === 'file') {
336
- hasVisionContent = true;
337
- break;
338
- }
339
- }
340
- if (hasVisionContent)
341
- break;
342
- }
343
- }
344
- if (hasVisionContent) {
345
- const credentials = await this.getCredentials('githubCopilotApi');
346
- const oauthToken = credentials.oauthToken;
347
- let supportsVision = DynamicModelsManager_1.DynamicModelsManager.modelSupportsVision(oauthToken, copilotModel);
348
- if (supportsVision === null) {
349
- const modelInfo = GitHubCopilotModels_1.GitHubCopilotModelsManager.getModelByValue(copilotModel);
350
- supportsVision = !!(((_f = modelInfo === null || modelInfo === void 0 ? void 0 : modelInfo.capabilities) === null || _f === void 0 ? void 0 : _f.vision) || ((_g = modelInfo === null || modelInfo === void 0 ? void 0 : modelInfo.capabilities) === null || _g === void 0 ? void 0 : _g.multimodal));
351
- console.log(`๐Ÿ‘๏ธ Vision check for model ${copilotModel}: using static list, supportsVision=${supportsVision}`);
352
- }
353
- else {
354
- console.log(`๐Ÿ‘๏ธ Vision check for model ${copilotModel}: using API cache, supportsVision=${supportsVision}`);
355
- }
356
- if (!supportsVision) {
357
- const enableVisionFallback = advancedOptions.enableVisionFallback || false;
358
- if (enableVisionFallback) {
359
- const fallbackModelRaw = advancedOptions.visionFallbackModel;
360
- const fallbackModel = fallbackModelRaw === '__manual__'
361
- ? advancedOptions.visionFallbackCustomModel
362
- : fallbackModelRaw;
363
- if (!fallbackModel || fallbackModel.trim() === '') {
364
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Vision fallback enabled but no fallback model was selected or provided. Please select a vision-capable model in Advanced Options.', { itemIndex: i });
365
- }
366
- console.log(`๐Ÿ‘๏ธ Model ${copilotModel} does not support vision - using fallback model: ${fallbackModel}`);
367
- copilotModel = fallbackModel;
368
- }
369
- else {
370
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Model ${copilotModel} does not support vision/image processing. Enable "Vision Fallback" in Advanced Options and select a vision-capable model, or choose a model with vision capabilities.`, { itemIndex: i });
371
- }
372
- }
373
- }
374
- const requestBody = {
375
- model: copilotModel,
376
- messages,
377
- stream,
378
- temperature,
379
- max_tokens,
380
- };
381
- if (top_p !== 1) {
382
- requestBody.top_p = top_p;
383
- }
384
- if (frequency_penalty !== 0) {
385
- requestBody.frequency_penalty = frequency_penalty;
386
- }
387
- if (presence_penalty !== 0) {
388
- requestBody.presence_penalty = presence_penalty;
389
- }
390
- if (user) {
391
- requestBody.user = user;
392
- }
393
- if (stop) {
394
- try {
395
- requestBody.stop = JSON.parse(stop);
396
- }
397
- catch {
398
- requestBody.stop = stop;
399
- }
400
- }
401
- if (parsedTools.length > 0) {
402
- requestBody.tools = parsedTools;
403
- const tool_choice = advancedOptions.tool_choice || 'auto';
404
- if (tool_choice !== 'auto') {
405
- requestBody.tool_choice = tool_choice;
406
- }
407
- }
408
- if (response_format) {
409
- requestBody.response_format = response_format;
410
- }
411
- if (seed > 0) {
412
- requestBody.seed = seed;
413
- }
414
- console.log('๐Ÿš€ Sending request to GitHub Copilot API:');
415
- console.log(' Model:', copilotModel);
416
- console.log(' Messages count:', messages.length);
417
- console.log(' Has Vision Content:', hasVisionContent);
418
- console.log(' Request body:', JSON.stringify(requestBody, null, 2));
419
- let response;
420
- try {
421
- response = await (0, utils_1.makeApiRequest)(this, GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.ENDPOINTS.CHAT_COMPLETIONS, requestBody, hasVisionContent);
422
- }
423
- catch (error) {
424
- const errorMsg = error instanceof Error ? error.message : String(error);
425
- const enhancedError = new n8n_workflow_1.NodeOperationError(this.getNode(), `${errorMsg}\n\n๐Ÿค– Model used: ${copilotModel}`);
426
- throw enhancedError;
427
- }
428
- const retriesUsed = ((_h = response._retryMetadata) === null || _h === void 0 ? void 0 : _h.retries) || 0;
429
- if (retriesUsed > 0) {
430
- console.log(`โ„น๏ธ Request completed with ${retriesUsed} retry(ies)`);
431
- }
432
- const cleanJsonFromMarkdown = (content) => {
433
- if (!content || typeof content !== 'string') {
434
- return content;
435
- }
436
- try {
437
- const trimmed = content.trim();
438
- console.log('๐Ÿงน cleanJsonFromMarkdown - Input length:', trimmed.length);
439
- const jsonBlockRegex = /^```(?:json)?\s*\n([\s\S]*?)\n```\s*$/;
440
- const match = trimmed.match(jsonBlockRegex);
441
- if (match && match[1]) {
442
- const extracted = match[1].trim();
443
- console.log('โœ… cleanJsonFromMarkdown - Extracted from markdown block');
444
- return extracted;
445
- }
446
- console.log('โ„น๏ธ cleanJsonFromMarkdown - No markdown block found, returning as is');
447
- return trimmed;
448
- }
449
- catch (error) {
450
- console.error('โŒ cleanJsonFromMarkdown - Error:', error);
451
- return content;
452
- }
453
- };
454
- console.log('๐Ÿ”จ Building OpenAI response...');
455
- console.log('๐Ÿ” response_format check:', (response_format === null || response_format === void 0 ? void 0 : response_format.type) === 'json_object' ? 'WILL CLEAN MARKDOWN' : 'WILL KEEP AS IS');
456
- const openAIResponse = {
457
- id: response.id || `chatcmpl-${Date.now()}`,
458
- object: response.object || 'chat.completion',
459
- created: response.created || Math.floor(Date.now() / 1000),
460
- model: model,
461
- choices: response.choices.map((choice, choiceIndex) => {
462
- var _a;
463
- console.log(`\n๐Ÿ“ Processing choice ${choiceIndex}:`);
464
- console.log(' - role:', choice.message.role);
465
- console.log(' - content type:', typeof choice.message.content);
466
- console.log(' - content length:', ((_a = choice.message.content) === null || _a === void 0 ? void 0 : _a.length) || 0);
467
- console.log(' - has tool_calls:', !!choice.message.tool_calls);
468
- let processedContent = choice.message.content;
469
- if (choice.message.content !== null && choice.message.content !== undefined) {
470
- if ((response_format === null || response_format === void 0 ? void 0 : response_format.type) === 'json_object') {
471
- console.log(' ๐Ÿงน Applying cleanJsonFromMarkdown (keeping as string)...');
472
- processedContent = cleanJsonFromMarkdown(choice.message.content);
473
- console.log(' โœ… Processed content type:', typeof processedContent);
474
- }
475
- else {
476
- console.log(' โ„น๏ธ Keeping content as is');
477
- }
478
- }
479
- const choiceObj = {
480
- index: choice.index,
481
- message: {
482
- role: choice.message.role,
483
- content: processedContent,
484
- refusal: choice.message.refusal || null,
485
- annotations: choice.message.annotations || [],
486
- },
487
- logprobs: choice.logprobs || null,
488
- finish_reason: choice.finish_reason,
489
- };
490
- if (choice.message.tool_calls && choice.message.tool_calls.length > 0) {
491
- choiceObj.message.tool_calls = choice.message.tool_calls;
492
- }
493
- return choiceObj;
494
- }),
495
- usage: response.usage || {
496
- prompt_tokens: 0,
497
- completion_tokens: 0,
498
- total_tokens: 0,
499
- },
500
- };
501
- if (response.system_fingerprint) {
502
- openAIResponse.system_fingerprint = response.system_fingerprint;
503
- }
504
- returnData.push({
505
- json: openAIResponse,
506
- pairedItem: { item: i },
507
- });
49
+ const operation = this.getNodeParameter("operation", i, "chatCompletions");
50
+ if (operation === "listModels") {
51
+ const result = await (0, listModels_1.executeListModels)(this, i);
52
+ returnData.push({ json: result, pairedItem: { item: i } });
53
+ continue;
54
+ }
55
+ const openAIResponse = await (0, chatCompletion_1.executeChatCompletion)(this, items, i);
56
+ returnData.push({ json: openAIResponse, pairedItem: { item: i } });
508
57
  }
509
58
  catch (error) {
510
59
  if (this.continueOnFail()) {
511
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
60
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
512
61
  const errorString = JSON.stringify(error);
513
- console.error('โŒ Error occurred:', errorMessage);
514
- console.error('โŒ Error details:', errorString);
515
- let cleanMessage = errorMessage;
516
- cleanMessage = cleanMessage.replace(/\[Token used: [^\]]+\]/g, '').trim();
517
- cleanMessage = cleanMessage.replace(/\[Attempt: \d+\/\d+\]/g, '').trim();
518
- cleanMessage = cleanMessage.replace(/^GitHub Copilot API error:\s*/i, '').trim();
519
- cleanMessage = cleanMessage.replace(/\s+/g, ' ').trim();
520
- console.log('๐Ÿงน Cleaned error message:', cleanMessage);
62
+ console.error("ERROR occurred:", errorMessage);
63
+ let cleanMessage = errorMessage
64
+ .replace(/\[Token used: [^\]]+\]/g, "")
65
+ .replace(/\[Attempt: \d+\/\d+\]/g, "")
66
+ .replace(/^GitHub Copilot API error:\s*/i, "")
67
+ .replace(/\s+/g, " ")
68
+ .trim();
521
69
  let apiError = null;
522
70
  try {
523
- if (error && typeof error === 'object' && 'cause' in error) {
71
+ if (error && typeof error === "object" && "cause" in error) {
524
72
  const cause = error.cause;
525
- if (cause && cause.error) {
73
+ if (cause === null || cause === void 0 ? void 0 : cause.error)
526
74
  apiError = cause.error;
527
- }
528
75
  }
529
- if (!apiError && errorString.includes('{') && errorString.includes('}')) {
76
+ if (!apiError && errorString.includes('"error"')) {
530
77
  const jsonMatch = errorString.match(/\{[^{}]*"error"[^{}]*\}/);
531
- if (jsonMatch) {
78
+ if (jsonMatch)
532
79
  apiError = JSON.parse(jsonMatch[0]);
533
- }
534
80
  }
535
81
  }
536
- catch (parseError) {
537
- console.error('Failed to parse API error:', parseError);
82
+ catch {
538
83
  }
539
- const lowerMessage = cleanMessage.toLowerCase();
540
- const is400Error = lowerMessage.includes('400') ||
541
- lowerMessage.includes('bad request') ||
542
- (apiError && apiError.error && apiError.error.code === 'invalid_request_body');
543
- if (is400Error) {
544
- console.log('๐Ÿšซ 400 Bad Request detected - throwing non-retryable error');
84
+ const lower = cleanMessage.toLowerCase();
85
+ const is400 = lower.includes("400") ||
86
+ lower.includes("bad request") ||
87
+ ((_a = apiError === null || apiError === void 0 ? void 0 : apiError.error) === null || _a === void 0 ? void 0 : _a.code) === "invalid_request_body";
88
+ if (is400) {
545
89
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Bad Request (400): ${cleanMessage}`, {
546
90
  itemIndex: i,
547
- description: 'The request was malformed or contains invalid parameters. Retrying will not help.',
91
+ description: "The request was malformed. Retrying will not help.",
548
92
  });
549
93
  }
550
- let errorType = 'invalid_request_error';
94
+ let errorType = "invalid_request_error";
551
95
  let errorCode = null;
552
96
  let errorParam = null;
553
97
  let finalMessage = cleanMessage;
554
- if (apiError && apiError.error) {
98
+ if (apiError === null || apiError === void 0 ? void 0 : apiError.error) {
555
99
  finalMessage = apiError.error.message || cleanMessage;
556
100
  errorType = apiError.error.type || errorType;
557
101
  errorCode = apiError.error.code || null;
558
102
  errorParam = apiError.error.param || null;
559
- console.log('โœ… Using GitHub Copilot API error details');
103
+ }
104
+ else if (lower.includes("403") || lower.includes("forbidden")) {
105
+ errorCode = "insufficient_quota";
106
+ finalMessage =
107
+ lower.includes("access") && lower.includes("forbidden")
108
+ ? "You exceeded your current quota, please check your plan and billing details."
109
+ : cleanMessage;
110
+ }
111
+ else if (lower.includes("max") && lower.includes("token")) {
112
+ errorCode = "context_length_exceeded";
113
+ errorParam = "max_tokens";
114
+ finalMessage = "This model's maximum context length is exceeded.";
115
+ }
116
+ else if (lower.includes("401") || lower.includes("unauthorized")) {
117
+ errorCode = "invalid_api_key";
118
+ finalMessage = "Incorrect API key provided.";
119
+ }
120
+ else if (lower.includes("429") || lower.includes("rate limit")) {
121
+ errorType = "rate_limit_error";
122
+ errorCode = "rate_limit_exceeded";
123
+ finalMessage = "Rate limit reached. Please wait before making more requests.";
124
+ }
125
+ else if (lower.includes("timeout")) {
126
+ errorType = "api_error";
127
+ errorCode = "timeout";
128
+ finalMessage = "Request timeout. Please try again.";
560
129
  }
561
130
  else {
562
- if (lowerMessage.includes('403') || lowerMessage.includes('forbidden')) {
563
- errorType = 'invalid_request_error';
564
- errorCode = 'insufficient_quota';
565
- if (lowerMessage.includes('access') && lowerMessage.includes('forbidden')) {
566
- finalMessage =
567
- 'You exceeded your current quota, please check your plan and billing details.';
568
- }
569
- else {
570
- finalMessage = cleanMessage;
571
- }
572
- }
573
- else if (lowerMessage.includes('max') && lowerMessage.includes('token')) {
574
- errorType = 'invalid_request_error';
575
- errorCode = 'context_length_exceeded';
576
- errorParam = 'max_tokens';
577
- finalMessage =
578
- "This model's maximum context length is exceeded. Please reduce the length of the messages or completion.";
579
- }
580
- else if (lowerMessage.includes('401') || lowerMessage.includes('unauthorized')) {
581
- errorType = 'invalid_request_error';
582
- errorCode = 'invalid_api_key';
583
- finalMessage =
584
- 'Incorrect API key provided. You can find your API key at https://platform.openai.com/account/api-keys.';
585
- }
586
- else if (lowerMessage.includes('429') || lowerMessage.includes('rate limit')) {
587
- errorType = 'rate_limit_error';
588
- errorCode = 'rate_limit_exceeded';
589
- finalMessage = 'Rate limit reached. Please wait before making more requests.';
590
- }
591
- else if (lowerMessage.includes('timeout')) {
592
- errorType = 'api_error';
593
- errorCode = 'timeout';
594
- finalMessage = 'Request timeout. Please try again.';
595
- }
596
- else {
597
- errorType = 'api_error';
598
- errorCode = 'internal_error';
599
- finalMessage = cleanMessage;
600
- }
601
- console.log('โš ๏ธ Using fallback error detection with cleaned message');
131
+ errorType = "api_error";
132
+ errorCode = "internal_error";
602
133
  }
603
- console.log('๐Ÿ“‹ Final error format:', {
604
- message: finalMessage,
605
- type: errorType,
606
- param: errorParam,
607
- code: errorCode,
608
- });
609
134
  returnData.push({
610
135
  json: {
611
136
  error: {