n8n-nodes-github-copilot 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.backup.d.ts +5 -0
- package/dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.backup.js +651 -0
- package/dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.js +263 -412
- package/dist/nodes/GitHubCopilotChatAPI/utils/audioProcessor.d.ts +11 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/audioProcessor.js +86 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/helpers.d.ts +21 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/helpers.js +126 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/imageProcessor.d.ts +8 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/imageProcessor.js +101 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/index.d.ts +4 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/index.js +20 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/types.d.ts +45 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/types.js +2 -0
- package/package.json +1 -1
|
@@ -1,183 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
3
|
exports.GitHubCopilotChatAPI = void 0;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const options = {
|
|
30
|
-
method: 'POST',
|
|
31
|
-
headers: {
|
|
32
|
-
'Authorization': `Bearer ${credentials.accessToken}`,
|
|
33
|
-
'Content-Type': 'application/json',
|
|
34
|
-
'User-Agent': 'n8n-github-copilot-chat/3.0.0',
|
|
35
|
-
},
|
|
36
|
-
body: JSON.stringify(body),
|
|
37
|
-
};
|
|
38
|
-
const response = await fetch(`https://api.githubcopilot.com${endpoint}`, options);
|
|
39
|
-
if (!response.ok) {
|
|
40
|
-
const errorText = await response.text();
|
|
41
|
-
throw new Error(`GitHub Copilot API error: ${response.status} ${response.statusText}. ${errorText}`);
|
|
42
|
-
}
|
|
43
|
-
return await response.json();
|
|
44
|
-
}
|
|
45
|
-
async function downloadFileFromUrl(url) {
|
|
46
|
-
const response = await fetch(url);
|
|
47
|
-
if (!response.ok) {
|
|
48
|
-
throw new Error(`Failed to download file from URL: ${response.status} ${response.statusText}`);
|
|
49
|
-
}
|
|
50
|
-
return Buffer.from(await response.arrayBuffer());
|
|
51
|
-
}
|
|
52
|
-
async function getFileFromBinary(context, itemIndex, propertyName) {
|
|
53
|
-
const items = context.getInputData();
|
|
54
|
-
const item = items[itemIndex];
|
|
55
|
-
if (!item.binary || !item.binary[propertyName]) {
|
|
56
|
-
throw new Error(`No binary data found in property "${propertyName}"`);
|
|
57
|
-
}
|
|
58
|
-
const binaryData = item.binary[propertyName];
|
|
59
|
-
if (binaryData.data) {
|
|
60
|
-
return Buffer.from(binaryData.data, 'base64');
|
|
61
|
-
}
|
|
62
|
-
else if (binaryData.id) {
|
|
63
|
-
return await context.helpers.getBinaryDataBuffer(itemIndex, propertyName);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
throw new Error(`Invalid binary data format in property "${propertyName}"`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
async function processAudioFile(context, itemIndex) {
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
async function processAudioFileLegacy(context, itemIndex) {
|
|
70
6
|
const audioSource = context.getNodeParameter('audioSource', itemIndex);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
filename = audioUrl.split('/').pop() || 'audio.mp3';
|
|
93
|
-
break;
|
|
94
|
-
case 'binary':
|
|
95
|
-
const audioBinaryProperty = context.getNodeParameter('audioBinaryProperty', itemIndex);
|
|
96
|
-
audioBuffer = await getFileFromBinary(context, itemIndex, audioBinaryProperty);
|
|
97
|
-
const items = context.getInputData();
|
|
98
|
-
const item = items[itemIndex];
|
|
99
|
-
if (item.binary && item.binary[audioBinaryProperty] && item.binary[audioBinaryProperty].fileName) {
|
|
100
|
-
filename = item.binary[audioBinaryProperty].fileName || 'audio.mp3';
|
|
101
|
-
}
|
|
102
|
-
break;
|
|
103
|
-
default:
|
|
104
|
-
throw new Error(`Unknown audio source: ${audioSource}`);
|
|
7
|
+
try {
|
|
8
|
+
const audioFile = context.getNodeParameter('audioFile', itemIndex, '');
|
|
9
|
+
const audioUrl = context.getNodeParameter('audioUrl', itemIndex, '');
|
|
10
|
+
const audioProperty = context.getNodeParameter('audioBinaryProperty', itemIndex, '');
|
|
11
|
+
const result = await (0, utils_1.processAudioFile)(context, itemIndex, audioSource, audioFile, audioUrl, audioProperty);
|
|
12
|
+
const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 100000);
|
|
13
|
+
if (!tokenValidation.valid) {
|
|
14
|
+
return {
|
|
15
|
+
dataUrl: '',
|
|
16
|
+
description: (0, utils_1.createAudioSummary)(result.filename, result.size)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const optimized = (0, utils_1.optimizeAudioForTokens)(result.data, 80000);
|
|
20
|
+
let description = `Audio file: ${result.filename} (${Math.round(result.size / 1024)}KB)`;
|
|
21
|
+
if (optimized.truncated) {
|
|
22
|
+
description += ` - Compressed from ${optimized.originalTokens} to ${optimized.finalTokens} tokens`;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
dataUrl: `data:${result.mimeType};base64,${optimized.data}`,
|
|
26
|
+
description
|
|
27
|
+
};
|
|
105
28
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
let imageBuffer;
|
|
112
|
-
let filename = 'image.jpg';
|
|
113
|
-
switch (imageSource) {
|
|
114
|
-
case 'manual':
|
|
115
|
-
const imageFile = context.getNodeParameter('imageFile', itemIndex);
|
|
116
|
-
if (imageFile.startsWith('data:image/')) {
|
|
117
|
-
return imageFile;
|
|
118
|
-
}
|
|
119
|
-
else if (imageFile.match(/^[A-Za-z0-9+/=]+$/)) {
|
|
120
|
-
imageBuffer = Buffer.from(imageFile, 'base64');
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
124
|
-
imageBuffer = fs.readFileSync(imageFile);
|
|
125
|
-
filename = imageFile.split(/[\\\/]/).pop() || 'image.jpg';
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
case 'url':
|
|
129
|
-
const imageUrl = context.getNodeParameter('imageUrl', itemIndex);
|
|
130
|
-
imageBuffer = await downloadFileFromUrl(imageUrl);
|
|
131
|
-
filename = imageUrl.split('/').pop() || 'image.jpg';
|
|
132
|
-
break;
|
|
133
|
-
case 'binary':
|
|
134
|
-
const imageBinaryProperty = context.getNodeParameter('imageBinaryProperty', itemIndex);
|
|
135
|
-
imageBuffer = await getFileFromBinary(context, itemIndex, imageBinaryProperty);
|
|
136
|
-
const items = context.getInputData();
|
|
137
|
-
const item = items[itemIndex];
|
|
138
|
-
if (item.binary && item.binary[imageBinaryProperty] && item.binary[imageBinaryProperty].fileName) {
|
|
139
|
-
filename = item.binary[imageBinaryProperty].fileName || 'image.jpg';
|
|
140
|
-
}
|
|
141
|
-
break;
|
|
142
|
-
default:
|
|
143
|
-
throw new Error(`Unknown image source: ${imageSource}`);
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
dataUrl: '',
|
|
32
|
+
description: `Error processing audio: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
33
|
+
};
|
|
144
34
|
}
|
|
145
|
-
const mimeType = getMimeType(filename);
|
|
146
|
-
return `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
|
|
147
35
|
}
|
|
148
|
-
function
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return 'image/jpeg';
|
|
36
|
+
async function processImageFileLegacy(context, itemIndex) {
|
|
37
|
+
const imageSource = context.getNodeParameter('imageSource', itemIndex);
|
|
38
|
+
try {
|
|
39
|
+
const imageFile = context.getNodeParameter('imageFile', itemIndex, '');
|
|
40
|
+
const imageUrl = context.getNodeParameter('imageUrl', itemIndex, '');
|
|
41
|
+
const imageProperty = context.getNodeParameter('imageBinaryProperty', itemIndex, '');
|
|
42
|
+
const result = await (0, utils_1.processImageFile)(context, itemIndex, imageSource, imageFile, imageUrl, imageProperty);
|
|
43
|
+
const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 50000);
|
|
44
|
+
if (!tokenValidation.valid) {
|
|
45
|
+
throw new Error(`Image too large: ${result.estimatedTokens} estimated tokens. Please use a smaller image.`);
|
|
46
|
+
}
|
|
47
|
+
const optimizedData = (0, utils_1.compressImageToTokenLimit)(result.data, 40000);
|
|
48
|
+
return `data:${result.mimeType};base64,${optimizedData}`;
|
|
162
49
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const ext = filename.toLowerCase().split('.').pop();
|
|
166
|
-
switch (ext) {
|
|
167
|
-
case 'mp3':
|
|
168
|
-
return 'audio/mpeg';
|
|
169
|
-
case 'wav':
|
|
170
|
-
return 'audio/wav';
|
|
171
|
-
case 'm4a':
|
|
172
|
-
return 'audio/mp4';
|
|
173
|
-
case 'flac':
|
|
174
|
-
return 'audio/flac';
|
|
175
|
-
case 'ogg':
|
|
176
|
-
return 'audio/ogg';
|
|
177
|
-
case 'aac':
|
|
178
|
-
return 'audio/aac';
|
|
179
|
-
default:
|
|
180
|
-
return 'audio/mpeg';
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new Error(`Error processing image: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
181
52
|
}
|
|
182
53
|
}
|
|
183
54
|
class GitHubCopilotChatAPI {
|
|
@@ -206,24 +77,15 @@ class GitHubCopilotChatAPI {
|
|
|
206
77
|
displayName: 'Operation',
|
|
207
78
|
name: 'operation',
|
|
208
79
|
type: 'options',
|
|
80
|
+
noDataExpression: true,
|
|
209
81
|
options: [
|
|
210
82
|
{
|
|
211
83
|
name: 'Chat Completion',
|
|
212
|
-
value: '
|
|
213
|
-
description: 'Send
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
name: 'Audio Transcription',
|
|
217
|
-
value: 'audioTranscription',
|
|
218
|
-
description: 'Transcribe audio file to text using Copilot',
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
name: 'Image Analysis',
|
|
222
|
-
value: 'imageAnalysis',
|
|
223
|
-
description: 'Analyze image with AI (vision models)',
|
|
84
|
+
value: 'chat',
|
|
85
|
+
description: 'Send messages to GitHub Copilot Chat API',
|
|
224
86
|
},
|
|
225
87
|
],
|
|
226
|
-
default: '
|
|
88
|
+
default: 'chat',
|
|
227
89
|
},
|
|
228
90
|
{
|
|
229
91
|
displayName: 'Model',
|
|
@@ -231,38 +93,38 @@ class GitHubCopilotChatAPI {
|
|
|
231
93
|
type: 'options',
|
|
232
94
|
options: [
|
|
233
95
|
{
|
|
234
|
-
name: 'GPT-5
|
|
96
|
+
name: 'GPT-5',
|
|
235
97
|
value: 'gpt-5',
|
|
236
|
-
description: 'OpenAI GPT-5
|
|
98
|
+
description: 'OpenAI GPT-5 (Latest and most capable)',
|
|
237
99
|
},
|
|
238
100
|
{
|
|
239
101
|
name: 'GPT-5 Mini',
|
|
240
102
|
value: 'gpt-5-mini',
|
|
241
|
-
description: 'OpenAI GPT-5 Mini
|
|
103
|
+
description: 'OpenAI GPT-5 Mini (Faster, cost-effective)',
|
|
242
104
|
},
|
|
243
105
|
{
|
|
244
106
|
name: 'Claude Opus 4.1',
|
|
245
107
|
value: 'claude-opus-4.1',
|
|
246
|
-
description: 'Anthropic Claude Opus 4.1
|
|
108
|
+
description: 'Anthropic Claude Opus 4.1 (Advanced reasoning)',
|
|
247
109
|
},
|
|
248
110
|
{
|
|
249
111
|
name: 'Gemini 2.5 Pro',
|
|
250
112
|
value: 'gemini-2.5-pro',
|
|
251
|
-
description: 'Google Gemini 2.5 Pro
|
|
113
|
+
description: 'Google Gemini 2.5 Pro (Multimodal capabilities)',
|
|
252
114
|
},
|
|
253
115
|
{
|
|
254
116
|
name: 'Grok Code Fast 1',
|
|
255
117
|
value: 'grok-code-fast-1',
|
|
256
|
-
description: 'xAI Grok
|
|
118
|
+
description: 'xAI Grok Code Fast 1 (Optimized for coding)',
|
|
257
119
|
},
|
|
258
120
|
{
|
|
259
121
|
name: 'GPT-4.1 Copilot',
|
|
260
122
|
value: 'gpt-4.1-copilot',
|
|
261
|
-
description: '
|
|
123
|
+
description: 'OpenAI GPT-4.1 specialized for coding assistance',
|
|
262
124
|
},
|
|
263
125
|
],
|
|
264
|
-
default: 'gpt-5',
|
|
265
|
-
description: '
|
|
126
|
+
default: 'gpt-5-mini',
|
|
127
|
+
description: 'Select AI model to use',
|
|
266
128
|
},
|
|
267
129
|
{
|
|
268
130
|
displayName: 'Message',
|
|
@@ -272,313 +134,302 @@ class GitHubCopilotChatAPI {
|
|
|
272
134
|
rows: 4,
|
|
273
135
|
},
|
|
274
136
|
default: '',
|
|
275
|
-
placeholder: '
|
|
276
|
-
description: 'The message to send to the AI',
|
|
277
|
-
displayOptions: {
|
|
278
|
-
show: {
|
|
279
|
-
operation: ['chatCompletion', 'imageAnalysis'],
|
|
280
|
-
},
|
|
281
|
-
},
|
|
137
|
+
placeholder: 'What can I help you with?',
|
|
138
|
+
description: 'The message to send to the AI model',
|
|
282
139
|
},
|
|
283
140
|
{
|
|
284
|
-
displayName: '
|
|
285
|
-
name: '
|
|
286
|
-
type: 'options',
|
|
287
|
-
options: [
|
|
288
|
-
{ name: 'File Path or Base64', value: 'manual' },
|
|
289
|
-
{ name: 'Download from URL', value: 'url' },
|
|
290
|
-
{ name: 'From Previous Node Binary', value: 'binary' },
|
|
291
|
-
],
|
|
292
|
-
default: 'manual',
|
|
293
|
-
description: 'Choose how to provide the audio file',
|
|
294
|
-
displayOptions: {
|
|
295
|
-
show: {
|
|
296
|
-
operation: ['audioTranscription'],
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
},
|
|
300
|
-
{
|
|
301
|
-
displayName: 'Audio File',
|
|
302
|
-
name: 'audioFile',
|
|
141
|
+
displayName: 'System Message',
|
|
142
|
+
name: 'systemMessage',
|
|
303
143
|
type: 'string',
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
description: 'Audio file to transcribe (supports MP3, WAV, M4A, FLAC, OGG)',
|
|
307
|
-
displayOptions: {
|
|
308
|
-
show: {
|
|
309
|
-
operation: ['audioTranscription'],
|
|
310
|
-
audioSource: ['manual'],
|
|
311
|
-
},
|
|
144
|
+
typeOptions: {
|
|
145
|
+
rows: 3,
|
|
312
146
|
},
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
displayName: 'Audio URL',
|
|
316
|
-
name: 'audioUrl',
|
|
317
|
-
type: 'string',
|
|
318
147
|
default: '',
|
|
319
|
-
placeholder: '
|
|
320
|
-
description: '
|
|
321
|
-
displayOptions: {
|
|
322
|
-
show: {
|
|
323
|
-
operation: ['audioTranscription'],
|
|
324
|
-
audioSource: ['url'],
|
|
325
|
-
},
|
|
326
|
-
},
|
|
148
|
+
placeholder: 'You are a helpful assistant...',
|
|
149
|
+
description: 'System message to set the behavior of the AI model',
|
|
327
150
|
},
|
|
328
151
|
{
|
|
329
|
-
displayName: '
|
|
330
|
-
name: '
|
|
331
|
-
type: '
|
|
332
|
-
default:
|
|
333
|
-
|
|
334
|
-
description: 'Name of the binary property containing the audio file',
|
|
335
|
-
displayOptions: {
|
|
336
|
-
show: {
|
|
337
|
-
operation: ['audioTranscription'],
|
|
338
|
-
audioSource: ['binary'],
|
|
339
|
-
},
|
|
340
|
-
},
|
|
152
|
+
displayName: 'Include Image',
|
|
153
|
+
name: 'includeImage',
|
|
154
|
+
type: 'boolean',
|
|
155
|
+
default: false,
|
|
156
|
+
description: 'Whether to include an image in the message',
|
|
341
157
|
},
|
|
342
158
|
{
|
|
343
|
-
displayName: '
|
|
344
|
-
name: '
|
|
159
|
+
displayName: 'Image Source',
|
|
160
|
+
name: 'imageSource',
|
|
345
161
|
type: 'options',
|
|
346
|
-
options: [
|
|
347
|
-
{ name: 'Auto-detect', value: 'auto' },
|
|
348
|
-
{ name: 'Portuguese (Brazil)', value: 'pt' },
|
|
349
|
-
{ name: 'English', value: 'en' },
|
|
350
|
-
{ name: 'Spanish', value: 'es' },
|
|
351
|
-
{ name: 'French', value: 'fr' },
|
|
352
|
-
{ name: 'German', value: 'de' },
|
|
353
|
-
{ name: 'Italian', value: 'it' },
|
|
354
|
-
{ name: 'Japanese', value: 'ja' },
|
|
355
|
-
{ name: 'Chinese', value: 'zh' },
|
|
356
|
-
],
|
|
357
|
-
default: 'auto',
|
|
358
|
-
description: 'Language of the audio (helps with accuracy)',
|
|
359
162
|
displayOptions: {
|
|
360
163
|
show: {
|
|
361
|
-
|
|
164
|
+
includeImage: [true],
|
|
362
165
|
},
|
|
363
166
|
},
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
displayName: 'Image Source',
|
|
367
|
-
name: 'imageSource',
|
|
368
|
-
type: 'options',
|
|
369
167
|
options: [
|
|
370
|
-
{
|
|
371
|
-
|
|
372
|
-
|
|
168
|
+
{
|
|
169
|
+
name: 'Manual Input',
|
|
170
|
+
value: 'manual',
|
|
171
|
+
description: 'Provide image as base64 string or file path',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'URL',
|
|
175
|
+
value: 'url',
|
|
176
|
+
description: 'Download image from URL',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'Binary Data',
|
|
180
|
+
value: 'binary',
|
|
181
|
+
description: 'Use binary data from previous node',
|
|
182
|
+
},
|
|
373
183
|
],
|
|
374
184
|
default: 'manual',
|
|
375
|
-
description: '
|
|
376
|
-
displayOptions: {
|
|
377
|
-
show: {
|
|
378
|
-
operation: ['imageAnalysis'],
|
|
379
|
-
},
|
|
380
|
-
},
|
|
185
|
+
description: 'Source of the image data',
|
|
381
186
|
},
|
|
382
187
|
{
|
|
383
188
|
displayName: 'Image File',
|
|
384
189
|
name: 'imageFile',
|
|
385
190
|
type: 'string',
|
|
386
|
-
default: '',
|
|
387
|
-
placeholder: 'Path to image file or base64 data',
|
|
388
|
-
description: 'Image file to analyze (supports JPG, PNG, WebP, GIF)',
|
|
389
191
|
displayOptions: {
|
|
390
192
|
show: {
|
|
391
|
-
|
|
193
|
+
includeImage: [true],
|
|
392
194
|
imageSource: ['manual'],
|
|
393
195
|
},
|
|
394
196
|
},
|
|
197
|
+
default: '',
|
|
198
|
+
placeholder: 'Paste base64 string or file path',
|
|
199
|
+
description: 'Image as base64 string or file path',
|
|
395
200
|
},
|
|
396
201
|
{
|
|
397
202
|
displayName: 'Image URL',
|
|
398
203
|
name: 'imageUrl',
|
|
399
204
|
type: 'string',
|
|
400
|
-
default: '',
|
|
401
|
-
placeholder: 'https://example.com/image.jpg',
|
|
402
|
-
description: 'URL to download image file from',
|
|
403
205
|
displayOptions: {
|
|
404
206
|
show: {
|
|
405
|
-
|
|
207
|
+
includeImage: [true],
|
|
406
208
|
imageSource: ['url'],
|
|
407
209
|
},
|
|
408
210
|
},
|
|
211
|
+
default: '',
|
|
212
|
+
placeholder: 'https://example.com/image.jpg',
|
|
213
|
+
description: 'URL of the image to download and include',
|
|
409
214
|
},
|
|
410
215
|
{
|
|
411
|
-
displayName: 'Binary Property
|
|
216
|
+
displayName: 'Image Binary Property',
|
|
412
217
|
name: 'imageBinaryProperty',
|
|
413
218
|
type: 'string',
|
|
219
|
+
displayOptions: {
|
|
220
|
+
show: {
|
|
221
|
+
includeImage: [true],
|
|
222
|
+
imageSource: ['binary'],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
414
225
|
default: 'data',
|
|
415
226
|
placeholder: 'data',
|
|
416
|
-
description: 'Name of the binary property containing the image
|
|
227
|
+
description: 'Name of the binary property containing the image',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
displayName: 'Include Audio',
|
|
231
|
+
name: 'includeAudio',
|
|
232
|
+
type: 'boolean',
|
|
233
|
+
default: false,
|
|
234
|
+
description: 'Whether to include an audio file in the message',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
displayName: 'Audio Source',
|
|
238
|
+
name: 'audioSource',
|
|
239
|
+
type: 'options',
|
|
417
240
|
displayOptions: {
|
|
418
241
|
show: {
|
|
419
|
-
|
|
420
|
-
imageSource: ['binary'],
|
|
242
|
+
includeAudio: [true],
|
|
421
243
|
},
|
|
422
244
|
},
|
|
245
|
+
options: [
|
|
246
|
+
{
|
|
247
|
+
name: 'Manual Input',
|
|
248
|
+
value: 'manual',
|
|
249
|
+
description: 'Provide audio as base64 string or file path',
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'URL',
|
|
253
|
+
value: 'url',
|
|
254
|
+
description: 'Download audio from URL',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: 'Binary Data',
|
|
258
|
+
value: 'binary',
|
|
259
|
+
description: 'Use binary data from previous node',
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
default: 'manual',
|
|
263
|
+
description: 'Source of the audio data',
|
|
423
264
|
},
|
|
424
265
|
{
|
|
425
|
-
displayName: '
|
|
426
|
-
name: '
|
|
266
|
+
displayName: 'Audio File',
|
|
267
|
+
name: 'audioFile',
|
|
427
268
|
type: 'string',
|
|
428
|
-
typeOptions: {
|
|
429
|
-
rows: 2,
|
|
430
|
-
},
|
|
431
|
-
default: '',
|
|
432
|
-
placeholder: 'You are a helpful assistant...',
|
|
433
|
-
description: 'Optional system prompt to set AI behavior',
|
|
434
269
|
displayOptions: {
|
|
435
270
|
show: {
|
|
436
|
-
|
|
271
|
+
includeAudio: [true],
|
|
272
|
+
audioSource: ['manual'],
|
|
437
273
|
},
|
|
438
274
|
},
|
|
275
|
+
default: '',
|
|
276
|
+
placeholder: 'Paste base64 string or file path',
|
|
277
|
+
description: 'Audio as base64 string or file path',
|
|
439
278
|
},
|
|
440
279
|
{
|
|
441
|
-
displayName: '
|
|
442
|
-
name: '
|
|
443
|
-
type: '
|
|
444
|
-
typeOptions: {
|
|
445
|
-
minValue: 0,
|
|
446
|
-
maxValue: 2,
|
|
447
|
-
numberStepSize: 0.1,
|
|
448
|
-
},
|
|
449
|
-
default: 1,
|
|
450
|
-
description: 'Controls randomness: 0 = focused, 2 = creative',
|
|
280
|
+
displayName: 'Audio URL',
|
|
281
|
+
name: 'audioUrl',
|
|
282
|
+
type: 'string',
|
|
451
283
|
displayOptions: {
|
|
452
284
|
show: {
|
|
453
|
-
|
|
285
|
+
includeAudio: [true],
|
|
286
|
+
audioSource: ['url'],
|
|
454
287
|
},
|
|
455
288
|
},
|
|
289
|
+
default: '',
|
|
290
|
+
placeholder: 'https://example.com/audio.mp3',
|
|
291
|
+
description: 'URL of the audio file to download and include',
|
|
456
292
|
},
|
|
457
293
|
{
|
|
458
|
-
displayName: '
|
|
459
|
-
name: '
|
|
460
|
-
type: '
|
|
461
|
-
typeOptions: {
|
|
462
|
-
minValue: 1,
|
|
463
|
-
maxValue: 8192,
|
|
464
|
-
},
|
|
465
|
-
default: 2000,
|
|
466
|
-
description: 'Maximum tokens in response',
|
|
294
|
+
displayName: 'Audio Binary Property',
|
|
295
|
+
name: 'audioBinaryProperty',
|
|
296
|
+
type: 'string',
|
|
467
297
|
displayOptions: {
|
|
468
298
|
show: {
|
|
469
|
-
|
|
299
|
+
includeAudio: [true],
|
|
300
|
+
audioSource: ['binary'],
|
|
470
301
|
},
|
|
471
302
|
},
|
|
303
|
+
default: 'data',
|
|
304
|
+
placeholder: 'data',
|
|
305
|
+
description: 'Name of the binary property containing the audio file',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
displayName: 'Advanced Options',
|
|
309
|
+
name: 'advancedOptions',
|
|
310
|
+
type: 'collection',
|
|
311
|
+
placeholder: 'Add Field',
|
|
312
|
+
default: {},
|
|
313
|
+
options: [
|
|
314
|
+
{
|
|
315
|
+
displayName: 'Temperature',
|
|
316
|
+
name: 'temperature',
|
|
317
|
+
type: 'number',
|
|
318
|
+
typeOptions: {
|
|
319
|
+
minValue: 0,
|
|
320
|
+
maxValue: 2,
|
|
321
|
+
numberPrecision: 2,
|
|
322
|
+
},
|
|
323
|
+
default: 1,
|
|
324
|
+
description: 'Controls randomness of the response. Higher values make output more random.',
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
displayName: 'Max Tokens',
|
|
328
|
+
name: 'max_tokens',
|
|
329
|
+
type: 'number',
|
|
330
|
+
typeOptions: {
|
|
331
|
+
minValue: 1,
|
|
332
|
+
maxValue: 128000,
|
|
333
|
+
},
|
|
334
|
+
default: 4096,
|
|
335
|
+
description: 'Maximum number of tokens to generate in the response',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
displayName: 'Top P',
|
|
339
|
+
name: 'top_p',
|
|
340
|
+
type: 'number',
|
|
341
|
+
typeOptions: {
|
|
342
|
+
minValue: 0,
|
|
343
|
+
maxValue: 1,
|
|
344
|
+
numberPrecision: 2,
|
|
345
|
+
},
|
|
346
|
+
default: 1,
|
|
347
|
+
description: 'Alternative to temperature, controls diversity via nucleus sampling',
|
|
348
|
+
},
|
|
349
|
+
],
|
|
472
350
|
},
|
|
473
351
|
],
|
|
474
352
|
};
|
|
475
353
|
}
|
|
476
354
|
async execute() {
|
|
355
|
+
var _a, _b, _c;
|
|
477
356
|
const items = this.getInputData();
|
|
478
357
|
const returnData = [];
|
|
479
358
|
for (let i = 0; i < items.length; i++) {
|
|
480
359
|
try {
|
|
481
360
|
const operation = this.getNodeParameter('operation', i);
|
|
482
361
|
const model = this.getNodeParameter('model', i);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const
|
|
486
|
-
const
|
|
487
|
-
const
|
|
488
|
-
const
|
|
362
|
+
if (operation === 'chat') {
|
|
363
|
+
const userMessage = this.getNodeParameter('message', i);
|
|
364
|
+
const systemMessage = this.getNodeParameter('systemMessage', i, '');
|
|
365
|
+
const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
|
|
366
|
+
const includeImage = this.getNodeParameter('includeImage', i, false);
|
|
367
|
+
const includeAudio = this.getNodeParameter('includeAudio', i, false);
|
|
489
368
|
const messages = [];
|
|
490
|
-
if (
|
|
491
|
-
messages.push({
|
|
492
|
-
}
|
|
493
|
-
messages.push({ role: 'user', content: message });
|
|
494
|
-
const requestBody = {
|
|
495
|
-
model: model,
|
|
496
|
-
messages: messages,
|
|
497
|
-
temperature: temperature,
|
|
498
|
-
max_tokens: maxTokens,
|
|
499
|
-
stream: false,
|
|
500
|
-
};
|
|
501
|
-
const response = await makeApiRequest(this, '/chat/completions', requestBody);
|
|
502
|
-
result = {
|
|
503
|
-
response: response.choices[0].message.content,
|
|
504
|
-
model: model,
|
|
505
|
-
usage: response.usage,
|
|
506
|
-
finish_reason: response.choices[0].finish_reason,
|
|
507
|
-
timestamp: new Date().toISOString(),
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
else if (operation === 'audioTranscription') {
|
|
511
|
-
const language = this.getNodeParameter('audioLanguage', i);
|
|
512
|
-
const audioDataUrl = await processAudioFile(this, i);
|
|
513
|
-
const transcriptionPrompt = language === 'auto'
|
|
514
|
-
? `Please transcribe this audio file to text. Detect the language automatically and provide the transcription.`
|
|
515
|
-
: `Please transcribe this audio file to text. The audio is in ${language} language.`;
|
|
516
|
-
const messages = [
|
|
517
|
-
{
|
|
369
|
+
if (systemMessage) {
|
|
370
|
+
messages.push({
|
|
518
371
|
role: 'system',
|
|
519
|
-
content:
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
372
|
+
content: systemMessage,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
let userContent = userMessage;
|
|
376
|
+
if (includeImage || includeAudio) {
|
|
377
|
+
const contentArray = [];
|
|
378
|
+
if (userMessage.trim()) {
|
|
379
|
+
contentArray.push({
|
|
380
|
+
type: 'text',
|
|
381
|
+
text: userMessage,
|
|
382
|
+
});
|
|
524
383
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
messages.push({ role: 'system', content: systemPrompt });
|
|
384
|
+
if (includeImage) {
|
|
385
|
+
const imageDataUrl = await processImageFileLegacy(this, i);
|
|
386
|
+
contentArray.push({
|
|
387
|
+
type: 'image_url',
|
|
388
|
+
image_url: {
|
|
389
|
+
url: imageDataUrl,
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
if (includeAudio) {
|
|
394
|
+
const audioResult = await processAudioFileLegacy(this, i);
|
|
395
|
+
if (audioResult.dataUrl) {
|
|
396
|
+
contentArray.push({
|
|
397
|
+
type: 'text',
|
|
398
|
+
text: `[Audio included: ${audioResult.description}]`,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
contentArray.push({
|
|
403
|
+
type: 'text',
|
|
404
|
+
text: `[Audio processing failed: ${audioResult.description}]`,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
userContent = contentArray;
|
|
551
409
|
}
|
|
552
410
|
messages.push({
|
|
553
411
|
role: 'user',
|
|
554
|
-
content:
|
|
555
|
-
{ type: 'text', text: message },
|
|
556
|
-
{ type: 'image_url', image_url: { url: imageBase64 } }
|
|
557
|
-
]
|
|
412
|
+
content: userContent,
|
|
558
413
|
});
|
|
559
414
|
const requestBody = {
|
|
560
|
-
model
|
|
561
|
-
messages
|
|
562
|
-
temperature: temperature,
|
|
563
|
-
max_tokens: maxTokens,
|
|
415
|
+
model,
|
|
416
|
+
messages,
|
|
564
417
|
stream: false,
|
|
418
|
+
...advancedOptions,
|
|
565
419
|
};
|
|
566
|
-
const response = await makeApiRequest(this, '/chat/completions', requestBody);
|
|
567
|
-
result = {
|
|
568
|
-
|
|
569
|
-
model
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
420
|
+
const response = await (0, utils_1.makeApiRequest)(this, '/chat/completions', requestBody);
|
|
421
|
+
const result = {
|
|
422
|
+
message: ((_b = (_a = response.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content) || '',
|
|
423
|
+
model,
|
|
424
|
+
operation,
|
|
425
|
+
usage: response.usage || null,
|
|
426
|
+
finish_reason: ((_c = response.choices[0]) === null || _c === void 0 ? void 0 : _c.finish_reason) || 'unknown',
|
|
573
427
|
};
|
|
428
|
+
returnData.push({
|
|
429
|
+
json: result,
|
|
430
|
+
pairedItem: { item: i },
|
|
431
|
+
});
|
|
574
432
|
}
|
|
575
|
-
else {
|
|
576
|
-
throw new Error(`Unknown operation: ${operation}`);
|
|
577
|
-
}
|
|
578
|
-
returnData.push({
|
|
579
|
-
json: result,
|
|
580
|
-
pairedItem: { item: i },
|
|
581
|
-
});
|
|
582
433
|
}
|
|
583
434
|
catch (error) {
|
|
584
435
|
if (this.continueOnFail()) {
|