@mupt-ai/dari-gateway 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/README.md +8 -4
  2. package/dist/bun.d.ts +2 -0
  3. package/dist/bun.js +4 -0
  4. package/dist/context/eval_catalog.d.ts +3 -0
  5. package/dist/context/eval_catalog.js +7 -0
  6. package/dist/context/interaction_fingerprint.d.ts +27 -0
  7. package/dist/context/interaction_fingerprint.js +519 -0
  8. package/dist/context/interaction_fingerprint_format.d.ts +42 -0
  9. package/dist/context/interaction_fingerprint_format.js +113 -0
  10. package/dist/context/interaction_lease_depth.d.ts +2 -0
  11. package/dist/context/interaction_lease_depth.js +110 -0
  12. package/dist/context/lease_history.d.ts +7 -0
  13. package/dist/context/lease_history.js +158 -0
  14. package/dist/context/pricing.d.ts +4 -0
  15. package/dist/context/pricing.js +27 -0
  16. package/dist/context/provider_prompt_accounting.d.ts +14 -0
  17. package/dist/context/provider_prompt_accounting.js +187 -0
  18. package/dist/context/routing_context.d.ts +40 -0
  19. package/dist/context/routing_context.js +211 -0
  20. package/dist/http/auth.d.ts +2 -0
  21. package/dist/http/auth.js +35 -0
  22. package/dist/http/errors.d.ts +19 -0
  23. package/dist/http/errors.js +89 -0
  24. package/dist/http/handler.d.ts +12 -0
  25. package/dist/http/handler.js +88 -0
  26. package/dist/http/http2_server.d.ts +6 -0
  27. package/dist/http/http2_server.js +103 -0
  28. package/dist/http/serve.d.ts +20 -0
  29. package/dist/http/serve.js +78 -0
  30. package/dist/index.d.ts +7 -0
  31. package/dist/index.js +9 -0
  32. package/dist/ingress/anthropic_messages.d.ts +3 -0
  33. package/dist/ingress/anthropic_messages.js +122 -0
  34. package/dist/ingress/anthropic_models.d.ts +1 -0
  35. package/dist/ingress/anthropic_models.js +23 -0
  36. package/dist/ingress/chat_completions.d.ts +2 -0
  37. package/dist/ingress/chat_completions.js +44 -0
  38. package/dist/ingress/claude_code/request_context.d.ts +2 -0
  39. package/dist/ingress/claude_code/request_context.js +31 -0
  40. package/dist/ingress/codex.d.ts +3 -0
  41. package/dist/ingress/codex.js +69 -0
  42. package/dist/ingress/codex_models.d.ts +2 -0
  43. package/dist/ingress/codex_models.js +89 -0
  44. package/dist/ingress/json.d.ts +1 -0
  45. package/dist/ingress/json.js +95 -0
  46. package/dist/ingress/models.d.ts +1 -0
  47. package/dist/ingress/models.js +65 -0
  48. package/dist/ingress/muse_code.d.ts +2 -0
  49. package/dist/ingress/muse_code.js +8 -0
  50. package/dist/ingress/muse_models.d.ts +9 -0
  51. package/dist/ingress/muse_models.js +32 -0
  52. package/dist/interaction/capabilities.d.ts +3 -0
  53. package/dist/interaction/capabilities.js +24 -0
  54. package/dist/interaction/image.d.ts +4 -0
  55. package/dist/interaction/image.js +13 -0
  56. package/dist/interaction/portable_reasoning_state.d.ts +4 -0
  57. package/dist/interaction/portable_reasoning_state.js +18 -0
  58. package/dist/interaction/provider_continuation_state.d.ts +6 -0
  59. package/dist/interaction/provider_continuation_state.js +27 -0
  60. package/dist/interaction/tool_identity.d.ts +10 -0
  61. package/dist/interaction/tool_identity.js +41 -0
  62. package/dist/interaction/types.d.ts +204 -0
  63. package/dist/interaction/types.js +5 -0
  64. package/dist/models/catalog.d.ts +35 -0
  65. package/dist/models/catalog.js +232 -0
  66. package/dist/models/context_limits.d.ts +2 -0
  67. package/dist/models/context_limits.js +7 -0
  68. package/dist/models/execution_provider.d.ts +9 -0
  69. package/dist/models/execution_provider.js +17 -0
  70. package/dist/models/gateways.d.ts +11 -0
  71. package/dist/models/gateways.js +45 -0
  72. package/dist/models/ids.d.ts +1 -0
  73. package/dist/models/ids.js +15 -0
  74. package/dist/models/provider_defaults.d.ts +4 -0
  75. package/dist/models/provider_defaults.js +44 -0
  76. package/dist/models/runtime_overrides.d.ts +7 -0
  77. package/dist/models/runtime_overrides.js +226 -0
  78. package/dist/models/subscriptions.d.ts +8 -0
  79. package/dist/models/subscriptions.js +37 -0
  80. package/dist/observability/errors.d.ts +8 -0
  81. package/dist/observability/errors.js +11 -0
  82. package/dist/platform/platform.d.ts +156 -0
  83. package/dist/platform/platform.js +11 -0
  84. package/dist/platform/routing_contract.d.ts +2 -0
  85. package/dist/platform/routing_contract.js +5 -0
  86. package/dist/protocol/input/anthropic_messages.d.ts +2 -0
  87. package/dist/protocol/input/anthropic_messages.js +494 -0
  88. package/dist/protocol/input/chat_completions.d.ts +4 -0
  89. package/dist/protocol/input/chat_completions.js +214 -0
  90. package/dist/protocol/input/chat_completions_validation.d.ts +2 -0
  91. package/dist/protocol/input/chat_completions_validation.js +258 -0
  92. package/dist/protocol/input/openai_responses.d.ts +2 -0
  93. package/dist/protocol/input/openai_responses.js +666 -0
  94. package/dist/protocol/input/router_model.d.ts +7 -0
  95. package/dist/protocol/input/router_model.js +43 -0
  96. package/dist/protocol/output/anthropic_messages.d.ts +10 -0
  97. package/dist/protocol/output/anthropic_messages.js +15 -0
  98. package/dist/protocol/output/anthropic_messages_output.d.ts +10 -0
  99. package/dist/protocol/output/anthropic_messages_output.js +124 -0
  100. package/dist/protocol/output/anthropic_messages_stream.d.ts +16 -0
  101. package/dist/protocol/output/anthropic_messages_stream.js +267 -0
  102. package/dist/protocol/output/chat_completions.d.ts +29 -0
  103. package/dist/protocol/output/chat_completions.js +406 -0
  104. package/dist/protocol/output/chat_reasoning_detail.d.ts +2 -0
  105. package/dist/protocol/output/chat_reasoning_detail.js +13 -0
  106. package/dist/protocol/output/delivered_stream_snapshot.d.ts +10 -0
  107. package/dist/protocol/output/delivered_stream_snapshot.js +31 -0
  108. package/dist/protocol/output/openai_responses.d.ts +10 -0
  109. package/dist/protocol/output/openai_responses.js +14 -0
  110. package/dist/protocol/output/openai_responses_output.d.ts +14 -0
  111. package/dist/protocol/output/openai_responses_output.js +125 -0
  112. package/dist/protocol/output/openai_responses_stream.d.ts +18 -0
  113. package/dist/protocol/output/openai_responses_stream.js +546 -0
  114. package/dist/protocol/output/sse_stream.d.ts +31 -0
  115. package/dist/protocol/output/sse_stream.js +67 -0
  116. package/dist/providers/anthropic_safeguards.d.ts +11 -0
  117. package/dist/providers/anthropic_safeguards.js +81 -0
  118. package/dist/providers/aws_assume_role.d.ts +17 -0
  119. package/dist/providers/aws_assume_role.js +107 -0
  120. package/dist/providers/cancel_estimate.d.ts +43 -0
  121. package/dist/providers/cancel_estimate.js +89 -0
  122. package/dist/providers/execution.d.ts +72 -0
  123. package/dist/providers/execution.js +491 -0
  124. package/dist/providers/model_capabilities.d.ts +11 -0
  125. package/dist/providers/model_capabilities.js +29 -0
  126. package/dist/providers/pi.d.ts +65 -0
  127. package/dist/providers/pi.js +325 -0
  128. package/dist/providers/pi_context.d.ts +7 -0
  129. package/dist/providers/pi_context.js +405 -0
  130. package/dist/providers/pi_options.d.ts +11 -0
  131. package/dist/providers/pi_options.js +410 -0
  132. package/dist/providers/pi_output.d.ts +2 -0
  133. package/dist/providers/pi_output.js +453 -0
  134. package/dist/providers/provider_attempt_runner.d.ts +55 -0
  135. package/dist/providers/provider_attempt_runner.js +90 -0
  136. package/dist/providers/provider_call.d.ts +77 -0
  137. package/dist/providers/provider_call.js +296 -0
  138. package/dist/providers/provider_reported_cost.d.ts +10 -0
  139. package/dist/providers/provider_reported_cost.js +123 -0
  140. package/dist/providers/reasoning.d.ts +12 -0
  141. package/dist/providers/reasoning.js +30 -0
  142. package/dist/providers/response_capture.d.ts +23 -0
  143. package/dist/providers/response_capture.js +47 -0
  144. package/dist/providers/stream_finalization.d.ts +46 -0
  145. package/dist/providers/stream_finalization.js +165 -0
  146. package/dist/providers/subscription_limits.d.ts +33 -0
  147. package/dist/providers/subscription_limits.js +179 -0
  148. package/dist/providers/subscription_usage.d.ts +10 -0
  149. package/dist/providers/subscription_usage.js +79 -0
  150. package/dist/runtime/claude_code/auxiliary_routing.d.ts +3 -0
  151. package/dist/runtime/claude_code/auxiliary_routing.js +60 -0
  152. package/dist/runtime/customer_disconnect.d.ts +20 -0
  153. package/dist/runtime/customer_disconnect.js +49 -0
  154. package/dist/runtime/explicit_model.d.ts +7 -0
  155. package/dist/runtime/explicit_model.js +57 -0
  156. package/dist/runtime/harness_identity/conversation.d.ts +33 -0
  157. package/dist/runtime/harness_identity/conversation.js +39 -0
  158. package/dist/runtime/harness_identity/extract.d.ts +8 -0
  159. package/dist/runtime/harness_identity/extract.js +214 -0
  160. package/dist/runtime/harness_identity/index.d.ts +2 -0
  161. package/dist/runtime/harness_identity/index.js +2 -0
  162. package/dist/runtime/prepare_routed_completion.d.ts +33 -0
  163. package/dist/runtime/prepare_routed_completion.js +186 -0
  164. package/dist/runtime/provider_stream_lifecycle.d.ts +31 -0
  165. package/dist/runtime/provider_stream_lifecycle.js +88 -0
  166. package/dist/runtime/provider_stream_outcome.d.ts +23 -0
  167. package/dist/runtime/provider_stream_outcome.js +55 -0
  168. package/dist/runtime/request_tags.d.ts +7 -0
  169. package/dist/runtime/request_tags.js +33 -0
  170. package/dist/runtime/routed_completion.d.ts +17 -0
  171. package/dist/runtime/routed_completion.js +80 -0
  172. package/dist/runtime/routed_protocol.d.ts +30 -0
  173. package/dist/runtime/routed_protocol.js +1 -0
  174. package/dist/runtime/routed_runtime.d.ts +40 -0
  175. package/dist/runtime/routed_runtime.js +535 -0
  176. package/dist/runtime/routing_lease.d.ts +26 -0
  177. package/dist/runtime/routing_lease.js +210 -0
  178. package/dist/runtime/runtime_reporter.d.ts +71 -0
  179. package/dist/runtime/runtime_reporter.js +35 -0
  180. package/dist/runtime/strict_provider_access.d.ts +6 -0
  181. package/dist/runtime/strict_provider_access.js +40 -0
  182. package/dist/self_host/config.d.ts +46 -0
  183. package/dist/self_host/config.js +105 -0
  184. package/dist/self_host/main.d.ts +16 -0
  185. package/dist/self_host/main.js +95 -0
  186. package/dist/self_host/platform.d.ts +13 -0
  187. package/dist/self_host/platform.js +50 -0
  188. package/dist/self_host/prefix_store.d.ts +46 -0
  189. package/dist/self_host/prefix_store.js +261 -0
  190. package/dist/self_host/resolve.d.ts +28 -0
  191. package/dist/self_host/resolve.js +167 -0
  192. package/dist/self_host/subscriptions/access.d.ts +10 -0
  193. package/dist/self_host/subscriptions/access.js +37 -0
  194. package/dist/self_host/subscriptions/http.d.ts +15 -0
  195. package/dist/self_host/subscriptions/http.js +187 -0
  196. package/dist/self_host/subscriptions/index.d.ts +24 -0
  197. package/dist/self_host/subscriptions/index.js +24 -0
  198. package/dist/self_host/subscriptions/oauth.d.ts +59 -0
  199. package/dist/self_host/subscriptions/oauth.js +376 -0
  200. package/dist/self_host/subscriptions/store.d.ts +78 -0
  201. package/dist/self_host/subscriptions/store.js +236 -0
  202. package/dist/strategies/auto_routing_selector.d.ts +12 -0
  203. package/dist/strategies/auto_routing_selector.js +61 -0
  204. package/dist/strategies/eval_relevance.d.ts +17 -0
  205. package/dist/strategies/eval_relevance.js +32 -0
  206. package/dist/strategies/fallback_decision.d.ts +8 -0
  207. package/dist/strategies/fallback_decision.js +17 -0
  208. package/dist/strategies/priority_selector.d.ts +7 -0
  209. package/dist/strategies/priority_selector.js +37 -0
  210. package/dist/strategies/route_decision.d.ts +22 -0
  211. package/dist/strategies/route_decision.js +65 -0
  212. package/dist/strategies/routing.d.ts +74 -0
  213. package/dist/strategies/routing.js +280 -0
  214. package/dist/strategies/selector_projection.d.ts +15 -0
  215. package/dist/strategies/selector_projection.js +161 -0
  216. package/dist/telemetry/background.d.ts +7 -0
  217. package/dist/telemetry/background.js +33 -0
  218. package/dist/telemetry/benchmark_replay.d.ts +37 -0
  219. package/dist/telemetry/benchmark_replay.js +108 -0
  220. package/dist/telemetry/billing_finalization.d.ts +64 -0
  221. package/dist/telemetry/billing_finalization.js +96 -0
  222. package/dist/telemetry/ingest_values.d.ts +4 -0
  223. package/dist/telemetry/ingest_values.js +12 -0
  224. package/dist/telemetry/model_cost.d.ts +3 -0
  225. package/dist/telemetry/model_cost.js +15 -0
  226. package/dist/telemetry/prefix_state.d.ts +47 -0
  227. package/dist/telemetry/prefix_state.js +116 -0
  228. package/dist/telemetry/report.d.ts +88 -0
  229. package/dist/telemetry/report.js +621 -0
  230. package/dist/telemetry/requests/interaction_observability.d.ts +45 -0
  231. package/dist/telemetry/requests/interaction_observability.js +180 -0
  232. package/dist/telemetry/requests/retained_interaction_request.d.ts +8 -0
  233. package/dist/telemetry/requests/retained_interaction_request.js +10 -0
  234. package/dist/telemetry/scrub.d.ts +2 -0
  235. package/dist/telemetry/scrub.js +31 -0
  236. package/dist/telemetry/skills/claude/catalog.d.ts +6 -0
  237. package/dist/telemetry/skills/claude/catalog.js +99 -0
  238. package/dist/telemetry/skills/claude/file_reads.d.ts +4 -0
  239. package/dist/telemetry/skills/claude/file_reads.js +110 -0
  240. package/dist/telemetry/skills/claude/observations.d.ts +3 -0
  241. package/dist/telemetry/skills/claude/observations.js +366 -0
  242. package/dist/telemetry/skills/codex/catalog.d.ts +11 -0
  243. package/dist/telemetry/skills/codex/catalog.js +138 -0
  244. package/dist/telemetry/skills/codex/exec_file_reads.d.ts +9 -0
  245. package/dist/telemetry/skills/codex/exec_file_reads.js +36 -0
  246. package/dist/telemetry/skills/codex/explicit.d.ts +3 -0
  247. package/dist/telemetry/skills/codex/explicit.js +90 -0
  248. package/dist/telemetry/skills/codex/file_reads.d.ts +4 -0
  249. package/dist/telemetry/skills/codex/file_reads.js +37 -0
  250. package/dist/telemetry/skills/codex/identity.d.ts +2 -0
  251. package/dist/telemetry/skills/codex/identity.js +20 -0
  252. package/dist/telemetry/skills/observations.d.ts +21 -0
  253. package/dist/telemetry/skills/observations.js +41 -0
  254. package/dist/telemetry/skills/paths.d.ts +11 -0
  255. package/dist/telemetry/skills/paths.js +61 -0
  256. package/dist/telemetry/skills/pi.d.ts +6 -0
  257. package/dist/telemetry/skills/pi.js +223 -0
  258. package/dist/telemetry/skills/shell_file_reads.d.ts +7 -0
  259. package/dist/telemetry/skills/shell_file_reads.js +226 -0
  260. package/dist/telemetry/skills/shell_words.d.ts +13 -0
  261. package/dist/telemetry/skills/shell_words.js +189 -0
  262. package/dist/telemetry/tools/input.d.ts +2 -0
  263. package/dist/telemetry/tools/input.js +14 -0
  264. package/dist/telemetry/tools/observations.d.ts +29 -0
  265. package/dist/telemetry/tools/observations.js +247 -0
  266. package/dist/telemetry/usage.d.ts +57 -0
  267. package/dist/telemetry/usage.js +136 -0
  268. package/dist/types.d.ts +97 -0
  269. package/dist/types.js +1 -0
  270. package/package.json +22 -7
  271. package/src/bun.ts +2 -2
  272. package/src/context/eval_catalog.ts +1 -1
  273. package/src/context/interaction_fingerprint.ts +9 -9
  274. package/src/context/interaction_fingerprint_format.ts +1 -1
  275. package/src/context/interaction_lease_depth.ts +1 -1
  276. package/src/context/pricing.ts +6 -3
  277. package/src/context/provider_prompt_accounting.ts +5 -5
  278. package/src/context/routing_context.ts +16 -16
  279. package/src/http/auth.ts +1 -1
  280. package/src/http/handler.ts +15 -8
  281. package/src/http/serve.ts +3 -3
  282. package/src/index.ts +14 -6
  283. package/src/ingress/anthropic_messages.ts +12 -12
  284. package/src/ingress/anthropic_models.ts +1 -1
  285. package/src/ingress/chat_completions.ts +17 -10
  286. package/src/ingress/claude_code/request_context.ts +2 -2
  287. package/src/ingress/codex.ts +14 -10
  288. package/src/ingress/codex_models.ts +4 -4
  289. package/src/ingress/json.ts +1 -1
  290. package/src/ingress/models.ts +22 -10
  291. package/src/ingress/muse_code.ts +9 -0
  292. package/src/ingress/muse_models.ts +36 -0
  293. package/src/interaction/capabilities.ts +1 -1
  294. package/src/interaction/portable_reasoning_state.ts +1 -1
  295. package/src/interaction/provider_continuation_state.ts +1 -1
  296. package/src/interaction/tool_identity.ts +2 -2
  297. package/src/interaction/types.ts +2 -0
  298. package/src/models/catalog.ts +310 -0
  299. package/src/models/context_limits.ts +1 -1
  300. package/src/models/execution_provider.ts +5 -2
  301. package/src/models/ids.ts +1 -1
  302. package/src/models/provider_defaults.ts +4 -1
  303. package/src/models/runtime_overrides.ts +23 -1
  304. package/src/models/subscriptions.ts +54 -0
  305. package/src/observability/errors.ts +2 -2
  306. package/src/platform/platform.ts +9 -5
  307. package/src/protocol/input/anthropic_messages.ts +7 -7
  308. package/src/protocol/input/chat_completions.ts +8 -6
  309. package/src/protocol/input/chat_completions_validation.ts +6 -5
  310. package/src/protocol/input/openai_responses.ts +6 -6
  311. package/src/protocol/output/anthropic_messages.ts +3 -3
  312. package/src/protocol/output/anthropic_messages_output.ts +6 -4
  313. package/src/protocol/output/anthropic_messages_stream.ts +7 -7
  314. package/src/protocol/output/chat_completions.ts +13 -13
  315. package/src/protocol/output/chat_reasoning_detail.ts +1 -1
  316. package/src/protocol/output/openai_responses.ts +3 -3
  317. package/src/protocol/output/openai_responses_output.ts +4 -2
  318. package/src/protocol/output/openai_responses_stream.ts +6 -6
  319. package/src/protocol/output/sse_stream.ts +3 -3
  320. package/src/providers/anthropic_safeguards.ts +2 -2
  321. package/src/providers/aws_assume_role.ts +2 -2
  322. package/src/providers/cancel_estimate.ts +1 -1
  323. package/src/providers/execution.ts +17 -17
  324. package/src/providers/pi.ts +34 -19
  325. package/src/providers/pi_context.ts +6 -6
  326. package/src/providers/pi_options.ts +27 -11
  327. package/src/providers/pi_output.ts +5 -5
  328. package/src/providers/provider_attempt_runner.ts +1 -1
  329. package/src/providers/provider_call.ts +21 -17
  330. package/src/providers/provider_reported_cost.ts +3 -3
  331. package/src/providers/reasoning.ts +2 -2
  332. package/src/providers/stream_finalization.ts +9 -9
  333. package/src/providers/subscription_limits.ts +24 -7
  334. package/src/runtime/claude_code/auxiliary_routing.ts +5 -5
  335. package/src/runtime/customer_disconnect.ts +1 -1
  336. package/src/runtime/explicit_model.ts +19 -20
  337. package/src/runtime/harness_identity/conversation.ts +1 -1
  338. package/src/runtime/harness_identity/extract.ts +3 -3
  339. package/src/runtime/harness_identity/index.ts +2 -2
  340. package/src/runtime/prepare_routed_completion.ts +35 -16
  341. package/src/runtime/provider_stream_lifecycle.ts +1 -1
  342. package/src/runtime/provider_stream_outcome.ts +2 -2
  343. package/src/runtime/request_tags.ts +2 -2
  344. package/src/runtime/routed_completion.ts +11 -11
  345. package/src/runtime/routed_protocol.ts +2 -2
  346. package/src/runtime/routed_runtime.ts +20 -19
  347. package/src/runtime/routing_lease.ts +7 -7
  348. package/src/runtime/runtime_reporter.ts +3 -3
  349. package/src/runtime/strict_provider_access.ts +42 -0
  350. package/src/self_host/main.ts +12 -12
  351. package/src/self_host/platform.ts +5 -5
  352. package/src/self_host/prefix_store.ts +2 -2
  353. package/src/self_host/resolve.ts +17 -31
  354. package/src/self_host/subscriptions/access.ts +2 -2
  355. package/src/self_host/subscriptions/http.ts +2 -2
  356. package/src/self_host/subscriptions/index.ts +4 -4
  357. package/src/self_host/subscriptions/oauth.ts +139 -8
  358. package/src/self_host/subscriptions/store.ts +2 -1
  359. package/src/strategies/auto_routing_selector.ts +2 -2
  360. package/src/strategies/eval_relevance.ts +3 -3
  361. package/src/strategies/priority_selector.ts +1 -1
  362. package/src/strategies/route_decision.ts +7 -7
  363. package/src/strategies/routing.ts +10 -10
  364. package/src/strategies/selector_projection.ts +2 -2
  365. package/src/telemetry/billing_finalization.ts +3 -3
  366. package/src/telemetry/model_cost.ts +1 -1
  367. package/src/telemetry/prefix_state.ts +4 -4
  368. package/src/telemetry/report.ts +21 -21
  369. package/src/telemetry/requests/interaction_observability.ts +1 -1
  370. package/src/telemetry/requests/retained_interaction_request.ts +1 -1
  371. package/src/telemetry/skills/claude/catalog.ts +1 -1
  372. package/src/telemetry/skills/claude/file_reads.ts +6 -6
  373. package/src/telemetry/skills/claude/observations.ts +3 -3
  374. package/src/telemetry/skills/codex/catalog.ts +3 -3
  375. package/src/telemetry/skills/codex/exec_file_reads.ts +4 -4
  376. package/src/telemetry/skills/codex/explicit.ts +3 -3
  377. package/src/telemetry/skills/codex/file_reads.ts +5 -5
  378. package/src/telemetry/skills/observations.ts +7 -7
  379. package/src/telemetry/skills/pi.ts +3 -3
  380. package/src/telemetry/skills/shell_file_reads.ts +2 -2
  381. package/src/telemetry/skills/shell_words.ts +1 -1
  382. package/src/telemetry/tools/input.ts +1 -1
  383. package/src/telemetry/tools/observations.ts +9 -9
  384. package/src/telemetry/usage.ts +5 -5
  385. package/src/types.ts +20 -3
@@ -0,0 +1,78 @@
1
+ /**
2
+ * SQLite storage for personal subscription credentials and pending OAuth flows.
3
+ *
4
+ * Tokens are stored as PLAINTEXT in the gateway's own database file. Anyone
5
+ * who can read that file can use the connected subscriptions until the
6
+ * provider revokes them, so keep the file on storage only the gateway's
7
+ * operator can read, and treat backups of it as secrets.
8
+ *
9
+ * Connection order (`position`) is the order routing switches through: the
10
+ * active credential for a provider is the first one, in that order, that is
11
+ * not held. A hold ends on its own once `unavailable_until` passes.
12
+ */
13
+ import type { Database } from "bun:sqlite";
14
+ import type { OAuthTokens, SubscriptionProvider } from "./oauth.js";
15
+ export type SubscriptionCredential = {
16
+ id: string;
17
+ provider: SubscriptionProvider;
18
+ name: string;
19
+ label: string | null;
20
+ accountEmail: string | null;
21
+ accountId: string | null;
22
+ accessToken: string;
23
+ refreshToken: string;
24
+ expiresAt: Date;
25
+ unavailableUntil: Date | null;
26
+ unavailableReason: string | null;
27
+ position: number;
28
+ createdAt: Date;
29
+ updatedAt: Date;
30
+ lastUsedAt: Date | null;
31
+ };
32
+ export type PendingOAuthSession = {
33
+ token: string;
34
+ provider: SubscriptionProvider;
35
+ flow: Record<string, string>;
36
+ credentialName: string | null;
37
+ expiresAt: Date;
38
+ };
39
+ export declare class SubscriptionStore {
40
+ private readonly db;
41
+ constructor(db: Database);
42
+ /** Every subscription, grouped by provider, in connection order. */
43
+ list(provider?: SubscriptionProvider): SubscriptionCredential[];
44
+ get(id: string): SubscriptionCredential | null;
45
+ getByName(name: string): SubscriptionCredential | null;
46
+ /** The credential routing uses now, or null when none is connected or every one is held. */
47
+ activeCredential(provider: SubscriptionProvider, now?: Date): SubscriptionCredential | null;
48
+ /**
49
+ * Store tokens from a completed OAuth flow. `reconnectName` replaces that
50
+ * subscription (null when it is not connected). Otherwise a subscription
51
+ * for the same account email is replaced, and anything else is connected
52
+ * last in order under the first free name.
53
+ */
54
+ connect(tokens: OAuthTokens, reconnectName: string | null, now?: Date): SubscriptionCredential | null;
55
+ /** Persist refreshed tokens. Keeps the stored account id when the refresh carries none. */
56
+ updateTokens(id: string, tokens: OAuthTokens, now?: Date): void;
57
+ touch(id: string, now?: Date): void;
58
+ /** An empty label clears it. Null when the subscription is not connected. */
59
+ rename(name: string, label: string | null, now?: Date): SubscriptionCredential | null;
60
+ /** Remove a subscription. Returns what was removed, or null when it was not connected. */
61
+ disconnect(name: string): SubscriptionCredential | null;
62
+ /**
63
+ * Hold an exhausted subscription until its reset. A hold only grows: a later
64
+ * report with an earlier reset never shortens a longer one. Returns whether
65
+ * a subscription for the same provider is available now.
66
+ */
67
+ markUnavailable(id: string, until: Date, reason: string, now?: Date): boolean;
68
+ /** Clear a usage-limit hold. Null when the subscription is not connected. */
69
+ markAvailable(name: string, now?: Date): SubscriptionCredential | null;
70
+ createSession(session: Omit<PendingOAuthSession, "token">, now?: Date): PendingOAuthSession;
71
+ /** A pending session, including an expired one so callers can say it expired. */
72
+ getSession(token: string): PendingOAuthSession | null;
73
+ deleteSession(token: string): void;
74
+ private freeName;
75
+ }
76
+ export declare function isHeld(credential: SubscriptionCredential, now: Date): boolean;
77
+ /** Subscription names are env-var-style identifiers; matching is case-insensitive. */
78
+ export declare function normalizeName(name: string): string;
@@ -0,0 +1,236 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ const BASE_NAMES = {
3
+ anthropic_claude_code: "ANTHROPIC_CLAUDE_CODE_OAUTH_TOKEN",
4
+ openai_codex: "OPENAI_CODEX_OAUTH_TOKEN",
5
+ meta_muse: "META_MUSE_OAUTH_TOKEN",
6
+ };
7
+ export class SubscriptionStore {
8
+ db;
9
+ constructor(db) {
10
+ this.db = db;
11
+ db.run(`CREATE TABLE IF NOT EXISTS subscription_credentials (
12
+ id TEXT PRIMARY KEY,
13
+ provider TEXT NOT NULL,
14
+ name TEXT NOT NULL UNIQUE,
15
+ label TEXT,
16
+ account_email TEXT,
17
+ account_id TEXT,
18
+ access_token TEXT NOT NULL,
19
+ refresh_token TEXT NOT NULL,
20
+ expires_at INTEGER NOT NULL,
21
+ unavailable_until INTEGER,
22
+ unavailable_reason TEXT,
23
+ position INTEGER NOT NULL,
24
+ created_at INTEGER NOT NULL,
25
+ updated_at INTEGER NOT NULL,
26
+ last_used_at INTEGER
27
+ )`);
28
+ db.run(`CREATE TABLE IF NOT EXISTS subscription_oauth_sessions (
29
+ token TEXT PRIMARY KEY,
30
+ provider TEXT NOT NULL,
31
+ flow_json TEXT NOT NULL,
32
+ credential_name TEXT,
33
+ expires_at INTEGER NOT NULL
34
+ )`);
35
+ }
36
+ /** Every subscription, grouped by provider, in connection order. */
37
+ list(provider) {
38
+ const rows = provider === undefined
39
+ ? this.db.query("SELECT * FROM subscription_credentials ORDER BY provider, position").all()
40
+ : this.db
41
+ .query("SELECT * FROM subscription_credentials WHERE provider = ? ORDER BY position")
42
+ .all(provider);
43
+ return rows.map(credentialFromRow);
44
+ }
45
+ get(id) {
46
+ const row = this.db.query("SELECT * FROM subscription_credentials WHERE id = ?").get(id);
47
+ return row === null ? null : credentialFromRow(row);
48
+ }
49
+ getByName(name) {
50
+ const row = this.db
51
+ .query("SELECT * FROM subscription_credentials WHERE name = ?")
52
+ .get(normalizeName(name));
53
+ return row === null ? null : credentialFromRow(row);
54
+ }
55
+ /** The credential routing uses now, or null when none is connected or every one is held. */
56
+ activeCredential(provider, now = new Date()) {
57
+ // Stay on the most recently used subscription while it serves, so its
58
+ // prompt cache stays warm; once it is held, move on in connection order.
59
+ const credentials = this.list(provider);
60
+ let start = 0;
61
+ credentials.forEach((credential, index) => {
62
+ const lastUsed = credential.lastUsedAt?.getTime() ?? -1;
63
+ if (lastUsed > (credentials[start].lastUsedAt?.getTime() ?? -1))
64
+ start = index;
65
+ });
66
+ for (let offset = 0; offset < credentials.length; offset += 1) {
67
+ const credential = credentials[(start + offset) % credentials.length];
68
+ if (!isHeld(credential, now))
69
+ return credential;
70
+ }
71
+ return null;
72
+ }
73
+ /**
74
+ * Store tokens from a completed OAuth flow. `reconnectName` replaces that
75
+ * subscription (null when it is not connected). Otherwise a subscription
76
+ * for the same account email is replaced, and anything else is connected
77
+ * last in order under the first free name.
78
+ */
79
+ connect(tokens, reconnectName, now = new Date()) {
80
+ return this.db.transaction(() => {
81
+ const existing = reconnectName !== null
82
+ ? this.getByName(reconnectName)
83
+ : this.list(tokens.provider).find((credential) => tokens.accountEmail !== null && credential.accountEmail === tokens.accountEmail) ?? null;
84
+ if (reconnectName !== null && existing?.provider !== tokens.provider)
85
+ return null;
86
+ if (existing !== null) {
87
+ // Another (or an unidentified) account must not inherit the previous account's hold.
88
+ const sameAccount = tokens.accountEmail !== null && tokens.accountEmail === existing.accountEmail;
89
+ this.db.run(`UPDATE subscription_credentials SET access_token = ?, refresh_token = ?, expires_at = ?,
90
+ account_id = ?, account_email = ?, unavailable_until = ?, unavailable_reason = ?, updated_at = ?
91
+ WHERE id = ?`, [
92
+ tokens.accessToken,
93
+ tokens.refreshToken,
94
+ tokens.expiresAt.getTime(),
95
+ tokens.accountId,
96
+ tokens.accountEmail,
97
+ sameAccount ? existing.unavailableUntil?.getTime() ?? null : null,
98
+ sameAccount ? existing.unavailableReason : null,
99
+ now.getTime(),
100
+ existing.id,
101
+ ]);
102
+ return this.get(existing.id);
103
+ }
104
+ const id = `cred_${randomUUID().replaceAll("-", "")}`;
105
+ const position = this.db
106
+ .query("SELECT COALESCE(MAX(position), 0) + 1 AS next FROM subscription_credentials")
107
+ .get().next;
108
+ this.db.run(`INSERT INTO subscription_credentials (id, provider, name, account_email, account_id, access_token,
109
+ refresh_token, expires_at, position, created_at, updated_at)
110
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
111
+ id,
112
+ tokens.provider,
113
+ this.freeName(BASE_NAMES[tokens.provider]),
114
+ tokens.accountEmail,
115
+ tokens.accountId,
116
+ tokens.accessToken,
117
+ tokens.refreshToken,
118
+ tokens.expiresAt.getTime(),
119
+ position,
120
+ now.getTime(),
121
+ now.getTime(),
122
+ ]);
123
+ return this.get(id);
124
+ })();
125
+ }
126
+ /** Persist refreshed tokens. Keeps the stored account id when the refresh carries none. */
127
+ updateTokens(id, tokens, now = new Date()) {
128
+ this.db.run(`UPDATE subscription_credentials SET access_token = ?, refresh_token = ?, expires_at = ?,
129
+ account_id = COALESCE(?, account_id), updated_at = ? WHERE id = ?`, [tokens.accessToken, tokens.refreshToken, tokens.expiresAt.getTime(), tokens.accountId, now.getTime(), id]);
130
+ }
131
+ touch(id, now = new Date()) {
132
+ this.db.run("UPDATE subscription_credentials SET last_used_at = ? WHERE id = ?", [now.getTime(), id]);
133
+ }
134
+ /** An empty label clears it. Null when the subscription is not connected. */
135
+ rename(name, label, now = new Date()) {
136
+ const normalized = label?.trim() || null;
137
+ if (normalized !== null && normalized.length > 120) {
138
+ throw new RangeError("Subscription name must be at most 120 characters.");
139
+ }
140
+ const credential = this.getByName(name);
141
+ if (credential === null)
142
+ return null;
143
+ this.db.run("UPDATE subscription_credentials SET label = ?, updated_at = ? WHERE id = ?", [
144
+ normalized,
145
+ now.getTime(),
146
+ credential.id,
147
+ ]);
148
+ return this.get(credential.id);
149
+ }
150
+ /** Remove a subscription. Returns what was removed, or null when it was not connected. */
151
+ disconnect(name) {
152
+ const credential = this.getByName(name);
153
+ if (credential === null)
154
+ return null;
155
+ this.db.run("DELETE FROM subscription_credentials WHERE id = ?", [credential.id]);
156
+ return credential;
157
+ }
158
+ /**
159
+ * Hold an exhausted subscription until its reset. A hold only grows: a later
160
+ * report with an earlier reset never shortens a longer one. Returns whether
161
+ * a subscription for the same provider is available now.
162
+ */
163
+ markUnavailable(id, until, reason, now = new Date()) {
164
+ const credential = this.get(id);
165
+ if (credential === null)
166
+ return false;
167
+ if (!isHeld(credential, now) || until > credential.unavailableUntil) {
168
+ this.db.run("UPDATE subscription_credentials SET unavailable_until = ?, unavailable_reason = ?, updated_at = ? WHERE id = ?", [until.getTime(), reason, now.getTime(), id]);
169
+ }
170
+ return this.activeCredential(credential.provider, now) !== null;
171
+ }
172
+ /** Clear a usage-limit hold. Null when the subscription is not connected. */
173
+ markAvailable(name, now = new Date()) {
174
+ const credential = this.getByName(name);
175
+ if (credential === null)
176
+ return null;
177
+ this.db.run("UPDATE subscription_credentials SET unavailable_until = NULL, unavailable_reason = NULL, updated_at = ? WHERE id = ?", [now.getTime(), credential.id]);
178
+ return this.get(credential.id);
179
+ }
180
+ createSession(session, now = new Date()) {
181
+ const token = randomBytes(32).toString("base64url");
182
+ this.db.run("DELETE FROM subscription_oauth_sessions WHERE expires_at <= ?", [now.getTime()]);
183
+ this.db.run("INSERT INTO subscription_oauth_sessions (token, provider, flow_json, credential_name, expires_at) VALUES (?, ?, ?, ?, ?)", [token, session.provider, JSON.stringify(session.flow), session.credentialName, session.expiresAt.getTime()]);
184
+ return { token, ...session };
185
+ }
186
+ /** A pending session, including an expired one so callers can say it expired. */
187
+ getSession(token) {
188
+ const row = this.db
189
+ .query("SELECT * FROM subscription_oauth_sessions WHERE token = ?")
190
+ .get(token);
191
+ if (row === null)
192
+ return null;
193
+ return {
194
+ token: row.token,
195
+ provider: row.provider,
196
+ flow: JSON.parse(row.flow_json),
197
+ credentialName: row.credential_name,
198
+ expiresAt: new Date(row.expires_at),
199
+ };
200
+ }
201
+ deleteSession(token) {
202
+ this.db.run("DELETE FROM subscription_oauth_sessions WHERE token = ?", [token]);
203
+ }
204
+ freeName(base) {
205
+ let name = base;
206
+ for (let index = 2; this.getByName(name) !== null; index += 1)
207
+ name = `${base}_${index}`;
208
+ return name;
209
+ }
210
+ }
211
+ export function isHeld(credential, now) {
212
+ return credential.unavailableUntil !== null && credential.unavailableUntil > now;
213
+ }
214
+ /** Subscription names are env-var-style identifiers; matching is case-insensitive. */
215
+ export function normalizeName(name) {
216
+ return name.trim().toUpperCase();
217
+ }
218
+ function credentialFromRow(row) {
219
+ return {
220
+ id: row.id,
221
+ provider: row.provider,
222
+ name: row.name,
223
+ label: row.label,
224
+ accountEmail: row.account_email,
225
+ accountId: row.account_id,
226
+ accessToken: row.access_token,
227
+ refreshToken: row.refresh_token,
228
+ expiresAt: new Date(row.expires_at),
229
+ unavailableUntil: row.unavailable_until === null ? null : new Date(row.unavailable_until),
230
+ unavailableReason: row.unavailable_reason,
231
+ position: row.position,
232
+ createdAt: new Date(row.created_at),
233
+ updatedAt: new Date(row.updated_at),
234
+ lastUsedAt: row.last_used_at === null ? null : new Date(row.last_used_at),
235
+ };
236
+ }
@@ -0,0 +1,12 @@
1
+ import type { SelectorBackend } from "./routing.js";
2
+ export declare const DARI_AUTO_ROUTING_ENDPOINT = "https://routing.dari.dev/v1/auto-router";
3
+ /**
4
+ * Choose among a router's models with Dari Auto Routing, the hosted routing
5
+ * policy. Each selection is one call to the Auto Router API with a Dari API
6
+ * key; the gateway still executes the chosen model with its own credentials.
7
+ */
8
+ export declare function createAutoRoutingSelectorBackend(config: {
9
+ apiKey: string;
10
+ endpoint?: string;
11
+ fetch?: typeof fetch;
12
+ }): SelectorBackend;
@@ -0,0 +1,61 @@
1
+ import { parseSelectorDecision } from "@mupt-ai/dari-router/policy-engine";
2
+ import { HttpError } from "../http/errors.js";
3
+ export const DARI_AUTO_ROUTING_ENDPOINT = "https://routing.dari.dev/v1/auto-router";
4
+ // Same selector identity and input budget as `createAutoRouter` in
5
+ // @mupt-ai/dari-router, which calls the same API.
6
+ const AUTO_ROUTER_MODEL = "dari/auto-router";
7
+ const AUTO_ROUTER_CONTEXT_WINDOW_CHARS = 32_000;
8
+ const AUTO_ROUTER_TIMEOUT_MS = 30_000;
9
+ /**
10
+ * Choose among a router's models with Dari Auto Routing, the hosted routing
11
+ * policy. Each selection is one call to the Auto Router API with a Dari API
12
+ * key; the gateway still executes the chosen model with its own credentials.
13
+ */
14
+ export function createAutoRoutingSelectorBackend(config) {
15
+ const apiKey = config.apiKey.trim();
16
+ if (!apiKey) {
17
+ throw new Error("Dari Auto Routing needs an API key.");
18
+ }
19
+ const endpoint = (config.endpoint ?? DARI_AUTO_ROUTING_ENDPOINT)
20
+ .replace(/\/+$/, "")
21
+ .replace(/\/select$/, "");
22
+ const fetchImpl = config.fetch ?? fetch;
23
+ return {
24
+ selectorModel: AUTO_ROUTER_MODEL,
25
+ contextWindowChars: async () => AUTO_ROUTER_CONTEXT_WINDOW_CHARS,
26
+ select: async (args) => {
27
+ let response;
28
+ try {
29
+ response = await fetchImpl(`${endpoint}/select`, {
30
+ method: "POST",
31
+ headers: {
32
+ Authorization: `Bearer ${apiKey}`,
33
+ "Content-Type": "application/json",
34
+ },
35
+ body: JSON.stringify(args.selectorRequest),
36
+ signal: AbortSignal.timeout(AUTO_ROUTER_TIMEOUT_MS),
37
+ });
38
+ }
39
+ catch {
40
+ throw new HttpError(502, "Dari Auto Routing request failed.", "selector_execution_failed");
41
+ }
42
+ if (!response.ok) {
43
+ throw new HttpError(502, `Dari Auto Routing returned HTTP ${response.status}.`, "selector_execution_failed");
44
+ }
45
+ const outputText = await response.text();
46
+ // The gateway applies its own stateless hold; drop any lease the
47
+ // policy stamps so it is not mistaken for a negotiated one.
48
+ const { leaseTurnsRemaining: _leaseTurnsRemaining, ...decision } = parseSelectorDecision(outputText).decision;
49
+ return {
50
+ decision,
51
+ selectorModel: args.selectorModel,
52
+ candidates: args.candidates,
53
+ selectorInput: args.selectorInput,
54
+ selectorRequest: args.selectorRequest,
55
+ outputText,
56
+ usage: null,
57
+ latencyMs: Date.now() - args.startedAt,
58
+ };
59
+ },
60
+ };
61
+ }
@@ -0,0 +1,17 @@
1
+ import { type ChatMessage, type JevEvalRelevanceSelection, type ResolvedJevConfig, type RouterEval } from "@mupt-ai/dari-router/policy-engine";
2
+ import type { ResolvedRouter } from "../types.js";
3
+ export type EvalRelevanceDetails = {
4
+ model: string;
5
+ cards: JevEvalRelevanceSelection["cards"];
6
+ usage: JevEvalRelevanceSelection["usage"];
7
+ } | {
8
+ error: "eval_relevance_failed";
9
+ };
10
+ export declare function relevantEvals(args: {
11
+ credentials: ResolvedRouter;
12
+ selectorMessages: ChatMessage[];
13
+ config: ResolvedJevConfig;
14
+ }): Promise<{
15
+ evals: RouterEval[];
16
+ details: EvalRelevanceDetails;
17
+ }>;
@@ -0,0 +1,32 @@
1
+ import { RouterFrameworkError } from "@mupt-ai/dari-router";
2
+ import { JevOutputError, selectRelevantEvals, } from "@mupt-ai/dari-router/policy-engine";
3
+ import { publicCatalogSelectorEvals } from "../context/eval_catalog.js";
4
+ import { scrubSecrets } from "../telemetry/scrub.js";
5
+ // The candidate pool is every eval the organization can use: its own cards
6
+ // plus the public catalog. Jev keeps the cards relevant to this request and
7
+ // those become the selector's evidence. A Jev outage degrades to the
8
+ // organization's own cards rather than failing the request.
9
+ export async function relevantEvals(args) {
10
+ const organizationEvals = args.credentials.evals ?? [];
11
+ const pool = [
12
+ ...organizationEvals,
13
+ ...publicCatalogSelectorEvals(args.credentials.public_eval_catalog),
14
+ ];
15
+ try {
16
+ const selection = await selectRelevantEvals(pool, {
17
+ task: args.selectorMessages.find((message) => message.role === "user") ?? null,
18
+ conversation: args.selectorMessages,
19
+ }, args.config);
20
+ return {
21
+ evals: selection.evals,
22
+ details: { model: selection.model, cards: selection.cards, usage: selection.usage },
23
+ };
24
+ }
25
+ catch (error) {
26
+ if (!(error instanceof RouterFrameworkError || error instanceof JevOutputError)) {
27
+ throw error;
28
+ }
29
+ console.error("eval relevance failed", scrubSecrets(error.message));
30
+ return { evals: organizationEvals, details: { error: "eval_relevance_failed" } };
31
+ }
32
+ }
@@ -0,0 +1,8 @@
1
+ import { type RoutingCandidate, type RoutingDecision } from "@mupt-ai/dari-router/policy-engine";
2
+ export declare function deriveFallbackDecision(args: {
3
+ candidates: RoutingCandidate[];
4
+ selected: RoutingCandidate;
5
+ enabled?: boolean;
6
+ requiresDifferentProvider?: boolean;
7
+ providerForModel?: (model: string) => string;
8
+ }): RoutingDecision | undefined;
@@ -0,0 +1,17 @@
1
+ import { providerForModel, } from "@mupt-ai/dari-router/policy-engine";
2
+ export function deriveFallbackDecision(args) {
3
+ if (!args.enabled)
4
+ return undefined;
5
+ const resolveProvider = args.providerForModel ?? providerForModel;
6
+ const selectedProvider = resolveProvider(args.selected.model);
7
+ const fallback = args.candidates.find((candidate) => candidate.model !== args.selected.model &&
8
+ (!args.requiresDifferentProvider ||
9
+ resolveProvider(candidate.model) !== selectedProvider));
10
+ return fallback
11
+ ? {
12
+ selectedModel: fallback.model,
13
+ reasoningEffort: fallback.reasoningEffort,
14
+ reason: "Runtime-selected fallback for the routed primary decision.",
15
+ }
16
+ : undefined;
17
+ }
@@ -0,0 +1,7 @@
1
+ import type { SelectorBackend } from "./routing.js";
2
+ /**
3
+ * Serve the router's first eligible model/thinking-level pair, in the order
4
+ * the router lists them. Provider failures still fall back to the next pair
5
+ * when the router enables model fallback.
6
+ */
7
+ export declare function createPrioritySelectorBackend(): SelectorBackend;
@@ -0,0 +1,37 @@
1
+ const PRIORITY_SELECTOR_MODEL = "dari-gateway/priority";
2
+ // Priority selection reads no messages, so the sized selector input only
3
+ // needs room for the router's candidates.
4
+ const PRIORITY_CONTEXT_WINDOW_CHARS = 1_000_000;
5
+ /**
6
+ * Serve the router's first eligible model/thinking-level pair, in the order
7
+ * the router lists them. Provider failures still fall back to the next pair
8
+ * when the router enables model fallback.
9
+ */
10
+ export function createPrioritySelectorBackend() {
11
+ return {
12
+ selectorModel: PRIORITY_SELECTOR_MODEL,
13
+ contextWindowChars: async () => PRIORITY_CONTEXT_WINDOW_CHARS,
14
+ prepareMessages: () => [],
15
+ supportsSpeculativeRouting: false,
16
+ select: async (args) => {
17
+ const first = args.candidates[0];
18
+ if (first === undefined) {
19
+ throw new Error("Priority selection needs at least one candidate.");
20
+ }
21
+ return {
22
+ decision: {
23
+ selectedModel: first.model,
24
+ reasoningEffort: first.reasoningEffort,
25
+ reason: "First eligible model on the router.",
26
+ },
27
+ selectorModel: args.selectorModel,
28
+ candidates: args.candidates,
29
+ selectorInput: args.selectorInput,
30
+ selectorRequest: args.selectorRequest,
31
+ outputText: "",
32
+ usage: null,
33
+ latencyMs: Date.now() - args.startedAt,
34
+ };
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,22 @@
1
+ import type { ChatMessage, PreviousDecision, RoutingDecision, StrategyCandidateResolution, SelectorLeaseHistoryEntry } from "@mupt-ai/dari-router/policy-engine";
2
+ import { type SelectorBackend, type SelectorRun } from "./routing.js";
3
+ import type { SelectorImage } from "./selector_projection.js";
4
+ import type { ResolvedRouter } from "../types.js";
5
+ export type RoutedDecision = {
6
+ decision: RoutingDecision;
7
+ fallbackDecision?: RoutingDecision;
8
+ run?: SelectorRun;
9
+ details?: Record<string, unknown>;
10
+ };
11
+ type RouteDecisionArgs = {
12
+ selectorMessages: ChatMessage[];
13
+ selectorImages?: SelectorImage[];
14
+ resolution: StrategyCandidateResolution;
15
+ credentials: ResolvedRouter;
16
+ previousDecision?: PreviousDecision;
17
+ conversationId: string;
18
+ selectorBackend: SelectorBackend;
19
+ leaseHistory?: SelectorLeaseHistoryEntry[];
20
+ };
21
+ export declare function routeDecision(args: RouteDecisionArgs): Promise<RoutedDecision>;
22
+ export {};
@@ -0,0 +1,65 @@
1
+ import { publicCatalogSelectorEvals } from "../context/eval_catalog.js";
2
+ import { HttpError } from "../http/errors.js";
3
+ import { relevantEvals } from "./eval_relevance.js";
4
+ import { executionProviderForModel } from "../models/execution_provider.js";
5
+ import { selectModel } from "./routing.js";
6
+ export async function routeDecision(args) {
7
+ const { credentials, resolution } = args;
8
+ const custom = resolution.strategy === "custom"
9
+ ? resolution.custom
10
+ : undefined;
11
+ if (custom === null) {
12
+ throw new HttpError(502, "Router custom rule configuration is missing.", "custom_config_missing");
13
+ }
14
+ if (custom && resolution.candidates.length === 0) {
15
+ const error = new HttpError(502, "No custom routing rule or default model/thinking-level pair is routable for this request.", "custom_no_routable_candidates");
16
+ error.decisionDetails = { custom_rules: custom.audit };
17
+ throw error;
18
+ }
19
+ const relevance = args.selectorBackend.evalRelevance !== undefined
20
+ && resolution.candidates.length > 1
21
+ ? await relevantEvals({
22
+ credentials,
23
+ selectorMessages: args.selectorMessages,
24
+ config: args.selectorBackend.evalRelevance,
25
+ })
26
+ : undefined;
27
+ const run = await selectModel({
28
+ messages: args.selectorMessages,
29
+ ...(args.selectorImages !== undefined && args.selectorImages.length > 0
30
+ ? { images: args.selectorImages }
31
+ : {}),
32
+ candidates: resolution.candidates,
33
+ allCandidates: resolution.allCandidates,
34
+ evals: relevance?.evals ?? credentials.evals ?? [],
35
+ imputationReferenceEvals: credentials.eval_score_imputation === true
36
+ ? publicCatalogSelectorEvals(credentials.public_eval_catalog)
37
+ : undefined,
38
+ previousDecision: args.previousDecision,
39
+ costEstimates: resolution.costEstimates,
40
+ sessionId: args.conversationId,
41
+ ...(custom
42
+ ? { customRules: custom.rules, defaultTarget: custom.defaultTarget }
43
+ : {}),
44
+ selectorBackend: args.selectorBackend,
45
+ modelFallbackEnabled: credentials.model_fallback_enabled !== false,
46
+ fallbackRequiresDifferentProvider: credentials.fallback_requires_different_provider !== false,
47
+ executionProviderForModel: (model) => executionProviderForModel(credentials, model),
48
+ imputeEvalScores: credentials.eval_score_imputation === true,
49
+ leaseHistory: args.leaseHistory,
50
+ leaseContextEnabled: credentials.lease_context_enabled !== false,
51
+ });
52
+ const details = {
53
+ ...(custom ? { custom_rules: custom.audit } : {}),
54
+ ...(run.usage?.policy_bypassed === "single_candidate"
55
+ ? { selector_policy_bypass: "single_candidate" }
56
+ : {}),
57
+ ...(relevance ? { eval_relevance: relevance.details } : {}),
58
+ };
59
+ return {
60
+ decision: run.decision,
61
+ fallbackDecision: run.fallbackDecision,
62
+ run,
63
+ ...(Object.keys(details).length > 0 ? { details } : {}),
64
+ };
65
+ }