@planu/cli 0.28.0 → 0.29.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/dist/config/model-routing-rules.json +98 -0
- package/dist/engine/ai-cost-estimator/core.d.ts.map +1 -1
- package/dist/engine/ai-cost-estimator/core.js +2 -202
- package/dist/engine/ai-cost-estimator/core.js.map +1 -1
- package/dist/engine/ai-cost-estimator/recommender.d.ts +8 -0
- package/dist/engine/ai-cost-estimator/recommender.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/recommender.js +94 -0
- package/dist/engine/ai-cost-estimator/recommender.js.map +1 -0
- package/dist/engine/ai-cost-estimator/spec-loader.d.ts +13 -0
- package/dist/engine/ai-cost-estimator/spec-loader.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/spec-loader.js +102 -0
- package/dist/engine/ai-cost-estimator/spec-loader.js.map +1 -0
- package/dist/engine/code-transforms/transform-engine.d.ts +7 -0
- package/dist/engine/code-transforms/transform-engine.d.ts.map +1 -0
- package/dist/engine/code-transforms/transform-engine.js +67 -0
- package/dist/engine/code-transforms/transform-engine.js.map +1 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.d.ts +6 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.js +92 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.js.map +1 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.d.ts +6 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.js +83 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.js.map +1 -0
- package/dist/engine/code-transforms/typescript/add-types.d.ts +3 -0
- package/dist/engine/code-transforms/typescript/add-types.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/add-types.js +182 -0
- package/dist/engine/code-transforms/typescript/add-types.js.map +1 -0
- package/dist/engine/code-transforms/typescript/ast-utils.d.ts +38 -0
- package/dist/engine/code-transforms/typescript/ast-utils.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/ast-utils.js +90 -0
- package/dist/engine/code-transforms/typescript/ast-utils.js.map +1 -0
- package/dist/engine/code-transforms/typescript/extract-interface.d.ts +6 -0
- package/dist/engine/code-transforms/typescript/extract-interface.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/extract-interface.js +103 -0
- package/dist/engine/code-transforms/typescript/extract-interface.js.map +1 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.d.ts +3 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.js +213 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.js.map +1 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.d.ts +8 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.js +40 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.js.map +1 -0
- package/dist/engine/mermaid/core.d.ts +18 -0
- package/dist/engine/mermaid/core.d.ts.map +1 -0
- package/dist/engine/mermaid/core.js +88 -0
- package/dist/engine/mermaid/core.js.map +1 -0
- package/dist/engine/mermaid/diagram-generators.d.ts +22 -0
- package/dist/engine/mermaid/diagram-generators.d.ts.map +1 -0
- package/dist/engine/mermaid/diagram-generators.js +139 -0
- package/dist/engine/mermaid/diagram-generators.js.map +1 -0
- package/dist/engine/mermaid/helpers.d.ts +8 -0
- package/dist/engine/mermaid/helpers.d.ts.map +1 -0
- package/dist/engine/mermaid/helpers.js +61 -0
- package/dist/engine/mermaid/helpers.js.map +1 -0
- package/dist/engine/mermaid-generator.d.ts +2 -37
- package/dist/engine/mermaid-generator.d.ts.map +1 -1
- package/dist/engine/mermaid-generator.js +4 -276
- package/dist/engine/mermaid-generator.js.map +1 -1
- package/dist/engine/model-router/complexity-analyzer.d.ts +26 -0
- package/dist/engine/model-router/complexity-analyzer.d.ts.map +1 -0
- package/dist/engine/model-router/complexity-analyzer.js +182 -0
- package/dist/engine/model-router/complexity-analyzer.js.map +1 -0
- package/dist/engine/model-router/cost-estimator.d.ts +6 -0
- package/dist/engine/model-router/cost-estimator.d.ts.map +1 -0
- package/dist/engine/model-router/cost-estimator.js +60 -0
- package/dist/engine/model-router/cost-estimator.js.map +1 -0
- package/dist/engine/model-router/historical-learner.d.ts +26 -0
- package/dist/engine/model-router/historical-learner.d.ts.map +1 -0
- package/dist/engine/model-router/historical-learner.js +91 -0
- package/dist/engine/model-router/historical-learner.js.map +1 -0
- package/dist/engine/model-router/rules-engine.d.ts +13 -0
- package/dist/engine/model-router/rules-engine.d.ts.map +1 -0
- package/dist/engine/model-router/rules-engine.js +142 -0
- package/dist/engine/model-router/rules-engine.js.map +1 -0
- package/dist/engine/spec-coverage/criteria-mapper.d.ts +1 -2
- package/dist/engine/spec-coverage/criteria-mapper.d.ts.map +1 -1
- package/dist/engine/spec-coverage/criteria-mapper.js +4 -203
- package/dist/engine/spec-coverage/criteria-mapper.js.map +1 -1
- package/dist/engine/spec-coverage/keyword-extractor.d.ts +10 -0
- package/dist/engine/spec-coverage/keyword-extractor.d.ts.map +1 -0
- package/dist/engine/spec-coverage/keyword-extractor.js +147 -0
- package/dist/engine/spec-coverage/keyword-extractor.js.map +1 -0
- package/dist/engine/spec-coverage/test-matchers.d.ts +9 -0
- package/dist/engine/spec-coverage/test-matchers.d.ts.map +1 -0
- package/dist/engine/spec-coverage/test-matchers.js +59 -0
- package/dist/engine/spec-coverage/test-matchers.js.map +1 -0
- package/dist/engine/spec-templates/catalog-extra.d.ts +1 -1
- package/dist/engine/spec-templates/catalog-extra.d.ts.map +1 -1
- package/dist/engine/spec-templates/catalog-extra.js +8 -363
- package/dist/engine/spec-templates/catalog-extra.js.map +1 -1
- package/dist/engine/spec-templates/catalog.d.ts.map +1 -1
- package/dist/engine/spec-templates/catalog.js +10 -381
- package/dist/engine/spec-templates/catalog.js.map +1 -1
- package/dist/engine/spec-templates/templates-api-ui.d.ts +6 -0
- package/dist/engine/spec-templates/templates-api-ui.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-api-ui.js +188 -0
- package/dist/engine/spec-templates/templates-api-ui.js.map +1 -0
- package/dist/engine/spec-templates/templates-auth-crud.d.ts +6 -0
- package/dist/engine/spec-templates/templates-auth-crud.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-auth-crud.js +198 -0
- package/dist/engine/spec-templates/templates-auth-crud.js.map +1 -0
- package/dist/engine/spec-templates/templates-data-security.d.ts +6 -0
- package/dist/engine/spec-templates/templates-data-security.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-data-security.js +172 -0
- package/dist/engine/spec-templates/templates-data-security.js.map +1 -0
- package/dist/engine/spec-templates/templates-perf-integration.d.ts +6 -0
- package/dist/engine/spec-templates/templates-perf-integration.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-perf-integration.js +199 -0
- package/dist/engine/spec-templates/templates-perf-integration.js.map +1 -0
- package/dist/engine/vector-store/hnsw.d.ts +37 -0
- package/dist/engine/vector-store/hnsw.d.ts.map +1 -0
- package/dist/engine/vector-store/hnsw.js +294 -0
- package/dist/engine/vector-store/hnsw.js.map +1 -0
- package/dist/engine/vector-store/similarity.d.ts +21 -0
- package/dist/engine/vector-store/similarity.d.ts.map +1 -0
- package/dist/engine/vector-store/similarity.js +86 -0
- package/dist/engine/vector-store/similarity.js.map +1 -0
- package/dist/engine/vector-store/tfidf.d.ts +35 -0
- package/dist/engine/vector-store/tfidf.d.ts.map +1 -0
- package/dist/engine/vector-store/tfidf.js +255 -0
- package/dist/engine/vector-store/tfidf.js.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/vector-store/backend-factory.d.ts +9 -0
- package/dist/storage/vector-store/backend-factory.d.ts.map +1 -0
- package/dist/storage/vector-store/backend-factory.js +33 -0
- package/dist/storage/vector-store/backend-factory.js.map +1 -0
- package/dist/storage/vector-store/json-fallback.d.ts +21 -0
- package/dist/storage/vector-store/json-fallback.d.ts.map +1 -0
- package/dist/storage/vector-store/json-fallback.js +85 -0
- package/dist/storage/vector-store/json-fallback.js.map +1 -0
- package/dist/storage/vector-store/migrator.d.ts +10 -0
- package/dist/storage/vector-store/migrator.d.ts.map +1 -0
- package/dist/storage/vector-store/migrator.js +139 -0
- package/dist/storage/vector-store/migrator.js.map +1 -0
- package/dist/storage/vector-store/sqlite-adapter.d.ts +28 -0
- package/dist/storage/vector-store/sqlite-adapter.d.ts.map +1 -0
- package/dist/storage/vector-store/sqlite-adapter.js +142 -0
- package/dist/storage/vector-store/sqlite-adapter.js.map +1 -0
- package/dist/tools/create-spec/constitution-validator.d.ts +4 -0
- package/dist/tools/create-spec/constitution-validator.d.ts.map +1 -0
- package/dist/tools/create-spec/constitution-validator.js +37 -0
- package/dist/tools/create-spec/constitution-validator.js.map +1 -0
- package/dist/tools/create-spec/post-creation.d.ts +11 -0
- package/dist/tools/create-spec/post-creation.d.ts.map +1 -0
- package/dist/tools/create-spec/post-creation.js +48 -0
- package/dist/tools/create-spec/post-creation.js.map +1 -0
- package/dist/tools/create-spec/spec-builder.d.ts +14 -0
- package/dist/tools/create-spec/spec-builder.d.ts.map +1 -0
- package/dist/tools/create-spec/spec-builder.js +131 -0
- package/dist/tools/create-spec/spec-builder.js.map +1 -0
- package/dist/tools/create-spec.d.ts.map +1 -1
- package/dist/tools/create-spec.js +42 -172
- package/dist/tools/create-spec.js.map +1 -1
- package/dist/tools/recommend-model-handler.d.ts +8 -0
- package/dist/tools/recommend-model-handler.d.ts.map +1 -0
- package/dist/tools/recommend-model-handler.js +65 -0
- package/dist/tools/recommend-model-handler.js.map +1 -0
- package/dist/tools/register-model-tools.d.ts +3 -0
- package/dist/tools/register-model-tools.d.ts.map +1 -0
- package/dist/tools/register-model-tools.js +50 -0
- package/dist/tools/register-model-tools.js.map +1 -0
- package/dist/tools/register-search-tools.d.ts +7 -0
- package/dist/tools/register-search-tools.d.ts.map +1 -0
- package/dist/tools/register-search-tools.js +34 -0
- package/dist/tools/register-search-tools.js.map +1 -0
- package/dist/tools/register-transform-tools.d.ts +3 -0
- package/dist/tools/register-transform-tools.d.ts.map +1 -0
- package/dist/tools/register-transform-tools.js +29 -0
- package/dist/tools/register-transform-tools.js.map +1 -0
- package/dist/tools/semantic-search-handler.d.ts +7 -0
- package/dist/tools/semantic-search-handler.d.ts.map +1 -0
- package/dist/tools/semantic-search-handler.js +72 -0
- package/dist/tools/semantic-search-handler.js.map +1 -0
- package/dist/tools/transform-code-handler.d.ts +7 -0
- package/dist/tools/transform-code-handler.d.ts.map +1 -0
- package/dist/tools/transform-code-handler.js +58 -0
- package/dist/tools/transform-code-handler.js.map +1 -0
- package/dist/types/advanced-framework.d.ts +47 -0
- package/dist/types/advanced-framework.d.ts.map +1 -0
- package/dist/types/advanced-framework.js +3 -0
- package/dist/types/advanced-framework.js.map +1 -0
- package/dist/types/code-transforms.d.ts +114 -0
- package/dist/types/code-transforms.d.ts.map +1 -0
- package/dist/types/code-transforms.js +11 -0
- package/dist/types/code-transforms.js.map +1 -0
- package/dist/types/css-framework.d.ts +110 -0
- package/dist/types/css-framework.d.ts.map +1 -0
- package/dist/types/css-framework.js +3 -0
- package/dist/types/css-framework.js.map +1 -0
- package/dist/types/dashboard.d.ts +77 -0
- package/dist/types/dashboard.d.ts.map +1 -0
- package/dist/types/dashboard.js +2 -0
- package/dist/types/dashboard.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/model-routing.d.ts +127 -0
- package/dist/types/model-routing.d.ts.map +1 -0
- package/dist/types/model-routing.js +3 -0
- package/dist/types/model-routing.js.map +1 -0
- package/dist/types/spec/core.d.ts +28 -1
- package/dist/types/spec/core.d.ts.map +1 -1
- package/dist/types/spec/inputs.d.ts +1 -6
- package/dist/types/spec/inputs.d.ts.map +1 -1
- package/dist/types/ui.d.ts +3 -231
- package/dist/types/ui.d.ts.map +1 -1
- package/dist/types/ui.js +7 -1
- package/dist/types/ui.js.map +1 -1
- package/dist/types/vector-store.d.ts +137 -0
- package/dist/types/vector-store.d.ts.map +1 -0
- package/dist/types/vector-store.js +3 -0
- package/dist/types/vector-store.js.map +1 -0
- package/package.json +1 -1
- package/src/config/model-routing-rules.json +98 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// engine/model-router/complexity-analyzer.ts — Extract and normalise complexity signals.
|
|
2
|
+
// SPEC-076 AC-01
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Normalisation ceilings (signals are clamped to 0-1 using these maximums)
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
const MAX_CRITERIA = 40;
|
|
7
|
+
const MAX_FILES = 30;
|
|
8
|
+
const MAX_DEPTH = 6;
|
|
9
|
+
const MAX_CROSS_LAYER = 5;
|
|
10
|
+
const MAX_HOURS = 80;
|
|
11
|
+
/** Clamp a value into [0, 1] by dividing by its ceiling. */
|
|
12
|
+
function norm(value, ceiling) {
|
|
13
|
+
if (ceiling <= 0) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
return Math.min(Math.max(value / ceiling, 0), 1);
|
|
17
|
+
}
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Signal extraction from text
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
const TASK_KEYWORDS = {
|
|
22
|
+
search: ['search', 'find', 'explore', 'list', 'read', 'summarize', 'grep'],
|
|
23
|
+
generation: ['generate', 'create', 'implement', 'write', 'build', 'add', 'scaffold'],
|
|
24
|
+
architecture: ['architect', 'design', 'restructure', 'system', 'plan', 'rfc'],
|
|
25
|
+
debugging: ['debug', 'fix', 'investigate', 'trace', 'diagnose', 'bug', 'error'],
|
|
26
|
+
refactor: ['refactor', 'rename', 'extract', 'simplify', 'clean', 'reorganize', 'move'],
|
|
27
|
+
};
|
|
28
|
+
const SECURITY_KEYWORDS = [
|
|
29
|
+
'security',
|
|
30
|
+
'auth',
|
|
31
|
+
'permission',
|
|
32
|
+
'rbac',
|
|
33
|
+
'encryption',
|
|
34
|
+
'token',
|
|
35
|
+
'jwt',
|
|
36
|
+
'cors',
|
|
37
|
+
'csrf',
|
|
38
|
+
'xss',
|
|
39
|
+
'injection',
|
|
40
|
+
'sanitize',
|
|
41
|
+
'pii',
|
|
42
|
+
'gdpr',
|
|
43
|
+
];
|
|
44
|
+
const DB_KEYWORDS = [
|
|
45
|
+
'database',
|
|
46
|
+
'migration',
|
|
47
|
+
'schema',
|
|
48
|
+
'table',
|
|
49
|
+
'column',
|
|
50
|
+
'index',
|
|
51
|
+
'query',
|
|
52
|
+
'sql',
|
|
53
|
+
'postgres',
|
|
54
|
+
'mysql',
|
|
55
|
+
'mongo',
|
|
56
|
+
'redis',
|
|
57
|
+
'prisma',
|
|
58
|
+
'drizzle',
|
|
59
|
+
];
|
|
60
|
+
/** Infer a TaskType from a text description. */
|
|
61
|
+
function inferTaskType(text) {
|
|
62
|
+
const lower = text.toLowerCase();
|
|
63
|
+
let bestType = 'generation';
|
|
64
|
+
let bestScore = 0;
|
|
65
|
+
for (const [type, keywords] of Object.entries(TASK_KEYWORDS)) {
|
|
66
|
+
const score = keywords.filter((kw) => lower.includes(kw)).length;
|
|
67
|
+
if (score > bestScore) {
|
|
68
|
+
bestScore = score;
|
|
69
|
+
bestType = type;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return bestType;
|
|
73
|
+
}
|
|
74
|
+
/** Count how many keywords from a list appear in a text. */
|
|
75
|
+
function keywordHits(text, keywords) {
|
|
76
|
+
const lower = text.toLowerCase();
|
|
77
|
+
return keywords.filter((kw) => lower.includes(kw)).length;
|
|
78
|
+
}
|
|
79
|
+
/** Count acceptance criteria by looking for AC- or numbered list patterns. */
|
|
80
|
+
function countCriteria(text) {
|
|
81
|
+
const acMatches = text.match(/\bAC-\d+/g);
|
|
82
|
+
if (acMatches) {
|
|
83
|
+
return acMatches.length;
|
|
84
|
+
}
|
|
85
|
+
// Fallback: count markdown list items that look like criteria
|
|
86
|
+
const listItems = text.match(/^[-*]\s+/gm);
|
|
87
|
+
return listItems ? Math.ceil(listItems.length / 2) : 0;
|
|
88
|
+
}
|
|
89
|
+
/** Estimate file count from technical sheet patterns. */
|
|
90
|
+
function countFiles(text) {
|
|
91
|
+
const fileRefs = text.match(/`[^`]+\.(ts|js|json|md|tsx|jsx|py|go|rs)`/g);
|
|
92
|
+
return fileRefs ? fileRefs.length : 0;
|
|
93
|
+
}
|
|
94
|
+
/** Count distinct architectural layers referenced. */
|
|
95
|
+
function countLayers(text) {
|
|
96
|
+
const layers = ['types/', 'engine/', 'tools/', 'storage/', 'config/', 'resources/'];
|
|
97
|
+
return layers.filter((l) => text.includes(l)).length;
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Internal helpers to reduce extractSignals complexity
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
/** Extract boolean and categorical signals from text, applying overrides. */
|
|
103
|
+
function extractQualitativeSignals(text, overrides) {
|
|
104
|
+
return {
|
|
105
|
+
hasNewTypes: overrides?.hasNewTypes ?? text.includes('types/'),
|
|
106
|
+
hasDBChanges: overrides?.hasDBChanges ?? keywordHits(text, DB_KEYWORDS) >= 2,
|
|
107
|
+
hasSecurityImplications: overrides?.hasSecurityImplications ?? keywordHits(text, SECURITY_KEYWORDS) >= 2,
|
|
108
|
+
isReadOnly: overrides?.isReadOnly ?? false,
|
|
109
|
+
taskType: overrides?.taskType ?? inferTaskType(text),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** Extract raw signal values from text, applying overrides. */
|
|
113
|
+
function extractRawSignals(text, overrides) {
|
|
114
|
+
const criteriaCount = overrides?.criteriaCount ?? countCriteria(text);
|
|
115
|
+
const fileCount = overrides?.fileCount ?? countFiles(text);
|
|
116
|
+
const crossLayerCount = overrides?.crossLayerCount ?? countLayers(text);
|
|
117
|
+
const dependencyDepth = overrides?.dependencyDepth ?? Math.min(crossLayerCount, MAX_DEPTH);
|
|
118
|
+
const estimatedHours = overrides?.estimatedHours ?? criteriaCount * 0.5;
|
|
119
|
+
const qualitative = extractQualitativeSignals(text, overrides);
|
|
120
|
+
return {
|
|
121
|
+
criteriaCount,
|
|
122
|
+
fileCount,
|
|
123
|
+
dependencyDepth,
|
|
124
|
+
crossLayerCount,
|
|
125
|
+
estimatedHours,
|
|
126
|
+
...qualitative,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Public API
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
/**
|
|
133
|
+
* Extract complexity signals from a text description (spec content or task description).
|
|
134
|
+
* Missing signals are filled with sensible defaults.
|
|
135
|
+
*/
|
|
136
|
+
export function extractSignals(text, overrides) {
|
|
137
|
+
const raw = extractRawSignals(text, overrides);
|
|
138
|
+
const normalised = normaliseSignals(raw);
|
|
139
|
+
const overallScore = computeOverallScore(normalised);
|
|
140
|
+
return { ...raw, overallScore };
|
|
141
|
+
}
|
|
142
|
+
/** Build signals entirely from an explicit partial object (no text analysis). */
|
|
143
|
+
export function buildSignalsFromPartial(partial) {
|
|
144
|
+
return extractSignals('', partial);
|
|
145
|
+
}
|
|
146
|
+
/** Normalise raw numeric signals to the 0-1 range. */
|
|
147
|
+
export function normaliseSignals(raw) {
|
|
148
|
+
return {
|
|
149
|
+
criteriaCount: norm(raw.criteriaCount, MAX_CRITERIA),
|
|
150
|
+
fileCount: norm(raw.fileCount, MAX_FILES),
|
|
151
|
+
dependencyDepth: norm(raw.dependencyDepth, MAX_DEPTH),
|
|
152
|
+
crossLayerCount: norm(raw.crossLayerCount, MAX_CROSS_LAYER),
|
|
153
|
+
estimatedHours: norm(raw.estimatedHours, MAX_HOURS),
|
|
154
|
+
hasNewTypes: raw.hasNewTypes ? 1 : 0,
|
|
155
|
+
hasDBChanges: raw.hasDBChanges ? 1 : 0,
|
|
156
|
+
hasSecurityImplications: raw.hasSecurityImplications ? 1 : 0,
|
|
157
|
+
isReadOnly: raw.isReadOnly ? 1 : 0,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Compute a weighted overall complexity score (0-1) from normalised signals.
|
|
162
|
+
* Higher = more complex.
|
|
163
|
+
*/
|
|
164
|
+
export function computeOverallScore(n) {
|
|
165
|
+
const weights = {
|
|
166
|
+
criteriaCount: 0.25,
|
|
167
|
+
fileCount: 0.15,
|
|
168
|
+
dependencyDepth: 0.1,
|
|
169
|
+
crossLayerCount: 0.15,
|
|
170
|
+
estimatedHours: 0.1,
|
|
171
|
+
hasNewTypes: 0.05,
|
|
172
|
+
hasDBChanges: 0.05,
|
|
173
|
+
hasSecurityImplications: 0.1,
|
|
174
|
+
isReadOnly: -0.05, // read-only reduces complexity
|
|
175
|
+
};
|
|
176
|
+
let score = 0;
|
|
177
|
+
for (const [key, weight] of Object.entries(weights)) {
|
|
178
|
+
score += n[key] * weight;
|
|
179
|
+
}
|
|
180
|
+
return Math.min(Math.max(score, 0), 1);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=complexity-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complexity-analyzer.js","sourceRoot":"","sources":["../../../src/engine/model-router/complexity-analyzer.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,iBAAiB;AASjB,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAE9E,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,4DAA4D;AAC5D,SAAS,IAAI,CAAC,KAAa,EAAE,OAAe;IAC1C,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACjB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,8BAA8B;AAC9B,8EAA8E;AAE9E,MAAM,aAAa,GAA+B;IAChD,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC;IAC1E,UAAU,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC;IACpF,YAAY,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC;IAC7E,SAAS,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC;IAC/E,QAAQ,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC;CACvF,CAAC;AAEF,MAAM,iBAAiB,GAAG;IACxB,UAAU;IACV,MAAM;IACN,YAAY;IACZ,MAAM;IACN,YAAY;IACZ,OAAO;IACP,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,WAAW;IACX,UAAU;IACV,KAAK;IACL,MAAM;CACP,CAAC;AAEF,MAAM,WAAW,GAAG;IAClB,UAAU;IACV,WAAW;IACX,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,OAAO;IACP,OAAO;IACP,KAAK;IACL,UAAU;IACV,OAAO;IACP,OAAO;IACP,OAAO;IACP,QAAQ;IACR,SAAS;CACV,CAAC;AAEF,gDAAgD;AAChD,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,QAAQ,GAAa,YAAY,CAAC;IACtC,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAA2B,EAAE,CAAC;QACvF,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QACjE,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;YACtB,SAAS,GAAG,KAAK,CAAC;YAClB,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4DAA4D;AAC5D,SAAS,WAAW,CAAC,IAAY,EAAE,QAAkB;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AAC5D,CAAC;AAED,8EAA8E;AAC9E,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,SAAS,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED,8DAA8D;IAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3C,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,yDAAyD;AACzD,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,sDAAsD;AACtD,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IACpF,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AACvD,CAAC;AAED,8EAA8E;AAC9E,uDAAuD;AACvD,8EAA8E;AAE9E,6EAA6E;AAC7E,SAAS,yBAAyB,CAChC,IAAY,EACZ,SAA0B;IAK1B,OAAO;QACL,WAAW,EAAE,SAAS,EAAE,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC9D,YAAY,EAAE,SAAS,EAAE,YAAY,IAAI,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;QAC5E,uBAAuB,EACrB,SAAS,EAAE,uBAAuB,IAAI,WAAW,CAAC,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC;QACjF,UAAU,EAAE,SAAS,EAAE,UAAU,IAAI,KAAK;QAC1C,QAAQ,EAAE,SAAS,EAAE,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC;KACrD,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,SAAS,iBAAiB,CACxB,IAAY,EACZ,SAA0B;IAE1B,MAAM,aAAa,GAAG,SAAS,EAAE,aAAa,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,SAAS,EAAE,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,eAAe,GAAG,SAAS,EAAE,eAAe,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,eAAe,GAAG,SAAS,EAAE,eAAe,IAAI,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAC3F,MAAM,cAAc,GAAG,SAAS,EAAE,cAAc,IAAI,aAAa,GAAG,GAAG,CAAC;IACxE,MAAM,WAAW,GAAG,yBAAyB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAE/D,OAAO;QACL,aAAa;QACb,SAAS;QACT,eAAe;QACf,eAAe;QACf,cAAc;QACd,GAAG,WAAW;KACf,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,SAA0B;IACrE,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAE/C,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAErD,OAAO,EAAE,GAAG,GAAG,EAAE,YAAY,EAAE,CAAC;AAClC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,uBAAuB,CAAC,OAAuB;IAC7D,OAAO,cAAc,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAC,GAUhC;IACC,OAAO;QACL,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,YAAY,CAAC;QACpD,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC;QACzC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,CAAC;QACrD,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,eAAe,CAAC;QAC3D,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,SAAS,CAAC;QACnD,WAAW,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,uBAAuB,EAAE,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACnC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAoB;IACtD,MAAM,OAAO,GAA4C;QACvD,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,IAAI;QACf,eAAe,EAAE,GAAG;QACpB,eAAe,EAAE,IAAI;QACrB,cAAc,EAAE,GAAG;QACnB,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,IAAI;QAClB,uBAAuB,EAAE,GAAG;QAC5B,UAAU,EAAE,CAAC,IAAI,EAAE,+BAA+B;KACnD,CAAC;IAEF,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,KAAK,IAAI,CAAC,CAAC,GAA8B,CAAC,GAAG,MAAM,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ComplexitySignals, CostEstimate, ModelTier } from '../../types/index.js';
|
|
2
|
+
/** Calculate the cost estimate for a given model tier and complexity signals. */
|
|
3
|
+
export declare function calculateCost(tier: ModelTier, signals: ComplexitySignals): CostEstimate;
|
|
4
|
+
/** Calculate costs for all three tiers so the user can compare. */
|
|
5
|
+
export declare function calculateAllTierCosts(signals: ComplexitySignals): Record<ModelTier, CostEstimate>;
|
|
6
|
+
//# sourceMappingURL=cost-estimator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-estimator.d.ts","sourceRoot":"","sources":["../../../src/engine/model-router/cost-estimator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACZ,SAAS,EAEV,MAAM,sBAAsB,CAAC;AAkD9B,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,YAAY,CAYvF;AAED,mEAAmE;AACnE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAMjG"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// engine/model-router/cost-estimator.ts — Token and USD cost estimation per model tier.
|
|
2
|
+
// SPEC-076 AC-04
|
|
3
|
+
import pricingJson from '../../config/ai-model-pricing.json' with { type: 'json' };
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Pricing lookup
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const MODEL_IDS = {
|
|
8
|
+
haiku: 'claude-haiku-3-5',
|
|
9
|
+
sonnet: 'claude-sonnet-4',
|
|
10
|
+
opus: 'claude-opus-4',
|
|
11
|
+
};
|
|
12
|
+
function getPricing(tier) {
|
|
13
|
+
const modelId = MODEL_IDS[tier];
|
|
14
|
+
const entry = pricingJson.models.find((m) => m.id === modelId);
|
|
15
|
+
/* c8 ignore next 3 */
|
|
16
|
+
if (!entry) {
|
|
17
|
+
return { id: modelId, inputPricePerMillion: 3, outputPricePerMillion: 15 };
|
|
18
|
+
}
|
|
19
|
+
return entry;
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Token estimation heuristic
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/** Base token estimates per complexity range (overall score brackets). */
|
|
25
|
+
function estimateTokens(signals) {
|
|
26
|
+
const score = signals.overallScore;
|
|
27
|
+
// Read-only tasks consume less output
|
|
28
|
+
const outputMultiplier = signals.isReadOnly ? 0.3 : 1;
|
|
29
|
+
if (score < 0.3) {
|
|
30
|
+
return { input: 2_000, output: Math.round(1_000 * outputMultiplier) };
|
|
31
|
+
}
|
|
32
|
+
if (score < 0.6) {
|
|
33
|
+
return { input: 8_000, output: Math.round(5_000 * outputMultiplier) };
|
|
34
|
+
}
|
|
35
|
+
return { input: 20_000, output: Math.round(15_000 * outputMultiplier) };
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Public API
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/** Calculate the cost estimate for a given model tier and complexity signals. */
|
|
41
|
+
export function calculateCost(tier, signals) {
|
|
42
|
+
const pricing = getPricing(tier);
|
|
43
|
+
const tokens = estimateTokens(signals);
|
|
44
|
+
const inputCost = (tokens.input / 1_000_000) * pricing.inputPricePerMillion;
|
|
45
|
+
const outputCost = (tokens.output / 1_000_000) * pricing.outputPricePerMillion;
|
|
46
|
+
return {
|
|
47
|
+
estimatedTokens: tokens.input + tokens.output,
|
|
48
|
+
estimatedUSD: Math.round((inputCost + outputCost) * 10_000) / 10_000,
|
|
49
|
+
breakdown: { input: tokens.input, output: tokens.output },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Calculate costs for all three tiers so the user can compare. */
|
|
53
|
+
export function calculateAllTierCosts(signals) {
|
|
54
|
+
return {
|
|
55
|
+
haiku: calculateCost('haiku', signals),
|
|
56
|
+
sonnet: calculateCost('sonnet', signals),
|
|
57
|
+
opus: calculateCost('opus', signals),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=cost-estimator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-estimator.js","sourceRoot":"","sources":["../../../src/engine/model-router/cost-estimator.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,iBAAiB;AASjB,OAAO,WAAW,MAAM,oCAAoC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEnF,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,SAAS,GAA8B;IAC3C,KAAK,EAAE,kBAAkB;IACzB,MAAM,EAAE,iBAAiB;IACzB,IAAI,EAAE,eAAe;CACtB,CAAC;AAEF,SAAS,UAAU,CAAC,IAAe;IACjC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAI,WAAW,CAAC,MAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IAEnF,sBAAsB;IACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,EAAE,qBAAqB,EAAE,EAAE,EAAE,CAAC;IAC7E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E,0EAA0E;AAC1E,SAAS,cAAc,CAAC,OAA0B;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC;IAEnC,sCAAsC;IACtC,MAAM,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtD,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QAChB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,gBAAgB,CAAC,EAAE,CAAC;IACxE,CAAC;IACD,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QAChB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,gBAAgB,CAAC,EAAE,CAAC;IACxE,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,gBAAgB,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,IAAe,EAAE,OAA0B;IACvE,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAEvC,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAC5E,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAE/E,OAAO;QACL,eAAe,EAAE,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM;QAC7C,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM;QACpE,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;KAC1D,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,qBAAqB,CAAC,OAA0B;IAC9D,OAAO;QACL,KAAK,EAAE,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC;QACtC,MAAM,EAAE,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC;QACxC,IAAI,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC;KACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HistoricalAdjustment, ModelOutcome, ModelTier } from '../../types/index.js';
|
|
2
|
+
/** Record a model outcome for future learning. */
|
|
3
|
+
export declare function recordOutcome(outcome: ModelOutcome): void;
|
|
4
|
+
/** Get all recorded outcomes (for testing / export). */
|
|
5
|
+
export declare function getOutcomes(): readonly ModelOutcome[];
|
|
6
|
+
/** Clear all outcomes (for testing). */
|
|
7
|
+
export declare function clearOutcomes(): void;
|
|
8
|
+
/** Load outcomes from an external source (e.g. JSON file). */
|
|
9
|
+
export declare function loadOutcomes(loaded: ModelOutcome[]): void;
|
|
10
|
+
/**
|
|
11
|
+
* Find outcomes whose taskSignature matches the given signature.
|
|
12
|
+
* Uses exact match for now; vector similarity can be added later (SPEC-075).
|
|
13
|
+
*/
|
|
14
|
+
export declare function findSimilarOutcomes(taskSignature: string): ModelOutcome[];
|
|
15
|
+
/**
|
|
16
|
+
* Compute a task signature from a description or spec ID.
|
|
17
|
+
* For now this is a simple normalisation; in the future it can use embeddings.
|
|
18
|
+
*/
|
|
19
|
+
export declare function computeTaskSignature(text: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Given the rules engine recommendation and a task signature,
|
|
22
|
+
* adjust the tier based on historical outcomes.
|
|
23
|
+
* Maximum adjustment: plus or minus 1 tier.
|
|
24
|
+
*/
|
|
25
|
+
export declare function adjustFromHistory(recommendedModel: ModelTier, taskSignature: string): HistoricalAdjustment;
|
|
26
|
+
//# sourceMappingURL=historical-learner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"historical-learner.d.ts","sourceRoot":"","sources":["../../../src/engine/model-router/historical-learner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAuB1F,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEzD;AAED,wDAAwD;AACxD,wBAAgB,WAAW,IAAI,SAAS,YAAY,EAAE,CAErD;AAED,wCAAwC;AACxC,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,CAEzD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,YAAY,EAAE,CAEzE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,gBAAgB,EAAE,SAAS,EAC3B,aAAa,EAAE,MAAM,GACpB,oBAAoB,CA6CtB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// engine/model-router/historical-learner.ts — Learn from past model outcomes.
|
|
2
|
+
// SPEC-076 AC-03
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// In-memory outcome store (JSON persistence is left to the storage layer)
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
let outcomes = [];
|
|
7
|
+
const TIER_ORDER = ['haiku', 'sonnet', 'opus'];
|
|
8
|
+
function tierIndex(tier) {
|
|
9
|
+
return TIER_ORDER.indexOf(tier);
|
|
10
|
+
}
|
|
11
|
+
function tierByIndex(index) {
|
|
12
|
+
const clamped = Math.max(0, Math.min(index, TIER_ORDER.length - 1));
|
|
13
|
+
return TIER_ORDER[clamped] ?? 'sonnet';
|
|
14
|
+
}
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Public API
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/** Record a model outcome for future learning. */
|
|
19
|
+
export function recordOutcome(outcome) {
|
|
20
|
+
outcomes.push(outcome);
|
|
21
|
+
}
|
|
22
|
+
/** Get all recorded outcomes (for testing / export). */
|
|
23
|
+
export function getOutcomes() {
|
|
24
|
+
return outcomes;
|
|
25
|
+
}
|
|
26
|
+
/** Clear all outcomes (for testing). */
|
|
27
|
+
export function clearOutcomes() {
|
|
28
|
+
outcomes = [];
|
|
29
|
+
}
|
|
30
|
+
/** Load outcomes from an external source (e.g. JSON file). */
|
|
31
|
+
export function loadOutcomes(loaded) {
|
|
32
|
+
outcomes = [...loaded];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Find outcomes whose taskSignature matches the given signature.
|
|
36
|
+
* Uses exact match for now; vector similarity can be added later (SPEC-075).
|
|
37
|
+
*/
|
|
38
|
+
export function findSimilarOutcomes(taskSignature) {
|
|
39
|
+
return outcomes.filter((o) => o.taskSignature === taskSignature);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Compute a task signature from a description or spec ID.
|
|
43
|
+
* For now this is a simple normalisation; in the future it can use embeddings.
|
|
44
|
+
*/
|
|
45
|
+
export function computeTaskSignature(text) {
|
|
46
|
+
return text.trim().toLowerCase().replace(/\s+/g, '-').slice(0, 80);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Given the rules engine recommendation and a task signature,
|
|
50
|
+
* adjust the tier based on historical outcomes.
|
|
51
|
+
* Maximum adjustment: plus or minus 1 tier.
|
|
52
|
+
*/
|
|
53
|
+
export function adjustFromHistory(recommendedModel, taskSignature) {
|
|
54
|
+
const similar = findSimilarOutcomes(taskSignature);
|
|
55
|
+
if (similar.length === 0) {
|
|
56
|
+
return {
|
|
57
|
+
adjustedModel: recommendedModel,
|
|
58
|
+
reason: 'No historical data available.',
|
|
59
|
+
adjusted: false,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const successCount = similar.filter((o) => o.success).length;
|
|
63
|
+
const failCount = similar.length - successCount;
|
|
64
|
+
const successRate = successCount / similar.length;
|
|
65
|
+
// Check if a lighter model was consistently successful
|
|
66
|
+
const lighterSuccesses = similar.filter((o) => o.success && tierIndex(o.modelUsed) < tierIndex(recommendedModel));
|
|
67
|
+
if (lighterSuccesses.length >= 2 && successRate > 0.8) {
|
|
68
|
+
const lighterTier = tierByIndex(tierIndex(recommendedModel) - 1);
|
|
69
|
+
return {
|
|
70
|
+
adjustedModel: lighterTier,
|
|
71
|
+
reason: `Historical data shows a lighter model (${lighterTier}) succeeded ${String(lighterSuccesses.length)} times for similar tasks.`,
|
|
72
|
+
adjusted: true,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Check if the recommended model or lighter models frequently failed
|
|
76
|
+
const recFailures = similar.filter((o) => !o.success && tierIndex(o.modelUsed) <= tierIndex(recommendedModel));
|
|
77
|
+
if (recFailures.length >= 2 && failCount > successCount) {
|
|
78
|
+
const heavierTier = tierByIndex(tierIndex(recommendedModel) + 1);
|
|
79
|
+
return {
|
|
80
|
+
adjustedModel: heavierTier,
|
|
81
|
+
reason: `Historical data shows ${String(recFailures.length)} failures at tier ${recommendedModel} or below — upgrading to ${heavierTier}.`,
|
|
82
|
+
adjusted: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
adjustedModel: recommendedModel,
|
|
87
|
+
reason: 'Historical data supports the current recommendation.',
|
|
88
|
+
adjusted: false,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=historical-learner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"historical-learner.js","sourceRoot":"","sources":["../../../src/engine/model-router/historical-learner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComplexitySignals, RoutingRule, RuleCondition, RuleMatch, RulesEngineResult } from '../../types/index.js';
|
|
2
|
+
/** Evaluate a single condition against a signal value. */
|
|
3
|
+
export declare function evaluateCondition(signals: ComplexitySignals, condition: RuleCondition): boolean;
|
|
4
|
+
/** Evaluate a single rule against the given signals. */
|
|
5
|
+
export declare function evaluateRule(signals: ComplexitySignals, rule: RoutingRule): RuleMatch;
|
|
6
|
+
/** Load rules from the built-in config. Custom rules support is left as a hook. */
|
|
7
|
+
export declare function loadRules(customRules?: RoutingRule[]): RoutingRule[];
|
|
8
|
+
/**
|
|
9
|
+
* Evaluate all rules against the given signals and return the best model
|
|
10
|
+
* recommendation with alternatives.
|
|
11
|
+
*/
|
|
12
|
+
export declare function evaluateRules(signals: ComplexitySignals, customRules?: RoutingRule[]): RulesEngineResult;
|
|
13
|
+
//# sourceMappingURL=rules-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules-engine.d.ts","sourceRoot":"","sources":["../../../src/engine/model-router/rules-engine.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EAEjB,WAAW,EAEX,aAAa,EACb,SAAS,EACT,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAyB9B,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,CA6B/F;AAMD,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,CAWrF;AAED,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAQpE;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,EAAE,GAC1B,iBAAiB,CAmDnB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// engine/model-router/rules-engine.ts — Deterministic rules evaluation for model routing.
|
|
2
|
+
// SPEC-076 AC-02
|
|
3
|
+
import defaultRulesJson from '../../config/model-routing-rules.json' with { type: 'json' };
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Model tier ordering (for tie-breaking: higher index = more capable)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const TIER_ORDER = ['haiku', 'sonnet', 'opus'];
|
|
8
|
+
function tierIndex(tier) {
|
|
9
|
+
return TIER_ORDER.indexOf(tier);
|
|
10
|
+
}
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Condition evaluation
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
function getSignalValue(signals, signal) {
|
|
15
|
+
return signals[signal];
|
|
16
|
+
}
|
|
17
|
+
/** Evaluate a single condition against a signal value. */
|
|
18
|
+
export function evaluateCondition(signals, condition) {
|
|
19
|
+
const raw = getSignalValue(signals, condition.signal);
|
|
20
|
+
if (raw === undefined) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const { operator, value } = condition;
|
|
24
|
+
switch (operator) {
|
|
25
|
+
case 'eq':
|
|
26
|
+
return raw === value;
|
|
27
|
+
case 'gt':
|
|
28
|
+
return typeof raw === 'number' && typeof value === 'number' && raw > value;
|
|
29
|
+
case 'gte':
|
|
30
|
+
return typeof raw === 'number' && typeof value === 'number' && raw >= value;
|
|
31
|
+
case 'lt':
|
|
32
|
+
return typeof raw === 'number' && typeof value === 'number' && raw < value;
|
|
33
|
+
case 'lte':
|
|
34
|
+
return typeof raw === 'number' && typeof value === 'number' && raw <= value;
|
|
35
|
+
case 'between': {
|
|
36
|
+
if (typeof raw !== 'number' || !Array.isArray(value)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const [lo, hi] = value;
|
|
40
|
+
return raw >= lo && raw <= hi;
|
|
41
|
+
}
|
|
42
|
+
default:
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Rule evaluation
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** Evaluate a single rule against the given signals. */
|
|
50
|
+
export function evaluateRule(signals, rule) {
|
|
51
|
+
// An empty conditions list means "always match" (catch-all)
|
|
52
|
+
if (rule.conditions.length === 0) {
|
|
53
|
+
return { rule, matchRatio: 1, confidence: rule.weight };
|
|
54
|
+
}
|
|
55
|
+
const matches = rule.conditions.filter((c) => evaluateCondition(signals, c));
|
|
56
|
+
const matchRatio = matches.length / rule.conditions.length;
|
|
57
|
+
const confidence = matchRatio * rule.weight;
|
|
58
|
+
return { rule, matchRatio, confidence };
|
|
59
|
+
}
|
|
60
|
+
/** Load rules from the built-in config. Custom rules support is left as a hook. */
|
|
61
|
+
export function loadRules(customRules) {
|
|
62
|
+
const config = defaultRulesJson;
|
|
63
|
+
const base = config.rules;
|
|
64
|
+
if (!customRules || customRules.length === 0) {
|
|
65
|
+
return base;
|
|
66
|
+
}
|
|
67
|
+
// Custom rules have higher priority: prepend them
|
|
68
|
+
return [...customRules, ...base];
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Main evaluation
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* Evaluate all rules against the given signals and return the best model
|
|
75
|
+
* recommendation with alternatives.
|
|
76
|
+
*/
|
|
77
|
+
export function evaluateRules(signals, customRules) {
|
|
78
|
+
const rules = loadRules(customRules);
|
|
79
|
+
const matches = rules.map((r) => evaluateRule(signals, r));
|
|
80
|
+
// Only keep matches where at least all conditions matched (matchRatio === 1)
|
|
81
|
+
// unless no rule fully matches, then relax to best partial match
|
|
82
|
+
let fullMatches = matches.filter((m) => m.matchRatio === 1 && m.confidence > 0);
|
|
83
|
+
if (fullMatches.length === 0) {
|
|
84
|
+
fullMatches = matches.filter((m) => m.confidence > 0);
|
|
85
|
+
}
|
|
86
|
+
// Sort by confidence desc, then by tier capability desc (tie-break: more capable wins)
|
|
87
|
+
fullMatches.sort((a, b) => {
|
|
88
|
+
const confDiff = b.confidence - a.confidence;
|
|
89
|
+
if (Math.abs(confDiff) > 0.001) {
|
|
90
|
+
return confDiff;
|
|
91
|
+
}
|
|
92
|
+
return tierIndex(b.rule.result) - tierIndex(a.rule.result);
|
|
93
|
+
});
|
|
94
|
+
const best = fullMatches[0];
|
|
95
|
+
/* c8 ignore next 5 */
|
|
96
|
+
if (!best) {
|
|
97
|
+
// Absolute fallback — should never happen because of the catch-all sonnet rule
|
|
98
|
+
return {
|
|
99
|
+
model: 'sonnet',
|
|
100
|
+
confidence: 0.5,
|
|
101
|
+
reasoning: ['Default: sonnet (no rule matched)'],
|
|
102
|
+
alternatives: [],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const reasoning = buildReasoning(best, signals);
|
|
106
|
+
// Collect alternatives (different model tiers from other matches)
|
|
107
|
+
const seen = new Set([best.rule.result]);
|
|
108
|
+
const alternatives = [];
|
|
109
|
+
for (const m of fullMatches) {
|
|
110
|
+
if (!seen.has(m.rule.result)) {
|
|
111
|
+
seen.add(m.rule.result);
|
|
112
|
+
alternatives.push({ model: m.rule.result, confidence: m.confidence });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
model: best.rule.result,
|
|
117
|
+
confidence: best.confidence,
|
|
118
|
+
reasoning,
|
|
119
|
+
alternatives,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Reasoning builder
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
function buildReasoning(match, signals) {
|
|
126
|
+
const reasons = [];
|
|
127
|
+
reasons.push(`Rule "${match.rule.name}" matched with confidence ${match.confidence.toFixed(2)}.`);
|
|
128
|
+
if (signals.criteriaCount > 15) {
|
|
129
|
+
reasons.push(`High criteria count (${String(signals.criteriaCount)}) suggests a complex task.`);
|
|
130
|
+
}
|
|
131
|
+
if (signals.crossLayerCount > 2) {
|
|
132
|
+
reasons.push(`Cross-layer changes (${String(signals.crossLayerCount)} layers) increase coordination complexity.`);
|
|
133
|
+
}
|
|
134
|
+
if (signals.isReadOnly) {
|
|
135
|
+
reasons.push('Read-only task — a lighter model can handle this efficiently.');
|
|
136
|
+
}
|
|
137
|
+
if (signals.hasSecurityImplications) {
|
|
138
|
+
reasons.push('Security implications detected — a more capable model is recommended.');
|
|
139
|
+
}
|
|
140
|
+
return reasons;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=rules-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules-engine.js","sourceRoot":"","sources":["../../../src/engine/model-router/rules-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CriterionCoverage, CoverageTestFile, RawCriterion } from '../../types/index.js';
|
|
2
|
+
export { extractKeywords } from './keyword-extractor.js';
|
|
2
3
|
/**
|
|
3
4
|
* Parses HU.md content and returns a list of acceptance criteria with their
|
|
4
5
|
* identifiers and descriptions. Supports flexible AC formats:
|
|
@@ -13,6 +14,4 @@ export declare function extractCriteria(huContent: string): readonly RawCriterio
|
|
|
13
14
|
* defined in SPEC-068. Returns the full CriterionCoverage for that criterion.
|
|
14
15
|
*/
|
|
15
16
|
export declare function mapTestsToCriterion(criterion: RawCriterion, testFiles: readonly CoverageTestFile[], testContents: ReadonlyMap<string, string>, specId: string): CriterionCoverage;
|
|
16
|
-
/** Extracts 3–5 meaningful keywords from an AC description text. */
|
|
17
|
-
export declare function extractKeywords(text: string): readonly string[];
|
|
18
17
|
//# sourceMappingURL=criteria-mapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"criteria-mapper.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-coverage/criteria-mapper.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EAIjB,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"criteria-mapper.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-coverage/criteria-mapper.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EAIjB,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAMzD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,YAAY,EAAE,CAmC1E;AAMD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,SAAS,gBAAgB,EAAE,EACtC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,MAAM,EAAE,MAAM,GACb,iBAAiB,CAsCnB"}
|