n8n-nodes-github-copilot 3.38.17 → 3.38.19
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/GitHubCopilotChatModel/GitHubCopilotChatModel.node.js +6 -1
- package/dist/nodes/GitHubCopilotOpenAI/GitHubCopilotOpenAI.node.js +24 -0
- package/dist/nodes/GitHubCopilotOpenAI/nodeProperties.js +27 -27
- package/dist/package.json +1 -1
- package/dist/shared/models/ModelVersionRequirements.d.ts +16 -0
- package/dist/shared/models/ModelVersionRequirements.js +83 -0
- package/dist/shared/utils/FileChunkingApiUtils.d.ts +22 -0
- package/dist/shared/utils/FileChunkingApiUtils.js +105 -0
- package/dist/shared/utils/FileOptimizationUtils.d.ts +36 -0
- package/dist/shared/utils/FileOptimizationUtils.js +148 -0
- package/package.json +86 -86
|
@@ -6,6 +6,7 @@ const GitHubCopilotModels_1 = require("../../shared/models/GitHubCopilotModels")
|
|
|
6
6
|
const GitHubCopilotEndpoints_1 = require("../../shared/utils/GitHubCopilotEndpoints");
|
|
7
7
|
const DynamicModelLoader_1 = require("../../shared/models/DynamicModelLoader");
|
|
8
8
|
const ModelProperties_1 = require("../../shared/properties/ModelProperties");
|
|
9
|
+
const ModelVersionRequirements_1 = require("../../shared/models/ModelVersionRequirements");
|
|
9
10
|
class GitHubCopilotChatModel {
|
|
10
11
|
constructor() {
|
|
11
12
|
this.description = {
|
|
@@ -157,6 +158,9 @@ class GitHubCopilotChatModel {
|
|
|
157
158
|
}
|
|
158
159
|
const safeModel = modelInfo ? model : GitHubCopilotModels_1.DEFAULT_MODELS.GENERAL;
|
|
159
160
|
const safeModelInfo = modelInfo || GitHubCopilotModels_1.GitHubCopilotModelsManager.getModelByValue(GitHubCopilotModels_1.DEFAULT_MODELS.GENERAL);
|
|
161
|
+
const minVSCodeVersion = (0, ModelVersionRequirements_1.getMinVSCodeVersion)(safeModel);
|
|
162
|
+
const additionalHeaders = (0, ModelVersionRequirements_1.getAdditionalHeaders)(safeModel);
|
|
163
|
+
console.log(`🔧 Model: ${safeModel} requires VS Code version: ${minVSCodeVersion}`);
|
|
160
164
|
const modelConfig = {
|
|
161
165
|
model: safeModel,
|
|
162
166
|
temperature: options.temperature || 0.7,
|
|
@@ -169,9 +173,10 @@ class GitHubCopilotChatModel {
|
|
|
169
173
|
defaultHeaders: {
|
|
170
174
|
"User-Agent": "GitHubCopilotChat/1.0.0 n8n/3.10.1",
|
|
171
175
|
Accept: "application/json",
|
|
172
|
-
"Editor-Version":
|
|
176
|
+
"Editor-Version": `vscode/${minVSCodeVersion}`,
|
|
173
177
|
"Editor-Plugin-Version": "copilot-chat/0.12.0",
|
|
174
178
|
"X-Request-Id": `n8n-chatmodel-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
|
179
|
+
...additionalHeaders,
|
|
175
180
|
...(options.enableVision &&
|
|
176
181
|
(safeModelInfo === null || safeModelInfo === void 0 ? void 0 : safeModelInfo.capabilities.vision) && {
|
|
177
182
|
"Copilot-Vision-Request": "true",
|
|
@@ -122,6 +122,30 @@ class GitHubCopilotOpenAI {
|
|
|
122
122
|
content: "Hello! How can you help me?",
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
|
+
for (let msgIndex = 0; msgIndex < messages.length; msgIndex++) {
|
|
126
|
+
const msg = messages[msgIndex];
|
|
127
|
+
if (Array.isArray(msg.content)) {
|
|
128
|
+
for (const contentItem of msg.content) {
|
|
129
|
+
if (contentItem.type === 'file') {
|
|
130
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `❌ GitHub Copilot API Error: File attachments cannot be used inside 'content' array.\n\n` +
|
|
131
|
+
`📋 INCORRECT FORMAT (OpenAI style - doesn't work):\n` +
|
|
132
|
+
`{\n` +
|
|
133
|
+
` "role": "user",\n` +
|
|
134
|
+
` "content": [\n` +
|
|
135
|
+
` {"type": "text", "text": "Analyze this"},\n` +
|
|
136
|
+
` {"type": "file", "file": "data:..."} ❌ WRONG\n` +
|
|
137
|
+
` ]\n` +
|
|
138
|
+
`}\n\n` +
|
|
139
|
+
`✅ CORRECT FORMAT (GitHub Copilot - message level):\n` +
|
|
140
|
+
`[\n` +
|
|
141
|
+
` {"role": "user", "content": "Analyze this file"},\n` +
|
|
142
|
+
` {"role": "user", "content": "data:image/png;base64,...", "type": "file"} ✅ CORRECT\n` +
|
|
143
|
+
`]\n\n` +
|
|
144
|
+
`💡 Solution: Use separate messages with 'type' property at message level, not inside content array.`, { itemIndex: i });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
125
149
|
console.log('📤 Final messages being sent to API:', JSON.stringify(messages, null, 2));
|
|
126
150
|
const advancedOptions = this.getNodeParameter("advancedOptions", i, {});
|
|
127
151
|
let parsedTools = [];
|
|
@@ -27,15 +27,15 @@ exports.nodeProperties = [
|
|
|
27
27
|
displayName: "Messages (JSON)",
|
|
28
28
|
name: "messagesJson",
|
|
29
29
|
type: "json",
|
|
30
|
-
default: `[
|
|
31
|
-
{
|
|
32
|
-
"role": "system",
|
|
33
|
-
"content": "You are a helpful assistant."
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"role": "user",
|
|
37
|
-
"content": "Hello!"
|
|
38
|
-
}
|
|
30
|
+
default: `[
|
|
31
|
+
{
|
|
32
|
+
"role": "system",
|
|
33
|
+
"content": "You are a helpful assistant."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"role": "user",
|
|
37
|
+
"content": "Hello!"
|
|
38
|
+
}
|
|
39
39
|
]`,
|
|
40
40
|
placeholder: "Enter messages as JSON array",
|
|
41
41
|
description: "Array of messages in OpenAI format: [{\"role\": \"user\", \"content\": \"...\"}]",
|
|
@@ -255,24 +255,24 @@ exports.nodeProperties = [
|
|
|
255
255
|
typeOptions: {
|
|
256
256
|
rows: 10,
|
|
257
257
|
},
|
|
258
|
-
placeholder: `[
|
|
259
|
-
{
|
|
260
|
-
"type": "function",
|
|
261
|
-
"function": {
|
|
262
|
-
"name": "get_weather",
|
|
263
|
-
"description": "Get current weather",
|
|
264
|
-
"parameters": {
|
|
265
|
-
"type": "object",
|
|
266
|
-
"properties": {
|
|
267
|
-
"location": {
|
|
268
|
-
"type": "string",
|
|
269
|
-
"description": "City name"
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
"required": ["location"]
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
258
|
+
placeholder: `[
|
|
259
|
+
{
|
|
260
|
+
"type": "function",
|
|
261
|
+
"function": {
|
|
262
|
+
"name": "get_weather",
|
|
263
|
+
"description": "Get current weather",
|
|
264
|
+
"parameters": {
|
|
265
|
+
"type": "object",
|
|
266
|
+
"properties": {
|
|
267
|
+
"location": {
|
|
268
|
+
"type": "string",
|
|
269
|
+
"description": "City name"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
"required": ["location"]
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
276
|
]`,
|
|
277
277
|
description: "Optional: Array of tools/functions available to the model (OpenAI format). Leave empty if not using function calling.",
|
|
278
278
|
hint: "JSON array of tool definitions in OpenAI format. Leave this field empty if you don't need function calling.",
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-github-copilot",
|
|
3
|
-
"version": "3.38.
|
|
3
|
+
"version": "3.38.19",
|
|
4
4
|
"description": "n8n community node for GitHub Copilot with CLI integration, Chat API access, and AI Chat Model for workflows - access GPT-5, Claude, Gemini and more using your Copilot subscription",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/sufficit/n8n-nodes-github-copilot",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ModelRequirements {
|
|
2
|
+
minVSCodeVersion: string;
|
|
3
|
+
supportedEndpoints: string[];
|
|
4
|
+
preview?: boolean;
|
|
5
|
+
additionalHeaders?: Record<string, string>;
|
|
6
|
+
notes?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const MODEL_VERSION_REQUIREMENTS: Record<string, ModelRequirements>;
|
|
9
|
+
export declare const DEFAULT_MODEL_REQUIREMENTS: ModelRequirements;
|
|
10
|
+
export declare function getModelRequirements(model: string): ModelRequirements;
|
|
11
|
+
export declare function modelSupportsEndpoint(model: string, endpoint: string): boolean;
|
|
12
|
+
export declare function getRecommendedEndpoint(model: string): string;
|
|
13
|
+
export declare function validateModelEndpoint(model: string, endpoint: string): void;
|
|
14
|
+
export declare function getMinVSCodeVersion(model: string): string;
|
|
15
|
+
export declare function isPreviewModel(model: string): boolean;
|
|
16
|
+
export declare function getAdditionalHeaders(model: string): Record<string, string>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_MODEL_REQUIREMENTS = exports.MODEL_VERSION_REQUIREMENTS = void 0;
|
|
4
|
+
exports.getModelRequirements = getModelRequirements;
|
|
5
|
+
exports.modelSupportsEndpoint = modelSupportsEndpoint;
|
|
6
|
+
exports.getRecommendedEndpoint = getRecommendedEndpoint;
|
|
7
|
+
exports.validateModelEndpoint = validateModelEndpoint;
|
|
8
|
+
exports.getMinVSCodeVersion = getMinVSCodeVersion;
|
|
9
|
+
exports.isPreviewModel = isPreviewModel;
|
|
10
|
+
exports.getAdditionalHeaders = getAdditionalHeaders;
|
|
11
|
+
exports.MODEL_VERSION_REQUIREMENTS = {
|
|
12
|
+
"gpt-5-codex": {
|
|
13
|
+
minVSCodeVersion: "1.104.1",
|
|
14
|
+
supportedEndpoints: ["/responses"],
|
|
15
|
+
preview: true,
|
|
16
|
+
notes: "Preview model requiring VS Code 1.104.1 or newer. Only supports /responses endpoint."
|
|
17
|
+
},
|
|
18
|
+
"gpt-5": {
|
|
19
|
+
minVSCodeVersion: "1.95.0",
|
|
20
|
+
supportedEndpoints: ["/chat/completions", "/responses"],
|
|
21
|
+
preview: false,
|
|
22
|
+
},
|
|
23
|
+
"gpt-5-mini": {
|
|
24
|
+
minVSCodeVersion: "1.95.0",
|
|
25
|
+
supportedEndpoints: ["/chat/completions", "/responses"],
|
|
26
|
+
preview: false,
|
|
27
|
+
},
|
|
28
|
+
"o3": {
|
|
29
|
+
minVSCodeVersion: "1.95.0",
|
|
30
|
+
supportedEndpoints: ["/chat/completions", "/responses"],
|
|
31
|
+
preview: true,
|
|
32
|
+
},
|
|
33
|
+
"o3-2025-04-16": {
|
|
34
|
+
minVSCodeVersion: "1.95.0",
|
|
35
|
+
supportedEndpoints: ["/chat/completions", "/responses"],
|
|
36
|
+
preview: true,
|
|
37
|
+
},
|
|
38
|
+
"o4-mini": {
|
|
39
|
+
minVSCodeVersion: "1.95.0",
|
|
40
|
+
supportedEndpoints: ["/chat/completions", "/responses"],
|
|
41
|
+
preview: true,
|
|
42
|
+
},
|
|
43
|
+
"o4-mini-2025-04-16": {
|
|
44
|
+
minVSCodeVersion: "1.95.0",
|
|
45
|
+
supportedEndpoints: ["/chat/completions", "/responses"],
|
|
46
|
+
preview: true,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
exports.DEFAULT_MODEL_REQUIREMENTS = {
|
|
50
|
+
minVSCodeVersion: "1.95.0",
|
|
51
|
+
supportedEndpoints: ["/chat/completions", "/responses"],
|
|
52
|
+
preview: false,
|
|
53
|
+
};
|
|
54
|
+
function getModelRequirements(model) {
|
|
55
|
+
return exports.MODEL_VERSION_REQUIREMENTS[model] || exports.DEFAULT_MODEL_REQUIREMENTS;
|
|
56
|
+
}
|
|
57
|
+
function modelSupportsEndpoint(model, endpoint) {
|
|
58
|
+
const requirements = getModelRequirements(model);
|
|
59
|
+
return requirements.supportedEndpoints.includes(endpoint);
|
|
60
|
+
}
|
|
61
|
+
function getRecommendedEndpoint(model) {
|
|
62
|
+
const requirements = getModelRequirements(model);
|
|
63
|
+
return requirements.supportedEndpoints[0] || "/chat/completions";
|
|
64
|
+
}
|
|
65
|
+
function validateModelEndpoint(model, endpoint) {
|
|
66
|
+
if (!modelSupportsEndpoint(model, endpoint)) {
|
|
67
|
+
const requirements = getModelRequirements(model);
|
|
68
|
+
throw new Error(`Model "${model}" does not support endpoint "${endpoint}". ` +
|
|
69
|
+
`Supported endpoints: ${requirements.supportedEndpoints.join(", ")}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function getMinVSCodeVersion(model) {
|
|
73
|
+
const requirements = getModelRequirements(model);
|
|
74
|
+
return requirements.minVSCodeVersion;
|
|
75
|
+
}
|
|
76
|
+
function isPreviewModel(model) {
|
|
77
|
+
const requirements = getModelRequirements(model);
|
|
78
|
+
return requirements.preview || false;
|
|
79
|
+
}
|
|
80
|
+
function getAdditionalHeaders(model) {
|
|
81
|
+
const requirements = getModelRequirements(model);
|
|
82
|
+
return requirements.additionalHeaders || {};
|
|
83
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ChunkRequest {
|
|
2
|
+
content: string;
|
|
3
|
+
embed: boolean;
|
|
4
|
+
qos: "Batch" | "Online";
|
|
5
|
+
}
|
|
6
|
+
export interface Chunk {
|
|
7
|
+
content: string;
|
|
8
|
+
embedding?: number[];
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
metadata?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface ChunkResponse {
|
|
14
|
+
chunks: Chunk[];
|
|
15
|
+
total: number;
|
|
16
|
+
contentLength: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function chunkFile(token: string, fileContent: string, embeddings?: boolean, qos?: "Batch" | "Online"): Promise<ChunkResponse>;
|
|
19
|
+
export declare function selectRelevantChunks(chunks: Chunk[], queryEmbedding: number[], maxTokens?: number, minRelevance?: number): string;
|
|
20
|
+
export declare function selectTopChunks(chunks: Chunk[], maxTokens?: number): string;
|
|
21
|
+
export declare function estimateTokens(text: string): number;
|
|
22
|
+
export declare function getQueryEmbedding(token: string, query: string): Promise<number[]>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.chunkFile = chunkFile;
|
|
4
|
+
exports.selectRelevantChunks = selectRelevantChunks;
|
|
5
|
+
exports.selectTopChunks = selectTopChunks;
|
|
6
|
+
exports.estimateTokens = estimateTokens;
|
|
7
|
+
exports.getQueryEmbedding = getQueryEmbedding;
|
|
8
|
+
const GitHubCopilotEndpoints_1 = require("./GitHubCopilotEndpoints");
|
|
9
|
+
async function chunkFile(token, fileContent, embeddings = true, qos = "Online") {
|
|
10
|
+
const url = "https://api.githubcopilot.com/chunks";
|
|
11
|
+
const headers = GitHubCopilotEndpoints_1.GitHubCopilotEndpoints.getAuthHeaders(token, true);
|
|
12
|
+
const requestBody = {
|
|
13
|
+
content: fileContent,
|
|
14
|
+
embed: embeddings,
|
|
15
|
+
qos,
|
|
16
|
+
};
|
|
17
|
+
console.log(`🔪 Chunking file (${fileContent.length} chars, embed=${embeddings}, qos=${qos})`);
|
|
18
|
+
const response = await fetch(url, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers,
|
|
21
|
+
body: JSON.stringify(requestBody),
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
const errorText = await response.text();
|
|
25
|
+
throw new Error(`Chunking API error: ${response.status} ${response.statusText}. ${errorText}`);
|
|
26
|
+
}
|
|
27
|
+
const data = await response.json();
|
|
28
|
+
console.log(`✅ Chunked into ${data.chunks.length} chunks`);
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
function cosineSimilarity(a, b) {
|
|
32
|
+
if (a.length !== b.length) {
|
|
33
|
+
throw new Error("Vectors must have same length");
|
|
34
|
+
}
|
|
35
|
+
let dotProduct = 0;
|
|
36
|
+
let normA = 0;
|
|
37
|
+
let normB = 0;
|
|
38
|
+
for (let i = 0; i < a.length; i++) {
|
|
39
|
+
dotProduct += a[i] * b[i];
|
|
40
|
+
normA += a[i] * a[i];
|
|
41
|
+
normB += b[i] * b[i];
|
|
42
|
+
}
|
|
43
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
44
|
+
}
|
|
45
|
+
function selectRelevantChunks(chunks, queryEmbedding, maxTokens = 10000, minRelevance = 0.5) {
|
|
46
|
+
if (!chunks.length) {
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
const rankedChunks = chunks
|
|
50
|
+
.filter(chunk => chunk.embedding)
|
|
51
|
+
.map(chunk => ({
|
|
52
|
+
chunk,
|
|
53
|
+
relevance: cosineSimilarity(chunk.embedding, queryEmbedding),
|
|
54
|
+
}))
|
|
55
|
+
.filter(item => item.relevance >= minRelevance)
|
|
56
|
+
.sort((a, b) => b.relevance - a.relevance);
|
|
57
|
+
const selectedChunks = [];
|
|
58
|
+
let totalTokens = 0;
|
|
59
|
+
for (const item of rankedChunks) {
|
|
60
|
+
const chunkTokens = Math.ceil(item.chunk.content.length / 4);
|
|
61
|
+
if (totalTokens + chunkTokens > maxTokens) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
selectedChunks.push(item.chunk.content);
|
|
65
|
+
totalTokens += chunkTokens;
|
|
66
|
+
console.log(` ✓ Selected chunk (relevance: ${item.relevance.toFixed(3)}, tokens: ~${chunkTokens})`);
|
|
67
|
+
}
|
|
68
|
+
console.log(`📊 Selected ${selectedChunks.length}/${rankedChunks.length} chunks (~${totalTokens} tokens)`);
|
|
69
|
+
return selectedChunks.join("\n\n---\n\n");
|
|
70
|
+
}
|
|
71
|
+
function selectTopChunks(chunks, maxTokens = 10000) {
|
|
72
|
+
const selectedChunks = [];
|
|
73
|
+
let totalTokens = 0;
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
const chunkTokens = Math.ceil(chunk.content.length / 4);
|
|
76
|
+
if (totalTokens + chunkTokens > maxTokens) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
selectedChunks.push(chunk.content);
|
|
80
|
+
totalTokens += chunkTokens;
|
|
81
|
+
}
|
|
82
|
+
console.log(`📊 Selected ${selectedChunks.length}/${chunks.length} chunks (~${totalTokens} tokens)`);
|
|
83
|
+
return selectedChunks.join("\n\n---\n\n");
|
|
84
|
+
}
|
|
85
|
+
function estimateTokens(text) {
|
|
86
|
+
return Math.ceil(text.length / 4);
|
|
87
|
+
}
|
|
88
|
+
async function getQueryEmbedding(token, query) {
|
|
89
|
+
const url = "https://api.githubcopilot.com/embeddings";
|
|
90
|
+
const headers = GitHubCopilotEndpoints_1.GitHubCopilotEndpoints.getEmbeddingsHeaders(token);
|
|
91
|
+
const response = await fetch(url, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers,
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
input: [query],
|
|
96
|
+
model: "text-embedding-3-small",
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
const errorText = await response.text();
|
|
101
|
+
throw new Error(`Embeddings API error: ${response.status} ${response.statusText}. ${errorText}`);
|
|
102
|
+
}
|
|
103
|
+
const data = await response.json();
|
|
104
|
+
return data.data[0].embedding;
|
|
105
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type FileProcessingMode = "direct" | "chunking" | "summarize" | "auto";
|
|
2
|
+
export interface FileOptimizationOptions {
|
|
3
|
+
mode: FileProcessingMode;
|
|
4
|
+
model: string;
|
|
5
|
+
fileSize: number;
|
|
6
|
+
maxContextUsage?: number;
|
|
7
|
+
minRelevance?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface OptimizationResult {
|
|
10
|
+
mode: "direct" | "chunking" | "summarize";
|
|
11
|
+
reason: string;
|
|
12
|
+
estimatedTokens: number;
|
|
13
|
+
maxAllowedTokens: number;
|
|
14
|
+
fitsInContext: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function estimateFileTokens(fileSize: number, isBase64?: boolean): number;
|
|
17
|
+
export declare function selectFileProcessingMode(options: FileOptimizationOptions): OptimizationResult;
|
|
18
|
+
export declare function getOptimalChunkSettings(model: string, maxContextUsage?: number): {
|
|
19
|
+
maxChunks: number;
|
|
20
|
+
maxTokensPerChunk: number;
|
|
21
|
+
totalMaxTokens: number;
|
|
22
|
+
};
|
|
23
|
+
export declare function compressText(text: string): string;
|
|
24
|
+
export declare function truncateToTokenLimit(text: string, maxTokens: number, addEllipsis?: boolean): {
|
|
25
|
+
content: string;
|
|
26
|
+
truncated: boolean;
|
|
27
|
+
originalTokens: number;
|
|
28
|
+
finalTokens: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function getFileSizeCategory(sizeBytes: number): string;
|
|
31
|
+
export declare function formatTokenCount(tokens: number): string;
|
|
32
|
+
export declare function calculateSavings(originalTokens: number, optimizedTokens: number): {
|
|
33
|
+
savedTokens: number;
|
|
34
|
+
savingsPercent: number;
|
|
35
|
+
savingsRatio: string;
|
|
36
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.estimateFileTokens = estimateFileTokens;
|
|
4
|
+
exports.selectFileProcessingMode = selectFileProcessingMode;
|
|
5
|
+
exports.getOptimalChunkSettings = getOptimalChunkSettings;
|
|
6
|
+
exports.compressText = compressText;
|
|
7
|
+
exports.truncateToTokenLimit = truncateToTokenLimit;
|
|
8
|
+
exports.getFileSizeCategory = getFileSizeCategory;
|
|
9
|
+
exports.formatTokenCount = formatTokenCount;
|
|
10
|
+
exports.calculateSavings = calculateSavings;
|
|
11
|
+
const FileChunkingApiUtils_1 = require("./FileChunkingApiUtils");
|
|
12
|
+
function estimateFileTokens(fileSize, isBase64 = true) {
|
|
13
|
+
const encodedSize = isBase64 ? fileSize * 1.33 : fileSize;
|
|
14
|
+
return Math.ceil(encodedSize / 4);
|
|
15
|
+
}
|
|
16
|
+
function selectFileProcessingMode(options) {
|
|
17
|
+
const { model, fileSize, mode, maxContextUsage = 0.5 } = options;
|
|
18
|
+
const maxContextTokens = 128000;
|
|
19
|
+
const maxAllowedTokens = Math.floor(maxContextTokens * maxContextUsage);
|
|
20
|
+
const estimatedTokens = estimateFileTokens(fileSize, true);
|
|
21
|
+
const fitsInContext = estimatedTokens <= maxAllowedTokens;
|
|
22
|
+
console.log(`📊 File Optimization Analysis:`);
|
|
23
|
+
console.log(` File size: ${(fileSize / 1024).toFixed(2)} KB`);
|
|
24
|
+
console.log(` Estimated tokens: ${estimatedTokens.toLocaleString()}`);
|
|
25
|
+
console.log(` Max allowed (${(maxContextUsage * 100).toFixed(0)}%): ${maxAllowedTokens.toLocaleString()}`);
|
|
26
|
+
console.log(` Model context: ${maxContextTokens.toLocaleString()} tokens`);
|
|
27
|
+
if (mode !== "auto") {
|
|
28
|
+
if (mode === "direct" && !fitsInContext) {
|
|
29
|
+
console.warn(`⚠️ Warning: Direct mode requested but file exceeds token limit`);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
mode,
|
|
33
|
+
reason: `User requested ${mode} mode`,
|
|
34
|
+
estimatedTokens,
|
|
35
|
+
maxAllowedTokens,
|
|
36
|
+
fitsInContext,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (estimatedTokens < maxAllowedTokens * 0.3) {
|
|
40
|
+
return {
|
|
41
|
+
mode: "direct",
|
|
42
|
+
reason: `File is small (${estimatedTokens.toLocaleString()} tokens < 30% of limit)`,
|
|
43
|
+
estimatedTokens,
|
|
44
|
+
maxAllowedTokens,
|
|
45
|
+
fitsInContext: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
else if (estimatedTokens < maxAllowedTokens * 0.8) {
|
|
49
|
+
return {
|
|
50
|
+
mode: "chunking",
|
|
51
|
+
reason: `File is medium-sized (${estimatedTokens.toLocaleString()} tokens, 30-80% of limit) - chunking recommended`,
|
|
52
|
+
estimatedTokens,
|
|
53
|
+
maxAllowedTokens,
|
|
54
|
+
fitsInContext: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
else if (fitsInContext) {
|
|
58
|
+
return {
|
|
59
|
+
mode: "chunking",
|
|
60
|
+
reason: `File is large (${estimatedTokens.toLocaleString()} tokens, >80% of limit) - chunking strongly recommended`,
|
|
61
|
+
estimatedTokens,
|
|
62
|
+
maxAllowedTokens,
|
|
63
|
+
fitsInContext: true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return {
|
|
68
|
+
mode: "summarize",
|
|
69
|
+
reason: `File exceeds token limit (${estimatedTokens.toLocaleString()} > ${maxAllowedTokens.toLocaleString()} tokens) - summarization required`,
|
|
70
|
+
estimatedTokens,
|
|
71
|
+
maxAllowedTokens,
|
|
72
|
+
fitsInContext: false,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function getOptimalChunkSettings(model, maxContextUsage = 0.5) {
|
|
77
|
+
const maxContextTokens = 128000;
|
|
78
|
+
const totalMaxTokens = Math.floor(maxContextTokens * maxContextUsage);
|
|
79
|
+
const maxChunks = 10;
|
|
80
|
+
const maxTokensPerChunk = Math.floor(totalMaxTokens / maxChunks);
|
|
81
|
+
return {
|
|
82
|
+
maxChunks,
|
|
83
|
+
maxTokensPerChunk,
|
|
84
|
+
totalMaxTokens,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function compressText(text) {
|
|
88
|
+
return text
|
|
89
|
+
.replace(/ {2,}/g, " ")
|
|
90
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
91
|
+
.replace(/\t/g, " ")
|
|
92
|
+
.split("\n")
|
|
93
|
+
.map(line => line.trim())
|
|
94
|
+
.join("\n")
|
|
95
|
+
.trim();
|
|
96
|
+
}
|
|
97
|
+
function truncateToTokenLimit(text, maxTokens, addEllipsis = true) {
|
|
98
|
+
const originalTokens = (0, FileChunkingApiUtils_1.estimateTokens)(text);
|
|
99
|
+
if (originalTokens <= maxTokens) {
|
|
100
|
+
return {
|
|
101
|
+
content: text,
|
|
102
|
+
truncated: false,
|
|
103
|
+
originalTokens,
|
|
104
|
+
finalTokens: originalTokens,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const maxChars = maxTokens * 4;
|
|
108
|
+
const truncated = text.slice(0, maxChars);
|
|
109
|
+
const ellipsis = addEllipsis ? "\n\n...[truncated]" : "";
|
|
110
|
+
const finalContent = truncated + ellipsis;
|
|
111
|
+
const finalTokens = (0, FileChunkingApiUtils_1.estimateTokens)(finalContent);
|
|
112
|
+
return {
|
|
113
|
+
content: finalContent,
|
|
114
|
+
truncated: true,
|
|
115
|
+
originalTokens,
|
|
116
|
+
finalTokens,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function getFileSizeCategory(sizeBytes) {
|
|
120
|
+
if (sizeBytes < 10 * 1024)
|
|
121
|
+
return "tiny (<10KB)";
|
|
122
|
+
if (sizeBytes < 50 * 1024)
|
|
123
|
+
return "small (<50KB)";
|
|
124
|
+
if (sizeBytes < 200 * 1024)
|
|
125
|
+
return "medium (<200KB)";
|
|
126
|
+
if (sizeBytes < 500 * 1024)
|
|
127
|
+
return "large (<500KB)";
|
|
128
|
+
if (sizeBytes < 1024 * 1024)
|
|
129
|
+
return "very large (<1MB)";
|
|
130
|
+
return "huge (>1MB)";
|
|
131
|
+
}
|
|
132
|
+
function formatTokenCount(tokens) {
|
|
133
|
+
if (tokens < 1000)
|
|
134
|
+
return `${tokens} tokens`;
|
|
135
|
+
if (tokens < 1000000)
|
|
136
|
+
return `${(tokens / 1000).toFixed(1)}K tokens`;
|
|
137
|
+
return `${(tokens / 1000000).toFixed(2)}M tokens`;
|
|
138
|
+
}
|
|
139
|
+
function calculateSavings(originalTokens, optimizedTokens) {
|
|
140
|
+
const savedTokens = originalTokens - optimizedTokens;
|
|
141
|
+
const savingsPercent = (savedTokens / originalTokens) * 100;
|
|
142
|
+
const savingsRatio = `${optimizedTokens.toLocaleString()}/${originalTokens.toLocaleString()}`;
|
|
143
|
+
return {
|
|
144
|
+
savedTokens,
|
|
145
|
+
savingsPercent,
|
|
146
|
+
savingsRatio,
|
|
147
|
+
};
|
|
148
|
+
}
|
package/package.json
CHANGED
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "n8n-nodes-github-copilot",
|
|
3
|
-
"version": "3.38.
|
|
4
|
-
"description": "n8n community node for GitHub Copilot with CLI integration, Chat API access, and AI Chat Model for workflows - access GPT-5, Claude, Gemini and more using your Copilot subscription",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"homepage": "https://github.com/sufficit/n8n-nodes-github-copilot",
|
|
7
|
-
"author": {
|
|
8
|
-
"name": "Sufficit",
|
|
9
|
-
"email": "development@sufficit.com.br"
|
|
10
|
-
},
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/sufficit/n8n-nodes-github-copilot.git"
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsc && gulp build:icons",
|
|
17
|
-
"dev": "tsc --watch",
|
|
18
|
-
"format": "prettier nodes credentials --write",
|
|
19
|
-
"lint": "eslint nodes credentials shared --ext .ts",
|
|
20
|
-
"lintfix": "eslint nodes credentials shared --ext .ts --fix",
|
|
21
|
-
"prepublishOnly": "npm run build"
|
|
22
|
-
},
|
|
23
|
-
"files": [
|
|
24
|
-
"dist"
|
|
25
|
-
],
|
|
26
|
-
"n8n": {
|
|
27
|
-
"n8nNodesApiVersion": 1,
|
|
28
|
-
"credentials": [
|
|
29
|
-
"dist/credentials/GitHubCopilotApi.credentials.js"
|
|
30
|
-
],
|
|
31
|
-
"nodes": [
|
|
32
|
-
"dist/nodes/GitHubCopilot/GitHubCopilot.node.js",
|
|
33
|
-
"dist/nodes/GitHubCopilotAuthHelper/GitHubCopilotAuthHelper.node.js",
|
|
34
|
-
"dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.js",
|
|
35
|
-
"dist/nodes/GitHubCopilotChatModel/GitHubCopilotChatModel.node.js",
|
|
36
|
-
"dist/nodes/GitHubCopilotEmbeddings/GitHubCopilotEmbeddings.node.js",
|
|
37
|
-
"dist/nodes/GitHubCopilotOpenAI/GitHubCopilotOpenAI.node.js",
|
|
38
|
-
"dist/nodes/GitHubCopilotTest/GitHubCopilotTest.node.js"
|
|
39
|
-
]
|
|
40
|
-
},
|
|
41
|
-
"keywords": [
|
|
42
|
-
"n8n-community-node-package",
|
|
43
|
-
"github",
|
|
44
|
-
"copilot",
|
|
45
|
-
"n8n-ai-agent",
|
|
46
|
-
"ai",
|
|
47
|
-
"gpt-5",
|
|
48
|
-
"claude",
|
|
49
|
-
"gemini",
|
|
50
|
-
"chat-completion",
|
|
51
|
-
"image-analysis",
|
|
52
|
-
"audio-transcription",
|
|
53
|
-
"code-generation",
|
|
54
|
-
"automation"
|
|
55
|
-
],
|
|
56
|
-
"engines": {
|
|
57
|
-
"node": ">=18.19",
|
|
58
|
-
"pnpm": ">=7.1"
|
|
59
|
-
},
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"@langchain/openai": "^0.6.14",
|
|
62
|
-
"n8n-workflow": "^1.110.0"
|
|
63
|
-
},
|
|
64
|
-
"overrides": {
|
|
65
|
-
"form-data": "^4.0.4",
|
|
66
|
-
"mysql2": "^3.15.0",
|
|
67
|
-
"axios": "^1.12.0",
|
|
68
|
-
"pdfjs-dist": "^4.1.393",
|
|
69
|
-
"semver": "^7.5.4",
|
|
70
|
-
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
71
|
-
"@azure/identity": "^4.2.1",
|
|
72
|
-
"pg-promise": "^11.5.5",
|
|
73
|
-
"snowflake-sdk": "^2.0.4"
|
|
74
|
-
},
|
|
75
|
-
"devDependencies": {
|
|
76
|
-
"@types/node": "^22.7.0",
|
|
77
|
-
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
|
78
|
-
"@typescript-eslint/parser": "^8.7.0",
|
|
79
|
-
"eslint": "^8.57.0",
|
|
80
|
-
"gulp": "^5.0.1",
|
|
81
|
-
"n8n-core": "^1.112.1",
|
|
82
|
-
"n8n-workflow": "^1.110.0",
|
|
83
|
-
"prettier": "^3.3.0",
|
|
84
|
-
"typescript": "^5.6.2"
|
|
85
|
-
}
|
|
86
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-github-copilot",
|
|
3
|
+
"version": "3.38.19",
|
|
4
|
+
"description": "n8n community node for GitHub Copilot with CLI integration, Chat API access, and AI Chat Model for workflows - access GPT-5, Claude, Gemini and more using your Copilot subscription",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/sufficit/n8n-nodes-github-copilot",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Sufficit",
|
|
9
|
+
"email": "development@sufficit.com.br"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/sufficit/n8n-nodes-github-copilot.git"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc && gulp build:icons",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"format": "prettier nodes credentials --write",
|
|
19
|
+
"lint": "eslint nodes credentials shared --ext .ts",
|
|
20
|
+
"lintfix": "eslint nodes credentials shared --ext .ts --fix",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"n8n": {
|
|
27
|
+
"n8nNodesApiVersion": 1,
|
|
28
|
+
"credentials": [
|
|
29
|
+
"dist/credentials/GitHubCopilotApi.credentials.js"
|
|
30
|
+
],
|
|
31
|
+
"nodes": [
|
|
32
|
+
"dist/nodes/GitHubCopilot/GitHubCopilot.node.js",
|
|
33
|
+
"dist/nodes/GitHubCopilotAuthHelper/GitHubCopilotAuthHelper.node.js",
|
|
34
|
+
"dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.js",
|
|
35
|
+
"dist/nodes/GitHubCopilotChatModel/GitHubCopilotChatModel.node.js",
|
|
36
|
+
"dist/nodes/GitHubCopilotEmbeddings/GitHubCopilotEmbeddings.node.js",
|
|
37
|
+
"dist/nodes/GitHubCopilotOpenAI/GitHubCopilotOpenAI.node.js",
|
|
38
|
+
"dist/nodes/GitHubCopilotTest/GitHubCopilotTest.node.js"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"n8n-community-node-package",
|
|
43
|
+
"github",
|
|
44
|
+
"copilot",
|
|
45
|
+
"n8n-ai-agent",
|
|
46
|
+
"ai",
|
|
47
|
+
"gpt-5",
|
|
48
|
+
"claude",
|
|
49
|
+
"gemini",
|
|
50
|
+
"chat-completion",
|
|
51
|
+
"image-analysis",
|
|
52
|
+
"audio-transcription",
|
|
53
|
+
"code-generation",
|
|
54
|
+
"automation"
|
|
55
|
+
],
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=18.19",
|
|
58
|
+
"pnpm": ">=7.1"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@langchain/openai": "^0.6.14",
|
|
62
|
+
"n8n-workflow": "^1.110.0"
|
|
63
|
+
},
|
|
64
|
+
"overrides": {
|
|
65
|
+
"form-data": "^4.0.4",
|
|
66
|
+
"mysql2": "^3.15.0",
|
|
67
|
+
"axios": "^1.12.0",
|
|
68
|
+
"pdfjs-dist": "^4.1.393",
|
|
69
|
+
"semver": "^7.5.4",
|
|
70
|
+
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
71
|
+
"@azure/identity": "^4.2.1",
|
|
72
|
+
"pg-promise": "^11.5.5",
|
|
73
|
+
"snowflake-sdk": "^2.0.4"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@types/node": "^22.7.0",
|
|
77
|
+
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
|
78
|
+
"@typescript-eslint/parser": "^8.7.0",
|
|
79
|
+
"eslint": "^8.57.0",
|
|
80
|
+
"gulp": "^5.0.1",
|
|
81
|
+
"n8n-core": "^1.112.1",
|
|
82
|
+
"n8n-workflow": "^1.110.0",
|
|
83
|
+
"prettier": "^3.3.0",
|
|
84
|
+
"typescript": "^5.6.2"
|
|
85
|
+
}
|
|
86
|
+
}
|