@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,210 @@
1
+ import { FIXED_TURN_COST_COMPARISON_TURNS, LEASE_DECISION_TURN_LAG, } from "@mupt-ai/dari-router/policy-engine";
2
+ import { deriveFallbackDecision } from "../strategies/fallback_decision.js";
3
+ import { executionProviderForModel } from "../models/execution_provider.js";
4
+ import { routeDecision, } from "../strategies/route_decision.js";
5
+ import { reportPendingLease } from "../telemetry/prefix_state.js";
6
+ import { scrubSecrets } from "../telemetry/scrub.js";
7
+ export function leasedRoutingDecision(args) {
8
+ if (!args.previousDecision)
9
+ return null;
10
+ const completedTurns = args.messages.filter((message) => message.role === "assistant").length;
11
+ const completedInLease = completedTurns % FIXED_TURN_COST_COMPARISON_TURNS;
12
+ if (completedInLease === 0)
13
+ return null;
14
+ return {
15
+ decision: {
16
+ selectedModel: args.previousDecision.model,
17
+ reasoningEffort: args.previousDecision.reasoningEffort,
18
+ reason: args.previousDecision.reason ||
19
+ `Continuing the current ${FIXED_TURN_COST_COMPARISON_TURNS}-turn routing lease.`,
20
+ },
21
+ completedTurns,
22
+ turnInLease: completedInLease + 1,
23
+ };
24
+ }
25
+ // The selector's ten-turn projection is only valid when its action owns ten
26
+ // turns. Deriving lease position from the request keeps production behavior
27
+ // stateless across gateway instances and process restarts.
28
+ export function leasedDecision(prepared) {
29
+ const lease = leasedRoutingDecision({
30
+ messages: prepared.routing.selectorMessages,
31
+ previousDecision: prepared.routing.previousDecision,
32
+ });
33
+ if (!lease)
34
+ return null;
35
+ // Hold only a pair this request may still serve: a custom router whose
36
+ // rules no longer allow it (or no longer resolve) must reselect.
37
+ const held = prepared.routing.candidateResolution.candidates.some((candidate) => candidate.model === lease.decision.selectedModel &&
38
+ candidate.reasoningEffort === lease.decision.reasoningEffort);
39
+ if (!held)
40
+ return null;
41
+ // Billable traffic must reselect when credits are exhausted so a managed
42
+ // incumbent cannot hide an available user-owned key behind the lease.
43
+ if (prepared.billable && prepared.credentials.credit_exhausted)
44
+ return null;
45
+ const fallbackDecision = leaseFallbackDecision({
46
+ candidates: prepared.routing.candidateResolution.candidates,
47
+ decision: lease.decision,
48
+ credentials: prepared.credentials,
49
+ });
50
+ // Use attachServingDecision rather than attachDecision so next_model
51
+ // follows the model that actually serves. When fallback handles a primary
52
+ // failure, it inherits this countdown and subsequent held turns continue
53
+ // on the healthy serving pair instead of retrying the failed primary.
54
+ prepared.reporter.attachServingDecision(lease.decision);
55
+ prepared.reporter.attachFallbackDecision(fallbackDecision);
56
+ prepared.reporter.attachDecisionDetails({
57
+ decision_source: "lease_hold",
58
+ routing_lease: {
59
+ lease_turns: FIXED_TURN_COST_COMPARISON_TURNS,
60
+ completed_turns: lease.completedTurns,
61
+ turn_in_lease: lease.turnInLease,
62
+ },
63
+ });
64
+ return { decision: lease.decision, fallbackDecision };
65
+ }
66
+ // Wraps a lease-held (or adopted) decision with a runtime-derived fallback so
67
+ // a mid-lease provider failure degrades like any other turn instead of
68
+ // erroring the request. The fallback inherits the remaining commitment so the
69
+ // completed prefix follows the healthy serving pair for the rest of the lease.
70
+ export function committedLeaseDecision(prepared, decision) {
71
+ const fallbackDecision = leaseFallbackDecision({
72
+ candidates: prepared.routing.candidateResolution.candidates,
73
+ decision,
74
+ credentials: prepared.credentials,
75
+ });
76
+ if (fallbackDecision)
77
+ prepared.reporter.attachFallbackDecision(fallbackDecision);
78
+ return fallbackDecision ? { decision, fallbackDecision } : { decision };
79
+ }
80
+ export function leaseFallbackDecision(args) {
81
+ const fallback = deriveFallbackDecision({
82
+ candidates: args.candidates,
83
+ selected: {
84
+ model: args.decision.selectedModel,
85
+ reasoningEffort: args.decision.reasoningEffort,
86
+ },
87
+ enabled: args.credentials.model_fallback_enabled !== false,
88
+ requiresDifferentProvider: args.credentials.fallback_requires_different_provider !== false,
89
+ providerForModel: (model) => executionProviderForModel(args.credentials, model),
90
+ });
91
+ if (!fallback || args.decision.leaseTurnsRemaining === undefined) {
92
+ return fallback;
93
+ }
94
+ return {
95
+ ...fallback,
96
+ leaseTurnsRemaining: args.decision.leaseTurnsRemaining,
97
+ };
98
+ }
99
+ export async function selectorNegotiatesLeases(backend) {
100
+ if (backend.negotiatedLeaseTurns === undefined)
101
+ return false;
102
+ return (await backend.negotiatedLeaseTurns()).length > 0;
103
+ }
104
+ // Upper bound on how long the adoption turn waits for its consume-clear.
105
+ const PENDING_LEASE_CLEAR_WAIT_MS = 1500;
106
+ function withTimeout(task, capMs) {
107
+ return new Promise((resolve) => {
108
+ const timer = setTimeout(resolve, capMs);
109
+ const settle = () => {
110
+ clearTimeout(timer);
111
+ resolve();
112
+ };
113
+ void task.then(settle, settle);
114
+ });
115
+ }
116
+ // Runs the routing decision for the next lease against this turn's state and
117
+ // stamps the result onto the conversation's prefix rows. Failures only cost
118
+ // the prefetch: expiry falls back to a synchronous decision.
119
+ export async function prefetchNextLease(prepared, selectorBackend) {
120
+ try {
121
+ const routed = await routeDecision({
122
+ selectorMessages: prepared.routing.selectorMessages,
123
+ selectorImages: prepared.routing.selectorImages,
124
+ resolution: prepared.routing.candidateResolution,
125
+ credentials: prepared.credentials,
126
+ previousDecision: prepared.routing.previousDecision,
127
+ conversationId: prepared.routing.conversationId,
128
+ selectorBackend,
129
+ leaseHistory: prepared.routing.leaseHistory,
130
+ });
131
+ const remaining = routed.decision.leaseTurnsRemaining;
132
+ if (remaining === undefined) {
133
+ console.error("lease prefetch returned a per-turn decision; skipping");
134
+ return;
135
+ }
136
+ await reportPendingLease({
137
+ routerId: prepared.routerId,
138
+ conversationId: prepared.routing.conversationId,
139
+ decidedAtDepth: prepared.routing.leaseDepth,
140
+ lease: {
141
+ model: routed.decision.selectedModel,
142
+ reasoningEffort: routed.decision.reasoningEffort,
143
+ // The strategy assumes its granting turn serves the pair; a
144
+ // prefetched grant starts serving at adoption, so the full grant is
145
+ // stored and adoption stamps remaining = turns - 1.
146
+ turns: remaining + 1,
147
+ reason: routed.decision.reason,
148
+ outputText: routed.run?.outputText ?? null,
149
+ },
150
+ });
151
+ }
152
+ catch (error) {
153
+ console.error("lease prefetch failed", scrubSecrets(error instanceof Error ? error.message : String(error)));
154
+ }
155
+ }
156
+ export function leasePrefetchDue(prepared) {
157
+ return prepared.routing.activeLease?.turnsRemaining === LEASE_DECISION_TURN_LAG;
158
+ }
159
+ export async function clearPendingLease(prepared) {
160
+ await withTimeout(reportPendingLease({
161
+ routerId: prepared.routerId,
162
+ conversationId: prepared.routing.conversationId,
163
+ decidedAtDepth: prepared.routing.leaseDepth,
164
+ lease: null,
165
+ }), PENDING_LEASE_CLEAR_WAIT_MS);
166
+ }
167
+ function canServeCommittedPair(prepared, pair) {
168
+ if (prepared.billable && prepared.credentials.credit_exhausted)
169
+ return false;
170
+ return prepared.routing.candidateResolution.candidates.some((candidate) => candidate.model === pair.model &&
171
+ candidate.reasoningEffort === pair.reasoningEffort);
172
+ }
173
+ // Adopts the lease prefetched before the previous one expired. The pair was
174
+ // re-validated against this request's candidates during preparation.
175
+ export function pendingLeaseDecision(prepared) {
176
+ const pending = prepared.routing.pendingLease;
177
+ if (pending === undefined)
178
+ return null;
179
+ if (!canServeCommittedPair(prepared, pending))
180
+ return null;
181
+ return {
182
+ selectedModel: pending.model,
183
+ reasoningEffort: pending.reasoningEffort,
184
+ // Distinguishable from a synchronous grant in activity and responses:
185
+ // this decision was made turns ago and adopted here without a policy call.
186
+ reason: pending.reason === null
187
+ ? `Prefetched: policy leased ${pending.model}/${pending.reasoningEffort} ` +
188
+ `for ${pending.turns} turns.`
189
+ : `Prefetched: ${pending.reason}`,
190
+ leaseTurnsRemaining: pending.turns - 1,
191
+ };
192
+ }
193
+ // A leased conversation serves its committed pair without consulting the
194
+ // selector: the policy already paid the decision cost when it granted the
195
+ // lease, and holding is what keeps the provider cache warm. The lease
196
+ // dissolves when its target stops being a candidate (core drops it during
197
+ // preparation) or when the countdown reaches zero.
198
+ export function leaseHoldDecision(prepared) {
199
+ const lease = prepared.routing.activeLease;
200
+ if (lease === undefined)
201
+ return null;
202
+ if (!canServeCommittedPair(prepared, lease))
203
+ return null;
204
+ return {
205
+ selectedModel: lease.model,
206
+ reasoningEffort: lease.reasoningEffort,
207
+ reason: `Lease hold: ${lease.turnsRemaining} committed turns remaining.`,
208
+ leaseTurnsRemaining: lease.turnsRemaining - 1,
209
+ };
210
+ }
@@ -0,0 +1,71 @@
1
+ import type { RoutingDecision } from "@mupt-ai/dari-router/policy-engine";
2
+ import type { InteractionOutput } from "../interaction/types.js";
3
+ import type { SelectorRun } from "../strategies/routing.js";
4
+ import type { ProviderKeySource } from "../types.js";
5
+ export type ReportedProviderExecution = {
6
+ provider: string;
7
+ model: string;
8
+ providerKeySource: ProviderKeySource;
9
+ };
10
+ export declare function reportedExecution(execution: ReportedProviderExecution): ReportedProviderExecution;
11
+ export type ReportableProviderOutcome = {
12
+ status: "completed";
13
+ output: InteractionOutput;
14
+ message: Record<string, unknown>;
15
+ responseBody: Record<string, unknown>;
16
+ providerLatencyMs: number;
17
+ execution: ReportedProviderExecution;
18
+ } | {
19
+ status: "provider_error";
20
+ message: Record<string, unknown>;
21
+ providerLatencyMs: number;
22
+ execution: ReportedProviderExecution;
23
+ } | {
24
+ status: "customer_canceled";
25
+ providerLatencyMs: number;
26
+ execution: ReportedProviderExecution;
27
+ message: Record<string, unknown> | null;
28
+ responseBody: Record<string, unknown> | null;
29
+ partialOutput?: InteractionOutput | null;
30
+ };
31
+ export type ProviderAttemptReport = {
32
+ model: string;
33
+ reasoning_effort: string;
34
+ role: "primary" | "fallback";
35
+ attempt: number;
36
+ outcome: "completed" | "error" | "terminal_error" | "canceled";
37
+ };
38
+ export type ProviderAttemptRecord = ProviderAttemptReport & {
39
+ execution: ReportedProviderExecution;
40
+ message: Record<string, unknown>;
41
+ billingOutcome: "completed" | "provider_failed" | "customer_canceled";
42
+ };
43
+ export type FrozenProviderTerminal = {
44
+ report(): void;
45
+ };
46
+ export type RoutedRuntimeReporter = {
47
+ attachSelectorRun(run: SelectorRun): void;
48
+ attachSpeculativeSelectorRun(run: SelectorRun, servingDecision: RoutingDecision): void;
49
+ attachDecision(decision: RoutingDecision): void;
50
+ attachFallbackDecision(decision: RoutingDecision | undefined): void;
51
+ attachServingDecision(decision: RoutingDecision): void;
52
+ recordProviderAttempt(attempt: ProviderAttemptRecord): void;
53
+ modelCost(attempt?: ProviderAttemptRecord): number | null;
54
+ attachDecisionDetails(details: Record<string, unknown>): void;
55
+ attachAdoptedSelectorOutput(adopted: {
56
+ outputText: string | null;
57
+ reason: string;
58
+ candidates: readonly {
59
+ model: string;
60
+ reasoningEffort: string;
61
+ }[];
62
+ }): void;
63
+ waitForPrefixEntryWrite(): Promise<void>;
64
+ freezeProviderTerminal(args: ReportableProviderOutcome): FrozenProviderTerminal;
65
+ freezeFailure(error: unknown): FrozenProviderTerminal;
66
+ reportSelectorFailure(error: unknown): void;
67
+ };
68
+ export type ExperimentRoutedRuntimeReporter = RoutedRuntimeReporter & {
69
+ decisionDetails: Record<string, unknown>;
70
+ };
71
+ export declare function createExperimentRoutedRuntimeReporter(): ExperimentRoutedRuntimeReporter;
@@ -0,0 +1,35 @@
1
+ export function reportedExecution(execution) {
2
+ return {
3
+ provider: execution.provider,
4
+ model: execution.model,
5
+ providerKeySource: execution.providerKeySource,
6
+ };
7
+ }
8
+ export function createExperimentRoutedRuntimeReporter() {
9
+ const terminal = {
10
+ report() { },
11
+ };
12
+ const reporter = {
13
+ decisionDetails: {},
14
+ attachSelectorRun() { },
15
+ attachSpeculativeSelectorRun() { },
16
+ attachDecision() { },
17
+ attachFallbackDecision() { },
18
+ attachServingDecision() { },
19
+ recordProviderAttempt() { },
20
+ modelCost() { return null; },
21
+ attachAdoptedSelectorOutput() { },
22
+ attachDecisionDetails(details) {
23
+ reporter.decisionDetails = { ...reporter.decisionDetails, ...details };
24
+ },
25
+ async waitForPrefixEntryWrite() { },
26
+ freezeProviderTerminal() {
27
+ return terminal;
28
+ },
29
+ freezeFailure() {
30
+ return terminal;
31
+ },
32
+ reportSelectorFailure() { },
33
+ };
34
+ return reporter;
35
+ }
@@ -0,0 +1,6 @@
1
+ import type { InteractionRequest } from "../interaction/types.js";
2
+ import type { ResolvedRouter } from "../types.js";
3
+ export declare function strictProviderAccess(credentials: ResolvedRouter, interaction: InteractionRequest): {
4
+ credentials: ResolvedRouter;
5
+ interaction: InteractionRequest;
6
+ };
@@ -0,0 +1,40 @@
1
+ import { getBuiltinModels, getBuiltinProviders } from "@mupt-ai/pi-ai/providers/all";
2
+ import { HttpError } from "../http/errors.js";
3
+ import { explicitModelId } from "../protocol/input/router_model.js";
4
+ import { modelNotEnabled, strictModelRequired } from "./explicit_model.js";
5
+ // Resolve only the requested catalog model, never turn a provider grant into
6
+ // selector candidates. Unknown IDs fail closed; a grant-only router has no
7
+ // candidates to route among, so it refuses them here with the strict errors.
8
+ export function strictProviderAccess(credentials, interaction) {
9
+ const providers = credentials.strict_providers;
10
+ if (credentials.strict !== true || !providers?.length)
11
+ return { credentials, interaction };
12
+ const requested = explicitModelId(interaction.model);
13
+ const grantOnly = credentials.enabled_models.length === 0;
14
+ if (requested === null) {
15
+ if (grantOnly)
16
+ throw strictModelRequired();
17
+ return { credentials, interaction };
18
+ }
19
+ const matches = new Set();
20
+ for (const provider of providers) {
21
+ if (!getBuiltinProviders().includes(provider)) {
22
+ throw new HttpError(400, `Unknown strict provider '${provider}'.`, "unknown_provider");
23
+ }
24
+ for (const model of getBuiltinModels(provider)) {
25
+ const id = `${provider}/${model.id}`;
26
+ if (requested === id)
27
+ matches.add(id);
28
+ }
29
+ }
30
+ const model = [...matches][0];
31
+ if (model === undefined) {
32
+ if (grantOnly)
33
+ throw modelNotEnabled(requested);
34
+ return { credentials, interaction };
35
+ }
36
+ return {
37
+ credentials: { ...credentials, enabled_models: [...new Set([...credentials.enabled_models, model])] },
38
+ interaction: { ...interaction, model: `dari/model/${model}` },
39
+ };
40
+ }
@@ -0,0 +1,46 @@
1
+ import { type ReasoningEffort } from "@mupt-ai/dari-router/policy-engine";
2
+ /**
3
+ * The self-hosted gateway's configuration file (YAML or JSON):
4
+ *
5
+ * ```yaml
6
+ * routers:
7
+ * default:
8
+ * models:
9
+ * - anthropic/claude-sonnet-5-5
10
+ * - id: openai/gpt-5.5
11
+ * thinking: [medium, high]
12
+ * fallback: true # retry on the next model when a provider fails
13
+ * subscriptions: true # serve with connected Claude Code / ChatGPT plans
14
+ * providers: # optional: OpenAI-compatible or self-run endpoints
15
+ * local:
16
+ * baseUrl: http://localhost:8000/v1
17
+ * apiKeyEnv: LOCAL_API_KEY
18
+ * ```
19
+ *
20
+ * Secrets never live here: gateway keys come from `DARI_GATEWAY_KEYS`,
21
+ * provider API keys from each provider's standard environment variable, and
22
+ * personal subscriptions from the gateway database.
23
+ */
24
+ export type GatewayConfig = {
25
+ routers: Record<string, RouterConfig>;
26
+ providers: Record<string, ProviderEndpointConfig>;
27
+ };
28
+ export type ProviderEndpointConfig = {
29
+ baseUrl: string;
30
+ api: string;
31
+ apiKeyEnv?: string;
32
+ };
33
+ export type RouterConfig = {
34
+ models: RouterModelConfig[];
35
+ fallback: boolean;
36
+ fallbackRequiresDifferentProvider: boolean;
37
+ retries: number;
38
+ subscriptions: boolean;
39
+ zdr: boolean;
40
+ };
41
+ export type RouterModelConfig = {
42
+ id: string;
43
+ thinking?: ReasoningEffort[];
44
+ };
45
+ export declare function loadGatewayConfig(path: string): Promise<GatewayConfig>;
46
+ export declare function parseGatewayConfig(raw: unknown): GatewayConfig;
@@ -0,0 +1,105 @@
1
+ import { isReasoningEffort, isRecord, } from "@mupt-ai/dari-router/policy-engine";
2
+ const ROUTER_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
3
+ const PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9.-]*$/;
4
+ const MODEL_ID_PATTERN = /^[a-z0-9][a-z0-9.-]*\/\S+$/;
5
+ export async function loadGatewayConfig(path) {
6
+ const file = Bun.file(path);
7
+ if (!(await file.exists())) {
8
+ throw new Error(`Gateway config not found at ${path}.`);
9
+ }
10
+ const text = await file.text();
11
+ const raw = path.endsWith(".json") ? JSON.parse(text) : Bun.YAML.parse(text);
12
+ return parseGatewayConfig(raw);
13
+ }
14
+ export function parseGatewayConfig(raw) {
15
+ if (!isRecord(raw) || !isRecord(raw.routers)) {
16
+ throw new Error("Gateway config needs a `routers` map.");
17
+ }
18
+ const routers = {};
19
+ for (const [id, router] of Object.entries(raw.routers)) {
20
+ if (!ROUTER_ID_PATTERN.test(id)) {
21
+ throw new Error(`Router id "${id}" may use only letters, digits, "-" and "_".`);
22
+ }
23
+ routers[id] = parseRouter(id, router);
24
+ }
25
+ if (Object.keys(routers).length === 0) {
26
+ throw new Error("Gateway config needs at least one router.");
27
+ }
28
+ const providers = {};
29
+ if (raw.providers !== undefined) {
30
+ if (!isRecord(raw.providers))
31
+ throw new Error("`providers` must be a map of provider ids.");
32
+ for (const [id, provider] of Object.entries(raw.providers)) {
33
+ if (!PROVIDER_ID_PATTERN.test(id)) {
34
+ throw new Error(`Provider id "${id}" may use only lowercase letters, digits, "." and "-".`);
35
+ }
36
+ providers[id] = parseProvider(id, provider);
37
+ }
38
+ }
39
+ return { routers, providers };
40
+ }
41
+ function parseProvider(id, raw) {
42
+ if (!isRecord(raw) || typeof raw.baseUrl !== "string" || !URL.canParse(raw.baseUrl)) {
43
+ throw new Error(`Provider "${id}" needs a \`baseUrl\` URL.`);
44
+ }
45
+ if (raw.api !== undefined && (typeof raw.api !== "string" || !raw.api.trim())) {
46
+ throw new Error(`providers.${id}.api must be a wire API name.`);
47
+ }
48
+ if (raw.apiKeyEnv !== undefined && (typeof raw.apiKeyEnv !== "string" || !raw.apiKeyEnv.trim())) {
49
+ throw new Error(`providers.${id}.apiKeyEnv must name an environment variable.`);
50
+ }
51
+ return {
52
+ baseUrl: raw.baseUrl.replace(/\/+$/, ""),
53
+ api: typeof raw.api === "string" ? raw.api.trim() : "openai-completions",
54
+ ...(typeof raw.apiKeyEnv === "string" ? { apiKeyEnv: raw.apiKeyEnv.trim() } : {}),
55
+ };
56
+ }
57
+ function parseRouter(id, raw) {
58
+ if (!isRecord(raw) || !Array.isArray(raw.models) || raw.models.length === 0) {
59
+ throw new Error(`Router "${id}" needs a non-empty \`models\` list.`);
60
+ }
61
+ const models = raw.models.map((model) => parseModel(id, model));
62
+ const seen = new Set();
63
+ for (const model of models) {
64
+ if (seen.has(model.id))
65
+ throw new Error(`Router "${id}" lists ${model.id} twice.`);
66
+ seen.add(model.id);
67
+ }
68
+ return {
69
+ models,
70
+ fallback: optionalBoolean(raw.fallback, `routers.${id}.fallback`) ?? true,
71
+ fallbackRequiresDifferentProvider: optionalBoolean(raw.fallbackRequiresDifferentProvider, `routers.${id}.fallbackRequiresDifferentProvider`) ?? false,
72
+ retries: optionalRetries(raw.retries, `routers.${id}.retries`) ?? 1,
73
+ subscriptions: optionalBoolean(raw.subscriptions, `routers.${id}.subscriptions`) ?? true,
74
+ zdr: optionalBoolean(raw.zdr, `routers.${id}.zdr`) ?? false,
75
+ };
76
+ }
77
+ function parseModel(routerId, raw) {
78
+ const entry = typeof raw === "string" ? { id: raw } : raw;
79
+ if (!isRecord(entry) || typeof entry.id !== "string" || !MODEL_ID_PATTERN.test(entry.id)) {
80
+ throw new Error(`Router "${routerId}" has an invalid model; use a canonical "provider/model" id.`);
81
+ }
82
+ if (entry.thinking === undefined)
83
+ return { id: entry.id };
84
+ if (!Array.isArray(entry.thinking) ||
85
+ entry.thinking.length === 0 ||
86
+ !entry.thinking.every(isReasoningEffort)) {
87
+ throw new Error(`Model ${entry.id} on router "${routerId}" has invalid thinking levels; use off, minimal, low, medium, high, xhigh, or max.`);
88
+ }
89
+ return { id: entry.id, thinking: [...new Set(entry.thinking)] };
90
+ }
91
+ function optionalBoolean(value, field) {
92
+ if (value === undefined)
93
+ return undefined;
94
+ if (typeof value !== "boolean")
95
+ throw new Error(`${field} must be true or false.`);
96
+ return value;
97
+ }
98
+ function optionalRetries(value, field) {
99
+ if (value === undefined)
100
+ return undefined;
101
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 5) {
102
+ throw new Error(`${field} must be an integer from 0 to 5.`);
103
+ }
104
+ return value;
105
+ }
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Start a self-hosted Dari Gateway from environment variables:
4
+ *
5
+ * - `DARI_GATEWAY_KEYS` (required): comma-separated bearer keys clients use.
6
+ * - `DARI_GATEWAY_CONFIG`: router config file (default `dari-gateway.yaml`).
7
+ * - `DARI_GATEWAY_DB`: SQLite database for subscriptions and prefix-cache
8
+ * state (default `dari-gateway.db`). It holds subscription tokens.
9
+ * - `DARI_AUTO_ROUTING_API_KEY`: choose among a router's models with Dari
10
+ * Auto Routing; without it, `dari/routing` serves the first listed model.
11
+ * - `PORT` (default 8080), and each provider's standard API key variable.
12
+ */
13
+ export declare function startSelfHostedGateway(env?: Record<string, string>): Promise<{
14
+ server: import("../http/http2_server.js").Http2RoutingServer | Bun.Server<undefined>;
15
+ close: () => void;
16
+ }>;
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bun
2
+ import { chmodSync, existsSync } from "node:fs";
3
+ import { Database } from "bun:sqlite";
4
+ import { builtinModels } from "@mupt-ai/pi-ai/providers/all";
5
+ import { bearerToken } from "../http/auth.js";
6
+ import { errorResponse, HttpError } from "../http/errors.js";
7
+ import { createRequestHandler } from "../http/handler.js";
8
+ import { installShutdownHandlers, startServer } from "../http/serve.js";
9
+ import { createAutoRoutingSelectorBackend } from "../strategies/auto_routing_selector.js";
10
+ import { createPrioritySelectorBackend } from "../strategies/priority_selector.js";
11
+ import { loadGatewayConfig } from "./config.js";
12
+ import { createSelfHostedPlatform } from "./platform.js";
13
+ import { gatewayKeyIndex } from "./resolve.js";
14
+ import { PrefixStore } from "./prefix_store.js";
15
+ import { handleSubscriptionRequest, SubscriptionStore } from "./subscriptions/index.js";
16
+ const SUBSCRIPTION_ROUTES = "/v1/organizations/current/credentials";
17
+ /**
18
+ * Start a self-hosted Dari Gateway from environment variables:
19
+ *
20
+ * - `DARI_GATEWAY_KEYS` (required): comma-separated bearer keys clients use.
21
+ * - `DARI_GATEWAY_CONFIG`: router config file (default `dari-gateway.yaml`).
22
+ * - `DARI_GATEWAY_DB`: SQLite database for subscriptions and prefix-cache
23
+ * state (default `dari-gateway.db`). It holds subscription tokens.
24
+ * - `DARI_AUTO_ROUTING_API_KEY`: choose among a router's models with Dari
25
+ * Auto Routing; without it, `dari/routing` serves the first listed model.
26
+ * - `PORT` (default 8080), and each provider's standard API key variable.
27
+ */
28
+ export async function startSelfHostedGateway(env = process.env) {
29
+ const gatewayKeys = (env.DARI_GATEWAY_KEYS ?? "")
30
+ .split(",")
31
+ .map((key) => key.trim())
32
+ .filter(Boolean);
33
+ if (gatewayKeys.length === 0) {
34
+ throw new Error("Set DARI_GATEWAY_KEYS to the comma-separated keys clients authenticate with.");
35
+ }
36
+ const config = await loadGatewayConfig(env.DARI_GATEWAY_CONFIG ?? "dari-gateway.yaml");
37
+ const databasePath = env.DARI_GATEWAY_DB ?? "dari-gateway.db";
38
+ const created = databasePath !== ":memory:" && !existsSync(databasePath);
39
+ const db = new Database(databasePath, { create: true });
40
+ if (created)
41
+ chmodSync(databasePath, 0o600);
42
+ db.exec("PRAGMA journal_mode = WAL");
43
+ const subscriptions = new SubscriptionStore(db);
44
+ const prefixes = new PrefixStore(db);
45
+ // Pi's provider auth reads each provider's standard key variable, such as
46
+ // OPENAI_API_KEY, from this environment only.
47
+ const piModels = builtinModels({
48
+ authContext: { env: async (name) => env[name], fileExists: async () => false },
49
+ });
50
+ const platform = createSelfHostedPlatform({
51
+ config,
52
+ gatewayKeys,
53
+ subscriptions,
54
+ prefixes,
55
+ providerApiKey: async (provider) => (await piModels.getAuth(provider))?.auth.apiKey,
56
+ env,
57
+ fetch,
58
+ });
59
+ const autoRoutingKey = env.DARI_AUTO_ROUTING_API_KEY?.trim();
60
+ const selectorBackend = autoRoutingKey
61
+ ? createAutoRoutingSelectorBackend({
62
+ apiKey: autoRoutingKey,
63
+ ...(env.DARI_AUTO_ROUTING_URL ? { endpoint: env.DARI_AUTO_ROUTING_URL } : {}),
64
+ })
65
+ : createPrioritySelectorBackend();
66
+ const gateway = createRequestHandler({ platform, selectorBackend });
67
+ const handler = async (request) => {
68
+ const { pathname } = new URL(request.url);
69
+ if (pathname !== SUBSCRIPTION_ROUTES && !pathname.startsWith(`${SUBSCRIPTION_ROUTES}/`)) {
70
+ return await gateway(request);
71
+ }
72
+ try {
73
+ if (gatewayKeyIndex(gatewayKeys, bearerToken(request.headers)) === null) {
74
+ throw new HttpError(401, "Missing or invalid gateway key.", "authentication_error");
75
+ }
76
+ }
77
+ catch (error) {
78
+ return errorResponse(error);
79
+ }
80
+ return (await handleSubscriptionRequest(request, { store: subscriptions, fetch }))
81
+ ?? errorResponse(new HttpError(404, "Not found", "not_found"));
82
+ };
83
+ const port = Number(env.PORT ?? "8080");
84
+ const server = startServer(port, handler);
85
+ console.info("dari-gateway listening", JSON.stringify({
86
+ port,
87
+ routers: Object.keys(config.routers),
88
+ auto_routing: autoRoutingKey ? "dari" : "priority",
89
+ }));
90
+ return { server, close: () => db.close() };
91
+ }
92
+ if (import.meta.main) {
93
+ const { server, close } = await startSelfHostedGateway();
94
+ installShutdownHandlers(server, async () => close());
95
+ }
@@ -0,0 +1,13 @@
1
+ import type { GatewayPlatform } from "../platform/platform.js";
2
+ import type { PrefixStore } from "./prefix_store.js";
3
+ import { type SelfHostedResolverDeps } from "./resolve.js";
4
+ import type { SubscriptionStore } from "./subscriptions/index.js";
5
+ /**
6
+ * The self-hosted platform: routers from the config file, subscriptions and
7
+ * prefix-cache state in the gateway's SQLite database, and one content-free
8
+ * log line per request.
9
+ */
10
+ export declare function createSelfHostedPlatform(deps: Omit<SelfHostedResolverDeps, "prefixHits"> & {
11
+ prefixes: PrefixStore;
12
+ subscriptions: SubscriptionStore;
13
+ }): GatewayPlatform;