n8n-nodes-github-copilot 3.2.3 → 3.2.5
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.js +36 -74
- package/dist/nodes/GitHubCopilotChatAPI/nodeProperties.js +29 -107
- package/dist/nodes/GitHubCopilotChatAPI/utils/mediaDetection.d.ts +6 -0
- package/dist/nodes/GitHubCopilotChatAPI/utils/mediaDetection.js +54 -4
- package/dist/nodes/GitHubCopilotChatAPI/utils/modelCapabilities.js +10 -10
- package/package.json +1 -1
|
@@ -4,55 +4,7 @@ exports.GitHubCopilotChatAPI = void 0;
|
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
5
|
const nodeProperties_1 = require("./nodeProperties");
|
|
6
6
|
const modelCapabilities_1 = require("./utils/modelCapabilities");
|
|
7
|
-
|
|
8
|
-
const audioSource = context.getNodeParameter('audioSource', itemIndex);
|
|
9
|
-
try {
|
|
10
|
-
const audioFile = context.getNodeParameter('audioFile', itemIndex, '');
|
|
11
|
-
const audioUrl = context.getNodeParameter('audioUrl', itemIndex, '');
|
|
12
|
-
const audioProperty = context.getNodeParameter('audioBinaryProperty', itemIndex, '');
|
|
13
|
-
const result = await (0, utils_1.processAudioFile)(context, itemIndex, audioSource, audioFile, audioUrl, audioProperty);
|
|
14
|
-
const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 100000);
|
|
15
|
-
if (!tokenValidation.valid) {
|
|
16
|
-
return {
|
|
17
|
-
dataUrl: '',
|
|
18
|
-
description: (0, utils_1.createAudioSummary)(result.filename, result.size)
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
const optimized = (0, utils_1.optimizeAudioForTokens)(result.data, 80000);
|
|
22
|
-
let description = `Audio file: ${result.filename} (${Math.round(result.size / 1024)}KB)`;
|
|
23
|
-
if (optimized.truncated) {
|
|
24
|
-
description += ` - Compressed from ${optimized.originalTokens} to ${optimized.finalTokens} tokens`;
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
27
|
-
dataUrl: `data:${result.mimeType};base64,${optimized.data}`,
|
|
28
|
-
description
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
return {
|
|
33
|
-
dataUrl: '',
|
|
34
|
-
description: `Error processing audio: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
async function processImageFileLegacy(context, itemIndex) {
|
|
39
|
-
const imageSource = context.getNodeParameter('imageSource', itemIndex);
|
|
40
|
-
try {
|
|
41
|
-
const imageFile = context.getNodeParameter('imageFile', itemIndex, '');
|
|
42
|
-
const imageUrl = context.getNodeParameter('imageUrl', itemIndex, '');
|
|
43
|
-
const imageProperty = context.getNodeParameter('imageBinaryProperty', itemIndex, '');
|
|
44
|
-
const result = await (0, utils_1.processImageFile)(context, itemIndex, imageSource, imageFile, imageUrl, imageProperty);
|
|
45
|
-
const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 50000);
|
|
46
|
-
if (!tokenValidation.valid) {
|
|
47
|
-
throw new Error(`Image too large: ${result.estimatedTokens} estimated tokens. Please use a smaller image.`);
|
|
48
|
-
}
|
|
49
|
-
const optimizedData = (0, utils_1.compressImageToTokenLimit)(result.data, 40000);
|
|
50
|
-
return `data:${result.mimeType};base64,${optimizedData}`;
|
|
51
|
-
}
|
|
52
|
-
catch (error) {
|
|
53
|
-
throw new Error(`Error processing image: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
7
|
+
const mediaDetection_1 = require("./utils/mediaDetection");
|
|
56
8
|
class GitHubCopilotChatAPI {
|
|
57
9
|
constructor() {
|
|
58
10
|
this.description = {
|
|
@@ -89,15 +41,16 @@ class GitHubCopilotChatAPI {
|
|
|
89
41
|
const userMessage = this.getNodeParameter('message', i);
|
|
90
42
|
const systemMessage = this.getNodeParameter('systemMessage', i, '');
|
|
91
43
|
const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
44
|
+
const includeMedia = this.getNodeParameter('includeMedia', i, false);
|
|
45
|
+
if (includeMedia) {
|
|
46
|
+
const validation = (0, modelCapabilities_1.validateModelCapabilities)(model, true, true);
|
|
47
|
+
if (!validation.isValid) {
|
|
48
|
+
throw new Error(validation.errorMessage || 'Model validation failed');
|
|
49
|
+
}
|
|
50
|
+
if (validation.warnings) {
|
|
51
|
+
for (const warning of validation.warnings) {
|
|
52
|
+
console.warn(`GitHub Copilot API Warning: ${warning}`);
|
|
53
|
+
}
|
|
101
54
|
}
|
|
102
55
|
}
|
|
103
56
|
const messages = [];
|
|
@@ -108,7 +61,11 @@ class GitHubCopilotChatAPI {
|
|
|
108
61
|
});
|
|
109
62
|
}
|
|
110
63
|
let userContent = userMessage;
|
|
111
|
-
if (
|
|
64
|
+
if (includeMedia) {
|
|
65
|
+
const mediaSource = this.getNodeParameter('mediaSource', i);
|
|
66
|
+
const mediaFile = this.getNodeParameter('mediaFile', i, '');
|
|
67
|
+
const mediaUrl = this.getNodeParameter('mediaUrl', i, '');
|
|
68
|
+
const mediaBinaryProperty = this.getNodeParameter('mediaBinaryProperty', i, '');
|
|
112
69
|
const contentArray = [];
|
|
113
70
|
if (userMessage.trim()) {
|
|
114
71
|
contentArray.push({
|
|
@@ -116,30 +73,35 @@ class GitHubCopilotChatAPI {
|
|
|
116
73
|
text: userMessage,
|
|
117
74
|
});
|
|
118
75
|
}
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (audioResult.dataUrl) {
|
|
76
|
+
try {
|
|
77
|
+
const mediaResult = await (0, mediaDetection_1.processMediaFile)(this, i, mediaSource, mediaFile, mediaUrl, mediaBinaryProperty);
|
|
78
|
+
if (mediaResult.type === 'image' && mediaResult.dataUrl) {
|
|
79
|
+
contentArray.push({
|
|
80
|
+
type: 'image_url',
|
|
81
|
+
image_url: {
|
|
82
|
+
url: mediaResult.dataUrl,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else if (mediaResult.type === 'audio' && mediaResult.dataUrl) {
|
|
131
87
|
contentArray.push({
|
|
132
88
|
type: 'text',
|
|
133
|
-
text: `Please analyze this audio file: ${
|
|
89
|
+
text: `Please analyze this audio file: ${mediaResult.description}\n\nAudio data (base64): ${mediaResult.dataUrl}`,
|
|
134
90
|
});
|
|
135
91
|
}
|
|
136
92
|
else {
|
|
137
93
|
contentArray.push({
|
|
138
94
|
type: 'text',
|
|
139
|
-
text: `[
|
|
95
|
+
text: `[Media processing failed: ${mediaResult.description}]`,
|
|
140
96
|
});
|
|
141
97
|
}
|
|
142
98
|
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
contentArray.push({
|
|
101
|
+
type: 'text',
|
|
102
|
+
text: `[Media processing error: ${error instanceof Error ? error.message : 'Unknown error'}]`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
143
105
|
userContent = contentArray;
|
|
144
106
|
}
|
|
145
107
|
messages.push({
|
|
@@ -152,7 +114,7 @@ class GitHubCopilotChatAPI {
|
|
|
152
114
|
stream: false,
|
|
153
115
|
...advancedOptions,
|
|
154
116
|
};
|
|
155
|
-
const hasMedia =
|
|
117
|
+
const hasMedia = includeMedia;
|
|
156
118
|
const response = await (0, utils_1.makeApiRequest)(this, '/chat/completions', requestBody, hasMedia);
|
|
157
119
|
const result = {
|
|
158
120
|
message: ((_b = (_a = response.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content) || '',
|
|
@@ -24,12 +24,12 @@ exports.nodeProperties = [
|
|
|
24
24
|
{
|
|
25
25
|
name: 'GPT-5',
|
|
26
26
|
value: 'gpt-5',
|
|
27
|
-
description: 'OpenAI GPT-5 (Latest and most capable) ✓ Images',
|
|
27
|
+
description: 'OpenAI GPT-5 (Latest and most capable) ✓ Images ✓ Audio',
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
name: 'GPT-5 Mini',
|
|
31
31
|
value: 'gpt-5-mini',
|
|
32
|
-
description: 'OpenAI GPT-5 Mini (Faster, cost-effective) ✓ Images',
|
|
32
|
+
description: 'OpenAI GPT-5 Mini (Faster, cost-effective) ✓ Images ✓ Audio',
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
name: 'Claude Opus 4.1',
|
|
@@ -39,7 +39,7 @@ exports.nodeProperties = [
|
|
|
39
39
|
{
|
|
40
40
|
name: 'Gemini 2.5 Pro',
|
|
41
41
|
value: 'gemini-2.5-pro',
|
|
42
|
-
description: 'Google Gemini 2.5 Pro (Multimodal capabilities) ✓ Images',
|
|
42
|
+
description: 'Google Gemini 2.5 Pro (Multimodal capabilities) ✓ Images ✓ Audio',
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
name: 'Grok Code Fast 1',
|
|
@@ -49,7 +49,7 @@ exports.nodeProperties = [
|
|
|
49
49
|
{
|
|
50
50
|
name: 'GPT-4.1 Copilot',
|
|
51
51
|
value: 'gpt-4.1-copilot',
|
|
52
|
-
description: 'OpenAI GPT-4.1 specialized for coding assistance ✓ Images',
|
|
52
|
+
description: 'OpenAI GPT-4.1 specialized for coding assistance ✓ Images ✓ Audio',
|
|
53
53
|
},
|
|
54
54
|
],
|
|
55
55
|
default: 'gpt-5-mini',
|
|
@@ -78,31 +78,31 @@ exports.nodeProperties = [
|
|
|
78
78
|
description: 'System message to set the behavior of the AI model',
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
|
-
displayName: 'Include
|
|
82
|
-
name: '
|
|
81
|
+
displayName: 'Include Media',
|
|
82
|
+
name: 'includeMedia',
|
|
83
83
|
type: 'boolean',
|
|
84
84
|
default: false,
|
|
85
|
-
description: 'Whether to include
|
|
85
|
+
description: 'Whether to include a media file in the message. Supported: Images (PNG, JPEG, GIF, WebP) and Audio files. Type is auto-detected.',
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
|
-
displayName: '
|
|
89
|
-
name: '
|
|
88
|
+
displayName: 'Media Source',
|
|
89
|
+
name: 'mediaSource',
|
|
90
90
|
type: 'options',
|
|
91
91
|
displayOptions: {
|
|
92
92
|
show: {
|
|
93
|
-
|
|
93
|
+
includeMedia: [true],
|
|
94
94
|
},
|
|
95
95
|
},
|
|
96
96
|
options: [
|
|
97
97
|
{
|
|
98
98
|
name: 'Manual Input',
|
|
99
99
|
value: 'manual',
|
|
100
|
-
description: 'Provide
|
|
100
|
+
description: 'Provide media as base64 string or file path',
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
name: 'URL',
|
|
104
104
|
value: 'url',
|
|
105
|
-
description: 'Download
|
|
105
|
+
description: 'Download media from URL',
|
|
106
106
|
},
|
|
107
107
|
{
|
|
108
108
|
name: 'Binary Data',
|
|
@@ -111,127 +111,49 @@ exports.nodeProperties = [
|
|
|
111
111
|
},
|
|
112
112
|
],
|
|
113
113
|
default: 'manual',
|
|
114
|
-
description: 'Source of the
|
|
114
|
+
description: 'Source of the media data',
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
|
-
displayName: '
|
|
118
|
-
name: '
|
|
117
|
+
displayName: 'Media File',
|
|
118
|
+
name: 'mediaFile',
|
|
119
119
|
type: 'string',
|
|
120
120
|
displayOptions: {
|
|
121
121
|
show: {
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
includeMedia: [true],
|
|
123
|
+
mediaSource: ['manual'],
|
|
124
124
|
},
|
|
125
125
|
},
|
|
126
126
|
default: '',
|
|
127
127
|
placeholder: 'Paste base64 string or file path',
|
|
128
|
-
description: '
|
|
128
|
+
description: 'Media file as base64 string or file path (Images: PNG/JPEG/GIF/WebP, Audio: MP3/WAV/M4A)',
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
|
-
displayName: '
|
|
132
|
-
name: '
|
|
131
|
+
displayName: 'Media URL',
|
|
132
|
+
name: 'mediaUrl',
|
|
133
133
|
type: 'string',
|
|
134
134
|
displayOptions: {
|
|
135
135
|
show: {
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
includeMedia: [true],
|
|
137
|
+
mediaSource: ['url'],
|
|
138
138
|
},
|
|
139
139
|
},
|
|
140
140
|
default: '',
|
|
141
|
-
placeholder: 'https://example.com/
|
|
142
|
-
description: 'URL of the
|
|
141
|
+
placeholder: 'https://example.com/file.jpg',
|
|
142
|
+
description: 'URL of the media file to download and include (Images: PNG/JPEG/GIF/WebP, Audio: MP3/WAV/M4A)',
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
|
-
displayName: '
|
|
146
|
-
name: '
|
|
145
|
+
displayName: 'Media Binary Property',
|
|
146
|
+
name: 'mediaBinaryProperty',
|
|
147
147
|
type: 'string',
|
|
148
148
|
displayOptions: {
|
|
149
149
|
show: {
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
includeMedia: [true],
|
|
151
|
+
mediaSource: ['binary'],
|
|
152
152
|
},
|
|
153
153
|
},
|
|
154
154
|
default: 'data',
|
|
155
155
|
placeholder: 'data',
|
|
156
|
-
description: 'Name of the binary property containing the
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
displayName: 'Include Audio',
|
|
160
|
-
name: 'includeAudio',
|
|
161
|
-
type: 'boolean',
|
|
162
|
-
default: false,
|
|
163
|
-
description: 'Whether to include an audio file in the message. ⚠️ Audio is sent as base64 text to models for analysis (GitHub Copilot API limitation).',
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
displayName: 'Audio Source',
|
|
167
|
-
name: 'audioSource',
|
|
168
|
-
type: 'options',
|
|
169
|
-
displayOptions: {
|
|
170
|
-
show: {
|
|
171
|
-
includeAudio: [true],
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
options: [
|
|
175
|
-
{
|
|
176
|
-
name: 'Manual Input',
|
|
177
|
-
value: 'manual',
|
|
178
|
-
description: 'Provide audio as base64 string or file path',
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
name: 'URL',
|
|
182
|
-
value: 'url',
|
|
183
|
-
description: 'Download audio from URL',
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
name: 'Binary Data',
|
|
187
|
-
value: 'binary',
|
|
188
|
-
description: 'Use binary data from previous node',
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
default: 'manual',
|
|
192
|
-
description: 'Source of the audio data',
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
displayName: 'Audio File',
|
|
196
|
-
name: 'audioFile',
|
|
197
|
-
type: 'string',
|
|
198
|
-
displayOptions: {
|
|
199
|
-
show: {
|
|
200
|
-
includeAudio: [true],
|
|
201
|
-
audioSource: ['manual'],
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
default: '',
|
|
205
|
-
placeholder: 'Paste base64 string or file path',
|
|
206
|
-
description: 'Audio as base64 string or file path',
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
displayName: 'Audio URL',
|
|
210
|
-
name: 'audioUrl',
|
|
211
|
-
type: 'string',
|
|
212
|
-
displayOptions: {
|
|
213
|
-
show: {
|
|
214
|
-
includeAudio: [true],
|
|
215
|
-
audioSource: ['url'],
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
default: '',
|
|
219
|
-
placeholder: 'https://example.com/audio.mp3',
|
|
220
|
-
description: 'URL of the audio file to download and include',
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
displayName: 'Audio Binary Property',
|
|
224
|
-
name: 'audioBinaryProperty',
|
|
225
|
-
type: 'string',
|
|
226
|
-
displayOptions: {
|
|
227
|
-
show: {
|
|
228
|
-
includeAudio: [true],
|
|
229
|
-
audioSource: ['binary'],
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
default: 'data',
|
|
233
|
-
placeholder: 'data',
|
|
234
|
-
description: 'Name of the binary property containing the audio file',
|
|
156
|
+
description: 'Name of the binary property containing the media file',
|
|
235
157
|
},
|
|
236
158
|
{
|
|
237
159
|
displayName: 'Advanced Options',
|
|
@@ -7,3 +7,9 @@ export declare function processMediaFile(context: IExecuteFunctions, itemIndex:
|
|
|
7
7
|
}>;
|
|
8
8
|
export declare function isImageMimeType(mimeType: string): boolean;
|
|
9
9
|
export declare function isAudioMimeType(mimeType: string): boolean;
|
|
10
|
+
export declare function validateImageFormat(mimeType: string): {
|
|
11
|
+
isValid: boolean;
|
|
12
|
+
error?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function getFileExtensionFromMimeType(mimeType: string): string;
|
|
15
|
+
export declare function suggestImageConversion(mimeType: string): string;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isAudioMimeType = exports.isImageMimeType = exports.processMediaFile = void 0;
|
|
3
|
+
exports.suggestImageConversion = exports.getFileExtensionFromMimeType = exports.validateImageFormat = exports.isAudioMimeType = exports.isImageMimeType = exports.processMediaFile = void 0;
|
|
4
4
|
const index_1 = require("./index");
|
|
5
5
|
async function processMediaFile(context, itemIndex, source, mediaFile, mediaUrl, binaryProperty) {
|
|
6
6
|
try {
|
|
7
7
|
try {
|
|
8
8
|
const imageResult = await (0, index_1.processImageFile)(context, itemIndex, source, mediaFile, mediaUrl, binaryProperty);
|
|
9
|
+
const formatValidation = validateImageFormat(imageResult.mimeType);
|
|
10
|
+
if (!formatValidation.isValid) {
|
|
11
|
+
throw new Error(suggestImageConversion(imageResult.mimeType));
|
|
12
|
+
}
|
|
9
13
|
return {
|
|
10
14
|
type: 'image',
|
|
11
15
|
dataUrl: `data:${imageResult.mimeType};base64,${imageResult.data}`,
|
|
12
|
-
description: `Image file: ${imageResult.filename} (${Math.round(imageResult.size / 1024)}KB)`,
|
|
16
|
+
description: `Image file: ${imageResult.filename} (${Math.round(imageResult.size / 1024)}KB, ${imageResult.mimeType})`,
|
|
13
17
|
mimeType: imageResult.mimeType,
|
|
14
18
|
};
|
|
15
19
|
}
|
|
@@ -24,7 +28,13 @@ async function processMediaFile(context, itemIndex, source, mediaFile, mediaUrl,
|
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
catch (audioError) {
|
|
27
|
-
|
|
31
|
+
const supportedImageFormats = ['PNG', 'JPEG', 'GIF', 'WebP'];
|
|
32
|
+
const supportedAudioFormats = ['MP3', 'WAV', 'M4A', 'OGG'];
|
|
33
|
+
throw new Error(`File is neither a valid image nor audio file.\n` +
|
|
34
|
+
`Supported image formats: ${supportedImageFormats.join(', ')}\n` +
|
|
35
|
+
`Supported audio formats: ${supportedAudioFormats.join(', ')}\n` +
|
|
36
|
+
`Image error: ${imageError instanceof Error ? imageError.message : 'Unknown'}\n` +
|
|
37
|
+
`Audio error: ${audioError instanceof Error ? audioError.message : 'Unknown'}`);
|
|
28
38
|
}
|
|
29
39
|
}
|
|
30
40
|
}
|
|
@@ -38,7 +48,14 @@ async function processMediaFile(context, itemIndex, source, mediaFile, mediaUrl,
|
|
|
38
48
|
}
|
|
39
49
|
exports.processMediaFile = processMediaFile;
|
|
40
50
|
function isImageMimeType(mimeType) {
|
|
41
|
-
|
|
51
|
+
const supportedFormats = [
|
|
52
|
+
'image/png',
|
|
53
|
+
'image/jpeg',
|
|
54
|
+
'image/jpg',
|
|
55
|
+
'image/gif',
|
|
56
|
+
'image/webp'
|
|
57
|
+
];
|
|
58
|
+
return supportedFormats.includes(mimeType.toLowerCase());
|
|
42
59
|
}
|
|
43
60
|
exports.isImageMimeType = isImageMimeType;
|
|
44
61
|
function isAudioMimeType(mimeType) {
|
|
@@ -47,3 +64,36 @@ function isAudioMimeType(mimeType) {
|
|
|
47
64
|
mimeType === 'video/mp4';
|
|
48
65
|
}
|
|
49
66
|
exports.isAudioMimeType = isAudioMimeType;
|
|
67
|
+
function validateImageFormat(mimeType) {
|
|
68
|
+
if (!isImageMimeType(mimeType)) {
|
|
69
|
+
const supportedFormats = ['PNG', 'JPEG', 'GIF', 'WebP'];
|
|
70
|
+
return {
|
|
71
|
+
isValid: false,
|
|
72
|
+
error: `Unsupported image format: ${mimeType}. GitHub Copilot API only supports: ${supportedFormats.join(', ')}`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { isValid: true };
|
|
76
|
+
}
|
|
77
|
+
exports.validateImageFormat = validateImageFormat;
|
|
78
|
+
function getFileExtensionFromMimeType(mimeType) {
|
|
79
|
+
const mimeToExt = {
|
|
80
|
+
'image/png': 'png',
|
|
81
|
+
'image/jpeg': 'jpg',
|
|
82
|
+
'image/jpg': 'jpg',
|
|
83
|
+
'image/gif': 'gif',
|
|
84
|
+
'image/webp': 'webp',
|
|
85
|
+
'image/bmp': 'bmp',
|
|
86
|
+
'image/tiff': 'tiff',
|
|
87
|
+
'image/svg+xml': 'svg',
|
|
88
|
+
};
|
|
89
|
+
return mimeToExt[mimeType.toLowerCase()] || 'unknown';
|
|
90
|
+
}
|
|
91
|
+
exports.getFileExtensionFromMimeType = getFileExtensionFromMimeType;
|
|
92
|
+
function suggestImageConversion(mimeType) {
|
|
93
|
+
const ext = getFileExtensionFromMimeType(mimeType);
|
|
94
|
+
const supportedFormats = ['PNG', 'JPEG', 'GIF', 'WebP'];
|
|
95
|
+
return `Image format ${ext.toUpperCase()} is not supported by GitHub Copilot API. ` +
|
|
96
|
+
`Please convert your image to one of these formats: ${supportedFormats.join(', ')}. ` +
|
|
97
|
+
`Recommended: Convert to PNG or WebP for best compatibility.`;
|
|
98
|
+
}
|
|
99
|
+
exports.suggestImageConversion = suggestImageConversion;
|
|
@@ -4,21 +4,21 @@ exports.getModelInfo = exports.getSupportedModels = exports.validateModelCapabil
|
|
|
4
4
|
exports.MODEL_CAPABILITIES = {
|
|
5
5
|
'gpt-5': {
|
|
6
6
|
supportsImages: true,
|
|
7
|
-
supportsAudio:
|
|
7
|
+
supportsAudio: true,
|
|
8
8
|
maxContextTokens: 200000,
|
|
9
|
-
description: 'OpenAI GPT-5 with image support via GitHub Copilot API',
|
|
9
|
+
description: 'OpenAI GPT-5 with image support and audio analysis via GitHub Copilot API',
|
|
10
10
|
},
|
|
11
11
|
'gpt-5-mini': {
|
|
12
12
|
supportsImages: true,
|
|
13
|
-
supportsAudio:
|
|
13
|
+
supportsAudio: true,
|
|
14
14
|
maxContextTokens: 128000,
|
|
15
|
-
description: 'OpenAI GPT-5 Mini with image support via GitHub Copilot API',
|
|
15
|
+
description: 'OpenAI GPT-5 Mini with image support and audio analysis via GitHub Copilot API',
|
|
16
16
|
},
|
|
17
17
|
'gpt-4.1-copilot': {
|
|
18
18
|
supportsImages: true,
|
|
19
|
-
supportsAudio:
|
|
19
|
+
supportsAudio: true,
|
|
20
20
|
maxContextTokens: 128000,
|
|
21
|
-
description: 'OpenAI GPT-4.1 with image support via GitHub Copilot API',
|
|
21
|
+
description: 'OpenAI GPT-4.1 with image support and audio analysis via GitHub Copilot API',
|
|
22
22
|
},
|
|
23
23
|
'claude-opus-4.1': {
|
|
24
24
|
supportsImages: false,
|
|
@@ -34,15 +34,15 @@ exports.MODEL_CAPABILITIES = {
|
|
|
34
34
|
},
|
|
35
35
|
'gemini-2.5-pro': {
|
|
36
36
|
supportsImages: true,
|
|
37
|
-
supportsAudio:
|
|
37
|
+
supportsAudio: true,
|
|
38
38
|
maxContextTokens: 1000000,
|
|
39
|
-
description: 'Google Gemini 2.5 Pro via GitHub Copilot API',
|
|
39
|
+
description: 'Google Gemini 2.5 Pro with multimodal support via GitHub Copilot API',
|
|
40
40
|
},
|
|
41
41
|
'gemini-2.0-flash': {
|
|
42
42
|
supportsImages: true,
|
|
43
|
-
supportsAudio:
|
|
43
|
+
supportsAudio: true,
|
|
44
44
|
maxContextTokens: 1000000,
|
|
45
|
-
description: 'Google Gemini 2.0 Flash via GitHub Copilot API',
|
|
45
|
+
description: 'Google Gemini 2.0 Flash with multimodal support via GitHub Copilot API',
|
|
46
46
|
},
|
|
47
47
|
'grok-code-fast-1': {
|
|
48
48
|
supportsImages: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-github-copilot",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
4
4
|
"description": "n8n community node for GitHub Copilot with CLI integration and official Chat API access to GPT-5, Claude, Gemini and more using your existing Copilot credits",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/sufficit/n8n-nodes-github-copilot",
|