@blockrun/clawrouter 0.8.9 → 0.8.11

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/index.d.ts CHANGED
@@ -860,6 +860,7 @@ type AggregatedStats = {
860
860
  percentage: number;
861
861
  }>;
862
862
  dailyBreakdown: DailyStats[];
863
+ entriesWithBaseline: number;
863
864
  };
864
865
  /**
865
866
  * Get aggregated statistics for the last N days.
package/dist/index.js CHANGED
@@ -908,12 +908,16 @@ function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPric
908
908
  const tierConfig = tierConfigs[tier];
909
909
  const model = tierConfig.primary;
910
910
  const pricing = modelPricing.get(model);
911
- const inputCost = pricing ? estimatedInputTokens / 1e6 * pricing.inputPrice : 0;
912
- const outputCost = pricing ? maxOutputTokens / 1e6 * pricing.outputPrice : 0;
911
+ const inputPrice = pricing?.inputPrice ?? 0;
912
+ const outputPrice = pricing?.outputPrice ?? 0;
913
+ const inputCost = estimatedInputTokens / 1e6 * inputPrice;
914
+ const outputCost = maxOutputTokens / 1e6 * outputPrice;
913
915
  const costEstimate = inputCost + outputCost;
914
916
  const opusPricing = modelPricing.get("anthropic/claude-opus-4");
915
- const baselineInput = opusPricing ? estimatedInputTokens / 1e6 * opusPricing.inputPrice : 0;
916
- const baselineOutput = opusPricing ? maxOutputTokens / 1e6 * opusPricing.outputPrice : 0;
917
+ const opusInputPrice = opusPricing?.inputPrice ?? 0;
918
+ const opusOutputPrice = opusPricing?.outputPrice ?? 0;
919
+ const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
920
+ const baselineOutput = maxOutputTokens / 1e6 * opusOutputPrice;
917
921
  const baselineCost = baselineInput + baselineOutput;
918
922
  const savings = baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;
919
923
  return {
@@ -933,12 +937,16 @@ function getFallbackChain(tier, tierConfigs) {
933
937
  }
934
938
  function calculateModelCost(model, modelPricing, estimatedInputTokens, maxOutputTokens) {
935
939
  const pricing = modelPricing.get(model);
936
- const inputCost = pricing ? estimatedInputTokens / 1e6 * pricing.inputPrice : 0;
937
- const outputCost = pricing ? maxOutputTokens / 1e6 * pricing.outputPrice : 0;
940
+ const inputPrice = pricing?.inputPrice ?? 0;
941
+ const outputPrice = pricing?.outputPrice ?? 0;
942
+ const inputCost = estimatedInputTokens / 1e6 * inputPrice;
943
+ const outputCost = maxOutputTokens / 1e6 * outputPrice;
938
944
  const costEstimate = inputCost + outputCost;
939
945
  const opusPricing = modelPricing.get("anthropic/claude-opus-4");
940
- const baselineInput = opusPricing ? estimatedInputTokens / 1e6 * opusPricing.inputPrice : 0;
941
- const baselineOutput = opusPricing ? maxOutputTokens / 1e6 * opusPricing.outputPrice : 0;
946
+ const opusInputPrice = opusPricing?.inputPrice ?? 0;
947
+ const opusOutputPrice = opusPricing?.outputPrice ?? 0;
948
+ const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
949
+ const baselineOutput = maxOutputTokens / 1e6 * opusOutputPrice;
942
950
  const baselineCost = baselineInput + baselineOutput;
943
951
  const savings = baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;
944
952
  return { costEstimate, baselineCost, savings };
@@ -1821,6 +1829,12 @@ async function getStats(days = 7) {
1821
1829
  }
1822
1830
  const totalSavings = totalBaselineCost - totalCost;
1823
1831
  const savingsPercentage = totalBaselineCost > 0 ? totalSavings / totalBaselineCost * 100 : 0;
1832
+ let entriesWithBaseline = 0;
1833
+ for (const day of dailyBreakdown) {
1834
+ if (day.totalBaselineCost !== day.totalCost) {
1835
+ entriesWithBaseline += day.totalRequests;
1836
+ }
1837
+ }
1824
1838
  return {
1825
1839
  period: days === 1 ? "today" : `last ${days} days`,
1826
1840
  totalRequests,
@@ -1832,8 +1846,10 @@ async function getStats(days = 7) {
1832
1846
  avgCostPerRequest: totalRequests > 0 ? totalCost / totalRequests : 0,
1833
1847
  byTier: byTierWithPercentage,
1834
1848
  byModel: byModelWithPercentage,
1835
- dailyBreakdown: dailyBreakdown.reverse()
1849
+ dailyBreakdown: dailyBreakdown.reverse(),
1836
1850
  // Oldest first for charts
1851
+ entriesWithBaseline
1852
+ // How many entries have valid baseline tracking
1837
1853
  };
1838
1854
  }
1839
1855
  function formatStatsAscii(stats) {
@@ -1845,20 +1861,27 @@ function formatStatsAscii(stats) {
1845
1861
  lines.push(`\u2551 Total Requests: ${stats.totalRequests.toString().padEnd(41)}\u2551`);
1846
1862
  lines.push(`\u2551 Total Cost: $${stats.totalCost.toFixed(4).padEnd(43)}\u2551`);
1847
1863
  lines.push(`\u2551 Baseline Cost (Opus): $${stats.totalBaselineCost.toFixed(4).padEnd(33)}\u2551`);
1848
- lines.push(
1849
- `\u2551 \u{1F4B0} Total Saved: $${stats.totalSavings.toFixed(4)} (${stats.savingsPercentage.toFixed(1)}%)`.padEnd(
1850
- 61
1851
- ) + "\u2551"
1852
- );
1864
+ const savingsLine = `\u2551 \u{1F4B0} Total Saved: $${stats.totalSavings.toFixed(4)} (${stats.savingsPercentage.toFixed(1)}%)`;
1865
+ if (stats.entriesWithBaseline < stats.totalRequests && stats.entriesWithBaseline > 0) {
1866
+ lines.push(savingsLine.padEnd(61) + "\u2551");
1867
+ const note = `\u2551 (based on ${stats.entriesWithBaseline}/${stats.totalRequests} tracked requests)`;
1868
+ lines.push(note.padEnd(61) + "\u2551");
1869
+ } else {
1870
+ lines.push(savingsLine.padEnd(61) + "\u2551");
1871
+ }
1853
1872
  lines.push(`\u2551 Avg Latency: ${stats.avgLatencyMs.toFixed(0)}ms`.padEnd(61) + "\u2551");
1854
1873
  lines.push("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
1855
1874
  lines.push("\u2551 Routing by Tier: \u2551");
1856
- const tierOrder = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"];
1875
+ const knownTiers = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"];
1876
+ const allTiers = Object.keys(stats.byTier);
1877
+ const otherTiers = allTiers.filter((t) => !knownTiers.includes(t));
1878
+ const tierOrder = [...knownTiers.filter((t) => stats.byTier[t]), ...otherTiers];
1857
1879
  for (const tier of tierOrder) {
1858
1880
  const data = stats.byTier[tier];
1859
1881
  if (data) {
1860
1882
  const bar = "\u2588".repeat(Math.min(20, Math.round(data.percentage / 5)));
1861
- const line = `\u2551 ${tier.padEnd(10)} ${bar.padEnd(20)} ${data.percentage.toFixed(1).padStart(5)}% (${data.count})`;
1883
+ const displayTier = tier === "UNKNOWN" ? "OTHER" : tier;
1884
+ const line = `\u2551 ${displayTier.padEnd(10)} ${bar.padEnd(20)} ${data.percentage.toFixed(1).padStart(5)}% (${data.count})`;
1862
1885
  lines.push(line.padEnd(61) + "\u2551");
1863
1886
  }
1864
1887
  }