adaptive-memory-multi-model-router 2.3.0 → 2.4.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/README.md +82 -764
- package/package.json +1 -1
- package/src/skills/__tests__/skill_manager.test.ts +328 -0
- package/assets/benchmark-results-pro.svg +0 -1857
- package/assets/benchmark-results.png +0 -0
- package/assets/complexity-scoring-v2.png +0 -0
- package/assets/complexity-scoring.png +0 -0
- package/assets/cost-comparison-chart.png +0 -0
- package/assets/cost-comparison-pro.svg +0 -2708
- package/assets/cost-comparison-v2.png +0 -0
- package/assets/feature-comparison-v2.png +0 -0
- package/assets/feature-comparison-v3.png +0 -0
- package/assets/feature-matrix-pro.svg +0 -2899
- package/assets/growth-chart-pro.svg +0 -1050
- package/assets/hero-banner.svg +0 -2033
- package/assets/logo-icon.svg +0 -99
- package/assets/provider-health-chart.png +0 -0
- package/assets/provider-health-pro.svg +0 -2710
- package/assets/provider-health-v2.png +0 -0
- package/assets/routing-flow-pro.svg +0 -2238
- package/assets/routing-flow-v2.png +0 -0
- package/assets/routing-flow-v3.png +0 -0
- package/assets/routing-flow.png +0 -0
- package/assets/social-preview-pro.svg +0 -1685
- package/assets/tier-distribution-pro.svg +0 -2110
- package/assets/tier-distribution.png +0 -0
- package/dist/cache/cacheKeyGenerator.d.ts +0 -67
- package/dist/cache/cacheKeyGenerator.d.ts.map +0 -1
- package/dist/cache/cacheKeyGenerator.js +0 -211
- package/dist/cache/cacheKeyGenerator.js.map +0 -1
- package/dist/cost/preCallCostEstimator.d.ts +0 -114
- package/dist/cost/preCallCostEstimator.d.ts.map +0 -1
- package/dist/cost/preCallCostEstimator.js +0 -256
- package/dist/cost/preCallCostEstimator.js.map +0 -1
- package/dist/inference/speculativeDecoding.d.ts +0 -133
- package/dist/inference/speculativeDecoding.d.ts.map +0 -1
- package/dist/inference/speculativeDecoding.js +0 -276
- package/dist/inference/speculativeDecoding.js.map +0 -1
- package/dist/providers/providerHealth.d.ts +0 -117
- package/dist/providers/providerHealth.d.ts.map +0 -1
- package/dist/providers/providerHealth.js +0 -309
- package/dist/providers/providerHealth.js.map +0 -1
- package/dist/routing/difficultyClassifier.d.ts +0 -79
- package/dist/routing/difficultyClassifier.d.ts.map +0 -1
- package/dist/routing/difficultyClassifier.js +0 -329
- package/dist/routing/difficultyClassifier.js.map +0 -1
- package/dist/sdk.d.ts +0 -125
- package/docs/HN_CAMPAIGN.md +0 -785
- package/src/cache/cacheKeyGenerator.ts +0 -242
- package/src/cost/preCallCostEstimator.ts +0 -345
- package/src/inference/speculativeDecoding.ts +0 -373
- package/src/providers/providerHealth.ts +0 -397
- package/src/routing/difficultyClassifier.ts +0 -420
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A3M Router - Difficulty Classifier
|
|
3
|
-
*
|
|
4
|
-
* Classifies queries as simple/medium/complex based on features.
|
|
5
|
-
* Used to route queries to appropriate model tiers.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* const classifier = new DifficultyClassifier();
|
|
9
|
-
* const result = classifier.classify("What is Python?");
|
|
10
|
-
* console.log(result); // { level: 'simple', confidence: 0.85, signals: [...] }
|
|
11
|
-
*
|
|
12
|
-
* const complex = classifier.classify("Implement a red-black tree in Rust with full tests");
|
|
13
|
-
* console.log(complex); // { level: 'complex', confidence: 0.92, signals: [...] }
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { ProviderTier } from '../providers/providerConfig';
|
|
17
|
-
|
|
18
|
-
// ============================================================
|
|
19
|
-
// Types
|
|
20
|
-
// ============================================================
|
|
21
|
-
|
|
22
|
-
export type DifficultyLevel = 'simple' | 'medium' | 'complex';
|
|
23
|
-
|
|
24
|
-
export interface ClassificationResult {
|
|
25
|
-
/** Difficulty level */
|
|
26
|
-
level: DifficultyLevel;
|
|
27
|
-
/** Confidence score 0-1 */
|
|
28
|
-
confidence: number;
|
|
29
|
-
/** Signals that contributed to the classification */
|
|
30
|
-
signals: string[];
|
|
31
|
-
/** Feature scores for each dimension */
|
|
32
|
-
features: {
|
|
33
|
-
length: number; // 0-1, longer = potentially more complex
|
|
34
|
-
keywords: number; // 0-1, presence of complex keywords
|
|
35
|
-
reasoning: number; // 0-1, reasoning requirements
|
|
36
|
-
language: number; // 0-1, language complexity
|
|
37
|
-
code: number; // 0-1, code-related content
|
|
38
|
-
};
|
|
39
|
-
/** Recommended model tier */
|
|
40
|
-
recommendedTier: ProviderTier;
|
|
41
|
-
/** Alternative tiers (in order of preference) */
|
|
42
|
-
alternativeTiers: ProviderTier[];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface ClassifierConfig {
|
|
46
|
-
/** Thresholds for classification */
|
|
47
|
-
thresholds?: {
|
|
48
|
-
simpleMax?: number;
|
|
49
|
-
mediumMax?: number;
|
|
50
|
-
};
|
|
51
|
-
/** Enable keyword-based classification */
|
|
52
|
-
useKeywords?: boolean;
|
|
53
|
-
/** Enable linguistic complexity analysis */
|
|
54
|
-
useLinguistic?: boolean;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const DEFAULT_CONFIG: Required<ClassifierConfig> = {
|
|
58
|
-
thresholds: {
|
|
59
|
-
simpleMax: 0.35,
|
|
60
|
-
mediumMax: 0.65,
|
|
61
|
-
},
|
|
62
|
-
useKeywords: true,
|
|
63
|
-
useLinguistic: true,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// ============================================================
|
|
67
|
-
// Signal patterns
|
|
68
|
-
// ============================================================
|
|
69
|
-
|
|
70
|
-
const COMPLEX_KEYWORDS = [
|
|
71
|
-
// Math/Science
|
|
72
|
-
'calculate', 'compute', 'algorithm', 'mathematical', 'equation',
|
|
73
|
-
'statistical', 'probability', 'derivative', 'integral', 'matrix',
|
|
74
|
-
'vector', 'optimize', 'optimization', 'minimize', 'maximize',
|
|
75
|
-
// Code
|
|
76
|
-
'implement', 'refactor', 'architect', 'design pattern', 'factory',
|
|
77
|
-
'singleton', 'decorator', 'middleware', 'pipeline', 'async',
|
|
78
|
-
'concurrency', 'parallel', 'thread', 'process', 'memory leak',
|
|
79
|
-
'database', 'sql', 'nosql', 'index', 'shard', 'replica',
|
|
80
|
-
'api', 'rest', 'graphql', 'microservice', 'container', 'kubernetes',
|
|
81
|
-
'deploy', 'ci/cd', 'pipeline', 'test', 'mock', 'stub',
|
|
82
|
-
// Reasoning
|
|
83
|
-
'analyze', 'compare', 'contrast', 'evaluate', 'synthesis',
|
|
84
|
-
'implications', 'hypothesis', 'theory', 'principle', 'why does',
|
|
85
|
-
'explain why', 'reasoning', 'logical', 'deduce', 'infer',
|
|
86
|
-
// Academic/Complex
|
|
87
|
-
'research', 'comprehensive', 'detailed', 'thorough', 'extensive',
|
|
88
|
-
'in-depth', 'multi-step', 'hierarchical', 'nested', 'recursive',
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
const SIMPLE_KEYWORDS = [
|
|
92
|
-
'what is', 'who is', 'when did', 'where is', 'simple',
|
|
93
|
-
'basic', 'intro', 'tutorial', 'hello', 'hi ',
|
|
94
|
-
'thanks', 'please', 'help me', 'quick', 'brief',
|
|
95
|
-
'yes', 'no', 'maybe', 'ok', 'sure', 'okay',
|
|
96
|
-
'list of', 'names of', 'definition', 'meaning of',
|
|
97
|
-
];
|
|
98
|
-
|
|
99
|
-
const CODE_INDICATORS = [
|
|
100
|
-
'code', 'function', 'class', 'method', 'variable',
|
|
101
|
-
'const', 'let', 'var', 'return', 'import', 'export',
|
|
102
|
-
'def ', 'fn ', 'pub ', 'struct', 'enum', 'trait',
|
|
103
|
-
'python', 'javascript', 'typescript', 'java', 'rust', 'go',
|
|
104
|
-
'html', 'css', 'sql', 'bash', 'shell', 'script',
|
|
105
|
-
'bug', 'error', 'exception', 'debug', 'stack trace',
|
|
106
|
-
'api', 'endpoint', 'route', 'handler', 'controller',
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
const REASONING_INDICATORS = [
|
|
110
|
-
'why', 'how', 'because', 'therefore', 'thus',
|
|
111
|
-
'reasoning', 'logic', 'deduce', 'infer', 'conclude',
|
|
112
|
-
'implies', 'suggest', 'indicate', 'evidence',
|
|
113
|
-
'analyze', 'investigate', 'examine', 'evaluate',
|
|
114
|
-
'compare', 'differences', 'similar', 'versus',
|
|
115
|
-
'if then', 'hypothesis', 'assumption', 'premise',
|
|
116
|
-
];
|
|
117
|
-
|
|
118
|
-
// ============================================================
|
|
119
|
-
// DifficultyClassifier
|
|
120
|
-
// ============================================================
|
|
121
|
-
|
|
122
|
-
export class DifficultyClassifier {
|
|
123
|
-
private config: Required<ClassifierConfig>;
|
|
124
|
-
private trainingData: Array<{ text: string; level: DifficultyLevel }> = [];
|
|
125
|
-
|
|
126
|
-
constructor(config: ClassifierConfig = {}) {
|
|
127
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Classify a query's difficulty.
|
|
132
|
-
*/
|
|
133
|
-
classify(query: string): ClassificationResult {
|
|
134
|
-
const query_lower = query.toLowerCase();
|
|
135
|
-
const words = query.split(/\s+/);
|
|
136
|
-
|
|
137
|
-
// Calculate feature scores
|
|
138
|
-
const features = {
|
|
139
|
-
length: this.calculateLengthScore(query),
|
|
140
|
-
keywords: this.calculateKeywordScore(query_lower),
|
|
141
|
-
reasoning: this.calculateReasoningScore(query_lower, words),
|
|
142
|
-
language: this.calculateLanguageScore(query_lower, words),
|
|
143
|
-
code: this.calculateCodeScore(query_lower),
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
// Weighted combination
|
|
147
|
-
const rawScore =
|
|
148
|
-
features.length * 0.15 +
|
|
149
|
-
features.keywords * 0.30 +
|
|
150
|
-
features.reasoning * 0.20 +
|
|
151
|
-
features.language * 0.15 +
|
|
152
|
-
features.code * 0.20;
|
|
153
|
-
|
|
154
|
-
// Clamp and determine level
|
|
155
|
-
const score = Math.max(0, Math.min(1, rawScore));
|
|
156
|
-
const level = this.determineLevel(score);
|
|
157
|
-
const confidence = this.calculateConfidence(features, score);
|
|
158
|
-
|
|
159
|
-
// Generate signals list
|
|
160
|
-
const signals = this.generateSignals(query_lower, features);
|
|
161
|
-
|
|
162
|
-
// Determine recommended tier
|
|
163
|
-
const { recommendedTier, alternativeTiers } = this.determineTier(level, features);
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
level,
|
|
167
|
-
confidence: Math.round(confidence * 1000) / 1000,
|
|
168
|
-
signals,
|
|
169
|
-
features: {
|
|
170
|
-
length: Math.round(features.length * 1000) / 1000,
|
|
171
|
-
keywords: Math.round(features.keywords * 1000) / 1000,
|
|
172
|
-
reasoning: Math.round(features.reasoning * 1000) / 1000,
|
|
173
|
-
language: Math.round(features.language * 1000) / 1000,
|
|
174
|
-
code: Math.round(features.code * 1000) / 1000,
|
|
175
|
-
},
|
|
176
|
-
recommendedTier,
|
|
177
|
-
alternativeTiers,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Classify multiple queries.
|
|
183
|
-
*/
|
|
184
|
-
classifyBatch(queries: string[]): ClassificationResult[] {
|
|
185
|
-
return queries.map(q => this.classify(q));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Add training example for future improvements.
|
|
190
|
-
*/
|
|
191
|
-
addTrainingExample(query: string, level: DifficultyLevel): void {
|
|
192
|
-
this.trainingData.push({ text: query, level });
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get distribution of difficulties in a batch.
|
|
197
|
-
*/
|
|
198
|
-
getDistribution(queries: string[]): Record<DifficultyLevel, number> {
|
|
199
|
-
const results = this.classifyBatch(queries);
|
|
200
|
-
const total = results.length;
|
|
201
|
-
|
|
202
|
-
const distribution = { simple: 0, medium: 0, complex: 0 };
|
|
203
|
-
for (const r of results) {
|
|
204
|
-
distribution[r.level]++;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
simple: Math.round((distribution.simple / total) * 1000) / 1000,
|
|
209
|
-
medium: Math.round((distribution.medium / total) * 1000) / 1000,
|
|
210
|
-
complex: Math.round((distribution.complex / total) * 1000) / 1000,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// ---- Feature calculations ----
|
|
215
|
-
|
|
216
|
-
private calculateLengthScore(query: string): number {
|
|
217
|
-
const words = query.split(/\s+/).length;
|
|
218
|
-
const chars = query.length;
|
|
219
|
-
|
|
220
|
-
// Normalize: 1-10 words = simple, 10-50 = medium, 50+ = complex
|
|
221
|
-
const wordScore = Math.min(words / 50, 1.0);
|
|
222
|
-
const charScore = Math.min(chars / 500, 1.0);
|
|
223
|
-
|
|
224
|
-
return (wordScore * 0.6 + charScore * 0.4);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
private calculateKeywordScore(query: string): number {
|
|
228
|
-
if (!this.config.useKeywords) return 0.5;
|
|
229
|
-
|
|
230
|
-
let score = 0.5; // Base score
|
|
231
|
-
|
|
232
|
-
// Check for complex keywords
|
|
233
|
-
for (const kw of COMPLEX_KEYWORDS) {
|
|
234
|
-
if (query.includes(kw)) score += 0.1;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Check for simple keywords
|
|
238
|
-
for (const kw of SIMPLE_KEYWORDS) {
|
|
239
|
-
if (query.includes(kw)) score -= 0.15;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return Math.max(0, Math.min(1, score));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
private calculateReasoningScore(query: string, words: string[]): number {
|
|
246
|
-
let score = 0.2; // Base score
|
|
247
|
-
|
|
248
|
-
// Check reasoning indicators
|
|
249
|
-
for (const indicator of REASONING_INDICATORS) {
|
|
250
|
-
if (query.includes(indicator)) score += 0.12;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// "How" questions often require explanation
|
|
254
|
-
if (query.startsWith('how')) score += 0.1;
|
|
255
|
-
|
|
256
|
-
// Multi-step indicators (first, then, finally, etc.)
|
|
257
|
-
const multiStep = ['first', 'then', 'next', 'finally', 'after', 'before'];
|
|
258
|
-
const hasMultiStep = multiStep.filter(m => query.includes(m)).length;
|
|
259
|
-
score += hasMultiStep * 0.05;
|
|
260
|
-
|
|
261
|
-
// Question length (>3 words after question word = more complex)
|
|
262
|
-
if (words.length > 10) score += 0.1;
|
|
263
|
-
|
|
264
|
-
return Math.max(0, Math.min(1, score));
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
private calculateLanguageScore(query: string, words: string[]): number {
|
|
268
|
-
if (!this.config.useLinguistic) return 0.5;
|
|
269
|
-
|
|
270
|
-
// Simple heuristics for language complexity
|
|
271
|
-
|
|
272
|
-
// Average word length (longer words = more complex)
|
|
273
|
-
const avgWordLen = words.reduce((sum, w) => sum + w.length, 0) / words.length;
|
|
274
|
-
const lenScore = Math.min((avgWordLen - 3) / 4, 1.0); // 3 chars = simple, 7+ = complex
|
|
275
|
-
|
|
276
|
-
// Presence of technical/academic vocabulary
|
|
277
|
-
const technicalWords = [
|
|
278
|
-
'analysis', 'methodology', 'framework', 'paradigm', 'synthesis',
|
|
279
|
-
'theoretical', 'empirical', 'conceptual', 'phenomenon', 'correlation',
|
|
280
|
-
];
|
|
281
|
-
let techCount = 0;
|
|
282
|
-
for (const tw of technicalWords) {
|
|
283
|
-
if (query.includes(tw)) techCount++;
|
|
284
|
-
}
|
|
285
|
-
const techScore = Math.min(techCount * 0.15, 0.4);
|
|
286
|
-
|
|
287
|
-
// Sentence complexity (commas, semicolons)
|
|
288
|
-
const punctCount = (query.match(/[,;:]/g) || []).length;
|
|
289
|
-
const punctScore = Math.min(punctCount * 0.1, 0.3);
|
|
290
|
-
|
|
291
|
-
return Math.max(0, Math.min(1, 0.3 + lenScore * 0.3 + techScore + punctScore));
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
private calculateCodeScore(query: string): number {
|
|
295
|
-
let score = 0.1; // Base score (low probability of code)
|
|
296
|
-
|
|
297
|
-
// Check code indicators
|
|
298
|
-
for (const indicator of CODE_INDICATORS) {
|
|
299
|
-
if (query.includes(indicator)) score += 0.1;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Code block indicators
|
|
303
|
-
if (query.includes('```') || query.includes('`')) score += 0.15;
|
|
304
|
-
|
|
305
|
-
// Programming language mentions
|
|
306
|
-
const langs = ['python', 'javascript', 'java', 'rust', 'go', 'c++', 'typescript', 'ruby'];
|
|
307
|
-
for (const lang of langs) {
|
|
308
|
-
if (query.includes(lang)) score += 0.1;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Code-like patterns (brackets, semicolons in unusual contexts)
|
|
312
|
-
if (/[{}\[\];]/.test(query)) score += 0.1;
|
|
313
|
-
|
|
314
|
-
return Math.max(0, Math.min(1, score));
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// ---- Classification ----
|
|
318
|
-
|
|
319
|
-
private determineLevel(score: number): DifficultyLevel {
|
|
320
|
-
if (score <= this.config.thresholds.simpleMax) {
|
|
321
|
-
return 'simple';
|
|
322
|
-
}
|
|
323
|
-
if (score <= this.config.thresholds.mediumMax) {
|
|
324
|
-
return 'medium';
|
|
325
|
-
}
|
|
326
|
-
return 'complex';
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private calculateConfidence(
|
|
330
|
-
features: ClassificationResult['features'],
|
|
331
|
-
score: number
|
|
332
|
-
): number {
|
|
333
|
-
// Higher agreement between features = higher confidence
|
|
334
|
-
const featureValues = Object.values(features);
|
|
335
|
-
const mean = featureValues.reduce((a, b) => a + b, 0) / featureValues.length;
|
|
336
|
-
const variance = featureValues.reduce((sum, f) => sum + Math.pow(f - mean, 2), 0) / featureValues.length;
|
|
337
|
-
const stdDev = Math.sqrt(variance);
|
|
338
|
-
|
|
339
|
-
// Low variance = high confidence
|
|
340
|
-
const agreement = 1 - Math.min(stdDev * 2, 1);
|
|
341
|
-
|
|
342
|
-
// Distance from 0.5 also matters (extreme scores = more confident)
|
|
343
|
-
const extremity = Math.abs(score - 0.5) * 2;
|
|
344
|
-
|
|
345
|
-
return (agreement * 0.6 + extremity * 0.4);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// ---- Signal generation ----
|
|
349
|
-
|
|
350
|
-
private generateSignals(query: string, features: ClassificationResult['features']): string[] {
|
|
351
|
-
const signals: string[] = [];
|
|
352
|
-
|
|
353
|
-
if (features.length > 0.6) signals.push('long query');
|
|
354
|
-
if (features.length < 0.2) signals.push('short query');
|
|
355
|
-
|
|
356
|
-
if (features.keywords > 0.6) signals.push('complex vocabulary');
|
|
357
|
-
if (features.keywords < 0.3) signals.push('simple vocabulary');
|
|
358
|
-
|
|
359
|
-
if (features.code > 0.5) signals.push('code-related');
|
|
360
|
-
if (features.reasoning > 0.5) signals.push('reasoning required');
|
|
361
|
-
if (features.language > 0.6) signals.push('complex language');
|
|
362
|
-
|
|
363
|
-
// Specific keyword matches
|
|
364
|
-
for (const kw of COMPLEX_KEYWORDS) {
|
|
365
|
-
if (query.includes(kw)) signals.push(`keyword: ${kw.slice(0, 10)}`);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return signals;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// ---- Tier determination ----
|
|
372
|
-
|
|
373
|
-
private determineTier(
|
|
374
|
-
level: DifficultyLevel,
|
|
375
|
-
features: ClassificationResult['features']
|
|
376
|
-
): { recommendedTier: ProviderTier; alternativeTiers: ProviderTier[] } {
|
|
377
|
-
if (level === 'simple') {
|
|
378
|
-
return {
|
|
379
|
-
recommendedTier: 'cheap',
|
|
380
|
-
alternativeTiers: ['free', 'mid'],
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (level === 'medium') {
|
|
385
|
-
// Medium but with high code = needs better model
|
|
386
|
-
if (features.code > 0.5) {
|
|
387
|
-
return {
|
|
388
|
-
recommendedTier: 'mid',
|
|
389
|
-
alternativeTiers: ['premium', 'cheap'],
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
return {
|
|
393
|
-
recommendedTier: 'mid',
|
|
394
|
-
alternativeTiers: ['cheap', 'premium'],
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Complex
|
|
399
|
-
if (features.code > 0.6 || features.reasoning > 0.6) {
|
|
400
|
-
return {
|
|
401
|
-
recommendedTier: 'premium',
|
|
402
|
-
alternativeTiers: ['mid', 'enterprise'],
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
return {
|
|
406
|
-
recommendedTier: 'premium',
|
|
407
|
-
alternativeTiers: ['mid', 'enterprise'],
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// ============================================================
|
|
413
|
-
// Factory
|
|
414
|
-
// ============================================================
|
|
415
|
-
|
|
416
|
-
export function createDifficultyClassifier(
|
|
417
|
-
config?: ClassifierConfig
|
|
418
|
-
): DifficultyClassifier {
|
|
419
|
-
return new DifficultyClassifier(config);
|
|
420
|
-
}
|