lynkr 3.2.0 → 3.2.1
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/package.json +1 -1
- package/src/db/database.sqlite +0 -0
- package/src/orchestrator/index.js +10 -26
- package/src/tools/smart-selection.js +23 -58
package/package.json
CHANGED
|
File without changes
|
|
@@ -10,6 +10,7 @@ const tokens = require("../utils/tokens");
|
|
|
10
10
|
const systemPrompt = require("../prompts/system");
|
|
11
11
|
const historyCompression = require("../context/compression");
|
|
12
12
|
const tokenBudget = require("../context/budget");
|
|
13
|
+
const { classifyRequestType, selectToolsSmartly } = require("../tools/smart-selection");
|
|
13
14
|
|
|
14
15
|
const DROP_KEYS = new Set([
|
|
15
16
|
"provider",
|
|
@@ -1001,39 +1002,22 @@ function sanitizePayload(payload) {
|
|
|
1001
1002
|
|
|
1002
1003
|
// Smart tool selection (universal, applies to all providers)
|
|
1003
1004
|
if (config.smartToolSelection?.enabled && Array.isArray(clean.tools) && clean.tools.length > 0) {
|
|
1004
|
-
const { classifyRequestType, selectToolsSmartly } = require('../tools/smart-selection');
|
|
1005
|
-
|
|
1006
1005
|
const classification = classifyRequestType(clean);
|
|
1007
|
-
|
|
1008
|
-
logger.debug({
|
|
1009
|
-
originalToolCount: clean.tools.length,
|
|
1010
|
-
classificationType: classification.type,
|
|
1011
|
-
confidence: classification.confidence,
|
|
1012
|
-
keywords: classification.keywords
|
|
1013
|
-
}, "Smart tool selection: classified request");
|
|
1014
|
-
|
|
1015
1006
|
const selectedTools = selectToolsSmartly(clean.tools, classification, {
|
|
1016
1007
|
provider: providerType,
|
|
1017
1008
|
tokenBudget: config.smartToolSelection.tokenBudget,
|
|
1018
1009
|
config: config.smartToolSelection
|
|
1019
1010
|
});
|
|
1020
1011
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
.
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
confidence: classification.confidence,
|
|
1031
|
-
originalToolCount: clean.tools.length,
|
|
1032
|
-
selectedToolCount: selectedTools.length,
|
|
1033
|
-
removedTools,
|
|
1034
|
-
keptTools,
|
|
1035
|
-
provider: providerType
|
|
1036
|
-
}, "Smart tool selection applied");
|
|
1012
|
+
// Only log if tools were actually filtered (avoid logging overhead)
|
|
1013
|
+
if (selectedTools.length !== clean.tools.length) {
|
|
1014
|
+
logger.info({
|
|
1015
|
+
requestType: classification.type,
|
|
1016
|
+
originalCount: clean.tools.length,
|
|
1017
|
+
selectedCount: selectedTools.length,
|
|
1018
|
+
provider: providerType
|
|
1019
|
+
}, "Smart tool selection applied");
|
|
1020
|
+
}
|
|
1037
1021
|
|
|
1038
1022
|
clean.tools = selectedTools.length > 0 ? selectedTools : undefined;
|
|
1039
1023
|
}
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
|
|
10
10
|
const logger = require('../logger');
|
|
11
11
|
|
|
12
|
+
// Pre-compiled regex patterns for performance (avoid recompiling on every request)
|
|
13
|
+
const GREETING_PATTERN = /^(hi|hello|hey|good morning|good afternoon|good evening|howdy|greetings|sup|yo)[\s\.\!\?]*$/i;
|
|
14
|
+
const QUESTION_PATTERN = /^(what is|what's|how does|when|where|why|explain|define|tell me about|can you explain)/i;
|
|
15
|
+
const TECHNICAL_KEYWORDS = /code|function|class|file|module|import|export|async|await|promise|callback|api|database|server|client|component|method|variable|array|object|string|number/i;
|
|
16
|
+
const EXPLANATION_PATTERN = /explain|describe|summarize|what does|how does|tell me about|give me an overview|clarify|elaborate/i;
|
|
17
|
+
const WEB_PATTERN = /search|lookup|find info|google|documentation|docs|website|url|link|online|internet|browse/i;
|
|
18
|
+
const READ_PATTERN = /read|show|display|view|cat|check|inspect|look at|see|examine|review|print|output/i;
|
|
19
|
+
const WRITE_PATTERN = /write|create|add|update|modify|change|fix|delete|remove|insert|append|replace|save/i;
|
|
20
|
+
const EDIT_PATTERN = /edit|refactor|rename|move|reorganize|restructure|rewrite/i;
|
|
21
|
+
const EXECUTION_PATTERN = /run|execute|test|compile|build|deploy|start|install|launch|boot|fire up|npm|git|python|node|docker|bash|sh|cmd/i;
|
|
22
|
+
const COMPLEX_PATTERN = /implement|build|create|develop|design|architect|plan|strategy|approach|help with|work on|improve|optimize|enhance|refactor|migrate/i;
|
|
23
|
+
|
|
12
24
|
/**
|
|
13
25
|
* Tool selection map: request type → relevant tools
|
|
14
26
|
*/
|
|
@@ -91,8 +103,7 @@ function extractContent(message) {
|
|
|
91
103
|
* Check if content matches greeting patterns
|
|
92
104
|
*/
|
|
93
105
|
function isGreeting(content) {
|
|
94
|
-
|
|
95
|
-
return greetingPattern.test(content.trim());
|
|
106
|
+
return GREETING_PATTERN.test(content.trim());
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
/**
|
|
@@ -100,85 +111,70 @@ function isGreeting(content) {
|
|
|
100
111
|
*/
|
|
101
112
|
function isShortNonTechnical(content) {
|
|
102
113
|
const trimmed = content.trim();
|
|
103
|
-
|
|
104
|
-
// Very short messages (< 20 chars)
|
|
105
|
-
if (trimmed.length >= 20) return false;
|
|
106
|
-
|
|
107
|
-
// Check for technical keywords
|
|
108
|
-
const technicalKeywords = /code|file|function|error|bug|fix|write|read|create|implement|class|module|import|export|async|await/i;
|
|
109
|
-
return !technicalKeywords.test(trimmed);
|
|
114
|
+
return trimmed.length < 20 && !TECHNICAL_KEYWORDS.test(trimmed);
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
/**
|
|
113
118
|
* Check if content is a simple question
|
|
114
119
|
*/
|
|
115
120
|
function isSimpleQuestion(content) {
|
|
116
|
-
|
|
117
|
-
return questionPattern.test(content.trim());
|
|
121
|
+
return QUESTION_PATTERN.test(content.trim());
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
/**
|
|
121
125
|
* Check for technical keywords
|
|
122
126
|
*/
|
|
123
127
|
function hasTechnicalKeywords(content) {
|
|
124
|
-
|
|
125
|
-
return technicalPattern.test(content);
|
|
128
|
+
return TECHNICAL_KEYWORDS.test(content);
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
/**
|
|
129
132
|
* Check for explanation/research keywords
|
|
130
133
|
*/
|
|
131
134
|
function hasExplanationKeywords(content) {
|
|
132
|
-
|
|
133
|
-
return explanationPattern.test(content);
|
|
135
|
+
return EXPLANATION_PATTERN.test(content);
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
/**
|
|
137
139
|
* Check for web/search keywords
|
|
138
140
|
*/
|
|
139
141
|
function hasWebKeywords(content) {
|
|
140
|
-
|
|
141
|
-
return webPattern.test(content);
|
|
142
|
+
return WEB_PATTERN.test(content);
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
/**
|
|
145
146
|
* Check for file reading keywords
|
|
146
147
|
*/
|
|
147
148
|
function hasReadKeywords(content) {
|
|
148
|
-
|
|
149
|
-
return readPattern.test(content);
|
|
149
|
+
return READ_PATTERN.test(content);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
153
|
* Check for file writing/modification keywords
|
|
154
154
|
*/
|
|
155
155
|
function hasWriteKeywords(content) {
|
|
156
|
-
|
|
157
|
-
return writePattern.test(content);
|
|
156
|
+
return WRITE_PATTERN.test(content);
|
|
158
157
|
}
|
|
159
158
|
|
|
160
159
|
/**
|
|
161
160
|
* Check for edit/refactor keywords
|
|
162
161
|
*/
|
|
163
162
|
function hasEditKeywords(content) {
|
|
164
|
-
|
|
165
|
-
return editPattern.test(content);
|
|
163
|
+
return EDIT_PATTERN.test(content);
|
|
166
164
|
}
|
|
167
165
|
|
|
168
166
|
/**
|
|
169
167
|
* Check for execution/testing keywords
|
|
170
168
|
*/
|
|
171
169
|
function hasExecutionKeywords(content) {
|
|
172
|
-
|
|
173
|
-
return executionPattern.test(content);
|
|
170
|
+
return EXECUTION_PATTERN.test(content);
|
|
174
171
|
}
|
|
175
172
|
|
|
176
173
|
/**
|
|
177
174
|
* Check for complex task keywords
|
|
178
175
|
*/
|
|
179
176
|
function hasComplexKeywords(content) {
|
|
180
|
-
|
|
181
|
-
return complexPattern.test(content);
|
|
177
|
+
return COMPLEX_PATTERN.test(content);
|
|
182
178
|
}
|
|
183
179
|
|
|
184
180
|
/**
|
|
@@ -191,7 +187,6 @@ function classifyRequestType(payload) {
|
|
|
191
187
|
const lastMessage = getLastUserMessage(payload);
|
|
192
188
|
|
|
193
189
|
if (!lastMessage) {
|
|
194
|
-
logger.debug('No user message found for classification');
|
|
195
190
|
return { type: 'coding', confidence: 0.5, keywords: [] };
|
|
196
191
|
}
|
|
197
192
|
|
|
@@ -199,12 +194,6 @@ function classifyRequestType(payload) {
|
|
|
199
194
|
const contentLower = content.toLowerCase();
|
|
200
195
|
const messageCount = payload.messages?.length ?? 0;
|
|
201
196
|
|
|
202
|
-
logger.debug({
|
|
203
|
-
contentLength: content.length,
|
|
204
|
-
messageCount,
|
|
205
|
-
contentPreview: content.substring(0, 100)
|
|
206
|
-
}, 'Classifying request type');
|
|
207
|
-
|
|
208
197
|
// 1. Conversational (no tools)
|
|
209
198
|
if (isGreeting(contentLower)) {
|
|
210
199
|
return { type: 'conversational', confidence: 1.0, keywords: ['greeting'] };
|
|
@@ -286,13 +275,6 @@ function selectToolsSmartly(tools, classification, options = {}) {
|
|
|
286
275
|
// Get relevant tool names for this request type
|
|
287
276
|
const relevantToolNames = TOOL_SELECTION_MAP[requestType] || TOOL_SELECTION_MAP.coding;
|
|
288
277
|
|
|
289
|
-
logger.debug({
|
|
290
|
-
requestType,
|
|
291
|
-
confidence: classification.confidence,
|
|
292
|
-
relevantToolNames,
|
|
293
|
-
mode: config.mode
|
|
294
|
-
}, 'Selecting tools for request type');
|
|
295
|
-
|
|
296
278
|
// Filter to relevant tools only
|
|
297
279
|
let selectedTools = tools.filter(tool => relevantToolNames.includes(tool.name));
|
|
298
280
|
|
|
@@ -312,10 +294,6 @@ function selectToolsSmartly(tools, classification, options = {}) {
|
|
|
312
294
|
|
|
313
295
|
// Provider-specific limits
|
|
314
296
|
if (provider === 'ollama' && selectedTools.length > 8) {
|
|
315
|
-
logger.debug({
|
|
316
|
-
originalCount: selectedTools.length,
|
|
317
|
-
limit: 8
|
|
318
|
-
}, 'Limiting tools for Ollama provider');
|
|
319
297
|
selectedTools = selectedTools.slice(0, 8);
|
|
320
298
|
}
|
|
321
299
|
|
|
@@ -323,11 +301,6 @@ function selectToolsSmartly(tools, classification, options = {}) {
|
|
|
323
301
|
const estimatedTokens = estimateToolTokens(selectedTools);
|
|
324
302
|
if (estimatedTokens > tokenBudget) {
|
|
325
303
|
const targetCount = Math.floor(tokenBudget / 175);
|
|
326
|
-
logger.debug({
|
|
327
|
-
estimatedTokens,
|
|
328
|
-
tokenBudget,
|
|
329
|
-
targetCount
|
|
330
|
-
}, 'Enforcing token budget on tools');
|
|
331
304
|
selectedTools = selectedTools.slice(0, Math.max(targetCount, 0));
|
|
332
305
|
}
|
|
333
306
|
|
|
@@ -337,14 +310,6 @@ function selectToolsSmartly(tools, classification, options = {}) {
|
|
|
337
310
|
selectedTools = selectedTools.filter(t => minimalTools.includes(t.name));
|
|
338
311
|
}
|
|
339
312
|
|
|
340
|
-
logger.info({
|
|
341
|
-
requestType,
|
|
342
|
-
originalToolCount: tools.length,
|
|
343
|
-
selectedToolCount: selectedTools.length,
|
|
344
|
-
removedToolCount: tools.length - selectedTools.length,
|
|
345
|
-
estimatedTokenSavings: estimateToolTokens(tools) - estimateToolTokens(selectedTools)
|
|
346
|
-
}, 'Smart tool selection completed');
|
|
347
|
-
|
|
348
313
|
return selectedTools;
|
|
349
314
|
}
|
|
350
315
|
|