agentic-qe 2.6.1 → 2.6.2

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +222 -159
  3. package/dist/agents/BaseAgent.d.ts +19 -0
  4. package/dist/agents/BaseAgent.d.ts.map +1 -1
  5. package/dist/agents/BaseAgent.js +41 -1
  6. package/dist/agents/BaseAgent.js.map +1 -1
  7. package/dist/agents/CodeIntelligenceAgent.d.ts +14 -0
  8. package/dist/agents/CodeIntelligenceAgent.d.ts.map +1 -1
  9. package/dist/agents/CodeIntelligenceAgent.js +66 -0
  10. package/dist/agents/CodeIntelligenceAgent.js.map +1 -1
  11. package/dist/agents/CoverageAnalyzerAgent.d.ts +8 -0
  12. package/dist/agents/CoverageAnalyzerAgent.d.ts.map +1 -1
  13. package/dist/agents/CoverageAnalyzerAgent.js +65 -1
  14. package/dist/agents/CoverageAnalyzerAgent.js.map +1 -1
  15. package/dist/agents/TestGeneratorAgent.d.ts +2 -2
  16. package/dist/agents/TestGeneratorAgent.d.ts.map +1 -1
  17. package/dist/agents/TestGeneratorAgent.js +16 -6
  18. package/dist/agents/TestGeneratorAgent.js.map +1 -1
  19. package/dist/agents/adapters/AgentLLMAdapter.d.ts +127 -0
  20. package/dist/agents/adapters/AgentLLMAdapter.d.ts.map +1 -0
  21. package/dist/agents/adapters/AgentLLMAdapter.js +366 -0
  22. package/dist/agents/adapters/AgentLLMAdapter.js.map +1 -0
  23. package/dist/agents/adapters/index.d.ts +1 -0
  24. package/dist/agents/adapters/index.d.ts.map +1 -1
  25. package/dist/agents/adapters/index.js +5 -1
  26. package/dist/agents/adapters/index.js.map +1 -1
  27. package/dist/agents/interfaces/IAgentLLM.d.ts +257 -0
  28. package/dist/agents/interfaces/IAgentLLM.d.ts.map +1 -0
  29. package/dist/agents/interfaces/IAgentLLM.js +39 -0
  30. package/dist/agents/interfaces/IAgentLLM.js.map +1 -0
  31. package/dist/agents/interfaces/index.d.ts +10 -0
  32. package/dist/agents/interfaces/index.d.ts.map +1 -0
  33. package/dist/agents/interfaces/index.js +14 -0
  34. package/dist/agents/interfaces/index.js.map +1 -0
  35. package/dist/agents/n8n/N8nBaseAgent.d.ts +18 -0
  36. package/dist/agents/n8n/N8nBaseAgent.d.ts.map +1 -1
  37. package/dist/agents/n8n/N8nBaseAgent.js +80 -0
  38. package/dist/agents/n8n/N8nBaseAgent.js.map +1 -1
  39. package/dist/cli/commands/providers.d.ts +50 -0
  40. package/dist/cli/commands/providers.d.ts.map +1 -0
  41. package/dist/cli/commands/providers.js +403 -0
  42. package/dist/cli/commands/providers.js.map +1 -0
  43. package/dist/cli/index.js +62 -0
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/code-intelligence/indexing/FileWatcher.d.ts.map +1 -1
  46. package/dist/code-intelligence/indexing/FileWatcher.js +11 -8
  47. package/dist/code-intelligence/indexing/FileWatcher.js.map +1 -1
  48. package/dist/config/ConfigLoader.d.ts +85 -0
  49. package/dist/config/ConfigLoader.d.ts.map +1 -0
  50. package/dist/config/ConfigLoader.js +420 -0
  51. package/dist/config/ConfigLoader.js.map +1 -0
  52. package/dist/config/ProviderConfig.d.ts +153 -0
  53. package/dist/config/ProviderConfig.d.ts.map +1 -0
  54. package/dist/config/ProviderConfig.js +155 -0
  55. package/dist/config/ProviderConfig.js.map +1 -0
  56. package/dist/config/index.d.ts +35 -0
  57. package/dist/config/index.d.ts.map +1 -0
  58. package/dist/config/index.js +45 -0
  59. package/dist/config/index.js.map +1 -0
  60. package/dist/core/memory/HNSWVectorMemory.js +1 -1
  61. package/dist/mcp/server-instructions.d.ts +1 -1
  62. package/dist/mcp/server-instructions.js +1 -1
  63. package/dist/mcp/server.d.ts +1 -0
  64. package/dist/mcp/server.d.ts.map +1 -1
  65. package/dist/memory/HNSWPatternStore.d.ts.map +1 -1
  66. package/dist/memory/HNSWPatternStore.js +23 -0
  67. package/dist/memory/HNSWPatternStore.js.map +1 -1
  68. package/dist/memory/RuVectorPatternStore.d.ts +5 -0
  69. package/dist/memory/RuVectorPatternStore.d.ts.map +1 -1
  70. package/dist/memory/RuVectorPatternStore.js +11 -0
  71. package/dist/memory/RuVectorPatternStore.js.map +1 -1
  72. package/dist/providers/CostOptimizationStrategies.d.ts +297 -0
  73. package/dist/providers/CostOptimizationStrategies.d.ts.map +1 -0
  74. package/dist/providers/CostOptimizationStrategies.js +831 -0
  75. package/dist/providers/CostOptimizationStrategies.js.map +1 -0
  76. package/dist/providers/HybridRouter.d.ts +142 -5
  77. package/dist/providers/HybridRouter.d.ts.map +1 -1
  78. package/dist/providers/HybridRouter.js +472 -6
  79. package/dist/providers/HybridRouter.js.map +1 -1
  80. package/dist/providers/HybridRouterComplexityIntegration.d.ts +169 -0
  81. package/dist/providers/HybridRouterComplexityIntegration.d.ts.map +1 -0
  82. package/dist/providers/HybridRouterComplexityIntegration.js +319 -0
  83. package/dist/providers/HybridRouterComplexityIntegration.js.map +1 -0
  84. package/dist/providers/HybridRouterModelSelection.d.ts +106 -0
  85. package/dist/providers/HybridRouterModelSelection.d.ts.map +1 -0
  86. package/dist/providers/HybridRouterModelSelection.js +420 -0
  87. package/dist/providers/HybridRouterModelSelection.js.map +1 -0
  88. package/dist/providers/LLMProviderFactory.d.ts +23 -9
  89. package/dist/providers/LLMProviderFactory.d.ts.map +1 -1
  90. package/dist/providers/LLMProviderFactory.js +54 -11
  91. package/dist/providers/LLMProviderFactory.js.map +1 -1
  92. package/dist/providers/OllamaProvider.d.ts +122 -0
  93. package/dist/providers/OllamaProvider.d.ts.map +1 -0
  94. package/dist/providers/OllamaProvider.js +425 -0
  95. package/dist/providers/OllamaProvider.js.map +1 -0
  96. package/dist/providers/index.d.ts +6 -1
  97. package/dist/providers/index.d.ts.map +1 -1
  98. package/dist/providers/index.js +17 -1
  99. package/dist/providers/index.js.map +1 -1
  100. package/dist/routing/ComplexityClassifier.d.ts +266 -0
  101. package/dist/routing/ComplexityClassifier.d.ts.map +1 -0
  102. package/dist/routing/ComplexityClassifier.js +567 -0
  103. package/dist/routing/ComplexityClassifier.js.map +1 -0
  104. package/dist/routing/ModelCapabilityRegistry.d.ts +98 -0
  105. package/dist/routing/ModelCapabilityRegistry.d.ts.map +1 -0
  106. package/dist/routing/ModelCapabilityRegistry.js +216 -0
  107. package/dist/routing/ModelCapabilityRegistry.js.map +1 -0
  108. package/dist/routing/index.d.ts +13 -0
  109. package/dist/routing/index.d.ts.map +1 -0
  110. package/dist/routing/index.js +24 -0
  111. package/dist/routing/index.js.map +1 -0
  112. package/docs/reference/model-capability-registry.md +402 -0
  113. package/docs/reference/provider-config-schema.md +608 -0
  114. package/package.json +19 -4
@@ -24,6 +24,8 @@ const ClaudeProvider_1 = require("./ClaudeProvider");
24
24
  const RuvllmProvider_1 = require("./RuvllmProvider");
25
25
  const RuVectorClient_1 = require("./RuVectorClient");
26
26
  const Logger_1 = require("../utils/Logger");
27
+ const ComplexityClassifier_1 = require("../routing/ComplexityClassifier");
28
+ const CostOptimizationStrategies_1 = require("./CostOptimizationStrategies");
27
29
  /**
28
30
  * Request priority levels
29
31
  */
@@ -100,7 +102,12 @@ class HybridRouter {
100
102
  },
101
103
  claude: config.claude,
102
104
  ruvllm: config.ruvllm,
103
- ruvector: config.ruvector
105
+ ruvector: config.ruvector,
106
+ // Phase 2 integration config
107
+ complexityClassifier: config.complexityClassifier,
108
+ costOptimization: config.costOptimization,
109
+ useMLClassifier: config.useMLClassifier ?? true,
110
+ useCostOptimization: config.useCostOptimization ?? true
104
111
  };
105
112
  this.circuitBreakers = new Map();
106
113
  this.routingHistory = [];
@@ -111,6 +118,9 @@ class HybridRouter {
111
118
  this.cloudRequestCount = 0;
112
119
  this.cacheHitCount = 0;
113
120
  this.cacheMissCount = 0;
121
+ this.costHistory = [];
122
+ this.trackingStartDate = new Date();
123
+ this.compressionStats = { totalSaved: 0, totalOriginal: 0, count: 0 };
114
124
  }
115
125
  /**
116
126
  * Initialize the hybrid router and its providers
@@ -184,11 +194,49 @@ class HybridRouter {
184
194
  if (!this.localProvider && !this.cloudProvider) {
185
195
  throw new ILLMProvider_1.LLMProviderError('Failed to initialize any providers', 'hybrid-router', 'INIT_ERROR', false);
186
196
  }
197
+ // Initialize ML-based complexity classifier (Phase 2.1.2)
198
+ if (this.config.useMLClassifier) {
199
+ try {
200
+ this.complexityClassifier = new ComplexityClassifier_1.ComplexityClassifier(this.config.complexityClassifier ?? {
201
+ enableLearning: true,
202
+ learningRate: 0.05
203
+ });
204
+ this.logger.info('ML ComplexityClassifier initialized', {
205
+ enableLearning: this.config.complexityClassifier?.enableLearning ?? true
206
+ });
207
+ }
208
+ catch (error) {
209
+ this.logger.warn('Failed to initialize ComplexityClassifier, using heuristics', {
210
+ error: error.message
211
+ });
212
+ }
213
+ }
214
+ // Initialize cost optimization manager (Phase 2.3.2)
215
+ if (this.config.useCostOptimization) {
216
+ try {
217
+ this.costOptimizationManager = new CostOptimizationStrategies_1.CostOptimizationManager(this.config.costOptimization ?? {
218
+ enableCompression: true,
219
+ enableBatching: true,
220
+ enableSmartCaching: true
221
+ });
222
+ this.logger.info('CostOptimizationManager initialized', {
223
+ compression: this.config.costOptimization?.enableCompression ?? true,
224
+ batching: this.config.costOptimization?.enableBatching ?? true
225
+ });
226
+ }
227
+ catch (error) {
228
+ this.logger.warn('Failed to initialize CostOptimizationManager', {
229
+ error: error.message
230
+ });
231
+ }
232
+ }
187
233
  this.isInitialized = true;
188
234
  this.logger.info('HybridRouter initialized', {
189
235
  hasLocal: !!this.localProvider,
190
236
  hasCloud: !!this.cloudProvider,
191
237
  hasRuVectorCache: !!this.ruVectorClient,
238
+ hasMLClassifier: !!this.complexityClassifier,
239
+ hasCostOptimization: !!this.costOptimizationManager,
192
240
  strategy: this.config.defaultStrategy
193
241
  });
194
242
  }
@@ -208,7 +256,47 @@ class HybridRouter {
208
256
  const startTime = Date.now();
209
257
  const priority = options.priority ?? RequestPriority.NORMAL;
210
258
  const strategy = options.routingStrategy ?? this.config.defaultStrategy;
211
- const complexity = this.analyzeComplexity(options);
259
+ // Apply cost optimization: prompt compression (Phase 2.3.2)
260
+ let optimizedOptions = options;
261
+ let compressionResult;
262
+ if (this.costOptimizationManager && this.config.useCostOptimization) {
263
+ try {
264
+ const originalContent = this.extractQueryFromOptions(options) || '';
265
+ const compressor = this.costOptimizationManager.getCompressor();
266
+ compressionResult = compressor.compress(originalContent);
267
+ if (compressionResult.tokensSaved > 0) {
268
+ // Track compression stats for benchmarking
269
+ // Estimate original tokens from ratio: ratio = tokensSaved / originalTokens
270
+ // So originalTokens ≈ tokensSaved / ratio (if ratio > 0)
271
+ const estimatedOriginalTokens = compressionResult.ratio > 0
272
+ ? Math.round(compressionResult.tokensSaved / compressionResult.ratio)
273
+ : compressionResult.original.split(/\s+/).length;
274
+ this.compressionStats.totalOriginal += estimatedOriginalTokens;
275
+ this.compressionStats.totalSaved += compressionResult.tokensSaved;
276
+ this.compressionStats.count++;
277
+ // Apply compressed content to options
278
+ optimizedOptions = this.applyCompressedContent(options, compressionResult.compressed);
279
+ if (this.config.debug) {
280
+ this.logger.debug('Prompt compressed', {
281
+ tokensSaved: compressionResult.tokensSaved,
282
+ ratio: compressionResult.ratio,
283
+ techniques: compressionResult.techniques
284
+ });
285
+ }
286
+ }
287
+ }
288
+ catch (error) {
289
+ this.logger.warn('Prompt compression failed, using original', {
290
+ error: error.message
291
+ });
292
+ }
293
+ }
294
+ const complexity = this.analyzeComplexity(optimizedOptions);
295
+ // Check budget before proceeding
296
+ const estimatedCost = strategy === RoutingStrategy.COST_OPTIMIZED ? 0 : 0.01;
297
+ if (!this.checkBudget(estimatedCost)) {
298
+ throw new ILLMProvider_1.LLMProviderError('Request rejected: budget limit exceeded', 'hybrid-router', 'BUDGET_EXCEEDED', false);
299
+ }
212
300
  // Phase 0.5: Try RuVector cache first (sub-ms pattern matching)
213
301
  if (this.ruVectorClient && this.shouldUseCache(options, complexity)) {
214
302
  try {
@@ -427,6 +515,8 @@ class HybridRouter {
427
515
  // Track cost
428
516
  const actualCost = this.calculateCost(response, decision.provider);
429
517
  this.totalCost += actualCost;
518
+ // Track detailed cost information
519
+ this.trackCostByProvider(decision.providerName, actualCost, response.model, decision.complexity, response.usage.input_tokens + response.usage.output_tokens);
430
520
  // Record outcome for learning
431
521
  if (this.config.enableLearning) {
432
522
  this.recordOutcome({
@@ -658,15 +748,55 @@ class HybridRouter {
658
748
  * Get cost savings report including RuVector cache savings
659
749
  */
660
750
  getCostSavingsReport() {
661
- // Estimate what cloud cost would have been for all requests
751
+ return this.getDetailedCostReport();
752
+ }
753
+ /**
754
+ * Get detailed cost report with optional date filtering
755
+ */
756
+ getDetailedCostReport(startDate, endDate) {
757
+ const start = startDate || this.trackingStartDate;
758
+ const end = endDate || new Date();
759
+ // Filter cost history by date range
760
+ const filteredHistory = this.costHistory.filter(entry => entry.timestamp >= start && entry.timestamp <= end);
761
+ // Calculate cost by provider
762
+ const costByProvider = {};
763
+ const costByTaskType = {};
764
+ const costByModel = {};
765
+ const taskTypeCounts = {};
766
+ for (const entry of filteredHistory) {
767
+ // By provider
768
+ costByProvider[entry.provider] = (costByProvider[entry.provider] || 0) + entry.cost;
769
+ // By task type
770
+ costByTaskType[entry.taskType] = (costByTaskType[entry.taskType] || 0) + entry.cost;
771
+ // By model
772
+ costByModel[entry.model] = (costByModel[entry.model] || 0) + entry.cost;
773
+ // Task type counts for top costly tasks
774
+ if (!taskTypeCounts[entry.taskType]) {
775
+ taskTypeCounts[entry.taskType] = { cost: 0, count: 0 };
776
+ }
777
+ taskTypeCounts[entry.taskType].cost += entry.cost;
778
+ taskTypeCounts[entry.taskType].count++;
779
+ }
780
+ // Calculate top costly tasks
781
+ const topCostlyTasks = Object.entries(taskTypeCounts)
782
+ .map(([taskType, data]) => ({
783
+ taskType,
784
+ cost: data.cost,
785
+ count: data.count
786
+ }))
787
+ .sort((a, b) => b.cost - a.cost)
788
+ .slice(0, 10);
789
+ // Calculate totals and estimates
662
790
  const cloudCostPerRequest = 0.01; // Rough estimate
663
791
  const estimatedCloudCost = this.requestCount * cloudCostPerRequest;
664
792
  const savings = estimatedCloudCost - this.totalCost;
665
793
  const savingsPercentage = estimatedCloudCost > 0
666
794
  ? (savings / estimatedCloudCost) * 100
667
795
  : 0;
668
- // Cache hits are essentially free
669
796
  const cacheSavings = this.cacheHitCount * cloudCostPerRequest;
797
+ const averageCostPerRequest = this.requestCount > 0 ? this.totalCost / this.requestCount : 0;
798
+ // Project monthly cost based on current usage rate
799
+ const monthlyCostProjection = this.projectMonthlyCost();
670
800
  return {
671
801
  totalRequests: this.requestCount,
672
802
  localRequests: this.localRequestCount,
@@ -676,8 +806,190 @@ class HybridRouter {
676
806
  savings,
677
807
  savingsPercentage,
678
808
  cacheHits: this.cacheHitCount,
679
- cacheSavings
809
+ cacheSavings,
810
+ costByProvider,
811
+ costByTaskType,
812
+ costByModel,
813
+ averageCostPerRequest,
814
+ topCostlyTasks,
815
+ monthlyCostProjection,
816
+ periodStart: start,
817
+ periodEnd: end
818
+ };
819
+ }
820
+ /**
821
+ * Get top costly operations
822
+ */
823
+ getTopCostlyOperations(limit = 10) {
824
+ const taskTypeCosts = {};
825
+ for (const entry of this.costHistory) {
826
+ taskTypeCosts[entry.taskType] = (taskTypeCosts[entry.taskType] || 0) + entry.cost;
827
+ }
828
+ return Object.entries(taskTypeCosts)
829
+ .map(([taskType, cost]) => ({ taskType, cost }))
830
+ .sort((a, b) => b.cost - a.cost)
831
+ .slice(0, limit);
832
+ }
833
+ /**
834
+ * Project monthly cost based on current usage rate
835
+ */
836
+ projectMonthlyCost() {
837
+ if (this.costHistory.length === 0) {
838
+ return 0;
839
+ }
840
+ const now = new Date();
841
+ const timeElapsed = now.getTime() - this.trackingStartDate.getTime();
842
+ const daysElapsed = timeElapsed / (1000 * 60 * 60 * 24);
843
+ if (daysElapsed < 0.1) {
844
+ // Less than 2.4 hours, not enough data
845
+ return this.totalCost * 30; // Rough estimate
846
+ }
847
+ // Calculate daily average and project to 30 days
848
+ const dailyAverage = this.totalCost / daysElapsed;
849
+ return dailyAverage * 30;
850
+ }
851
+ /**
852
+ * Set budget configuration
853
+ */
854
+ setBudget(config) {
855
+ this.budgetConfig = config;
856
+ this.logger.info('Budget configuration updated', {
857
+ monthlyBudget: config.monthlyBudget,
858
+ dailyBudget: config.dailyBudget,
859
+ alertThreshold: config.alertThreshold,
860
+ enforceLimit: config.enforceLimit
861
+ });
862
+ }
863
+ /**
864
+ * Get current budget status
865
+ */
866
+ getBudgetStatus() {
867
+ if (!this.budgetConfig) {
868
+ return {
869
+ dailySpent: this.totalCost,
870
+ dailyRemaining: Infinity,
871
+ monthlySpent: this.totalCost,
872
+ monthlyRemaining: Infinity,
873
+ utilizationPercentage: 0,
874
+ isOverBudget: false,
875
+ alertTriggered: false
876
+ };
877
+ }
878
+ const now = new Date();
879
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
880
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
881
+ // Calculate daily spending
882
+ const dailyHistory = this.costHistory.filter(entry => entry.timestamp >= today);
883
+ const dailySpent = dailyHistory.reduce((sum, entry) => sum + entry.cost, 0);
884
+ // Calculate monthly spending
885
+ const monthlyHistory = this.costHistory.filter(entry => entry.timestamp >= monthStart);
886
+ const monthlySpent = monthlyHistory.reduce((sum, entry) => sum + entry.cost, 0);
887
+ const dailyBudget = this.budgetConfig.dailyBudget ?? Infinity;
888
+ const monthlyBudget = this.budgetConfig.monthlyBudget ?? Infinity;
889
+ const dailyRemaining = Math.max(0, dailyBudget - dailySpent);
890
+ const monthlyRemaining = Math.max(0, monthlyBudget - monthlySpent);
891
+ // Calculate utilization based on the most restrictive budget
892
+ let utilizationPercentage = 0;
893
+ if (monthlyBudget !== Infinity) {
894
+ utilizationPercentage = (monthlySpent / monthlyBudget) * 100;
895
+ }
896
+ else if (dailyBudget !== Infinity) {
897
+ utilizationPercentage = (dailySpent / dailyBudget) * 100;
898
+ }
899
+ const isOverBudget = dailySpent > dailyBudget || monthlySpent > monthlyBudget;
900
+ const alertTriggered = utilizationPercentage >= (this.budgetConfig.alertThreshold * 100);
901
+ if (alertTriggered && !isOverBudget) {
902
+ this.logger.warn('Budget alert threshold reached', {
903
+ utilizationPercentage,
904
+ threshold: this.budgetConfig.alertThreshold * 100,
905
+ dailySpent,
906
+ monthlySpent
907
+ });
908
+ }
909
+ if (isOverBudget) {
910
+ this.logger.error('Budget limit exceeded', {
911
+ dailySpent,
912
+ dailyBudget,
913
+ monthlySpent,
914
+ monthlyBudget
915
+ });
916
+ }
917
+ return {
918
+ dailySpent,
919
+ dailyRemaining,
920
+ monthlySpent,
921
+ monthlyRemaining,
922
+ utilizationPercentage,
923
+ isOverBudget,
924
+ alertTriggered
925
+ };
926
+ }
927
+ /**
928
+ * Check if budget allows this request
929
+ */
930
+ checkBudget(estimatedCost) {
931
+ if (!this.budgetConfig || !this.budgetConfig.enforceLimit) {
932
+ return true;
933
+ }
934
+ const status = this.getBudgetStatus();
935
+ if (status.isOverBudget) {
936
+ this.logger.error('Request rejected: budget exceeded', {
937
+ estimatedCost,
938
+ dailySpent: status.dailySpent,
939
+ monthlySpent: status.monthlySpent
940
+ });
941
+ return false;
942
+ }
943
+ // Check if this request would exceed budget
944
+ if (this.budgetConfig.dailyBudget !== undefined) {
945
+ if (status.dailySpent + estimatedCost > this.budgetConfig.dailyBudget) {
946
+ this.logger.error('Request rejected: would exceed daily budget', {
947
+ estimatedCost,
948
+ dailySpent: status.dailySpent,
949
+ dailyBudget: this.budgetConfig.dailyBudget
950
+ });
951
+ return false;
952
+ }
953
+ }
954
+ if (this.budgetConfig.monthlyBudget !== undefined) {
955
+ if (status.monthlySpent + estimatedCost > this.budgetConfig.monthlyBudget) {
956
+ this.logger.error('Request rejected: would exceed monthly budget', {
957
+ estimatedCost,
958
+ monthlySpent: status.monthlySpent,
959
+ monthlyBudget: this.budgetConfig.monthlyBudget
960
+ });
961
+ return false;
962
+ }
963
+ }
964
+ return true;
965
+ }
966
+ /**
967
+ * Track cost by provider
968
+ */
969
+ trackCostByProvider(provider, cost, model, taskType, tokens) {
970
+ const entry = {
971
+ timestamp: new Date(),
972
+ provider,
973
+ model,
974
+ taskType,
975
+ cost,
976
+ tokens
680
977
  };
978
+ this.costHistory.push(entry);
979
+ // Keep only last 10,000 entries to prevent unbounded growth
980
+ if (this.costHistory.length > 10000) {
981
+ this.costHistory.shift();
982
+ }
983
+ if (this.config.debug) {
984
+ this.logger.debug('Cost tracked', {
985
+ provider,
986
+ model,
987
+ taskType,
988
+ cost,
989
+ tokens,
990
+ historySize: this.costHistory.length
991
+ });
992
+ }
681
993
  }
682
994
  /**
683
995
  * Get routing statistics including cache metrics
@@ -872,9 +1184,36 @@ class HybridRouter {
872
1184
  return options;
873
1185
  }
874
1186
  /**
875
- * Analyze task complexity
1187
+ * Analyze task complexity using ML classifier or heuristics fallback
876
1188
  */
877
1189
  analyzeComplexity(options) {
1190
+ // Use ML-based classifier if available (Phase 2.1.2)
1191
+ if (this.complexityClassifier && this.config.useMLClassifier) {
1192
+ try {
1193
+ const mlComplexity = this.complexityClassifier.classifyTask(options);
1194
+ const confidence = this.complexityClassifier.getClassificationConfidence();
1195
+ if (this.config.debug) {
1196
+ this.logger.debug('ML complexity classification', {
1197
+ complexity: mlComplexity,
1198
+ confidence: confidence.toFixed(3)
1199
+ });
1200
+ }
1201
+ // Map ComplexityClassifier output to TaskComplexity enum
1202
+ return mlComplexity;
1203
+ }
1204
+ catch (error) {
1205
+ this.logger.warn('ML classifier failed, using heuristics', {
1206
+ error: error.message
1207
+ });
1208
+ }
1209
+ }
1210
+ // Fallback to heuristics
1211
+ return this.analyzeComplexityHeuristics(options);
1212
+ }
1213
+ /**
1214
+ * Heuristic-based complexity analysis (fallback)
1215
+ */
1216
+ analyzeComplexityHeuristics(options) {
878
1217
  const maxTokens = options.maxTokens || 0;
879
1218
  const messageCount = options.messages.length;
880
1219
  const totalContent = options.messages
@@ -911,6 +1250,27 @@ class HybridRouter {
911
1250
  return TaskComplexity.MODERATE;
912
1251
  return TaskComplexity.SIMPLE;
913
1252
  }
1253
+ /**
1254
+ * Apply compressed content to options
1255
+ */
1256
+ applyCompressedContent(options, compressed) {
1257
+ if (!options.messages || options.messages.length === 0) {
1258
+ return options;
1259
+ }
1260
+ // Clone options to avoid mutation
1261
+ const newOptions = { ...options, messages: [...options.messages] };
1262
+ // Find and replace last user message with compressed version
1263
+ for (let i = newOptions.messages.length - 1; i >= 0; i--) {
1264
+ if (newOptions.messages[i].role === 'user') {
1265
+ newOptions.messages[i] = {
1266
+ ...newOptions.messages[i],
1267
+ content: compressed
1268
+ };
1269
+ break;
1270
+ }
1271
+ }
1272
+ return newOptions;
1273
+ }
914
1274
  /**
915
1275
  * Check for privacy-sensitive data
916
1276
  */
@@ -1061,6 +1421,112 @@ class HybridRouter {
1061
1421
  throw new ILLMProvider_1.LLMProviderError('HybridRouter not initialized. Call initialize() first.', 'hybrid-router', 'NOT_INITIALIZED', false);
1062
1422
  }
1063
1423
  }
1424
+ // ============================================================
1425
+ // Phase 2 Integration: Public Methods for Benchmarking & Stats
1426
+ // ============================================================
1427
+ /**
1428
+ * Get compression statistics for benchmarking (Phase 2.3.2)
1429
+ *
1430
+ * Returns actual measured compression savings, not estimated values.
1431
+ */
1432
+ getCompressionStats() {
1433
+ const avgSavings = this.compressionStats.totalOriginal > 0
1434
+ ? (this.compressionStats.totalSaved / this.compressionStats.totalOriginal) * 100
1435
+ : 0;
1436
+ return {
1437
+ totalRequests: this.compressionStats.count,
1438
+ totalOriginalTokens: this.compressionStats.totalOriginal,
1439
+ totalSavedTokens: this.compressionStats.totalSaved,
1440
+ averageSavingsPercent: avgSavings,
1441
+ compressionEnabled: !!this.costOptimizationManager && !!this.config.useCostOptimization
1442
+ };
1443
+ }
1444
+ /**
1445
+ * Get ML classifier statistics (Phase 2.1.2)
1446
+ *
1447
+ * Returns classifier performance metrics if ML classifier is enabled.
1448
+ */
1449
+ getMLClassifierStats() {
1450
+ if (!this.complexityClassifier) {
1451
+ return null;
1452
+ }
1453
+ try {
1454
+ const stats = this.complexityClassifier.getStatistics();
1455
+ return {
1456
+ enabled: true,
1457
+ totalClassifications: stats.totalClassifications,
1458
+ averageConfidence: stats.averageConfidence,
1459
+ successRate: stats.successRate,
1460
+ complexityDistribution: stats.complexityDistribution
1461
+ };
1462
+ }
1463
+ catch {
1464
+ return {
1465
+ enabled: true,
1466
+ totalClassifications: 0,
1467
+ averageConfidence: 0,
1468
+ successRate: 0,
1469
+ complexityDistribution: {
1470
+ [TaskComplexity.SIMPLE]: 0,
1471
+ [TaskComplexity.MODERATE]: 0,
1472
+ [TaskComplexity.COMPLEX]: 0,
1473
+ [TaskComplexity.VERY_COMPLEX]: 0
1474
+ }
1475
+ };
1476
+ }
1477
+ }
1478
+ /**
1479
+ * Train classifier from routing outcome (Phase 2.1.2)
1480
+ *
1481
+ * Allows manual training feedback for the complexity classifier.
1482
+ */
1483
+ trainClassifierFromOutcome(entry) {
1484
+ if (!this.complexityClassifier) {
1485
+ this.logger.warn('Cannot train: ML classifier not enabled');
1486
+ return;
1487
+ }
1488
+ this.complexityClassifier.recordOutcome(entry);
1489
+ if (this.config.debug) {
1490
+ this.logger.debug('Classifier trained from outcome', {
1491
+ complexity: entry.selectedComplexity,
1492
+ success: entry.actualOutcome.success
1493
+ });
1494
+ }
1495
+ }
1496
+ /**
1497
+ * Get feature weights from the ML classifier (Phase 2.1.2)
1498
+ */
1499
+ getClassifierWeights() {
1500
+ if (!this.complexityClassifier) {
1501
+ return null;
1502
+ }
1503
+ const weights = this.complexityClassifier.getWeights();
1504
+ // Convert typed object to generic Record
1505
+ return { ...weights };
1506
+ }
1507
+ /**
1508
+ * Get complexity thresholds from the ML classifier (Phase 2.1.2)
1509
+ */
1510
+ getComplexityThresholds() {
1511
+ if (!this.complexityClassifier) {
1512
+ return null;
1513
+ }
1514
+ const thresholds = this.complexityClassifier.getThresholds();
1515
+ // Convert typed object to generic Record
1516
+ return { ...thresholds };
1517
+ }
1518
+ /**
1519
+ * Check if ML classifier is enabled and active
1520
+ */
1521
+ isMLClassifierEnabled() {
1522
+ return !!this.complexityClassifier && !!this.config.useMLClassifier;
1523
+ }
1524
+ /**
1525
+ * Check if cost optimization is enabled and active
1526
+ */
1527
+ isCostOptimizationEnabled() {
1528
+ return !!this.costOptimizationManager && !!this.config.useCostOptimization;
1529
+ }
1064
1530
  }
1065
1531
  exports.HybridRouter = HybridRouter;
1066
1532
  //# sourceMappingURL=HybridRouter.js.map