agentic-qe 3.8.10 → 3.8.12
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/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +40 -0
- package/dist/cli/bundle.js +1345 -1003
- package/dist/cli/command-registry.js +5 -1
- package/dist/cli/commands/pipeline.d.ts +16 -0
- package/dist/cli/commands/pipeline.js +314 -0
- package/dist/cli/commands/ruvector-commands.js +17 -0
- package/dist/cli/commands/token-usage.js +24 -1
- package/dist/cli/handlers/heartbeat-handler.d.ts +26 -0
- package/dist/cli/handlers/heartbeat-handler.js +382 -0
- package/dist/cli/handlers/index.d.ts +2 -0
- package/dist/cli/handlers/index.js +2 -0
- package/dist/cli/handlers/routing-handler.d.ts +22 -0
- package/dist/cli/handlers/routing-handler.js +227 -0
- package/dist/cli/index.js +2 -0
- package/dist/coordination/deterministic-actions.d.ts +36 -0
- package/dist/coordination/deterministic-actions.js +257 -0
- package/dist/coordination/workflow-orchestrator.d.ts +18 -1
- package/dist/coordination/workflow-orchestrator.js +113 -3
- package/dist/coordination/workflow-types.d.ts +19 -1
- package/dist/coordination/workflow-types.js +3 -0
- package/dist/coordination/yaml-pipeline-loader.d.ts +1 -0
- package/dist/coordination/yaml-pipeline-loader.js +34 -0
- package/dist/domains/code-intelligence/coordinator-gnn.d.ts +21 -0
- package/dist/domains/code-intelligence/coordinator-gnn.js +102 -0
- package/dist/domains/contract-testing/coordinator.js +13 -0
- package/dist/domains/coverage-analysis/coordinator.js +5 -0
- package/dist/domains/defect-intelligence/coordinator.d.ts +1 -0
- package/dist/domains/defect-intelligence/coordinator.js +43 -0
- package/dist/domains/quality-assessment/coordinator.js +26 -0
- package/dist/domains/test-generation/coordinator.js +14 -0
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts +11 -0
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js +44 -1
- package/dist/integrations/rl-suite/algorithms/eprop.d.ts +79 -0
- package/dist/integrations/rl-suite/algorithms/eprop.js +284 -0
- package/dist/integrations/rl-suite/algorithms/index.d.ts +2 -1
- package/dist/integrations/rl-suite/algorithms/index.js +2 -1
- package/dist/integrations/rl-suite/index.d.ts +2 -2
- package/dist/integrations/rl-suite/index.js +2 -2
- package/dist/integrations/rl-suite/interfaces.d.ts +3 -3
- package/dist/integrations/rl-suite/interfaces.js +1 -1
- package/dist/integrations/rl-suite/orchestrator.d.ts +2 -2
- package/dist/integrations/rl-suite/orchestrator.js +3 -2
- package/dist/integrations/rl-suite/reward-signals.d.ts +1 -1
- package/dist/integrations/rl-suite/reward-signals.js +1 -1
- package/dist/integrations/ruvector/coherence-gate-cohomology.d.ts +41 -0
- package/dist/integrations/ruvector/coherence-gate-cohomology.js +47 -0
- package/dist/integrations/ruvector/coherence-gate-core.d.ts +200 -0
- package/dist/integrations/ruvector/coherence-gate-core.js +294 -0
- package/dist/integrations/ruvector/coherence-gate-energy.d.ts +136 -0
- package/dist/integrations/ruvector/coherence-gate-energy.js +373 -0
- package/dist/integrations/ruvector/coherence-gate-vector.d.ts +38 -0
- package/dist/integrations/ruvector/coherence-gate-vector.js +76 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +10 -311
- package/dist/integrations/ruvector/coherence-gate.js +10 -652
- package/dist/integrations/ruvector/cold-tier-trainer.d.ts +103 -0
- package/dist/integrations/ruvector/cold-tier-trainer.js +377 -0
- package/dist/integrations/ruvector/cusum-detector.d.ts +70 -0
- package/dist/integrations/ruvector/cusum-detector.js +142 -0
- package/dist/integrations/ruvector/delta-tracker.d.ts +122 -0
- package/dist/integrations/ruvector/delta-tracker.js +311 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +79 -1
- package/dist/integrations/ruvector/domain-transfer.js +158 -2
- package/dist/integrations/ruvector/eprop-learner.d.ts +135 -0
- package/dist/integrations/ruvector/eprop-learner.js +351 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +177 -0
- package/dist/integrations/ruvector/feature-flags.js +145 -0
- package/dist/integrations/ruvector/graphmae-encoder.d.ts +88 -0
- package/dist/integrations/ruvector/graphmae-encoder.js +360 -0
- package/dist/integrations/ruvector/hdc-fingerprint.d.ts +127 -0
- package/dist/integrations/ruvector/hdc-fingerprint.js +222 -0
- package/dist/integrations/ruvector/hopfield-memory.d.ts +97 -0
- package/dist/integrations/ruvector/hopfield-memory.js +238 -0
- package/dist/integrations/ruvector/index.d.ts +13 -2
- package/dist/integrations/ruvector/index.js +46 -2
- package/dist/integrations/ruvector/mincut-wrapper.d.ts +7 -0
- package/dist/integrations/ruvector/mincut-wrapper.js +54 -2
- package/dist/integrations/ruvector/reservoir-replay.d.ts +172 -0
- package/dist/integrations/ruvector/reservoir-replay.js +335 -0
- package/dist/integrations/ruvector/solver-adapter.d.ts +93 -0
- package/dist/integrations/ruvector/solver-adapter.js +299 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +33 -0
- package/dist/integrations/ruvector/sona-persistence.js +47 -0
- package/dist/integrations/ruvector/spectral-sparsifier.d.ts +154 -0
- package/dist/integrations/ruvector/spectral-sparsifier.js +389 -0
- package/dist/integrations/ruvector/temporal-causality.d.ts +63 -0
- package/dist/integrations/ruvector/temporal-causality.js +317 -0
- package/dist/learning/pattern-promotion.d.ts +63 -0
- package/dist/learning/pattern-promotion.js +235 -1
- package/dist/learning/pattern-store.d.ts +2 -0
- package/dist/learning/pattern-store.js +187 -1
- package/dist/learning/sqlite-persistence.d.ts +2 -0
- package/dist/learning/sqlite-persistence.js +4 -0
- package/dist/mcp/bundle.js +477 -380
- package/dist/mcp/handlers/heartbeat-handlers.d.ts +67 -0
- package/dist/mcp/handlers/heartbeat-handlers.js +180 -0
- package/dist/mcp/handlers/index.d.ts +2 -1
- package/dist/mcp/handlers/index.js +5 -1
- package/dist/mcp/handlers/task-handlers.d.ts +28 -0
- package/dist/mcp/handlers/task-handlers.js +39 -0
- package/dist/mcp/protocol-server.js +45 -1
- package/dist/mcp/server.js +41 -1
- package/dist/optimization/index.d.ts +2 -0
- package/dist/optimization/index.js +1 -0
- package/dist/optimization/session-cache.d.ts +80 -0
- package/dist/optimization/session-cache.js +227 -0
- package/dist/optimization/token-optimizer-service.d.ts +10 -0
- package/dist/optimization/token-optimizer-service.js +51 -0
- package/dist/routing/economic-routing.d.ts +126 -0
- package/dist/routing/economic-routing.js +290 -0
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.js +2 -0
- package/dist/routing/routing-feedback.d.ts +29 -0
- package/dist/routing/routing-feedback.js +75 -0
- package/dist/shared/utils/index.d.ts +1 -0
- package/dist/shared/utils/index.js +1 -0
- package/dist/shared/utils/xorshift128.d.ts +24 -0
- package/dist/shared/utils/xorshift128.js +50 -0
- package/package.json +1 -1
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Economic Routing Model — Imp-18 (Issue #334)
|
|
3
|
+
*
|
|
4
|
+
* Quality-weighted cost optimization for the routing system.
|
|
5
|
+
* Scores tiers by quality-per-dollar efficiency, respects budget limits,
|
|
6
|
+
* and produces cost-adjusted rewards so the neural router learns to
|
|
7
|
+
* prefer cost-efficient tiers.
|
|
8
|
+
*
|
|
9
|
+
* @module routing/economic-routing
|
|
10
|
+
*/
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Constants & Types
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Tier cost estimates (per typical QE task, in USD).
|
|
16
|
+
* Based on average token usage per task type.
|
|
17
|
+
*/
|
|
18
|
+
export const TIER_COST_ESTIMATES = {
|
|
19
|
+
booster: { avgInputTokens: 0, avgOutputTokens: 0, costPerTask: 0 },
|
|
20
|
+
haiku: { avgInputTokens: 2000, avgOutputTokens: 1000, costPerTask: 0.0035 },
|
|
21
|
+
sonnet: { avgInputTokens: 2000, avgOutputTokens: 1000, costPerTask: 0.021 },
|
|
22
|
+
opus: { avgInputTokens: 2000, avgOutputTokens: 1000, costPerTask: 0.105 },
|
|
23
|
+
};
|
|
24
|
+
/** All tiers ordered cheapest to most expensive */
|
|
25
|
+
const TIER_ORDER = ['booster', 'haiku', 'sonnet', 'opus'];
|
|
26
|
+
/** Maximum cost per task across all tiers (used for normalization) */
|
|
27
|
+
const MAX_TIER_COST = TIER_COST_ESTIMATES.opus.costPerTask;
|
|
28
|
+
export const DEFAULT_ECONOMIC_CONFIG = {
|
|
29
|
+
qualityWeight: 0.6,
|
|
30
|
+
costWeight: 0.4,
|
|
31
|
+
budgetPerHourUsd: 0,
|
|
32
|
+
budgetPerDayUsd: 0,
|
|
33
|
+
minQualityThreshold: 0.5,
|
|
34
|
+
enabled: true,
|
|
35
|
+
};
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// EconomicRoutingModel
|
|
38
|
+
// ============================================================================
|
|
39
|
+
/** EMA smoothing factor for quality estimate updates */
|
|
40
|
+
const EMA_ALPHA = 0.15;
|
|
41
|
+
export class EconomicRoutingModel {
|
|
42
|
+
config;
|
|
43
|
+
costTracker;
|
|
44
|
+
tierQualityEstimates = new Map();
|
|
45
|
+
tierOutcomeCounts = new Map();
|
|
46
|
+
constructor(costTracker, config) {
|
|
47
|
+
const merged = { ...DEFAULT_ECONOMIC_CONFIG, ...config };
|
|
48
|
+
// Validate and clamp config values to safe ranges
|
|
49
|
+
merged.qualityWeight = Math.max(0, Math.min(1, merged.qualityWeight));
|
|
50
|
+
merged.costWeight = Math.max(0, Math.min(1, merged.costWeight));
|
|
51
|
+
merged.minQualityThreshold = Math.max(0, Math.min(1, merged.minQualityThreshold));
|
|
52
|
+
merged.budgetPerHourUsd = Math.max(0, merged.budgetPerHourUsd);
|
|
53
|
+
merged.budgetPerDayUsd = Math.max(0, merged.budgetPerDayUsd);
|
|
54
|
+
// Normalize weights so they sum to 1.0
|
|
55
|
+
const weightSum = merged.qualityWeight + merged.costWeight;
|
|
56
|
+
if (weightSum > 0 && weightSum !== 1) {
|
|
57
|
+
merged.qualityWeight /= weightSum;
|
|
58
|
+
merged.costWeight /= weightSum;
|
|
59
|
+
}
|
|
60
|
+
this.config = merged;
|
|
61
|
+
this.costTracker = costTracker;
|
|
62
|
+
// Initialize with prior assumptions
|
|
63
|
+
this.tierQualityEstimates.set('booster', 0.3);
|
|
64
|
+
this.tierQualityEstimates.set('haiku', 0.55);
|
|
65
|
+
this.tierQualityEstimates.set('sonnet', 0.75);
|
|
66
|
+
this.tierQualityEstimates.set('opus', 0.90);
|
|
67
|
+
}
|
|
68
|
+
// --------------------------------------------------------------------------
|
|
69
|
+
// Core Methods
|
|
70
|
+
// --------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Score each tier by quality-per-dollar efficiency.
|
|
73
|
+
* Returns all tiers sorted by economicScore descending.
|
|
74
|
+
*/
|
|
75
|
+
scoreTiers(taskComplexity) {
|
|
76
|
+
const scores = TIER_ORDER.map(tier => {
|
|
77
|
+
const qualityScore = this.getQualityEstimate(tier, taskComplexity);
|
|
78
|
+
const estimatedCostUsd = TIER_COST_ESTIMATES[tier].costPerTask;
|
|
79
|
+
// quality / cost (handle zero-cost booster)
|
|
80
|
+
const qualityPerDollar = estimatedCostUsd > 0
|
|
81
|
+
? qualityScore / estimatedCostUsd
|
|
82
|
+
: qualityScore > 0 ? Infinity : 0;
|
|
83
|
+
// Normalized cost efficiency: 1 - (cost / maxCost)
|
|
84
|
+
const costEfficiency = MAX_TIER_COST > 0
|
|
85
|
+
? 1 - estimatedCostUsd / MAX_TIER_COST
|
|
86
|
+
: 1;
|
|
87
|
+
const economicScore = this.config.qualityWeight * qualityScore +
|
|
88
|
+
this.config.costWeight * costEfficiency;
|
|
89
|
+
return { tier, qualityScore, estimatedCostUsd, qualityPerDollar, economicScore };
|
|
90
|
+
});
|
|
91
|
+
scores.sort((a, b) => b.economicScore - a.economicScore);
|
|
92
|
+
return scores;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Select the best tier considering quality AND cost.
|
|
96
|
+
* Respects budget limits and minimum quality thresholds.
|
|
97
|
+
*/
|
|
98
|
+
selectTier(taskComplexity) {
|
|
99
|
+
const scores = this.scoreTiers(taskComplexity);
|
|
100
|
+
for (const score of scores) {
|
|
101
|
+
// Skip tiers below minimum quality threshold
|
|
102
|
+
if (score.qualityScore < this.config.minQualityThreshold) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Skip tiers that would exceed budget
|
|
106
|
+
if (this.wouldExceedBudget(score.tier)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
tier: score.tier,
|
|
111
|
+
reason: `Best economic score (${score.economicScore.toFixed(3)}): ` +
|
|
112
|
+
`quality=${score.qualityScore.toFixed(2)}, cost=$${score.estimatedCostUsd.toFixed(4)}`,
|
|
113
|
+
scores,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Fallback: pick the cheapest tier that meets quality, ignoring budget
|
|
117
|
+
// Use spread copies to avoid mutating the original scores array
|
|
118
|
+
const qualityFiltered = scores.filter(s => s.qualityScore >= this.config.minQualityThreshold);
|
|
119
|
+
if (qualityFiltered.length > 0) {
|
|
120
|
+
const cheapest = [...qualityFiltered].sort((a, b) => a.estimatedCostUsd - b.estimatedCostUsd)[0];
|
|
121
|
+
return {
|
|
122
|
+
tier: cheapest.tier,
|
|
123
|
+
reason: `Budget constrained fallback to ${cheapest.tier}`,
|
|
124
|
+
scores,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Final fallback: pick the best quality regardless
|
|
128
|
+
const bestQuality = [...scores].sort((a, b) => b.qualityScore - a.qualityScore)[0];
|
|
129
|
+
return {
|
|
130
|
+
tier: bestQuality.tier,
|
|
131
|
+
reason: `No tier meets quality threshold ${this.config.minQualityThreshold}; ` +
|
|
132
|
+
`using best quality: ${bestQuality.tier}`,
|
|
133
|
+
scores,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Check if a tier would exceed the budget.
|
|
138
|
+
*/
|
|
139
|
+
wouldExceedBudget(tier) {
|
|
140
|
+
const cost = TIER_COST_ESTIMATES[tier].costPerTask;
|
|
141
|
+
if (cost === 0)
|
|
142
|
+
return false;
|
|
143
|
+
const hourlyCost = this.costTracker.getCurrentCost('hour');
|
|
144
|
+
const dailyCost = this.costTracker.getCurrentCost('day');
|
|
145
|
+
if (this.config.budgetPerHourUsd > 0 &&
|
|
146
|
+
hourlyCost + cost > this.config.budgetPerHourUsd) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
if (this.config.budgetPerDayUsd > 0 &&
|
|
150
|
+
dailyCost + cost > this.config.budgetPerDayUsd) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Update quality estimates from observed outcomes.
|
|
157
|
+
* Uses EMA to smooth estimates.
|
|
158
|
+
*/
|
|
159
|
+
updateFromOutcome(outcome, tier) {
|
|
160
|
+
const observedQuality = outcome.outcome.qualityScore;
|
|
161
|
+
const current = this.tierQualityEstimates.get(tier) ?? 0.5;
|
|
162
|
+
const count = this.tierOutcomeCounts.get(tier) ?? 0;
|
|
163
|
+
// Use EMA; for the first few observations, use a higher alpha
|
|
164
|
+
const alpha = count < 5 ? 0.4 : EMA_ALPHA;
|
|
165
|
+
const updated = current * (1 - alpha) + observedQuality * alpha;
|
|
166
|
+
this.tierQualityEstimates.set(tier, updated);
|
|
167
|
+
this.tierOutcomeCounts.set(tier, count + 1);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get economic efficiency report.
|
|
171
|
+
*/
|
|
172
|
+
getEconomicReport() {
|
|
173
|
+
const tierEfficiency = this.scoreTiers(0.5); // mid-complexity as baseline
|
|
174
|
+
const currentHourlyCostUsd = this.costTracker.getCurrentCost('hour');
|
|
175
|
+
const currentDailyCostUsd = this.costTracker.getCurrentCost('day');
|
|
176
|
+
const budgetRemaining = {
|
|
177
|
+
hourly: this.config.budgetPerHourUsd > 0
|
|
178
|
+
? Math.max(0, this.config.budgetPerHourUsd - currentHourlyCostUsd)
|
|
179
|
+
: null,
|
|
180
|
+
daily: this.config.budgetPerDayUsd > 0
|
|
181
|
+
? Math.max(0, this.config.budgetPerDayUsd - currentDailyCostUsd)
|
|
182
|
+
: null,
|
|
183
|
+
};
|
|
184
|
+
// Find savings opportunity: compare most-used expensive tier vs cheaper alternative
|
|
185
|
+
let savingsOpportunity = null;
|
|
186
|
+
const sorted = [...tierEfficiency].sort((a, b) => b.qualityPerDollar - a.qualityPerDollar);
|
|
187
|
+
const mostEfficient = sorted.find(s => s.estimatedCostUsd > 0 && isFinite(s.qualityPerDollar));
|
|
188
|
+
const leastEfficient = [...sorted].reverse().find((s) => s.estimatedCostUsd > 0 && isFinite(s.qualityPerDollar));
|
|
189
|
+
if (mostEfficient && leastEfficient && mostEfficient.tier !== leastEfficient.tier) {
|
|
190
|
+
const potentialSavings = leastEfficient.estimatedCostUsd - mostEfficient.estimatedCostUsd;
|
|
191
|
+
if (potentialSavings > 0) {
|
|
192
|
+
savingsOpportunity = {
|
|
193
|
+
usd: potentialSavings,
|
|
194
|
+
description: `Switch from ${leastEfficient.tier} ($${leastEfficient.estimatedCostUsd.toFixed(4)}/task) ` +
|
|
195
|
+
`to ${mostEfficient.tier} ($${mostEfficient.estimatedCostUsd.toFixed(4)}/task) ` +
|
|
196
|
+
`for comparable tasks to save ~$${potentialSavings.toFixed(4)}/task`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const recommendation = this.generateRecommendation(tierEfficiency, budgetRemaining);
|
|
201
|
+
return {
|
|
202
|
+
tierEfficiency,
|
|
203
|
+
currentHourlyCostUsd,
|
|
204
|
+
currentDailyCostUsd,
|
|
205
|
+
budgetRemaining,
|
|
206
|
+
recommendation,
|
|
207
|
+
savingsOpportunity,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Compute cost-adjusted reward for the neural router.
|
|
212
|
+
* Penalizes expensive tiers that don't deliver proportionally higher quality.
|
|
213
|
+
*/
|
|
214
|
+
computeCostAdjustedReward(baseReward, tier, qualityScore) {
|
|
215
|
+
const tierCost = TIER_COST_ESTIMATES[tier].costPerTask;
|
|
216
|
+
if (MAX_TIER_COST === 0)
|
|
217
|
+
return baseReward;
|
|
218
|
+
const costRatio = tierCost / MAX_TIER_COST; // 0-1, where opus=1.0
|
|
219
|
+
const qualityGain = Math.max(0, qualityScore - this.config.minQualityThreshold);
|
|
220
|
+
const costPenalty = costRatio * (1 - qualityGain);
|
|
221
|
+
const adjusted = baseReward - costPenalty * this.config.costWeight;
|
|
222
|
+
// Clamp to [-1, 1]
|
|
223
|
+
return Math.max(-1, Math.min(1, adjusted));
|
|
224
|
+
}
|
|
225
|
+
// --------------------------------------------------------------------------
|
|
226
|
+
// Persistence helpers
|
|
227
|
+
// --------------------------------------------------------------------------
|
|
228
|
+
/**
|
|
229
|
+
* Serialize quality estimates for persistence.
|
|
230
|
+
*/
|
|
231
|
+
serializeEstimates() {
|
|
232
|
+
const result = {};
|
|
233
|
+
for (const tier of TIER_ORDER) {
|
|
234
|
+
result[tier] = {
|
|
235
|
+
quality: this.tierQualityEstimates.get(tier) ?? 0.5,
|
|
236
|
+
count: this.tierOutcomeCounts.get(tier) ?? 0,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Deserialize quality estimates from persistence.
|
|
243
|
+
*/
|
|
244
|
+
deserializeEstimates(data) {
|
|
245
|
+
for (const tier of TIER_ORDER) {
|
|
246
|
+
if (data[tier]) {
|
|
247
|
+
this.tierQualityEstimates.set(tier, data[tier].quality);
|
|
248
|
+
this.tierOutcomeCounts.set(tier, data[tier].count);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get the current config (read-only copy).
|
|
254
|
+
*/
|
|
255
|
+
getConfig() {
|
|
256
|
+
return { ...this.config };
|
|
257
|
+
}
|
|
258
|
+
// --------------------------------------------------------------------------
|
|
259
|
+
// Private helpers
|
|
260
|
+
// --------------------------------------------------------------------------
|
|
261
|
+
/**
|
|
262
|
+
* Get quality estimate for a tier, adjusted by task complexity.
|
|
263
|
+
* Higher complexity tasks benefit more from higher-tier models.
|
|
264
|
+
*/
|
|
265
|
+
getQualityEstimate(tier, taskComplexity) {
|
|
266
|
+
const baseQuality = this.tierQualityEstimates.get(tier) ?? 0.5;
|
|
267
|
+
// For complex tasks, cheaper tiers degrade more
|
|
268
|
+
const complexityPenalty = tier === 'booster'
|
|
269
|
+
? taskComplexity * 0.4
|
|
270
|
+
: tier === 'haiku'
|
|
271
|
+
? taskComplexity * 0.2
|
|
272
|
+
: 0;
|
|
273
|
+
return Math.max(0, Math.min(1, baseQuality - complexityPenalty));
|
|
274
|
+
}
|
|
275
|
+
generateRecommendation(tierEfficiency, budgetRemaining) {
|
|
276
|
+
if (budgetRemaining.hourly !== null && budgetRemaining.hourly < 0.01) {
|
|
277
|
+
return 'Hourly budget nearly exhausted. Consider increasing budget or routing to cheaper tiers.';
|
|
278
|
+
}
|
|
279
|
+
if (budgetRemaining.daily !== null && budgetRemaining.daily < 0.1) {
|
|
280
|
+
return 'Daily budget nearly exhausted. Only critical tasks should use expensive tiers.';
|
|
281
|
+
}
|
|
282
|
+
const best = tierEfficiency[0];
|
|
283
|
+
if (best) {
|
|
284
|
+
return `Most cost-efficient tier: ${best.tier} ` +
|
|
285
|
+
`(score=${best.economicScore.toFixed(3)}, quality=${best.qualityScore.toFixed(2)})`;
|
|
286
|
+
}
|
|
287
|
+
return 'No economic data available yet.';
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=economic-routing.js.map
|
package/dist/routing/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export type { CoExecutionRecord, CoExecutionStats, } from './co-execution-reposi
|
|
|
16
16
|
export { SignalMerger, createSignalMerger, DEFAULT_SIGNAL_MERGER_CONFIG, } from './signal-merger.js';
|
|
17
17
|
export type { SignalSource, RoutingSignal, MergedAgentScore, SignalMergerConfig, } from './signal-merger.js';
|
|
18
18
|
export { RoutingFeedbackCollector, createRoutingFeedbackCollector, } from './routing-feedback.js';
|
|
19
|
+
export { EconomicRoutingModel, TIER_COST_ESTIMATES, DEFAULT_ECONOMIC_CONFIG, } from './economic-routing.js';
|
|
20
|
+
export type { EconomicScore, EconomicRoutingConfig, EconomicReport, } from './economic-routing.js';
|
|
19
21
|
export { classifyTask, isSimpleTask, requiresOpus, getRecommendedModel, getComplexityScore, COMPLEX_DOMAINS, MODERATE_DOMAINS, COMPLEX_CAPABILITIES, COMPLEXITY_THRESHOLDS, COMPLEXITY_TO_MODEL, } from './task-classifier.js';
|
|
20
22
|
export type { TaskComplexity, ClaudeModel, ComplexityFactor, ClassificationResult, ClassifiableTask, } from './task-classifier.js';
|
|
21
23
|
export { TinyDancerRouter, createTinyDancerRouter, createSmartTinyDancerRouter, } from './tiny-dancer-router.js';
|
package/dist/routing/index.js
CHANGED
|
@@ -18,6 +18,8 @@ export { CoExecutionRepository, getCoExecutionRepository, } from './co-execution
|
|
|
18
18
|
export { SignalMerger, createSignalMerger, DEFAULT_SIGNAL_MERGER_CONFIG, } from './signal-merger.js';
|
|
19
19
|
// Feedback
|
|
20
20
|
export { RoutingFeedbackCollector, createRoutingFeedbackCollector, } from './routing-feedback.js';
|
|
21
|
+
// Economic Routing (Imp-18, Issue #334)
|
|
22
|
+
export { EconomicRoutingModel, TIER_COST_ESTIMATES, DEFAULT_ECONOMIC_CONFIG, } from './economic-routing.js';
|
|
21
23
|
// Task Classifier (TD-002)
|
|
22
24
|
export { classifyTask, isSimpleTask, requiresOpus, getRecommendedModel, getComplexityScore, COMPLEX_DOMAINS, MODERATE_DOMAINS, COMPLEX_CAPABILITIES, COMPLEXITY_THRESHOLDS, COMPLEXITY_TO_MODEL, } from './task-classifier.js';
|
|
23
25
|
// TinyDancer Router (TD-003)
|
|
@@ -10,6 +10,8 @@ import type { QETaskRouter } from './qe-task-router.js';
|
|
|
10
10
|
import { type EMAConfig } from './calibration/index.js';
|
|
11
11
|
import { type EscalationConfig, type EscalationState } from './escalation/index.js';
|
|
12
12
|
import type { RoutingConfig } from './routing-config.js';
|
|
13
|
+
import { type EconomicRoutingConfig, type EconomicScore, type EconomicReport } from './economic-routing.js';
|
|
14
|
+
import { CostTracker } from '../shared/llm/cost-tracker.js';
|
|
13
15
|
/**
|
|
14
16
|
* Collects and processes routing feedback for continuous learning
|
|
15
17
|
*/
|
|
@@ -19,6 +21,9 @@ export declare class RoutingFeedbackCollector {
|
|
|
19
21
|
private db;
|
|
20
22
|
private calibrator;
|
|
21
23
|
private escalationTracker;
|
|
24
|
+
private economicModel;
|
|
25
|
+
private economicPersistCounter;
|
|
26
|
+
private static readonly ECONOMIC_PERSIST_INTERVAL;
|
|
22
27
|
private persistCount;
|
|
23
28
|
private readonly maxOutcomes;
|
|
24
29
|
private static readonly RETENTION_CLEANUP_INTERVAL;
|
|
@@ -53,6 +58,14 @@ export declare class RoutingFeedbackCollector {
|
|
|
53
58
|
* Persist EMA calibrator state to the database
|
|
54
59
|
*/
|
|
55
60
|
private persistCalibratorState;
|
|
61
|
+
/**
|
|
62
|
+
* Load persisted economic routing state from the database
|
|
63
|
+
*/
|
|
64
|
+
private loadEconomicState;
|
|
65
|
+
/**
|
|
66
|
+
* Persist economic routing state to the database
|
|
67
|
+
*/
|
|
68
|
+
private persistEconomicState;
|
|
56
69
|
/**
|
|
57
70
|
* Connect to router for automatic performance updates
|
|
58
71
|
*/
|
|
@@ -77,6 +90,22 @@ export declare class RoutingFeedbackCollector {
|
|
|
77
90
|
* Returns null if auto-escalation is not enabled or agent has no state.
|
|
78
91
|
*/
|
|
79
92
|
getEscalationState(agentId: string): EscalationState | null;
|
|
93
|
+
/**
|
|
94
|
+
* Enable economic routing model for quality-weighted cost optimization.
|
|
95
|
+
* Once enabled, every recorded outcome feeds economic quality estimates
|
|
96
|
+
* and the neural router receives cost-adjusted rewards.
|
|
97
|
+
*/
|
|
98
|
+
enableEconomicRouting(config?: Partial<EconomicRoutingConfig>, costTracker?: CostTracker): void;
|
|
99
|
+
/**
|
|
100
|
+
* Get the economic routing report.
|
|
101
|
+
* Returns null if economic routing is not enabled.
|
|
102
|
+
*/
|
|
103
|
+
getEconomicReport(): EconomicReport | null;
|
|
104
|
+
/**
|
|
105
|
+
* Get economic scores for a given task complexity.
|
|
106
|
+
* Returns null if economic routing is not enabled.
|
|
107
|
+
*/
|
|
108
|
+
getEconomicScore(taskComplexity: number): EconomicScore[] | null;
|
|
80
109
|
/**
|
|
81
110
|
* Record a routing outcome
|
|
82
111
|
*/
|
|
@@ -12,6 +12,8 @@ import { safeJsonParse } from '../shared/safe-json.js';
|
|
|
12
12
|
import { toErrorMessage } from '../shared/error-utils.js';
|
|
13
13
|
import { EMACalibrator } from './calibration/index.js';
|
|
14
14
|
import { AutoEscalationTracker } from './escalation/index.js';
|
|
15
|
+
import { EconomicRoutingModel, } from './economic-routing.js';
|
|
16
|
+
import { getGlobalCostTracker } from '../shared/llm/cost-tracker.js';
|
|
15
17
|
// ============================================================================
|
|
16
18
|
// In-Memory Storage (can be replaced with SQLite)
|
|
17
19
|
// ============================================================================
|
|
@@ -64,6 +66,9 @@ export class RoutingFeedbackCollector {
|
|
|
64
66
|
db = null;
|
|
65
67
|
calibrator = null;
|
|
66
68
|
escalationTracker = null;
|
|
69
|
+
economicModel = null;
|
|
70
|
+
economicPersistCounter = 0;
|
|
71
|
+
static ECONOMIC_PERSIST_INTERVAL = 10;
|
|
67
72
|
persistCount = 0;
|
|
68
73
|
maxOutcomes;
|
|
69
74
|
static RETENTION_CLEANUP_INTERVAL = 100;
|
|
@@ -229,6 +234,42 @@ export class RoutingFeedbackCollector {
|
|
|
229
234
|
console.warn('[RoutingFeedbackCollector] Failed to persist calibrator state:', toErrorMessage(error));
|
|
230
235
|
}
|
|
231
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Load persisted economic routing state from the database
|
|
239
|
+
*/
|
|
240
|
+
loadEconomicState() {
|
|
241
|
+
if (!this.db || !this.economicModel)
|
|
242
|
+
return;
|
|
243
|
+
try {
|
|
244
|
+
const database = this.db.getDatabase();
|
|
245
|
+
const row = database.prepare(`SELECT value FROM kv_store WHERE key = 'routing:economic_quality_estimates'`).get();
|
|
246
|
+
if (row) {
|
|
247
|
+
const data = safeJsonParse(row.value);
|
|
248
|
+
if (data && typeof data === 'object') {
|
|
249
|
+
this.economicModel.deserializeEstimates(data);
|
|
250
|
+
console.log('[RoutingFeedbackCollector] Loaded economic quality estimates from DB');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
console.warn('[RoutingFeedbackCollector] Failed to load economic state:', toErrorMessage(error));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Persist economic routing state to the database
|
|
260
|
+
*/
|
|
261
|
+
persistEconomicState() {
|
|
262
|
+
if (!this.db || !this.economicModel)
|
|
263
|
+
return;
|
|
264
|
+
try {
|
|
265
|
+
const database = this.db.getDatabase();
|
|
266
|
+
const serialized = JSON.stringify(this.economicModel.serializeEstimates());
|
|
267
|
+
database.prepare(`INSERT OR REPLACE INTO kv_store (key, value, updated_at) VALUES (?, ?, datetime('now'))`).run('routing:economic_quality_estimates', serialized);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
console.warn('[RoutingFeedbackCollector] Failed to persist economic state:', toErrorMessage(error));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
232
273
|
/**
|
|
233
274
|
* Connect to router for automatic performance updates
|
|
234
275
|
*/
|
|
@@ -263,6 +304,30 @@ export class RoutingFeedbackCollector {
|
|
|
263
304
|
getEscalationState(agentId) {
|
|
264
305
|
return this.escalationTracker?.getState(agentId) ?? null;
|
|
265
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Enable economic routing model for quality-weighted cost optimization.
|
|
309
|
+
* Once enabled, every recorded outcome feeds economic quality estimates
|
|
310
|
+
* and the neural router receives cost-adjusted rewards.
|
|
311
|
+
*/
|
|
312
|
+
enableEconomicRouting(config, costTracker) {
|
|
313
|
+
const tracker = costTracker ?? getGlobalCostTracker();
|
|
314
|
+
this.economicModel = new EconomicRoutingModel(tracker, config);
|
|
315
|
+
this.loadEconomicState();
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Get the economic routing report.
|
|
319
|
+
* Returns null if economic routing is not enabled.
|
|
320
|
+
*/
|
|
321
|
+
getEconomicReport() {
|
|
322
|
+
return this.economicModel?.getEconomicReport() ?? null;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get economic scores for a given task complexity.
|
|
326
|
+
* Returns null if economic routing is not enabled.
|
|
327
|
+
*/
|
|
328
|
+
getEconomicScore(taskComplexity) {
|
|
329
|
+
return this.economicModel?.scoreTiers(taskComplexity) ?? null;
|
|
330
|
+
}
|
|
266
331
|
/**
|
|
267
332
|
* Record a routing outcome
|
|
268
333
|
*/
|
|
@@ -301,6 +366,16 @@ export class RoutingFeedbackCollector {
|
|
|
301
366
|
`${escalationAction.previousTier} → ${escalationAction.newTier}`);
|
|
302
367
|
}
|
|
303
368
|
}
|
|
369
|
+
// Update economic model if enabled
|
|
370
|
+
if (this.economicModel) {
|
|
371
|
+
const tier = this.inferTier(usedAgent);
|
|
372
|
+
this.economicModel.updateFromOutcome(routingOutcome, tier);
|
|
373
|
+
// Persist economic state periodically
|
|
374
|
+
this.economicPersistCounter++;
|
|
375
|
+
if (this.economicPersistCounter % RoutingFeedbackCollector.ECONOMIC_PERSIST_INTERVAL === 0) {
|
|
376
|
+
this.persistEconomicState();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
304
379
|
return routingOutcome;
|
|
305
380
|
}
|
|
306
381
|
/**
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Xorshift128 PRNG — Deterministic pseudo-random number generator
|
|
3
|
+
*
|
|
4
|
+
* Seeded xorshift128 producing repeatable sequences for use in
|
|
5
|
+
* fingerprinting, graph training, weight initialization, and
|
|
6
|
+
* any context requiring deterministic randomness.
|
|
7
|
+
*
|
|
8
|
+
* @module shared/utils/xorshift128
|
|
9
|
+
*/
|
|
10
|
+
export declare class Xorshift128 {
|
|
11
|
+
private s0;
|
|
12
|
+
private s1;
|
|
13
|
+
private s2;
|
|
14
|
+
private s3;
|
|
15
|
+
constructor(seed: number);
|
|
16
|
+
private splitmix32;
|
|
17
|
+
/** Returns a raw unsigned 32-bit integer */
|
|
18
|
+
next(): number;
|
|
19
|
+
/** Returns a float in [0, 1) */
|
|
20
|
+
nextFloat(): number;
|
|
21
|
+
/** Returns a standard normal variate via Box-Muller transform */
|
|
22
|
+
nextGaussian(): number;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=xorshift128.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Xorshift128 PRNG — Deterministic pseudo-random number generator
|
|
3
|
+
*
|
|
4
|
+
* Seeded xorshift128 producing repeatable sequences for use in
|
|
5
|
+
* fingerprinting, graph training, weight initialization, and
|
|
6
|
+
* any context requiring deterministic randomness.
|
|
7
|
+
*
|
|
8
|
+
* @module shared/utils/xorshift128
|
|
9
|
+
*/
|
|
10
|
+
export class Xorshift128 {
|
|
11
|
+
s0;
|
|
12
|
+
s1;
|
|
13
|
+
s2;
|
|
14
|
+
s3;
|
|
15
|
+
constructor(seed) {
|
|
16
|
+
this.s0 = this.splitmix32(seed);
|
|
17
|
+
this.s1 = this.splitmix32(this.s0);
|
|
18
|
+
this.s2 = this.splitmix32(this.s1);
|
|
19
|
+
this.s3 = this.splitmix32(this.s2);
|
|
20
|
+
}
|
|
21
|
+
splitmix32(state) {
|
|
22
|
+
state = (state + 0x9e3779b9) | 0;
|
|
23
|
+
state = Math.imul(state ^ (state >>> 16), 0x85ebca6b);
|
|
24
|
+
state = Math.imul(state ^ (state >>> 13), 0xc2b2ae35);
|
|
25
|
+
return (state ^ (state >>> 16)) >>> 0;
|
|
26
|
+
}
|
|
27
|
+
/** Returns a raw unsigned 32-bit integer */
|
|
28
|
+
next() {
|
|
29
|
+
const t = this.s3;
|
|
30
|
+
let s = this.s0;
|
|
31
|
+
this.s3 = this.s2;
|
|
32
|
+
this.s2 = this.s1;
|
|
33
|
+
this.s1 = s;
|
|
34
|
+
s ^= s << 11;
|
|
35
|
+
s ^= s >>> 8;
|
|
36
|
+
this.s0 = s ^ t ^ (t >>> 19);
|
|
37
|
+
return this.s0 >>> 0;
|
|
38
|
+
}
|
|
39
|
+
/** Returns a float in [0, 1) */
|
|
40
|
+
nextFloat() {
|
|
41
|
+
return this.next() / 0x100000000;
|
|
42
|
+
}
|
|
43
|
+
/** Returns a standard normal variate via Box-Muller transform */
|
|
44
|
+
nextGaussian() {
|
|
45
|
+
const u1 = this.nextFloat() || 1e-10;
|
|
46
|
+
const u2 = this.nextFloat();
|
|
47
|
+
return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=xorshift128.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-qe",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.12",
|
|
4
4
|
"description": "Agentic Quality Engineering V3 - Domain-Driven Design Architecture with 13 Bounded Contexts, O(log n) coverage analysis, ReasoningBank learning, 60 specialized QE agents, mathematical Coherence verification, deep Claude Flow integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|