@blockrun/clawrouter 0.12.95 → 0.12.97

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 (282) hide show
  1. package/dist/auth.d.ts +82 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +388 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/balance.d.ts +97 -0
  6. package/dist/balance.d.ts.map +1 -0
  7. package/dist/balance.js +157 -0
  8. package/dist/balance.js.map +1 -0
  9. package/dist/cli.d.ts +15 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +256 -54994
  12. package/dist/cli.js.map +1 -1
  13. package/dist/compression/codebook.d.ts +24 -0
  14. package/dist/compression/codebook.d.ts.map +1 -0
  15. package/dist/compression/codebook.js +119 -0
  16. package/dist/compression/codebook.js.map +1 -0
  17. package/dist/compression/index.d.ts +33 -0
  18. package/dist/compression/index.d.ts.map +1 -0
  19. package/dist/compression/index.js +246 -0
  20. package/dist/compression/index.js.map +1 -0
  21. package/dist/compression/layers/deduplication.d.ts +28 -0
  22. package/dist/compression/layers/deduplication.d.ts.map +1 -0
  23. package/dist/compression/layers/deduplication.js +101 -0
  24. package/dist/compression/layers/deduplication.js.map +1 -0
  25. package/dist/compression/layers/dictionary.d.ts +21 -0
  26. package/dist/compression/layers/dictionary.d.ts.map +1 -0
  27. package/dist/compression/layers/dictionary.js +73 -0
  28. package/dist/compression/layers/dictionary.js.map +1 -0
  29. package/dist/compression/layers/dynamic-codebook.d.ts +26 -0
  30. package/dist/compression/layers/dynamic-codebook.d.ts.map +1 -0
  31. package/dist/compression/layers/dynamic-codebook.js +149 -0
  32. package/dist/compression/layers/dynamic-codebook.js.map +1 -0
  33. package/dist/compression/layers/json-compact.d.ts +23 -0
  34. package/dist/compression/layers/json-compact.d.ts.map +1 -0
  35. package/dist/compression/layers/json-compact.js +79 -0
  36. package/dist/compression/layers/json-compact.js.map +1 -0
  37. package/dist/compression/layers/observation.d.ts +21 -0
  38. package/dist/compression/layers/observation.d.ts.map +1 -0
  39. package/dist/compression/layers/observation.js +130 -0
  40. package/dist/compression/layers/observation.js.map +1 -0
  41. package/dist/compression/layers/paths.d.ts +24 -0
  42. package/dist/compression/layers/paths.d.ts.map +1 -0
  43. package/dist/compression/layers/paths.js +110 -0
  44. package/dist/compression/layers/paths.js.map +1 -0
  45. package/dist/compression/layers/whitespace.d.ts +27 -0
  46. package/dist/compression/layers/whitespace.d.ts.map +1 -0
  47. package/dist/compression/layers/whitespace.js +60 -0
  48. package/dist/compression/layers/whitespace.js.map +1 -0
  49. package/dist/compression/types.d.ts +84 -0
  50. package/dist/compression/types.d.ts.map +1 -0
  51. package/dist/compression/types.js +27 -0
  52. package/dist/compression/types.js.map +1 -0
  53. package/dist/config.d.ts +12 -0
  54. package/dist/config.d.ts.map +1 -0
  55. package/dist/config.js +22 -0
  56. package/dist/config.js.map +1 -0
  57. package/dist/dedup.d.ts +34 -0
  58. package/dist/dedup.d.ts.map +1 -0
  59. package/dist/dedup.js +150 -0
  60. package/dist/dedup.js.map +1 -0
  61. package/dist/doctor.d.ts +8 -0
  62. package/dist/doctor.d.ts.map +1 -0
  63. package/dist/doctor.js +375 -0
  64. package/dist/doctor.js.map +1 -0
  65. package/dist/error-classification.test.d.ts +2 -0
  66. package/dist/error-classification.test.d.ts.map +1 -0
  67. package/dist/error-classification.test.js +59 -0
  68. package/dist/error-classification.test.js.map +1 -0
  69. package/dist/errors.d.ts +54 -0
  70. package/dist/errors.d.ts.map +1 -0
  71. package/dist/errors.js +85 -0
  72. package/dist/errors.js.map +1 -0
  73. package/dist/exclude-models.d.ts +28 -0
  74. package/dist/exclude-models.d.ts.map +1 -0
  75. package/dist/exclude-models.integration.test.d.ts +2 -0
  76. package/dist/exclude-models.integration.test.d.ts.map +1 -0
  77. package/dist/exclude-models.integration.test.js +46 -0
  78. package/dist/exclude-models.integration.test.js.map +1 -0
  79. package/dist/exclude-models.js +70 -0
  80. package/dist/exclude-models.js.map +1 -0
  81. package/dist/exclude-models.test.d.ts +5 -0
  82. package/dist/exclude-models.test.d.ts.map +1 -0
  83. package/dist/exclude-models.test.js +95 -0
  84. package/dist/exclude-models.test.js.map +1 -0
  85. package/dist/fs-read.d.ts +11 -0
  86. package/dist/fs-read.d.ts.map +1 -0
  87. package/dist/fs-read.js +47 -0
  88. package/dist/fs-read.js.map +1 -0
  89. package/dist/index.d.ts +34 -1368
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +1071 -56194
  92. package/dist/index.js.map +1 -1
  93. package/dist/journal.d.ts +67 -0
  94. package/dist/journal.d.ts.map +1 -0
  95. package/dist/journal.js +184 -0
  96. package/dist/journal.js.map +1 -0
  97. package/dist/journal.test.d.ts +2 -0
  98. package/dist/journal.test.d.ts.map +1 -0
  99. package/dist/journal.test.js +201 -0
  100. package/dist/journal.test.js.map +1 -0
  101. package/dist/logger.d.ts +33 -0
  102. package/dist/logger.d.ts.map +1 -0
  103. package/dist/logger.js +35 -0
  104. package/dist/logger.js.map +1 -0
  105. package/dist/models.d.ts +107 -0
  106. package/dist/models.d.ts.map +1 -0
  107. package/dist/models.js +941 -0
  108. package/dist/models.js.map +1 -0
  109. package/dist/models.test.d.ts +2 -0
  110. package/dist/models.test.d.ts.map +1 -0
  111. package/dist/models.test.js +38 -0
  112. package/dist/models.test.js.map +1 -0
  113. package/dist/partners/index.d.ts +8 -0
  114. package/dist/partners/index.d.ts.map +1 -0
  115. package/dist/partners/index.js +6 -0
  116. package/dist/partners/index.js.map +1 -0
  117. package/dist/partners/registry.d.ts +51 -0
  118. package/dist/partners/registry.d.ts.map +1 -0
  119. package/dist/partners/registry.js +256 -0
  120. package/dist/partners/registry.js.map +1 -0
  121. package/dist/partners/tools.d.ts +24 -0
  122. package/dist/partners/tools.d.ts.map +1 -0
  123. package/dist/partners/tools.js +97 -0
  124. package/dist/partners/tools.js.map +1 -0
  125. package/dist/payment-preauth.d.ts +17 -0
  126. package/dist/payment-preauth.d.ts.map +1 -0
  127. package/dist/payment-preauth.js +102 -0
  128. package/dist/payment-preauth.js.map +1 -0
  129. package/dist/provider.d.ts +19 -0
  130. package/dist/provider.d.ts.map +1 -0
  131. package/dist/provider.js +45 -0
  132. package/dist/provider.js.map +1 -0
  133. package/dist/proxy.d.ts +210 -0
  134. package/dist/proxy.d.ts.map +1 -0
  135. package/dist/proxy.debrand.test.d.ts +2 -0
  136. package/dist/proxy.debrand.test.d.ts.map +1 -0
  137. package/dist/proxy.debrand.test.js +101 -0
  138. package/dist/proxy.debrand.test.js.map +1 -0
  139. package/dist/proxy.degraded-response.test.d.ts +2 -0
  140. package/dist/proxy.degraded-response.test.d.ts.map +1 -0
  141. package/dist/proxy.degraded-response.test.js +105 -0
  142. package/dist/proxy.degraded-response.test.js.map +1 -0
  143. package/dist/proxy.image-download.test.d.ts +2 -0
  144. package/dist/proxy.image-download.test.d.ts.map +1 -0
  145. package/dist/proxy.image-download.test.js +92 -0
  146. package/dist/proxy.image-download.test.js.map +1 -0
  147. package/dist/proxy.js +3810 -0
  148. package/dist/proxy.js.map +1 -0
  149. package/dist/proxy.models-endpoint.test.d.ts +2 -0
  150. package/dist/proxy.models-endpoint.test.d.ts.map +1 -0
  151. package/dist/proxy.models-endpoint.test.js +19 -0
  152. package/dist/proxy.models-endpoint.test.js.map +1 -0
  153. package/dist/proxy.multiturn-thinking.test.d.ts +2 -0
  154. package/dist/proxy.multiturn-thinking.test.d.ts.map +1 -0
  155. package/dist/proxy.multiturn-thinking.test.js +82 -0
  156. package/dist/proxy.multiturn-thinking.test.js.map +1 -0
  157. package/dist/proxy.payment-chain-reuse.test.d.ts +2 -0
  158. package/dist/proxy.payment-chain-reuse.test.d.ts.map +1 -0
  159. package/dist/proxy.payment-chain-reuse.test.js +30 -0
  160. package/dist/proxy.payment-chain-reuse.test.js.map +1 -0
  161. package/dist/proxy.solana-resilience.test.d.ts +12 -0
  162. package/dist/proxy.solana-resilience.test.d.ts.map +1 -0
  163. package/dist/proxy.solana-resilience.test.js +39 -0
  164. package/dist/proxy.solana-resilience.test.js.map +1 -0
  165. package/dist/report.d.ts +6 -0
  166. package/dist/report.d.ts.map +1 -0
  167. package/dist/report.js +44 -0
  168. package/dist/report.js.map +1 -0
  169. package/dist/response-cache.advanced.test.d.ts +2 -0
  170. package/dist/response-cache.advanced.test.d.ts.map +1 -0
  171. package/dist/response-cache.advanced.test.js +566 -0
  172. package/dist/response-cache.advanced.test.js.map +1 -0
  173. package/dist/response-cache.d.ts +85 -0
  174. package/dist/response-cache.d.ts.map +1 -0
  175. package/dist/response-cache.edge-cases.test.d.ts +2 -0
  176. package/dist/response-cache.edge-cases.test.d.ts.map +1 -0
  177. package/dist/response-cache.edge-cases.test.js +488 -0
  178. package/dist/response-cache.edge-cases.test.js.map +1 -0
  179. package/dist/response-cache.extreme.test.d.ts +2 -0
  180. package/dist/response-cache.extreme.test.d.ts.map +1 -0
  181. package/dist/response-cache.extreme.test.js +561 -0
  182. package/dist/response-cache.extreme.test.js.map +1 -0
  183. package/dist/response-cache.js +240 -0
  184. package/dist/response-cache.js.map +1 -0
  185. package/dist/response-cache.test.d.ts +2 -0
  186. package/dist/response-cache.test.d.ts.map +1 -0
  187. package/dist/response-cache.test.js +242 -0
  188. package/dist/response-cache.test.js.map +1 -0
  189. package/dist/retry.d.ts +42 -0
  190. package/dist/retry.d.ts.map +1 -0
  191. package/dist/retry.js +99 -0
  192. package/dist/retry.js.map +1 -0
  193. package/dist/router/config.d.ts +11 -0
  194. package/dist/router/config.d.ts.map +1 -0
  195. package/dist/router/config.js +1215 -0
  196. package/dist/router/config.js.map +1 -0
  197. package/dist/router/index.d.ts +18 -0
  198. package/dist/router/index.d.ts.map +1 -0
  199. package/dist/router/index.js +19 -0
  200. package/dist/router/index.js.map +1 -0
  201. package/dist/router/llm-classifier.d.ts +29 -0
  202. package/dist/router/llm-classifier.d.ts.map +1 -0
  203. package/dist/router/llm-classifier.js +99 -0
  204. package/dist/router/llm-classifier.js.map +1 -0
  205. package/dist/router/rules.d.ts +12 -0
  206. package/dist/router/rules.d.ts.map +1 -0
  207. package/dist/router/rules.js +202 -0
  208. package/dist/router/rules.js.map +1 -0
  209. package/dist/router/selector.d.ts +58 -0
  210. package/dist/router/selector.d.ts.map +1 -0
  211. package/dist/router/selector.js +165 -0
  212. package/dist/router/selector.js.map +1 -0
  213. package/dist/router/selector.test.d.ts +2 -0
  214. package/dist/router/selector.test.d.ts.map +1 -0
  215. package/dist/router/selector.test.js +67 -0
  216. package/dist/router/selector.test.js.map +1 -0
  217. package/dist/router/strategy.d.ts +19 -0
  218. package/dist/router/strategy.d.ts.map +1 -0
  219. package/dist/router/strategy.js +138 -0
  220. package/dist/router/strategy.js.map +1 -0
  221. package/dist/router/strategy.test.d.ts +2 -0
  222. package/dist/router/strategy.test.d.ts.map +1 -0
  223. package/dist/router/strategy.test.js +131 -0
  224. package/dist/router/strategy.test.js.map +1 -0
  225. package/dist/router/types.d.ts +127 -0
  226. package/dist/router/types.d.ts.map +1 -0
  227. package/dist/router/types.js +11 -0
  228. package/dist/router/types.js.map +1 -0
  229. package/dist/session.d.ts +124 -0
  230. package/dist/session.d.ts.map +1 -0
  231. package/dist/session.js +256 -0
  232. package/dist/session.js.map +1 -0
  233. package/dist/session.test.d.ts +2 -0
  234. package/dist/session.test.d.ts.map +1 -0
  235. package/dist/session.test.js +163 -0
  236. package/dist/session.test.js.map +1 -0
  237. package/dist/solana-balance.d.ts +48 -0
  238. package/dist/solana-balance.d.ts.map +1 -0
  239. package/dist/solana-balance.js +142 -0
  240. package/dist/solana-balance.js.map +1 -0
  241. package/dist/spend-control.d.ts +112 -0
  242. package/dist/spend-control.d.ts.map +1 -0
  243. package/dist/spend-control.js +278 -0
  244. package/dist/spend-control.js.map +1 -0
  245. package/dist/spend-control.test.d.ts +5 -0
  246. package/dist/spend-control.test.d.ts.map +1 -0
  247. package/dist/spend-control.test.js +210 -0
  248. package/dist/spend-control.test.js.map +1 -0
  249. package/dist/stats.d.ts +64 -0
  250. package/dist/stats.d.ts.map +1 -0
  251. package/dist/stats.js +295 -0
  252. package/dist/stats.js.map +1 -0
  253. package/dist/types.d.ts +150 -0
  254. package/dist/types.d.ts.map +1 -0
  255. package/dist/types.js +9 -0
  256. package/dist/types.js.map +1 -0
  257. package/dist/update-hint.test.d.ts +9 -0
  258. package/dist/update-hint.test.d.ts.map +1 -0
  259. package/dist/update-hint.test.js +156 -0
  260. package/dist/update-hint.test.js.map +1 -0
  261. package/dist/updater.d.ts +10 -0
  262. package/dist/updater.d.ts.map +1 -0
  263. package/dist/updater.js +55 -0
  264. package/dist/updater.js.map +1 -0
  265. package/dist/version.d.ts +3 -0
  266. package/dist/version.d.ts.map +1 -0
  267. package/dist/version.js +16 -0
  268. package/dist/version.js.map +1 -0
  269. package/dist/wallet.d.ts +52 -0
  270. package/dist/wallet.d.ts.map +1 -0
  271. package/dist/wallet.js +93 -0
  272. package/dist/wallet.js.map +1 -0
  273. package/dist/wallet.test.d.ts +6 -0
  274. package/dist/wallet.test.d.ts.map +1 -0
  275. package/dist/wallet.test.js +117 -0
  276. package/dist/wallet.test.js.map +1 -0
  277. package/dist/x402-sdk.test.d.ts +6 -0
  278. package/dist/x402-sdk.test.d.ts.map +1 -0
  279. package/dist/x402-sdk.test.js +161 -0
  280. package/dist/x402-sdk.test.js.map +1 -0
  281. package/package.json +1 -1
  282. package/scripts/e2e-multiturn.mjs +4 -1
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Tier → Model Selection
3
+ *
4
+ * Maps a classification tier to the cheapest capable model.
5
+ * Builds RoutingDecision metadata with cost estimates and savings.
6
+ */
7
+ const BASELINE_MODEL_ID = "anthropic/claude-opus-4.6";
8
+ // Hardcoded fallback: Claude Opus 4.6 pricing (per 1M tokens)
9
+ // Used when baseline model not found in dynamic pricing map
10
+ const BASELINE_INPUT_PRICE = 5.0;
11
+ const BASELINE_OUTPUT_PRICE = 25.0;
12
+ /**
13
+ * Select the primary model for a tier and build the RoutingDecision.
14
+ */
15
+ export function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile, agenticScore) {
16
+ const tierConfig = tierConfigs[tier];
17
+ const model = tierConfig.primary;
18
+ const pricing = modelPricing.get(model);
19
+ let costEstimate;
20
+ if (pricing?.flatPrice !== undefined) {
21
+ costEstimate = pricing.flatPrice;
22
+ }
23
+ else {
24
+ const inputPrice = pricing?.inputPrice ?? 0;
25
+ const outputPrice = pricing?.outputPrice ?? 0;
26
+ costEstimate =
27
+ (estimatedInputTokens / 1_000_000) * inputPrice +
28
+ (maxOutputTokens / 1_000_000) * outputPrice;
29
+ }
30
+ // Baseline: what Claude Opus 4.5 would cost (the premium reference)
31
+ const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
32
+ const opusInputPrice = opusPricing?.inputPrice ?? BASELINE_INPUT_PRICE;
33
+ const opusOutputPrice = opusPricing?.outputPrice ?? BASELINE_OUTPUT_PRICE;
34
+ const baselineInput = (estimatedInputTokens / 1_000_000) * opusInputPrice;
35
+ const baselineOutput = (maxOutputTokens / 1_000_000) * opusOutputPrice;
36
+ const baselineCost = baselineInput + baselineOutput;
37
+ // Premium profile doesn't calculate savings (it's about quality, not cost)
38
+ const savings = routingProfile === "premium"
39
+ ? 0
40
+ : baselineCost > 0
41
+ ? Math.max(0, (baselineCost - costEstimate) / baselineCost)
42
+ : 0;
43
+ return {
44
+ model,
45
+ tier,
46
+ confidence,
47
+ method,
48
+ reasoning,
49
+ costEstimate,
50
+ baselineCost,
51
+ savings,
52
+ ...(agenticScore !== undefined && { agenticScore }),
53
+ };
54
+ }
55
+ /**
56
+ * Get the ordered fallback chain for a tier: [primary, ...fallbacks].
57
+ */
58
+ export function getFallbackChain(tier, tierConfigs) {
59
+ const config = tierConfigs[tier];
60
+ return [config.primary, ...config.fallback];
61
+ }
62
+ /**
63
+ * Calculate cost for a specific model (used when fallback model is used).
64
+ * Returns updated cost fields for RoutingDecision.
65
+ */
66
+ // Server-side margin applied to all x402 payments (must match blockrun server's MARGIN_PERCENT)
67
+ const SERVER_MARGIN_PERCENT = 5;
68
+ // Minimum payment enforced by CDP Facilitator (must match blockrun server's MIN_PAYMENT_USD)
69
+ const MIN_PAYMENT_USD = 0.001;
70
+ export function calculateModelCost(model, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile) {
71
+ const pricing = modelPricing.get(model);
72
+ let costEstimate;
73
+ if (pricing?.flatPrice !== undefined) {
74
+ // Active promo: fixed cost per request
75
+ costEstimate = Math.max(pricing.flatPrice * (1 + SERVER_MARGIN_PERCENT / 100), MIN_PAYMENT_USD);
76
+ }
77
+ else {
78
+ // Defensive: guard against undefined price fields (not just undefined pricing)
79
+ const inputPrice = pricing?.inputPrice ?? 0;
80
+ const outputPrice = pricing?.outputPrice ?? 0;
81
+ const inputCost = (estimatedInputTokens / 1_000_000) * inputPrice;
82
+ const outputCost = (maxOutputTokens / 1_000_000) * outputPrice;
83
+ // Include server margin + minimum payment to match actual x402 charge
84
+ costEstimate = Math.max((inputCost + outputCost) * (1 + SERVER_MARGIN_PERCENT / 100), MIN_PAYMENT_USD);
85
+ }
86
+ // Baseline: what Claude Opus 4.5 would cost (the premium reference)
87
+ const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
88
+ const opusInputPrice = opusPricing?.inputPrice ?? BASELINE_INPUT_PRICE;
89
+ const opusOutputPrice = opusPricing?.outputPrice ?? BASELINE_OUTPUT_PRICE;
90
+ const baselineInput = (estimatedInputTokens / 1_000_000) * opusInputPrice;
91
+ const baselineOutput = (maxOutputTokens / 1_000_000) * opusOutputPrice;
92
+ const baselineCost = baselineInput + baselineOutput;
93
+ // Premium profile doesn't calculate savings (it's about quality, not cost)
94
+ const savings = routingProfile === "premium"
95
+ ? 0
96
+ : baselineCost > 0
97
+ ? Math.max(0, (baselineCost - costEstimate) / baselineCost)
98
+ : 0;
99
+ return { costEstimate, baselineCost, savings };
100
+ }
101
+ /**
102
+ * Filter a model list to only those that support tool calling.
103
+ * When hasTools is false, returns the list unchanged.
104
+ * When all models lack tool calling support, returns the full list as a fallback
105
+ * (better to let the API error than produce an empty chain).
106
+ */
107
+ export function filterByToolCalling(models, hasTools, supportsToolCalling) {
108
+ if (!hasTools)
109
+ return models;
110
+ const filtered = models.filter(supportsToolCalling);
111
+ return filtered.length > 0 ? filtered : models;
112
+ }
113
+ /**
114
+ * Filter a model list to only those that support vision (image inputs).
115
+ * When hasVision is false, returns the list unchanged.
116
+ * When all models lack vision support, returns the full list as a fallback
117
+ * (better to let the API error than produce an empty chain).
118
+ */
119
+ export function filterByVision(models, hasVision, supportsVision) {
120
+ if (!hasVision)
121
+ return models;
122
+ const filtered = models.filter(supportsVision);
123
+ return filtered.length > 0 ? filtered : models;
124
+ }
125
+ /**
126
+ * Filter a model list to remove user-excluded models.
127
+ * When all models are excluded, returns the full list as a fallback
128
+ * (same safety pattern as filterByToolCalling/filterByVision).
129
+ */
130
+ export function filterByExcludeList(models, excludeList) {
131
+ if (excludeList.size === 0)
132
+ return models;
133
+ const filtered = models.filter((m) => !excludeList.has(m));
134
+ return filtered.length > 0 ? filtered : models;
135
+ }
136
+ /**
137
+ * Get the fallback chain filtered by context length.
138
+ * Only returns models that can handle the estimated total context.
139
+ *
140
+ * @param tier - The tier to get fallback chain for
141
+ * @param tierConfigs - Tier configurations
142
+ * @param estimatedTotalTokens - Estimated total context (input + output)
143
+ * @param getContextWindow - Function to get context window for a model ID
144
+ * @returns Filtered list of models that can handle the context
145
+ */
146
+ export function getFallbackChainFiltered(tier, tierConfigs, estimatedTotalTokens, getContextWindow) {
147
+ const fullChain = getFallbackChain(tier, tierConfigs);
148
+ // Filter to models that can handle the context
149
+ const filtered = fullChain.filter((modelId) => {
150
+ const contextWindow = getContextWindow(modelId);
151
+ if (contextWindow === undefined) {
152
+ // Unknown model - include it (let API reject if needed)
153
+ return true;
154
+ }
155
+ // Add 10% buffer for safety
156
+ return contextWindow >= estimatedTotalTokens * 1.1;
157
+ });
158
+ // If all models filtered out, return the original chain
159
+ // (let the API error out - better than no options)
160
+ if (filtered.length === 0) {
161
+ return fullChain;
162
+ }
163
+ return filtered;
164
+ }
165
+ //# sourceMappingURL=selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/router/selector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;AAEtD,8DAA8D;AAC9D,4DAA4D;AAC5D,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACjC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,IAAU,EACV,UAAkB,EAClB,MAAuB,EACvB,SAAiB,EACjB,WAAqC,EACrC,YAAuC,EACvC,oBAA4B,EAC5B,eAAuB,EACvB,cAAoD,EACpD,YAAqB;IAErB,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC;IACjC,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAExC,IAAI,YAAoB,CAAC;IACzB,IAAI,OAAO,EAAE,SAAS,KAAK,SAAS,EAAE,CAAC;QACrC,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IACnC,CAAC;SAAM,CAAC;QACN,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC;QAC5C,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,CAAC,CAAC;QAC9C,YAAY;YACV,CAAC,oBAAoB,GAAG,SAAS,CAAC,GAAG,UAAU;gBAC/C,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,WAAW,CAAC;IAChD,CAAC;IAED,oEAAoE;IACpE,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACxD,MAAM,cAAc,GAAG,WAAW,EAAE,UAAU,IAAI,oBAAoB,CAAC;IACvE,MAAM,eAAe,GAAG,WAAW,EAAE,WAAW,IAAI,qBAAqB,CAAC;IAC1E,MAAM,aAAa,GAAG,CAAC,oBAAoB,GAAG,SAAS,CAAC,GAAG,cAAc,CAAC;IAC1E,MAAM,cAAc,GAAG,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,eAAe,CAAC;IACvE,MAAM,YAAY,GAAG,aAAa,GAAG,cAAc,CAAC;IAEpD,2EAA2E;IAC3E,MAAM,OAAO,GACX,cAAc,KAAK,SAAS;QAC1B,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,YAAY,GAAG,CAAC;YAChB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,YAAY,GAAG,YAAY,CAAC,GAAG,YAAY,CAAC;YAC3D,CAAC,CAAC,CAAC,CAAC;IAEV,OAAO;QACL,KAAK;QACL,IAAI;QACJ,UAAU;QACV,MAAM;QACN,SAAS;QACT,YAAY;QACZ,YAAY;QACZ,OAAO;QACP,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,CAAC;KACpD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAU,EAAE,WAAqC;IAChF,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,gGAAgG;AAChG,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,6FAA6F;AAC7F,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,YAAuC,EACvC,oBAA4B,EAC5B,eAAuB,EACvB,cAAoD;IAEpD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAExC,IAAI,YAAoB,CAAC;IACzB,IAAI,OAAO,EAAE,SAAS,KAAK,SAAS,EAAE,CAAC;QACrC,uCAAuC;QACvC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,qBAAqB,GAAG,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC;IAClG,CAAC;SAAM,CAAC;QACN,+EAA+E;QAC/E,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC;QAC5C,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,CAAC,oBAAoB,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC;QAClE,MAAM,UAAU,GAAG,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,WAAW,CAAC;QAC/D,sEAAsE;QACtE,YAAY,GAAG,IAAI,CAAC,GAAG,CACrB,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,qBAAqB,GAAG,GAAG,CAAC,EAC5D,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACxD,MAAM,cAAc,GAAG,WAAW,EAAE,UAAU,IAAI,oBAAoB,CAAC;IACvE,MAAM,eAAe,GAAG,WAAW,EAAE,WAAW,IAAI,qBAAqB,CAAC;IAC1E,MAAM,aAAa,GAAG,CAAC,oBAAoB,GAAG,SAAS,CAAC,GAAG,cAAc,CAAC;IAC1E,MAAM,cAAc,GAAG,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,eAAe,CAAC;IACvE,MAAM,YAAY,GAAG,aAAa,GAAG,cAAc,CAAC;IAEpD,2EAA2E;IAC3E,MAAM,OAAO,GACX,cAAc,KAAK,SAAS;QAC1B,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,YAAY,GAAG,CAAC;YAChB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,YAAY,GAAG,YAAY,CAAC,GAAG,YAAY,CAAC;YAC3D,CAAC,CAAC,CAAC,CAAC;IAEV,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAgB,EAChB,QAAiB,EACjB,mBAAiD;IAEjD,IAAI,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACpD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAgB,EAChB,SAAkB,EAClB,cAA4C;IAE5C,IAAI,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC;IAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC/C,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAgB,EAAE,WAAwB;IAC5E,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;AACjD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAU,EACV,WAAqC,EACrC,oBAA4B,EAC5B,gBAAyD;IAEzD,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAEtD,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QAC5C,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAChC,wDAAwD;YACxD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,4BAA4B;QAC5B,OAAO,aAAa,IAAI,oBAAoB,GAAG,GAAG,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,wDAAwD;IACxD,mDAAmD;IACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=selector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.test.d.ts","sourceRoot":"","sources":["../../src/router/selector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { calculateModelCost, filterByExcludeList, filterByToolCalling, selectModel, } from "./selector.js";
3
+ const TIER_CONFIGS = {
4
+ SIMPLE: { primary: "moonshot/kimi-k2.5", fallback: [] },
5
+ MEDIUM: { primary: "moonshot/kimi-k2.5", fallback: [] },
6
+ COMPLEX: { primary: "moonshot/kimi-k2.5", fallback: [] },
7
+ REASONING: { primary: "moonshot/kimi-k2.5", fallback: [] },
8
+ };
9
+ const MODEL_PRICING = new Map([
10
+ ["moonshot/kimi-k2.5", { inputPrice: 0.5, outputPrice: 2.4 }],
11
+ ["anthropic/claude-opus-4.6", { inputPrice: 5, outputPrice: 25 }],
12
+ ]);
13
+ describe("selectModel", () => {
14
+ it("uses claude-opus-4.6 as baseline ID when computing savings", () => {
15
+ const decision = selectModel("SIMPLE", 0.95, "rules", "test", TIER_CONFIGS, MODEL_PRICING, 1000, 1000);
16
+ expect(decision.baselineCost).toBeGreaterThan(0);
17
+ expect(decision.savings).toBeGreaterThan(0);
18
+ });
19
+ });
20
+ describe("filterByToolCalling", () => {
21
+ const supportsToolCalling = (modelId) => !["minimax/minimax-m2.5", "nvidia/gpt-oss-120b"].includes(modelId);
22
+ it("removes models without tool calling support when request has tools", () => {
23
+ const models = ["moonshot/kimi-k2.5", "minimax/minimax-m2.5", "deepseek/deepseek-chat"];
24
+ const filtered = filterByToolCalling(models, true, supportsToolCalling);
25
+ expect(filtered).toEqual(["moonshot/kimi-k2.5", "deepseek/deepseek-chat"]);
26
+ });
27
+ it("keeps all models when request has no tools", () => {
28
+ const models = ["moonshot/kimi-k2.5", "minimax/minimax-m2.5", "nvidia/gpt-oss-120b"];
29
+ const filtered = filterByToolCalling(models, false, supportsToolCalling);
30
+ expect(filtered).toEqual(models);
31
+ });
32
+ it("returns original list unchanged when all models support tool calling", () => {
33
+ const models = ["moonshot/kimi-k2.5", "anthropic/claude-sonnet-4.6", "deepseek/deepseek-chat"];
34
+ const filtered = filterByToolCalling(models, true, supportsToolCalling);
35
+ expect(filtered).toEqual(models);
36
+ });
37
+ it("returns full list unchanged when no models support tool calling, to avoid empty chain", () => {
38
+ const models = ["minimax/minimax-m2.5", "nvidia/gpt-oss-120b"];
39
+ const filtered = filterByToolCalling(models, true, supportsToolCalling);
40
+ expect(filtered).toEqual(models);
41
+ });
42
+ });
43
+ describe("filterByExcludeList", () => {
44
+ const chain = ["moonshot/kimi-k2.5", "deepseek/deepseek-chat", "anthropic/claude-sonnet-4.6"];
45
+ it("removes excluded models from chain", () => {
46
+ const excludeList = new Set(["deepseek/deepseek-chat"]);
47
+ const filtered = filterByExcludeList(chain, excludeList);
48
+ expect(filtered).toEqual(["moonshot/kimi-k2.5", "anthropic/claude-sonnet-4.6"]);
49
+ });
50
+ it("returns original chain if ALL models excluded (safety net)", () => {
51
+ const excludeList = new Set(chain);
52
+ const filtered = filterByExcludeList(chain, excludeList);
53
+ expect(filtered).toEqual(chain);
54
+ });
55
+ it("returns original chain for empty exclude set", () => {
56
+ const filtered = filterByExcludeList(chain, new Set());
57
+ expect(filtered).toEqual(chain);
58
+ });
59
+ });
60
+ describe("calculateModelCost", () => {
61
+ it("uses claude-opus-4.6 as baseline ID when recomputing fallback costs", () => {
62
+ const costs = calculateModelCost("moonshot/kimi-k2.5", MODEL_PRICING, 1000, 1000);
63
+ expect(costs.baselineCost).toBeGreaterThan(0);
64
+ expect(costs.savings).toBeGreaterThan(0);
65
+ });
66
+ });
67
+ //# sourceMappingURL=selector.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.test.js","sourceRoot":"","sources":["../../src/router/selector.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Router Strategy Registry
3
+ *
4
+ * Pluggable strategy system for request routing.
5
+ * Default: RulesStrategy — identical to the original inline route() logic, <1ms.
6
+ */
7
+ import type { RoutingDecision, RouterStrategy, RouterOptions } from "./types.js";
8
+ /**
9
+ * Rules-based routing strategy.
10
+ * Extracted from the original route() in index.ts — logic is identical.
11
+ * Attaches tierConfigs and profile to the decision for downstream use.
12
+ */
13
+ export declare class RulesStrategy implements RouterStrategy {
14
+ readonly name = "rules";
15
+ route(prompt: string, systemPrompt: string | undefined, maxOutputTokens: number, options: RouterOptions): RoutingDecision;
16
+ }
17
+ export declare function getStrategy(name: string): RouterStrategy;
18
+ export declare function registerStrategy(strategy: RouterStrategy): void;
19
+ //# sourceMappingURL=strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../../src/router/strategy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAA+B,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA8C9G;;;;GAIG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD,QAAQ,CAAC,IAAI,WAAW;IAExB,KAAK,CACH,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,aAAa,GACrB,eAAe;CAwGnB;AAOD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAMxD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAE/D"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Router Strategy Registry
3
+ *
4
+ * Pluggable strategy system for request routing.
5
+ * Default: RulesStrategy — identical to the original inline route() logic, <1ms.
6
+ */
7
+ import { classifyByRules } from "./rules.js";
8
+ import { selectModel } from "./selector.js";
9
+ /**
10
+ * Apply active time-windowed promotions to tier configs.
11
+ * Returns a new tierConfigs object with promotion overrides merged in.
12
+ * Expired or not-yet-active promotions are ignored.
13
+ */
14
+ function applyPromotions(tierConfigs, promotions, profile, now = new Date()) {
15
+ if (!promotions || promotions.length === 0)
16
+ return tierConfigs;
17
+ let result = tierConfigs;
18
+ for (const promo of promotions) {
19
+ // Check time window
20
+ const start = new Date(promo.startDate);
21
+ const end = new Date(promo.endDate);
22
+ if (now < start || now >= end)
23
+ continue;
24
+ // Check profile filter
25
+ if (promo.profiles && !promo.profiles.includes(profile))
26
+ continue;
27
+ // Shallow-clone on first mutation
28
+ if (result === tierConfigs) {
29
+ result = { ...tierConfigs };
30
+ for (const t of Object.keys(result)) {
31
+ result[t] = { ...result[t] };
32
+ }
33
+ }
34
+ // Merge overrides
35
+ for (const [tier, override] of Object.entries(promo.tierOverrides)) {
36
+ if (!result[tier])
37
+ continue;
38
+ if (override.primary)
39
+ result[tier].primary = override.primary;
40
+ if (override.fallback)
41
+ result[tier].fallback = override.fallback;
42
+ }
43
+ }
44
+ return result;
45
+ }
46
+ /**
47
+ * Rules-based routing strategy.
48
+ * Extracted from the original route() in index.ts — logic is identical.
49
+ * Attaches tierConfigs and profile to the decision for downstream use.
50
+ */
51
+ export class RulesStrategy {
52
+ name = "rules";
53
+ route(prompt, systemPrompt, maxOutputTokens, options) {
54
+ const { config, modelPricing } = options;
55
+ // Estimate input tokens (~4 chars per token)
56
+ const fullText = `${systemPrompt ?? ""} ${prompt}`;
57
+ const estimatedTokens = Math.ceil(fullText.length / 4);
58
+ // --- Rule-based classification (runs first to get agenticScore) ---
59
+ const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);
60
+ // --- Select tier configs based on routing profile ---
61
+ const { routingProfile } = options;
62
+ let tierConfigs;
63
+ let profileSuffix;
64
+ let profile;
65
+ if (routingProfile === "eco" && config.ecoTiers) {
66
+ tierConfigs = config.ecoTiers;
67
+ profileSuffix = " | eco";
68
+ profile = "eco";
69
+ }
70
+ else if (routingProfile === "premium" && config.premiumTiers) {
71
+ tierConfigs = config.premiumTiers;
72
+ profileSuffix = " | premium";
73
+ profile = "premium";
74
+ }
75
+ else {
76
+ // Auto profile (or undefined): intelligent routing with agentic detection
77
+ const agenticScore = ruleResult.agenticScore ?? 0;
78
+ const isAutoAgentic = agenticScore >= 0.5;
79
+ const isExplicitAgentic = config.overrides.agenticMode ?? false;
80
+ const hasToolsInRequest = options.hasTools ?? false;
81
+ const useAgenticTiers = (hasToolsInRequest || isAutoAgentic || isExplicitAgentic) && config.agenticTiers != null;
82
+ tierConfigs = useAgenticTiers ? config.agenticTiers : config.tiers;
83
+ profileSuffix = useAgenticTiers ? ` | agentic${hasToolsInRequest ? " (tools)" : ""}` : "";
84
+ profile = useAgenticTiers ? "agentic" : "auto";
85
+ }
86
+ // Apply time-windowed promotions
87
+ tierConfigs = applyPromotions(tierConfigs, config.promotions, profile);
88
+ const agenticScoreValue = ruleResult.agenticScore;
89
+ // --- Override: large context → force COMPLEX ---
90
+ if (estimatedTokens > config.overrides.maxTokensForceComplex) {
91
+ const decision = selectModel("COMPLEX", 0.95, "rules", `Input exceeds ${config.overrides.maxTokensForceComplex} tokens${profileSuffix}`, tierConfigs, modelPricing, estimatedTokens, maxOutputTokens, routingProfile, agenticScoreValue);
92
+ return { ...decision, tierConfigs, profile };
93
+ }
94
+ // Structured output detection
95
+ const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;
96
+ let tier;
97
+ let confidence;
98
+ const method = "rules";
99
+ let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(", ")}`;
100
+ if (ruleResult.tier !== null) {
101
+ tier = ruleResult.tier;
102
+ confidence = ruleResult.confidence;
103
+ }
104
+ else {
105
+ // Ambiguous — default to configurable tier (no external API call)
106
+ tier = config.overrides.ambiguousDefaultTier;
107
+ confidence = 0.5;
108
+ reasoning += ` | ambiguous -> default: ${tier}`;
109
+ }
110
+ // Apply structured output minimum tier
111
+ if (hasStructuredOutput) {
112
+ const tierRank = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };
113
+ const minTier = config.overrides.structuredOutputMinTier;
114
+ if (tierRank[tier] < tierRank[minTier]) {
115
+ reasoning += ` | upgraded to ${minTier} (structured output)`;
116
+ tier = minTier;
117
+ }
118
+ }
119
+ // Add routing profile suffix to reasoning
120
+ reasoning += profileSuffix;
121
+ const decision = selectModel(tier, confidence, method, reasoning, tierConfigs, modelPricing, estimatedTokens, maxOutputTokens, routingProfile, agenticScoreValue);
122
+ return { ...decision, tierConfigs, profile };
123
+ }
124
+ }
125
+ // --- Strategy Registry ---
126
+ const registry = new Map();
127
+ registry.set("rules", new RulesStrategy());
128
+ export function getStrategy(name) {
129
+ const strategy = registry.get(name);
130
+ if (!strategy) {
131
+ throw new Error(`Unknown routing strategy: ${name}`);
132
+ }
133
+ return strategy;
134
+ }
135
+ export function registerStrategy(strategy) {
136
+ registry.set(strategy.name, strategy);
137
+ }
138
+ //# sourceMappingURL=strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy.js","sourceRoot":"","sources":["../../src/router/strategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=strategy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy.test.d.ts","sourceRoot":"","sources":["../../src/router/strategy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,131 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { RulesStrategy, getStrategy, registerStrategy } from "./strategy.js";
3
+ import { DEFAULT_ROUTING_CONFIG } from "./config.js";
4
+ import { route } from "./index.js";
5
+ const MODEL_PRICING = new Map([
6
+ ["moonshot/kimi-k2.5", { inputPrice: 0.5, outputPrice: 2.4 }],
7
+ ["anthropic/claude-opus-4.6", { inputPrice: 5, outputPrice: 25 }],
8
+ ["google/gemini-2.5-flash", { inputPrice: 0.15, outputPrice: 0.6 }],
9
+ ["google/gemini-2.5-flash-lite", { inputPrice: 0.1, outputPrice: 0.4 }],
10
+ ["deepseek/deepseek-chat", { inputPrice: 0.14, outputPrice: 0.28 }],
11
+ ["anthropic/claude-sonnet-4.6", { inputPrice: 3, outputPrice: 15 }],
12
+ ["google/gemini-3.1-pro", { inputPrice: 1.25, outputPrice: 10 }],
13
+ ["xai/grok-4-1-fast-reasoning", { inputPrice: 0.2, outputPrice: 0.5 }],
14
+ ["nvidia/gpt-oss-120b", { inputPrice: 0, outputPrice: 0 }],
15
+ ["nvidia/gpt-oss-20b", { inputPrice: 0, outputPrice: 0 }],
16
+ ["nvidia/nemotron-ultra-253b", { inputPrice: 0, outputPrice: 0 }],
17
+ ["nvidia/nemotron-3-super-120b", { inputPrice: 0, outputPrice: 0 }],
18
+ ["nvidia/nemotron-super-49b", { inputPrice: 0, outputPrice: 0 }],
19
+ ["nvidia/deepseek-v3.2", { inputPrice: 0, outputPrice: 0 }],
20
+ ["nvidia/mistral-large-3-675b", { inputPrice: 0, outputPrice: 0 }],
21
+ ["nvidia/qwen3-coder-480b", { inputPrice: 0, outputPrice: 0 }],
22
+ ["nvidia/devstral-2-123b", { inputPrice: 0, outputPrice: 0 }],
23
+ ["nvidia/glm-4.7", { inputPrice: 0, outputPrice: 0 }],
24
+ ["nvidia/llama-4-maverick", { inputPrice: 0, outputPrice: 0 }],
25
+ ]);
26
+ const baseOptions = {
27
+ config: DEFAULT_ROUTING_CONFIG,
28
+ modelPricing: MODEL_PRICING,
29
+ };
30
+ describe("RulesStrategy", () => {
31
+ it("returns tierConfigs in the decision", () => {
32
+ const strategy = new RulesStrategy();
33
+ const decision = strategy.route("hello", undefined, 100, baseOptions);
34
+ expect(decision.tierConfigs).toBeDefined();
35
+ expect(decision.tierConfigs.SIMPLE).toBeDefined();
36
+ expect(decision.tierConfigs.MEDIUM).toBeDefined();
37
+ expect(decision.tierConfigs.COMPLEX).toBeDefined();
38
+ expect(decision.tierConfigs.REASONING).toBeDefined();
39
+ });
40
+ it("returns profile in the decision", () => {
41
+ const strategy = new RulesStrategy();
42
+ const decision = strategy.route("hello", undefined, 100, baseOptions);
43
+ expect(decision.profile).toBeDefined();
44
+ expect(["auto", "eco", "premium", "agentic"]).toContain(decision.profile);
45
+ });
46
+ it("sets eco profile when routingProfile is eco", () => {
47
+ const strategy = new RulesStrategy();
48
+ const decision = strategy.route("hello", undefined, 100, {
49
+ ...baseOptions,
50
+ routingProfile: "eco",
51
+ });
52
+ expect(decision.profile).toBe("eco");
53
+ expect(decision.tierConfigs).toEqual(DEFAULT_ROUTING_CONFIG.ecoTiers);
54
+ });
55
+ it("sets premium profile when routingProfile is premium", () => {
56
+ const strategy = new RulesStrategy();
57
+ const decision = strategy.route("hello", undefined, 100, {
58
+ ...baseOptions,
59
+ routingProfile: "premium",
60
+ });
61
+ expect(decision.profile).toBe("premium");
62
+ expect(decision.tierConfigs).toEqual(DEFAULT_ROUTING_CONFIG.premiumTiers);
63
+ });
64
+ it("sets agentic profile when tools are present", () => {
65
+ const strategy = new RulesStrategy();
66
+ const decision = strategy.route("hello", undefined, 100, {
67
+ ...baseOptions,
68
+ hasTools: true,
69
+ });
70
+ expect(decision.profile).toBe("agentic");
71
+ expect(decision.tierConfigs).toEqual(DEFAULT_ROUTING_CONFIG.agenticTiers);
72
+ });
73
+ it("sets auto profile for default requests", () => {
74
+ const strategy = new RulesStrategy();
75
+ const decision = strategy.route("what is the capital of France", undefined, 100, baseOptions);
76
+ expect(decision.profile).toBe("auto");
77
+ expect(decision.tierConfigs).toEqual(DEFAULT_ROUTING_CONFIG.tiers);
78
+ });
79
+ });
80
+ describe("Strategy Registry", () => {
81
+ it("retrieves the default rules strategy", () => {
82
+ const strategy = getStrategy("rules");
83
+ expect(strategy).toBeInstanceOf(RulesStrategy);
84
+ expect(strategy.name).toBe("rules");
85
+ });
86
+ it("throws for unknown strategy", () => {
87
+ expect(() => getStrategy("nonexistent")).toThrow("Unknown routing strategy: nonexistent");
88
+ });
89
+ it("registers and retrieves a custom strategy", () => {
90
+ const custom = {
91
+ name: "custom-test",
92
+ route: (_prompt, _sys, _max, options) => ({
93
+ model: "test/model",
94
+ tier: "SIMPLE",
95
+ confidence: 1,
96
+ method: "rules",
97
+ reasoning: "custom strategy",
98
+ costEstimate: 0,
99
+ baselineCost: 0,
100
+ savings: 0,
101
+ tierConfigs: options.config.tiers,
102
+ profile: "auto",
103
+ }),
104
+ };
105
+ registerStrategy(custom);
106
+ const retrieved = getStrategy("custom-test");
107
+ expect(retrieved.name).toBe("custom-test");
108
+ const decision = retrieved.route("test", undefined, 100, baseOptions);
109
+ expect(decision.model).toBe("test/model");
110
+ expect(decision.reasoning).toBe("custom strategy");
111
+ });
112
+ });
113
+ describe("Backward compatibility", () => {
114
+ it("route() produces same model/tier/method as before", () => {
115
+ // Simple prompt → SIMPLE tier
116
+ const simple = route("hello", undefined, 100, baseOptions);
117
+ expect(simple.tier).toBe("SIMPLE");
118
+ expect(simple.method).toBe("rules");
119
+ expect(simple.model).toBeDefined();
120
+ // Reasoning prompt → REASONING tier
121
+ const reasoning = route("prove the theorem step by step using mathematical induction", undefined, 4096, baseOptions);
122
+ expect(reasoning.tier).toBe("REASONING");
123
+ expect(reasoning.method).toBe("rules");
124
+ // New fields are present
125
+ expect(simple.tierConfigs).toBeDefined();
126
+ expect(simple.profile).toBeDefined();
127
+ expect(reasoning.tierConfigs).toBeDefined();
128
+ expect(reasoning.profile).toBeDefined();
129
+ });
130
+ });
131
+ //# sourceMappingURL=strategy.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy.test.js","sourceRoot":"","sources":["../../src/router/strategy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAuB;IAClD,CAAC,oBAAoB,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;IAC7D,CAAC,2BAA2B,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IACjE,CAAC,yBAAyB,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;IACnE,CAAC,8BAA8B,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;IACvE,CAAC,wBAAwB,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACnE,CAAC,6BAA6B,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IACnE,CAAC,uBAAuB,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAChE,CAAC,6BAA6B,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;IACtE,CAAC,qBAAqB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC1D,CAAC,oBAAoB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC,4BAA4B,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACjE,CAAC,8BAA8B,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACnE,CAAC,2BAA2B,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAChE,CAAC,sBAAsB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC3D,CAAC,6BAA6B,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAClE,CAAC,yBAAyB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC9D,CAAC,wBAAwB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC7D,CAAC,gBAAgB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACrD,CAAC,yBAAyB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;CAC/D,CAAC,CAAC;AAEH,MAAM,WAAW,GAAkB;IACjC,MAAM,EAAE,sBAAsB;IAC9B,YAAY,EAAE,aAAa;CAC5B,CAAC;AAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QAEtE,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,CAAC,QAAQ,CAAC,WAAY,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAY,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAY,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,CAAC,QAAQ,CAAC,WAAY,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QAEtE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE;YACvD,GAAG,WAAW;YACd,cAAc,EAAE,KAAK;SACtB,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE;YACvD,GAAG,WAAW;YACd,cAAc,EAAE,SAAS;SAC1B,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,YAAY,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE;YACvD,GAAG,WAAW;YACd,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,YAAY,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,+BAA+B,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QAE9F,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,MAAM,GAAmB;YAC7B,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;gBACxC,KAAK,EAAE,YAAY;gBACnB,IAAI,EAAE,QAAiB;gBACvB,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,OAAgB;gBACxB,SAAS,EAAE,iBAAiB;gBAC5B,YAAY,EAAE,CAAC;gBACf,YAAY,EAAE,CAAC;gBACf,OAAO,EAAE,CAAC;gBACV,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK;gBACjC,OAAO,EAAE,MAAM;aAChB,CAAC;SACH,CAAC;QAEF,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACzB,MAAM,SAAS,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;QAC7C,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAE3C,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QACtE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1C,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,8BAA8B;QAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAEnC,oCAAoC;QACpC,MAAM,SAAS,GAAG,KAAK,CACrB,6DAA6D,EAC7D,SAAS,EACT,IAAI,EACJ,WAAW,CACZ,CAAC;QACF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEvC,yBAAyB;QACzB,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}