@juspay/neurolink 7.36.0 → 7.37.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/CHANGELOG.md +6 -0
- package/dist/config/taskClassificationConfig.d.ts +51 -0
- package/dist/config/taskClassificationConfig.js +148 -0
- package/dist/lib/config/taskClassificationConfig.d.ts +51 -0
- package/dist/lib/config/taskClassificationConfig.js +148 -0
- package/dist/lib/neurolink.d.ts +20 -0
- package/dist/lib/neurolink.js +268 -5
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/taskClassificationTypes.d.ts +52 -0
- package/dist/lib/types/taskClassificationTypes.js +5 -0
- package/dist/lib/utils/modelRouter.d.ts +107 -0
- package/dist/lib/utils/modelRouter.js +292 -0
- package/dist/lib/utils/promptRedaction.d.ts +29 -0
- package/dist/lib/utils/promptRedaction.js +62 -0
- package/dist/lib/utils/taskClassificationUtils.d.ts +55 -0
- package/dist/lib/utils/taskClassificationUtils.js +149 -0
- package/dist/lib/utils/taskClassifier.d.ts +23 -0
- package/dist/lib/utils/taskClassifier.js +94 -0
- package/dist/neurolink.d.ts +20 -0
- package/dist/neurolink.js +268 -5
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/taskClassificationTypes.d.ts +52 -0
- package/dist/types/taskClassificationTypes.js +5 -0
- package/dist/utils/modelRouter.d.ts +107 -0
- package/dist/utils/modelRouter.js +292 -0
- package/dist/utils/promptRedaction.d.ts +29 -0
- package/dist/utils/promptRedaction.js +62 -0
- package/dist/utils/taskClassificationUtils.d.ts +55 -0
- package/dist/utils/taskClassificationUtils.js +149 -0
- package/dist/utils/taskClassifier.d.ts +23 -0
- package/dist/utils/taskClassifier.js +94 -0
- package/package.json +1 -1
@@ -0,0 +1,292 @@
|
|
1
|
+
/**
|
2
|
+
* Model Router for NeuroLink Orchestration
|
3
|
+
* Routes tasks to optimal models based on classification and requirements
|
4
|
+
*/
|
5
|
+
import { logger } from "./logger.js";
|
6
|
+
import { BinaryTaskClassifier } from "./taskClassifier.js";
|
7
|
+
import { redactForRouting } from "./promptRedaction.js";
|
8
|
+
/**
|
9
|
+
* Routing configuration constants
|
10
|
+
*/
|
11
|
+
const ROUTING_CONFIG = {
|
12
|
+
minRouteConfidence: 0.3,
|
13
|
+
maxRouteConfidence: 0.95,
|
14
|
+
confidenceBoost: 0.1,
|
15
|
+
};
|
16
|
+
/**
|
17
|
+
* Model configurations for different task types and providers
|
18
|
+
*/
|
19
|
+
const MODEL_CONFIGS = {
|
20
|
+
fast: {
|
21
|
+
primary: {
|
22
|
+
provider: "vertex",
|
23
|
+
model: "gemini-2.5-flash",
|
24
|
+
capabilities: ["speed", "general", "code", "basic-reasoning"],
|
25
|
+
avgResponseTime: 800, // ms
|
26
|
+
costPerToken: 0.0001,
|
27
|
+
reasoning: "Optimized for speed and efficiency via Vertex AI",
|
28
|
+
},
|
29
|
+
fallback: {
|
30
|
+
provider: "vertex",
|
31
|
+
model: "gemini-2.5-pro",
|
32
|
+
capabilities: ["speed", "general", "basic-reasoning"],
|
33
|
+
avgResponseTime: 1200,
|
34
|
+
costPerToken: 0.0002,
|
35
|
+
reasoning: "Vertex AI Gemini Pro fallback",
|
36
|
+
},
|
37
|
+
},
|
38
|
+
reasoning: {
|
39
|
+
primary: {
|
40
|
+
provider: "vertex",
|
41
|
+
model: "claude-sonnet-4@20250514",
|
42
|
+
capabilities: [
|
43
|
+
"reasoning",
|
44
|
+
"analysis",
|
45
|
+
"complex-logic",
|
46
|
+
"code",
|
47
|
+
"creativity",
|
48
|
+
],
|
49
|
+
avgResponseTime: 3000, // ms
|
50
|
+
costPerToken: 0.003,
|
51
|
+
reasoning: "Advanced reasoning and analysis via Claude Sonnet 4 on Vertex AI",
|
52
|
+
},
|
53
|
+
fallback: {
|
54
|
+
provider: "vertex",
|
55
|
+
model: "claude-opus-4@20250514",
|
56
|
+
capabilities: [
|
57
|
+
"reasoning",
|
58
|
+
"analysis",
|
59
|
+
"complex-logic",
|
60
|
+
"code",
|
61
|
+
"creativity",
|
62
|
+
"agentic",
|
63
|
+
],
|
64
|
+
avgResponseTime: 4000,
|
65
|
+
costPerToken: 0.005,
|
66
|
+
reasoning: "Claude Opus 4 fallback on Vertex AI for most complex tasks",
|
67
|
+
},
|
68
|
+
},
|
69
|
+
};
|
70
|
+
/**
|
71
|
+
* Model Router
|
72
|
+
* Intelligently routes tasks to optimal models based on classification
|
73
|
+
*/
|
74
|
+
export class ModelRouter {
|
75
|
+
/**
|
76
|
+
* Route a prompt to the optimal model configuration
|
77
|
+
*/
|
78
|
+
static route(prompt, options = {}) {
|
79
|
+
const startTime = Date.now();
|
80
|
+
// 1. Classify the task if not overridden
|
81
|
+
let classification;
|
82
|
+
if (options.forceTaskType) {
|
83
|
+
classification = {
|
84
|
+
type: options.forceTaskType,
|
85
|
+
confidence: ROUTING_CONFIG.maxRouteConfidence, // Use maxRouteConfidence instead of 1.0
|
86
|
+
reasoning: "forced task type",
|
87
|
+
};
|
88
|
+
}
|
89
|
+
else {
|
90
|
+
classification = BinaryTaskClassifier.classify(prompt);
|
91
|
+
}
|
92
|
+
// 2. Apply special requirements
|
93
|
+
let taskType = classification.type;
|
94
|
+
const reasons = [classification.reasoning];
|
95
|
+
if (options.requireFast) {
|
96
|
+
taskType = "fast";
|
97
|
+
reasons.push("speed required");
|
98
|
+
}
|
99
|
+
if (options.requireCapability) {
|
100
|
+
// Check if the capability suggests a specific task type
|
101
|
+
const capability = options.requireCapability.toLowerCase();
|
102
|
+
if (["analysis", "reasoning", "complex", "research"].some((c) => capability.includes(c))) {
|
103
|
+
taskType = "reasoning";
|
104
|
+
reasons.push(`capability: ${capability}`);
|
105
|
+
}
|
106
|
+
else if (["speed", "quick", "fast", "simple"].some((c) => capability.includes(c))) {
|
107
|
+
taskType = "fast";
|
108
|
+
reasons.push(`capability: ${capability}`);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
// 3. Select model configuration
|
112
|
+
const config = MODEL_CONFIGS[taskType];
|
113
|
+
const selectedConfig = config.primary;
|
114
|
+
// 4. Calculate confidence based on multiple factors
|
115
|
+
let confidence = classification.confidence;
|
116
|
+
// Adjust confidence based on prompt characteristics
|
117
|
+
if (taskType === "fast" && prompt.length < 30) {
|
118
|
+
confidence = Math.min(ROUTING_CONFIG.maxRouteConfidence, confidence + ROUTING_CONFIG.confidenceBoost);
|
119
|
+
reasons.push("very short prompt");
|
120
|
+
}
|
121
|
+
if (taskType === "reasoning" && prompt.length > 150) {
|
122
|
+
confidence = Math.min(ROUTING_CONFIG.maxRouteConfidence, confidence + ROUTING_CONFIG.confidenceBoost);
|
123
|
+
reasons.push("detailed prompt");
|
124
|
+
}
|
125
|
+
// Ensure final confidence is within configured bounds
|
126
|
+
confidence = Math.max(ROUTING_CONFIG.minRouteConfidence, Math.min(ROUTING_CONFIG.maxRouteConfidence, confidence));
|
127
|
+
// 5. Create route result
|
128
|
+
const route = {
|
129
|
+
provider: selectedConfig.provider,
|
130
|
+
model: selectedConfig.model,
|
131
|
+
reasoning: reasons.join(", "),
|
132
|
+
confidence,
|
133
|
+
};
|
134
|
+
const routingTime = Date.now() - startTime;
|
135
|
+
logger.debug("Model routing decision", {
|
136
|
+
prompt: redactForRouting(prompt),
|
137
|
+
taskType,
|
138
|
+
route: {
|
139
|
+
provider: route.provider,
|
140
|
+
model: route.model,
|
141
|
+
confidence: route.confidence.toFixed(2),
|
142
|
+
},
|
143
|
+
reasoning: route.reasoning,
|
144
|
+
routingTime: `${routingTime}ms`,
|
145
|
+
options: Object.keys(options).length > 0 ? options : undefined,
|
146
|
+
});
|
147
|
+
return route;
|
148
|
+
}
|
149
|
+
/**
|
150
|
+
* Get fallback route if primary route fails
|
151
|
+
*/
|
152
|
+
static getFallbackRoute(prompt, primaryRoute, options = {}) {
|
153
|
+
// Determine fallback strategy
|
154
|
+
let fallbackType;
|
155
|
+
if (options.fallbackStrategy) {
|
156
|
+
if (options.fallbackStrategy === "auto") {
|
157
|
+
// Use opposite of primary for fallback
|
158
|
+
const primaryType = this.getTaskTypeFromRoute(primaryRoute);
|
159
|
+
fallbackType = primaryType === "fast" ? "reasoning" : "fast";
|
160
|
+
}
|
161
|
+
else {
|
162
|
+
fallbackType = options.fallbackStrategy;
|
163
|
+
}
|
164
|
+
}
|
165
|
+
else {
|
166
|
+
// Default: use fallback model of same type
|
167
|
+
fallbackType = this.getTaskTypeFromRoute(primaryRoute);
|
168
|
+
}
|
169
|
+
const config = MODEL_CONFIGS[fallbackType];
|
170
|
+
const fallbackConfig = config.fallback;
|
171
|
+
const route = {
|
172
|
+
provider: fallbackConfig.provider,
|
173
|
+
model: fallbackConfig.model,
|
174
|
+
reasoning: `fallback from ${primaryRoute.provider}/${primaryRoute.model}`,
|
175
|
+
confidence: Math.max(ROUTING_CONFIG.minRouteConfidence, Math.min(ROUTING_CONFIG.maxRouteConfidence, primaryRoute.confidence - 0.2)),
|
176
|
+
};
|
177
|
+
logger.debug("Fallback route selected", {
|
178
|
+
originalRoute: `${primaryRoute.provider}/${primaryRoute.model}`,
|
179
|
+
fallbackRoute: `${route.provider}/${route.model}`,
|
180
|
+
fallbackType,
|
181
|
+
strategy: options.fallbackStrategy || "default",
|
182
|
+
});
|
183
|
+
return route;
|
184
|
+
}
|
185
|
+
/**
|
186
|
+
* Determine task type from a model route
|
187
|
+
*/
|
188
|
+
static getTaskTypeFromRoute(route) {
|
189
|
+
// Check which config matches this route
|
190
|
+
for (const [taskType, config] of Object.entries(MODEL_CONFIGS)) {
|
191
|
+
if (config.primary.provider === route.provider &&
|
192
|
+
config.primary.model === route.model) {
|
193
|
+
return taskType;
|
194
|
+
}
|
195
|
+
if (config.fallback.provider === route.provider &&
|
196
|
+
config.fallback.model === route.model) {
|
197
|
+
return taskType;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
// Default fallback based on model name patterns
|
201
|
+
if (route.model.includes("flash") || route.model.includes("mini")) {
|
202
|
+
return "fast";
|
203
|
+
}
|
204
|
+
return "reasoning";
|
205
|
+
}
|
206
|
+
/**
|
207
|
+
* Get all available model configurations
|
208
|
+
*/
|
209
|
+
static getAvailableModels() {
|
210
|
+
return MODEL_CONFIGS;
|
211
|
+
}
|
212
|
+
/**
|
213
|
+
* Validate model availability for a given route
|
214
|
+
*/
|
215
|
+
static async validateRoute(route) {
|
216
|
+
try {
|
217
|
+
// This would typically check provider availability
|
218
|
+
// For now, just validate the configuration exists
|
219
|
+
const configs = Object.values(MODEL_CONFIGS).flatMap((config) => [
|
220
|
+
config.primary,
|
221
|
+
config.fallback,
|
222
|
+
]);
|
223
|
+
return configs.some((config) => config.provider === route.provider && config.model === route.model);
|
224
|
+
}
|
225
|
+
catch (error) {
|
226
|
+
logger.error("Route validation failed", {
|
227
|
+
route,
|
228
|
+
error: error instanceof Error ? error.message : String(error),
|
229
|
+
});
|
230
|
+
return false;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
/**
|
234
|
+
* Get routing statistics for multiple prompts
|
235
|
+
*/
|
236
|
+
static getRoutingStats(prompts) {
|
237
|
+
const routes = prompts.map((prompt) => this.route(prompt));
|
238
|
+
// Handle empty prompts array to avoid divide-by-zero
|
239
|
+
if (routes.length === 0) {
|
240
|
+
const stats = {
|
241
|
+
total: 0,
|
242
|
+
fastRoutes: 0,
|
243
|
+
reasoningRoutes: 0,
|
244
|
+
averageConfidence: 0,
|
245
|
+
providerDistribution: {},
|
246
|
+
};
|
247
|
+
logger.debug("Routing statistics", stats);
|
248
|
+
return stats;
|
249
|
+
}
|
250
|
+
const stats = {
|
251
|
+
total: routes.length,
|
252
|
+
fastRoutes: routes.filter((r) => {
|
253
|
+
const taskType = this.getTaskTypeFromRoute(r);
|
254
|
+
return taskType === "fast";
|
255
|
+
}).length,
|
256
|
+
reasoningRoutes: routes.filter((r) => {
|
257
|
+
const taskType = this.getTaskTypeFromRoute(r);
|
258
|
+
return taskType === "reasoning";
|
259
|
+
}).length,
|
260
|
+
averageConfidence: routes.reduce((sum, r) => sum + r.confidence, 0) / routes.length,
|
261
|
+
providerDistribution: routes.reduce((dist, r) => {
|
262
|
+
dist[r.provider] = (dist[r.provider] || 0) + 1;
|
263
|
+
return dist;
|
264
|
+
}, {}),
|
265
|
+
};
|
266
|
+
logger.debug("Routing statistics", stats);
|
267
|
+
return stats;
|
268
|
+
}
|
269
|
+
/**
|
270
|
+
* Estimate cost and performance for a route
|
271
|
+
*/
|
272
|
+
static getRouteEstimates(route, estimatedTokens = 500) {
|
273
|
+
// Find the config for this route
|
274
|
+
const allConfigs = Object.values(MODEL_CONFIGS).flatMap((config) => [
|
275
|
+
config.primary,
|
276
|
+
config.fallback,
|
277
|
+
]);
|
278
|
+
const config = allConfigs.find((c) => c.provider === route.provider && c.model === route.model);
|
279
|
+
if (!config) {
|
280
|
+
return {
|
281
|
+
estimatedCost: 0,
|
282
|
+
estimatedResponseTime: 2000,
|
283
|
+
capabilities: [],
|
284
|
+
};
|
285
|
+
}
|
286
|
+
return {
|
287
|
+
estimatedCost: config.costPerToken * estimatedTokens,
|
288
|
+
estimatedResponseTime: config.avgResponseTime,
|
289
|
+
capabilities: [...config.capabilities],
|
290
|
+
};
|
291
|
+
}
|
292
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/**
|
2
|
+
* Prompt redaction utilities for safe logging
|
3
|
+
* Provides consistent prompt masking across NeuroLink components
|
4
|
+
*/
|
5
|
+
export interface PromptRedactionOptions {
|
6
|
+
/** Maximum length of redacted prompt */
|
7
|
+
maxLength?: number;
|
8
|
+
/** Whether to show word count */
|
9
|
+
showWordCount?: boolean;
|
10
|
+
/** Mask character to use for redaction */
|
11
|
+
maskChar?: string;
|
12
|
+
}
|
13
|
+
/**
|
14
|
+
* Redact a prompt for safe logging
|
15
|
+
* Truncates to maxLength and optionally shows word count
|
16
|
+
*/
|
17
|
+
export declare function redactPrompt(prompt: string, options?: PromptRedactionOptions): string;
|
18
|
+
/**
|
19
|
+
* Create a short safe mask for highly sensitive contexts
|
20
|
+
*/
|
21
|
+
export declare function createSafeMask(prompt: string, _maskLength?: number): string;
|
22
|
+
/**
|
23
|
+
* Redact for classification context (matches classifier behavior)
|
24
|
+
*/
|
25
|
+
export declare function redactForClassification(prompt: string): string;
|
26
|
+
/**
|
27
|
+
* Redact for routing context (matches router behavior)
|
28
|
+
*/
|
29
|
+
export declare function redactForRouting(prompt: string): string;
|
@@ -0,0 +1,62 @@
|
|
1
|
+
/**
|
2
|
+
* Prompt redaction utilities for safe logging
|
3
|
+
* Provides consistent prompt masking across NeuroLink components
|
4
|
+
*/
|
5
|
+
/**
|
6
|
+
* Default redaction options
|
7
|
+
*/
|
8
|
+
const DEFAULT_REDACTION_OPTIONS = {
|
9
|
+
maxLength: 100,
|
10
|
+
showWordCount: true,
|
11
|
+
maskChar: "*",
|
12
|
+
};
|
13
|
+
/**
|
14
|
+
* Redact a prompt for safe logging
|
15
|
+
* Truncates to maxLength and optionally shows word count
|
16
|
+
*/
|
17
|
+
export function redactPrompt(prompt, options = {}) {
|
18
|
+
const opts = { ...DEFAULT_REDACTION_OPTIONS, ...options };
|
19
|
+
if (!prompt || typeof prompt !== 'string') {
|
20
|
+
return '[INVALID_PROMPT]';
|
21
|
+
}
|
22
|
+
const wordCount = prompt.trim().split(/\s+/).length;
|
23
|
+
let redacted = prompt.substring(0, opts.maxLength);
|
24
|
+
// Add ellipsis if truncated
|
25
|
+
if (prompt.length > opts.maxLength) {
|
26
|
+
redacted = redacted.substring(0, opts.maxLength - 3) + "...";
|
27
|
+
}
|
28
|
+
// Optionally append word count
|
29
|
+
if (opts.showWordCount) {
|
30
|
+
redacted += ` [${wordCount} words]`;
|
31
|
+
}
|
32
|
+
return redacted;
|
33
|
+
}
|
34
|
+
/**
|
35
|
+
* Create a short safe mask for highly sensitive contexts
|
36
|
+
*/
|
37
|
+
export function createSafeMask(prompt, _maskLength = 20) {
|
38
|
+
if (!prompt || typeof prompt !== 'string') {
|
39
|
+
return '[INVALID_PROMPT]';
|
40
|
+
}
|
41
|
+
const wordCount = prompt.trim().split(/\s+/).length;
|
42
|
+
const charCount = prompt.length;
|
43
|
+
return `[REDACTED: ${charCount} chars, ${wordCount} words]`;
|
44
|
+
}
|
45
|
+
/**
|
46
|
+
* Redact for classification context (matches classifier behavior)
|
47
|
+
*/
|
48
|
+
export function redactForClassification(prompt) {
|
49
|
+
return redactPrompt(prompt, {
|
50
|
+
maxLength: 100,
|
51
|
+
showWordCount: false,
|
52
|
+
});
|
53
|
+
}
|
54
|
+
/**
|
55
|
+
* Redact for routing context (matches router behavior)
|
56
|
+
*/
|
57
|
+
export function redactForRouting(prompt) {
|
58
|
+
return redactPrompt(prompt, {
|
59
|
+
maxLength: 100,
|
60
|
+
showWordCount: true,
|
61
|
+
});
|
62
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
/**
|
2
|
+
* Task Classification Utility Functions
|
3
|
+
* Helper functions for analyzing prompts and calculating scores
|
4
|
+
*/
|
5
|
+
import type { ClassificationScores } from "../types/taskClassificationTypes.js";
|
6
|
+
/**
|
7
|
+
* Analyze prompt length and apply scoring bonuses
|
8
|
+
*/
|
9
|
+
export declare function analyzeLengthFactors(prompt: string, reasons: string[]): {
|
10
|
+
fastScore: number;
|
11
|
+
reasoningScore: number;
|
12
|
+
};
|
13
|
+
/**
|
14
|
+
* Check prompt against fast task patterns
|
15
|
+
*/
|
16
|
+
export declare function checkFastPatterns(normalizedPrompt: string, reasons: string[]): number;
|
17
|
+
/**
|
18
|
+
* Check prompt against reasoning task patterns
|
19
|
+
*/
|
20
|
+
export declare function checkReasoningPatterns(normalizedPrompt: string, reasons: string[]): number;
|
21
|
+
/**
|
22
|
+
* Analyze keyword matches in the prompt
|
23
|
+
*/
|
24
|
+
export declare function analyzeKeywords(normalizedPrompt: string, reasons: string[]): {
|
25
|
+
fastScore: number;
|
26
|
+
reasoningScore: number;
|
27
|
+
};
|
28
|
+
/**
|
29
|
+
* Analyze question complexity
|
30
|
+
*/
|
31
|
+
export declare function analyzeQuestionComplexity(prompt: string, reasons: string[]): number;
|
32
|
+
/**
|
33
|
+
* Analyze prompt structure and punctuation
|
34
|
+
*/
|
35
|
+
export declare function analyzePromptStructure(prompt: string, reasons: string[]): number;
|
36
|
+
/**
|
37
|
+
* Analyze domain-specific indicators
|
38
|
+
*/
|
39
|
+
export declare function analyzeDomainIndicators(normalizedPrompt: string, prompt: string, reasons: string[]): {
|
40
|
+
fastScore: number;
|
41
|
+
reasoningScore: number;
|
42
|
+
};
|
43
|
+
/**
|
44
|
+
* Calculate final confidence score
|
45
|
+
*/
|
46
|
+
export declare function calculateConfidence(fastScore: number, reasoningScore: number): number;
|
47
|
+
/**
|
48
|
+
* Determine task type based on scores
|
49
|
+
*/
|
50
|
+
export declare function determineTaskType(fastScore: number, reasoningScore: number): "fast" | "reasoning";
|
51
|
+
/**
|
52
|
+
* Comprehensive prompt analysis
|
53
|
+
* Runs all analysis functions and returns combined scores
|
54
|
+
*/
|
55
|
+
export declare function analyzePrompt(prompt: string): ClassificationScores;
|
@@ -0,0 +1,149 @@
|
|
1
|
+
/**
|
2
|
+
* Task Classification Utility Functions
|
3
|
+
* Helper functions for analyzing prompts and calculating scores
|
4
|
+
*/
|
5
|
+
import { FAST_PATTERNS, REASONING_PATTERNS, FAST_KEYWORDS, REASONING_KEYWORDS, SCORING_WEIGHTS, CLASSIFICATION_THRESHOLDS, DOMAIN_PATTERNS, } from "../config/taskClassificationConfig.js";
|
6
|
+
/**
|
7
|
+
* Analyze prompt length and apply scoring bonuses
|
8
|
+
*/
|
9
|
+
export function analyzeLengthFactors(prompt, reasons) {
|
10
|
+
let fastScore = 0;
|
11
|
+
let reasoningScore = 0;
|
12
|
+
if (prompt.length < CLASSIFICATION_THRESHOLDS.SHORT_PROMPT_LENGTH) {
|
13
|
+
fastScore += SCORING_WEIGHTS.SHORT_PROMPT_BONUS;
|
14
|
+
reasons.push("short prompt");
|
15
|
+
}
|
16
|
+
else if (prompt.length > CLASSIFICATION_THRESHOLDS.LONG_PROMPT_LENGTH) {
|
17
|
+
reasoningScore += SCORING_WEIGHTS.LONG_PROMPT_BONUS;
|
18
|
+
reasons.push("detailed prompt");
|
19
|
+
}
|
20
|
+
return { fastScore, reasoningScore };
|
21
|
+
}
|
22
|
+
/**
|
23
|
+
* Check prompt against fast task patterns
|
24
|
+
*/
|
25
|
+
export function checkFastPatterns(normalizedPrompt, reasons) {
|
26
|
+
for (const pattern of FAST_PATTERNS) {
|
27
|
+
if (pattern.test(normalizedPrompt)) {
|
28
|
+
reasons.push("fast pattern match");
|
29
|
+
return SCORING_WEIGHTS.PATTERN_MATCH_SCORE;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
return 0;
|
33
|
+
}
|
34
|
+
/**
|
35
|
+
* Check prompt against reasoning task patterns
|
36
|
+
*/
|
37
|
+
export function checkReasoningPatterns(normalizedPrompt, reasons) {
|
38
|
+
for (const pattern of REASONING_PATTERNS) {
|
39
|
+
if (pattern.test(normalizedPrompt)) {
|
40
|
+
reasons.push("reasoning pattern match");
|
41
|
+
return SCORING_WEIGHTS.PATTERN_MATCH_SCORE;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
return 0;
|
45
|
+
}
|
46
|
+
/**
|
47
|
+
* Analyze keyword matches in the prompt
|
48
|
+
*/
|
49
|
+
export function analyzeKeywords(normalizedPrompt, reasons) {
|
50
|
+
const fastKeywordMatches = FAST_KEYWORDS.filter((keyword) => normalizedPrompt.includes(keyword)).length;
|
51
|
+
const reasoningKeywordMatches = REASONING_KEYWORDS.filter((keyword) => normalizedPrompt.includes(keyword)).length;
|
52
|
+
const fastScore = fastKeywordMatches * SCORING_WEIGHTS.KEYWORD_MATCH_SCORE;
|
53
|
+
const reasoningScore = reasoningKeywordMatches * SCORING_WEIGHTS.KEYWORD_MATCH_SCORE;
|
54
|
+
if (fastKeywordMatches > 0) {
|
55
|
+
reasons.push(`${fastKeywordMatches} fast keywords`);
|
56
|
+
}
|
57
|
+
if (reasoningKeywordMatches > 0) {
|
58
|
+
reasons.push(`${reasoningKeywordMatches} reasoning keywords`);
|
59
|
+
}
|
60
|
+
return { fastScore, reasoningScore };
|
61
|
+
}
|
62
|
+
/**
|
63
|
+
* Analyze question complexity
|
64
|
+
*/
|
65
|
+
export function analyzeQuestionComplexity(prompt, reasons) {
|
66
|
+
const questionMarks = (prompt.match(/\?/g) || []).length;
|
67
|
+
if (questionMarks > 1) {
|
68
|
+
reasons.push("multiple questions");
|
69
|
+
return SCORING_WEIGHTS.MULTIPLE_QUESTIONS_BONUS;
|
70
|
+
}
|
71
|
+
return 0;
|
72
|
+
}
|
73
|
+
/**
|
74
|
+
* Analyze prompt structure and punctuation
|
75
|
+
*/
|
76
|
+
export function analyzePromptStructure(prompt, reasons) {
|
77
|
+
const sentences = prompt.split(/[.!?]+/).filter((s) => s.trim().length > 0);
|
78
|
+
if (sentences.length > 3) {
|
79
|
+
reasons.push("multi-sentence structure");
|
80
|
+
return SCORING_WEIGHTS.MULTI_SENTENCE_BONUS;
|
81
|
+
}
|
82
|
+
return 0;
|
83
|
+
}
|
84
|
+
/**
|
85
|
+
* Analyze domain-specific indicators
|
86
|
+
*/
|
87
|
+
export function analyzeDomainIndicators(normalizedPrompt, prompt, reasons) {
|
88
|
+
let fastScore = 0;
|
89
|
+
let reasoningScore = 0;
|
90
|
+
// Check for technical domain
|
91
|
+
if (DOMAIN_PATTERNS.TECHNICAL.test(normalizedPrompt)) {
|
92
|
+
reasoningScore += SCORING_WEIGHTS.TECHNICAL_DOMAIN_BONUS;
|
93
|
+
reasons.push("technical domain");
|
94
|
+
}
|
95
|
+
// Check for simple definition requests
|
96
|
+
if (DOMAIN_PATTERNS.SIMPLE_DEFINITION.test(normalizedPrompt) &&
|
97
|
+
prompt.length < CLASSIFICATION_THRESHOLDS.SIMPLE_DEFINITION_LENGTH) {
|
98
|
+
fastScore += SCORING_WEIGHTS.SIMPLE_DEFINITION_BONUS;
|
99
|
+
reasons.push("simple definition request");
|
100
|
+
}
|
101
|
+
return { fastScore, reasoningScore };
|
102
|
+
}
|
103
|
+
/**
|
104
|
+
* Calculate final confidence score
|
105
|
+
*/
|
106
|
+
export function calculateConfidence(fastScore, reasoningScore) {
|
107
|
+
const totalScore = fastScore + reasoningScore;
|
108
|
+
if (totalScore === 0) {
|
109
|
+
return CLASSIFICATION_THRESHOLDS.DEFAULT_CONFIDENCE;
|
110
|
+
}
|
111
|
+
const rawConfidence = Math.max(fastScore, reasoningScore) / totalScore;
|
112
|
+
return Math.max(CLASSIFICATION_THRESHOLDS.MIN_CONFIDENCE, Math.min(CLASSIFICATION_THRESHOLDS.MAX_CONFIDENCE, rawConfidence));
|
113
|
+
}
|
114
|
+
/**
|
115
|
+
* Determine task type based on scores
|
116
|
+
*/
|
117
|
+
export function determineTaskType(fastScore, reasoningScore) {
|
118
|
+
return fastScore >= reasoningScore ? "fast" : "reasoning";
|
119
|
+
}
|
120
|
+
/**
|
121
|
+
* Comprehensive prompt analysis
|
122
|
+
* Runs all analysis functions and returns combined scores
|
123
|
+
*/
|
124
|
+
export function analyzePrompt(prompt) {
|
125
|
+
const normalizedPrompt = prompt.toLowerCase().trim();
|
126
|
+
const reasons = [];
|
127
|
+
let fastScore = 0;
|
128
|
+
let reasoningScore = 0;
|
129
|
+
// 1. Length analysis
|
130
|
+
const lengthScores = analyzeLengthFactors(prompt, reasons);
|
131
|
+
fastScore += lengthScores.fastScore;
|
132
|
+
reasoningScore += lengthScores.reasoningScore;
|
133
|
+
// 2. Pattern matching
|
134
|
+
fastScore += checkFastPatterns(normalizedPrompt, reasons);
|
135
|
+
reasoningScore += checkReasoningPatterns(normalizedPrompt, reasons);
|
136
|
+
// 3. Keyword analysis
|
137
|
+
const keywordScores = analyzeKeywords(normalizedPrompt, reasons);
|
138
|
+
fastScore += keywordScores.fastScore;
|
139
|
+
reasoningScore += keywordScores.reasoningScore;
|
140
|
+
// 4. Question complexity
|
141
|
+
reasoningScore += analyzeQuestionComplexity(prompt, reasons);
|
142
|
+
// 5. Structure analysis
|
143
|
+
reasoningScore += analyzePromptStructure(prompt, reasons);
|
144
|
+
// 6. Domain analysis
|
145
|
+
const domainScores = analyzeDomainIndicators(normalizedPrompt, prompt, reasons);
|
146
|
+
fastScore += domainScores.fastScore;
|
147
|
+
reasoningScore += domainScores.reasoningScore;
|
148
|
+
return { fastScore, reasoningScore, reasons };
|
149
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
/**
|
2
|
+
* Binary Task Classifier for NeuroLink Orchestration
|
3
|
+
* Classifies tasks as either 'fast' (quick responses) or 'reasoning' (complex analysis)
|
4
|
+
*/
|
5
|
+
import type { TaskType, TaskClassification, ClassificationStats, ClassificationValidation } from "../types/taskClassificationTypes.js";
|
6
|
+
/**
|
7
|
+
* Binary Task Classifier
|
8
|
+
* Determines if a task requires fast response or deeper reasoning
|
9
|
+
*/
|
10
|
+
export declare class BinaryTaskClassifier {
|
11
|
+
/**
|
12
|
+
* Classify a prompt as either fast or reasoning task
|
13
|
+
*/
|
14
|
+
static classify(prompt: string): TaskClassification;
|
15
|
+
/**
|
16
|
+
* Get classification statistics for multiple prompts
|
17
|
+
*/
|
18
|
+
static getClassificationStats(prompts: string[]): ClassificationStats;
|
19
|
+
/**
|
20
|
+
* Validate classification accuracy (for testing/tuning)
|
21
|
+
*/
|
22
|
+
static validateClassification(prompt: string, expectedType: TaskType): ClassificationValidation;
|
23
|
+
}
|