@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,621 @@
1
+ // Reporting of served routing requests to the platform: the request record
2
+ // and the prefix-state write the next turn resolves. Most callers
3
+ // fire-and-forget; streams hold their terminal marker on the prefix-state
4
+ // write so the immediate next turn can see next_model. Reporting failures are
5
+ // always logged and swallowed.
6
+ import { selectPrefixHit, } from "@mupt-ai/dari-router/policy-engine";
7
+ import { errorCode } from "../http/errors.js";
8
+ import { executionProviderForModel } from "../models/execution_provider.js";
9
+ import { isBackgroundRequest, } from "../interaction/types.js";
10
+ import { captureOperationalError } from "../observability/errors.js";
11
+ import { gatewayPlatform } from "../platform/platform.js";
12
+ import { selectorTraceFromError, } from "../strategies/routing.js";
13
+ import { trackBackgroundTelemetry } from "./background.js";
14
+ import { modelCost } from "./model_cost.js";
15
+ import { routingBillingProviderAttemptFacts, } from "./billing_finalization.js";
16
+ import { estimateBenchmarkReplay } from "./benchmark_replay.js";
17
+ import { extractInteractionObservability } from "./requests/interaction_observability.js";
18
+ import { costString, finiteNumber, isRecord, tokenCount, } from "./ingest_values.js";
19
+ import { buildCompletedPrefixHit, leaseCountdownForPrefix, prefixEntryForIngest, reportPendingLease, reportPrefixState, } from "./prefix_state.js";
20
+ import { subscriptionUsageLimitFromFailure } from "../providers/subscription_limits.js";
21
+ import { scrubSecrets } from "./scrub.js";
22
+ import { encodeRetainedInteractionRequest } from "./requests/retained_interaction_request.js";
23
+ import { toolResultObservations, toolSkillEvents } from "./tools/observations.js";
24
+ // Log pipelines commonly cap entries at 256 KB and split longer stdout lines
25
+ // into fragments; one guard keeps pathological provider errors
26
+ // (request-payload echoes) from mangling log entries.
27
+ const LOG_TEXT_MAX_CHARS = 64 * 1024;
28
+ // Upper bound on how long a response's terminal marker waits for the
29
+ // prefix-state write. The write is one small intra-region round trip
30
+ // (tens of milliseconds typical); the cap only matters when the platform is
31
+ // degraded, where closing the stream on time and letting the full report
32
+ // carry the entry is the right trade.
33
+ const PREFIX_STATE_WRITE_WAIT_MS = 1500;
34
+ function guardedLogText(text) {
35
+ const scrubbed = scrubSecrets(text);
36
+ return scrubbed.length > LOG_TEXT_MAX_CHARS
37
+ ? `${scrubbed.slice(0, LOG_TEXT_MAX_CHARS)}…[truncated]`
38
+ : scrubbed;
39
+ }
40
+ function errorText(error) {
41
+ if (error instanceof Error && error.message)
42
+ return error.message;
43
+ return null;
44
+ }
45
+ export class RequestReporter {
46
+ args;
47
+ selectorRun = null;
48
+ selectorTrace = null;
49
+ adoptedSelectorOutput = null;
50
+ selectorOutputAccepted = false;
51
+ decision = null;
52
+ nextDecision = null;
53
+ fallbackDecision = null;
54
+ providerAttempts = [];
55
+ billingProviderAttempts = [];
56
+ decisionDetails = {};
57
+ reported = false;
58
+ terminal = null;
59
+ prefixStatePromise = null;
60
+ constructor(args) {
61
+ this.args = args;
62
+ }
63
+ attachSelectorRun(run) {
64
+ this.selectorRun = run;
65
+ this.selectorTrace = run;
66
+ this.selectorOutputAccepted = true;
67
+ this.decision = run.decision;
68
+ this.nextDecision = run.decision;
69
+ this.fallbackDecision = run.fallbackDecision ?? null;
70
+ }
71
+ attachSpeculativeSelectorRun(run, servingDecision) {
72
+ this.selectorRun = run;
73
+ this.selectorTrace = run;
74
+ this.selectorOutputAccepted = true;
75
+ this.decision = servingDecision;
76
+ this.nextDecision = run.decision;
77
+ this.fallbackDecision = run.fallbackDecision ?? null;
78
+ }
79
+ // Speculative execution can begin before a selector run; its serving
80
+ // decision drives selected-model attribution, prefix entries, and errors.
81
+ attachDecision(decision) {
82
+ this.decision = decision;
83
+ this.nextDecision = decision;
84
+ }
85
+ attachFallbackDecision(decision) {
86
+ this.fallbackDecision = decision ?? null;
87
+ }
88
+ attachServingDecision(decision) {
89
+ this.decision = decision;
90
+ }
91
+ recordProviderAttempt(attempt) {
92
+ const { execution, message, billingOutcome, ...audit } = attempt;
93
+ this.providerAttempts.push(audit);
94
+ this.billingProviderAttempts.push(this.billingFacts(attempt));
95
+ }
96
+ modelCost(attempt) {
97
+ return modelCost(attempt ? [...this.billingProviderAttempts, this.billingFacts(attempt)] : this.billingProviderAttempts);
98
+ }
99
+ billingFacts({ execution, message, role, attempt, billingOutcome }) {
100
+ return routingBillingProviderAttemptFacts({
101
+ provider: execution.provider, model: execution.model,
102
+ providerKeySource: execution.providerKeySource, role, attempt, outcome: billingOutcome,
103
+ providerMessage: message, usage: isRecord(message.usage) ? message.usage : null,
104
+ providerPricingKnown: this.pricingKnown(execution.model),
105
+ });
106
+ }
107
+ // Strategy-specific audit data (e.g. custom-rule drops and cache-aware
108
+ // pruning) merged into the logged decision_details next to the strategy.
109
+ attachAdoptedSelectorOutput(adopted) {
110
+ this.adoptedSelectorOutput = adopted;
111
+ }
112
+ attachDecisionDetails(details) {
113
+ this.decisionDetails = { ...this.decisionDetails, ...details };
114
+ }
115
+ // Resolves once this turn's prefix-state write has settled (committed,
116
+ // failed, or timed out). Stream serializers hold the response's terminal
117
+ // marker on this so the immediate next turn always resolves the entry;
118
+ // the cap bounds the worst-case close delay, and the full report remains
119
+ // the fallback writer whenever the fast write loses.
120
+ waitForPrefixEntryWrite() {
121
+ const pending = this.prefixStatePromise;
122
+ if (!pending)
123
+ return Promise.resolve();
124
+ return new Promise((resolve) => {
125
+ const timer = setTimeout(() => resolve(), PREFIX_STATE_WRITE_WAIT_MS);
126
+ void pending.then(() => {
127
+ clearTimeout(timer);
128
+ resolve();
129
+ }, () => {
130
+ clearTimeout(timer);
131
+ resolve();
132
+ });
133
+ });
134
+ }
135
+ freezeProviderTerminal(args) {
136
+ return this.freezeTerminal(reportOutcome(args));
137
+ }
138
+ reportSelectorFailure(error) {
139
+ this.attachFailedSelectorTrace(error);
140
+ }
141
+ // Catch-all for errors that escape the request handler after resolution
142
+ // (missing provider key, thrown provider calls). send() is idempotent, so
143
+ // paths that already reported are unaffected.
144
+ freezeFailure(error) {
145
+ this.attachFailedSelectorTrace(error);
146
+ if (this.terminal !== null)
147
+ return this.terminal;
148
+ const code = errorCode(error, "internal_error");
149
+ return this.freezeTerminal({
150
+ status: this.decision ? "provider_error" : "selector_error",
151
+ errorCode: code,
152
+ errorMessage: errorText(error) ?? undefined,
153
+ });
154
+ }
155
+ attachFailedSelectorTrace(error) {
156
+ const selectorTrace = selectorTraceFromError(error);
157
+ if (!selectorTrace)
158
+ return;
159
+ this.selectorTrace = selectorTrace;
160
+ this.selectorOutputAccepted = false;
161
+ }
162
+ pricingKnown(model) {
163
+ try {
164
+ return this.args.pricing?.(model) != null;
165
+ }
166
+ catch {
167
+ return false;
168
+ }
169
+ }
170
+ freezeTerminal(outcome) {
171
+ if (this.terminal !== null)
172
+ return this.terminal;
173
+ const billing = this.registerBilling(outcome);
174
+ const terminal = {
175
+ report: () => this.send(outcome, billing),
176
+ };
177
+ this.terminal = terminal;
178
+ return terminal;
179
+ }
180
+ registerBilling(outcome) {
181
+ const providerMessage = providerMessageForOutcome(outcome);
182
+ const terminalUsage = outcome.status === "completed"
183
+ ? outcome.interactionOutput.usage
184
+ : isRecord(providerMessage?.usage)
185
+ ? providerMessage.usage
186
+ : null;
187
+ const benchmarkReplay = estimateBenchmarkReplay({
188
+ enabledModels: this.args.credentials.enabled_models,
189
+ modelPrices: this.args.credentials.model_prices ?? {},
190
+ executionProvider: (model) => executionProviderForModel(this.args.credentials, model),
191
+ priorHit: this.args.identityHit,
192
+ receivedAtMs: this.args.receivedAt.getTime(),
193
+ promptTokens: (tokenCount(terminalUsage?.input) ?? 0) +
194
+ (tokenCount(terminalUsage?.cacheRead) ?? 0) +
195
+ (tokenCount(terminalUsage?.cacheWrite) ?? 0),
196
+ outputTokens: tokenCount(terminalUsage?.output) ?? 0,
197
+ });
198
+ const terminal = this.args.billingFinalization.freeze(billingTerminalOutcome(outcome), this.billingProviderAttempts, terminalUsage === null ? null : benchmarkReplay.costUsd);
199
+ return {
200
+ payload: terminal.readPayload(),
201
+ benchmarkReplay,
202
+ terminal,
203
+ };
204
+ }
205
+ send(outcome, billing) {
206
+ if (this.reported)
207
+ return;
208
+ this.reported = true;
209
+ try {
210
+ const a = this.args;
211
+ const scrubbedError = outcome.errorMessage ? guardedLogText(outcome.errorMessage) : null;
212
+ if (outcome.status !== "completed" && outcome.status !== "aborted") {
213
+ // Provider errors can echo request content; logs are outside the
214
+ // ingest boundary's ZDR scrub, so only the code is logged.
215
+ console.error("routing request failed", JSON.stringify({
216
+ request_id: a.requestId,
217
+ router_id: a.routerId,
218
+ status: outcome.status,
219
+ error_code: outcome.errorCode ?? null,
220
+ }));
221
+ }
222
+ const run = this.selectorRun;
223
+ const selectorTrace = this.selectorTrace;
224
+ const decision = this.decision;
225
+ const selectedModel = outcome.providerExecution?.model ?? decision?.selectedModel ?? null;
226
+ const reasoningBucket = decision?.reasoningEffort ?? null;
227
+ // A completed fallback attempt means the intended pair failed this
228
+ // turn: the next-turn recommendation and any lease countdown follow the
229
+ // pair that actually served instead of re-pinning the failed one.
230
+ const fallbackServed = this.providerAttempts.some((attempt) => attempt.role === "fallback" && attempt.outcome === "completed");
231
+ const nextModel = fallbackServed
232
+ ? selectedModel
233
+ : this.nextDecision?.selectedModel ?? selectedModel;
234
+ const nextReasoningEffort = fallbackServed
235
+ ? reasoningBucket
236
+ : this.nextDecision?.reasoningEffort ?? reasoningBucket;
237
+ const selectedProvider = outcome.providerExecution?.provider ?? (selectedModel
238
+ ? executionProviderForModel(a.credentials, selectedModel)
239
+ : null);
240
+ const keySource = outcome.providerExecution?.providerKeySource ?? (selectedModel && selectedProvider
241
+ ? (a.credentials.provider_key_sources?.[selectedProvider] ?? "user")
242
+ : null);
243
+ const selectedEstimate = a.costEstimates.find((item) => item.model === selectedModel &&
244
+ item.reasoning_effort === reasoningBucket);
245
+ const completedOutput = outcome.status === "completed"
246
+ ? outcome.interactionOutput
247
+ : null;
248
+ const responseBody = outcome.status === "completed"
249
+ ? outcome.responseBody
250
+ : outcome.status === "aborted"
251
+ ? outcome.responseBody ?? null
252
+ : null;
253
+ const providerMessage = providerMessageForOutcome(outcome);
254
+ const usage = completedOutput?.usage ?? (isRecord(providerMessage?.usage) ? providerMessage.usage : null);
255
+ const stopReason = completedOutput?.stopReason ?? (typeof providerMessage?.stopReason === "string"
256
+ ? providerMessage.stopReason
257
+ : null);
258
+ const pricingKnown = selectedModel ? this.pricingKnown(selectedModel) : null;
259
+ const costSource = usage?.costSource === "provider"
260
+ ? "provider"
261
+ : pricingKnown === true ? "pricing" : null;
262
+ const observability = extractInteractionObservability(a.interaction);
263
+ const inputTokens = tokenCount(usage?.input) ?? 0;
264
+ const retainedClientRequest = encodeRetainedInteractionRequest(a.interaction);
265
+ const cacheReadTokens = tokenCount(usage?.cacheRead) ?? 0;
266
+ const cacheWriteTokens = tokenCount(usage?.cacheWrite) ?? 0;
267
+ const outputTokens = tokenCount(usage?.output) ?? 0;
268
+ const { benchmarkReplay, payload: billingPayload } = billing;
269
+ const selectedChain = selectedModel ? a.chainByModel.get(selectedModel) : undefined;
270
+ const selectedTarget = selectedModel
271
+ ? a.targetsByModel.get(selectedModel)
272
+ : undefined;
273
+ // An interrupted turn writes its delivered partial too, so a follow-up
274
+ // that echoes the partial text matches this turn instead of the last.
275
+ const prefixOutput = completedOutput ?? (outcome.status === "aborted" ? outcome.partialOutput ?? null : null);
276
+ const backgroundRequest = isBackgroundRequest(a.interaction);
277
+ const completedPrefix = !backgroundRequest && prefixOutput &&
278
+ selectedModel &&
279
+ selectedChain &&
280
+ selectedTarget
281
+ ? buildCompletedPrefixHit({
282
+ request: a.interaction,
283
+ output: prefixOutput,
284
+ model: selectedModel,
285
+ nextModel,
286
+ nextReasoningEffort,
287
+ reasoningBucket,
288
+ // The strategy stamps remaining = turns - 1 assuming the
289
+ // granting turn served the leased pair. A speculative switch
290
+ // serves the previous pair instead, so the commitment has not
291
+ // started: credit the turn back.
292
+ leaseTurnsRemaining: leaseCountdownForPrefix({
293
+ served: decision ?? null,
294
+ next: this.nextDecision,
295
+ fallbackServed,
296
+ }),
297
+ reason: this.nextDecision?.reason ?? decision?.reason ?? null,
298
+ chain: selectedChain,
299
+ looseChain: a.looseChain,
300
+ target: selectedTarget,
301
+ conversationId: a.conversationId,
302
+ leaseDepth: a.leaseDepth,
303
+ providerBlockDepth: a.providerBlockDepthFor(selectedModel),
304
+ fingerprints: a.fingerprints,
305
+ usage: prefixOutput.usage,
306
+ benchmarkReplayDepth: benchmarkReplay.depth,
307
+ })
308
+ : null;
309
+ const prefixEntry = completedPrefix === null
310
+ ? null
311
+ : prefixEntryForIngest(completedPrefix);
312
+ // Touch the SELECTED model's hit whose provider cache was just read (on
313
+ // a switch-back it is shallower than the global deepest hit), so its
314
+ // freshness clock restarts. For effort-keyed models that is the entry
315
+ // in the serving reasoning bucket; a cross-effort turn read no existing
316
+ // entry, and refreshing another bucket's entry would keep a partition
317
+ // alive that the provider never touched.
318
+ const touchedHash = !backgroundRequest && outcome.status === "completed" && selectedModel && cacheReadTokens > 0
319
+ ? (selectPrefixHit(a.hits, selectedModel, reasoningBucket ?? "off")?.entry.hash ?? null)
320
+ : null;
321
+ // Fast-path prefix-state write, committed before the response's
322
+ // terminal marker is released (waitForPrefixEntryWrite): the next
323
+ // turn — even one arriving milliseconds after stream close — always
324
+ // resolves this turn's conversation id and next-model prediction.
325
+ // The full report below re-applies the same state idempotently, so a
326
+ // lost or timed-out fast write only degrades to the old ordering.
327
+ // An ordinary fallback breaks any lease commitment, prefetched ones
328
+ // included. A lease-aware fallback transfers the active countdown to
329
+ // the serving pair, so only clear pending state when no countdown was
330
+ // transferred. The depth fence orders the clear safely against
331
+ // in-flight prefetch writes.
332
+ if (!backgroundRequest && fallbackServed && decision?.leaseTurnsRemaining === undefined) {
333
+ trackBackgroundTelemetry(reportPendingLease({
334
+ routerId: a.routerId,
335
+ conversationId: a.conversationId,
336
+ decidedAtDepth: a.leaseDepth,
337
+ lease: null,
338
+ }));
339
+ }
340
+ if (prefixEntry !== null || touchedHash !== null) {
341
+ this.prefixStatePromise = reportPrefixState({
342
+ organization_id: a.credentials.organization_id,
343
+ router_id: a.routerId,
344
+ received_at: a.receivedAt.toISOString(),
345
+ zdr: a.zdr,
346
+ prefix_entry: prefixEntry,
347
+ touched_prefix_hash: touchedHash,
348
+ });
349
+ trackBackgroundTelemetry(this.prefixStatePromise);
350
+ }
351
+ const ingestPayload = {
352
+ request_id: this.args.requestId,
353
+ client_request_id: a.clientRequestId,
354
+ organization_id: a.credentials.organization_id,
355
+ router_id: a.routerId,
356
+ api_key_id: a.credentials.api_key_id,
357
+ attributed_user_id: a.credentials.attributed_user_id,
358
+ source_protocol: a.interaction.sourceProtocol,
359
+ request_kind: observability.requestKind,
360
+ received_at: a.receivedAt.toISOString(),
361
+ completed_at: billingPayload.finalized_at,
362
+ billing_terminal_outcome: billingPayload.terminal_outcome,
363
+ billing_provider_attempts: billingPayload.provider_attempts,
364
+ stream: a.interaction.stream,
365
+ zdr: a.zdr,
366
+ status: outcome.status,
367
+ error_code: outcome.errorCode ?? null,
368
+ error_message: scrubbedError,
369
+ conversation_id: a.conversationId,
370
+ agent_session_id: a.agentSessionId ?? null,
371
+ prefix_match_depth: a.identityMatchDepth ?? a.hits.deepest?.depth ?? null,
372
+ prefix_warm_tokens: selectedEstimate?.warm_tokens ?? null,
373
+ message_count: observability.messageCount,
374
+ has_tools: observability.hasTools,
375
+ request_metadata: observability.metadata,
376
+ selected_model: selectedModel,
377
+ selected_provider: selectedProvider,
378
+ provider_key_source: keySource,
379
+ selector_latency_ms: selectorTrace?.latencyMs ?? null,
380
+ provider_latency_ms: outcome.providerLatencyMs ?? null,
381
+ total_latency_ms: Date.now() - a.startedMs,
382
+ client_request_json: retainedClientRequest.json,
383
+ client_request_format: retainedClientRequest.format,
384
+ client_response_json: responseBody ? JSON.stringify(responseBody) : null,
385
+ selector: selectorTrace
386
+ ? selectorIngest({
387
+ trace: selectorTrace,
388
+ reason: run?.decision.reason ?? null,
389
+ outputAccepted: this.selectorOutputAccepted,
390
+ pricingKnown: this.pricingKnown(selectorTrace.selectorModel),
391
+ })
392
+ : this.adoptedSelectorOutput
393
+ ? {
394
+ selector_model: "grpo-policy",
395
+ candidate_models: this.adoptedSelectorOutput.candidates.map((candidate) => ({
396
+ model: candidate.model,
397
+ thinking_level: candidate.reasoningEffort,
398
+ })),
399
+ reason: this.adoptedSelectorOutput.reason,
400
+ output_text: this.adoptedSelectorOutput.outputText,
401
+ }
402
+ : null,
403
+ decision_details: {
404
+ strategy: a.strategy,
405
+ selected_reasoning_effort: reasoningBucket,
406
+ next_reasoning_effort: nextReasoningEffort,
407
+ ...(this.fallbackDecision
408
+ ? {
409
+ fallback_model: this.fallbackDecision.selectedModel,
410
+ fallback_reasoning_effort: this.fallbackDecision.reasoningEffort,
411
+ }
412
+ : {}),
413
+ ...(this.providerAttempts.length > 0
414
+ ? { provider_attempts: this.providerAttempts }
415
+ : {}),
416
+ ...this.decisionDetails,
417
+ ...(a.pruning ? { candidate_pruning: a.pruning } : {}),
418
+ },
419
+ // Model/provider/key source and latency live at the top level (they
420
+ // apply to failures with no provider call too).
421
+ provider_call: selectedModel && selectedProvider && (completedOutput || providerMessage)
422
+ ? {
423
+ stop_reason: stopReason,
424
+ usage: usageIngest(usage, pricingKnown === true),
425
+ }
426
+ : null,
427
+ benchmark_replay_cost_usd: billingPayload.benchmark_replay_cost_usd,
428
+ benchmark_replay_raw_cost_usd: usage === null ? null : costString(benchmarkReplay.rawCostUsd),
429
+ benchmark_model_id: benchmarkReplay.model,
430
+ benchmark_estimator_version: benchmarkReplay.estimatorVersion,
431
+ benchmark_input_rate_usd_per_mtok: costString(benchmarkReplay.price?.input ?? null),
432
+ benchmark_cached_input_rate_usd_per_mtok: costString(benchmarkReplay.price?.cached_input ?? null),
433
+ benchmark_cache_write_rate_usd_per_mtok: costString(benchmarkReplay.price?.cache_write ?? null),
434
+ benchmark_output_rate_usd_per_mtok: costString(benchmarkReplay.price?.output ?? null),
435
+ tool_skill_events: toolSkillEvents({
436
+ routerId: a.routerId,
437
+ request: a.interaction,
438
+ output: completedOutput ?? undefined,
439
+ harnessConversationIdentity: a.harnessConversationIdentity,
440
+ zdr: a.zdr,
441
+ }),
442
+ tool_result_observations: toolResultObservations({
443
+ routerId: a.routerId,
444
+ request: a.interaction,
445
+ zdr: a.zdr,
446
+ }),
447
+ prefix_entry: prefixEntry,
448
+ touched_prefix_hash: touchedHash,
449
+ };
450
+ gatewayPlatform().recordRequest({
451
+ row: ingestPayload,
452
+ summary: {
453
+ request_id: a.requestId,
454
+ organization_id: a.credentials.organization_id,
455
+ router_id: a.routerId,
456
+ api_key_id: a.credentials.api_key_id,
457
+ source_protocol: a.interaction.sourceProtocol,
458
+ request_kind: observability.requestKind,
459
+ status: outcome.status,
460
+ error_code: outcome.errorCode ?? null,
461
+ stream: a.interaction.stream,
462
+ zdr: a.zdr,
463
+ strategy: a.strategy,
464
+ selected_model: selectedModel,
465
+ selected_provider: selectedProvider,
466
+ selected_reasoning_effort: reasoningBucket,
467
+ next_model: nextModel,
468
+ next_reasoning_effort: nextReasoningEffort,
469
+ fallback_model: this.fallbackDecision?.selectedModel ?? null,
470
+ fallback_reasoning_effort: this.fallbackDecision?.reasoningEffort ?? null,
471
+ provider_key_source: keySource,
472
+ message_count: observability.messageCount,
473
+ tool_count: observability.metadata.declared_tool_counts.total,
474
+ candidate_count: a.costEstimates.length,
475
+ provider_attempt_count: this.providerAttempts.length,
476
+ prefix_match_depth: a.identityMatchDepth ?? a.hits.deepest?.depth ?? null,
477
+ prefix_warm_tokens: selectedEstimate?.warm_tokens ?? null,
478
+ selector_latency_ms: selectorTrace?.latencyMs ?? null,
479
+ provider_latency_ms: outcome.providerLatencyMs ?? null,
480
+ total_latency_ms: Date.now() - a.startedMs,
481
+ input_tokens: tokenCount(usage?.input),
482
+ cache_read_tokens: tokenCount(usage?.cacheRead),
483
+ cache_write_tokens: tokenCount(usage?.cacheWrite),
484
+ output_tokens: tokenCount(usage?.output),
485
+ total_tokens: tokenCount(usage?.totalTokens),
486
+ total_cost_usd: costSource === null || !isRecord(usage?.cost)
487
+ ? null
488
+ : finiteNumber(usage.cost.total),
489
+ cost_source: costSource,
490
+ pricing_known: pricingKnown,
491
+ },
492
+ distinctId: a.credentials.attributed_user_id ??
493
+ a.credentials.api_key_id ??
494
+ a.credentials.organization_id,
495
+ interaction: a.interaction,
496
+ isNewConversation: a.isNewConversation,
497
+ zdr: a.zdr,
498
+ harnessConversationIdentity: a.harnessConversationIdentity,
499
+ tags: a.tags,
500
+ });
501
+ }
502
+ catch (error) {
503
+ console.error("routing report assembly failed", error);
504
+ captureOperationalError(error, {
505
+ code: "routing_report_assembly_failed",
506
+ requestId: this.args.requestId,
507
+ routerId: this.args.routerId,
508
+ });
509
+ }
510
+ }
511
+ }
512
+ function billingTerminalOutcome(outcome) {
513
+ if (outcome.status === "completed")
514
+ return "completed";
515
+ if (outcome.status === "aborted") {
516
+ return outcome.billingOutcome;
517
+ }
518
+ return "provider_failed";
519
+ }
520
+ function providerMessageForOutcome(outcome) {
521
+ return outcome.status === "selector_error"
522
+ ? null
523
+ : outcome.providerMessage ?? null;
524
+ }
525
+ function reportOutcome(args) {
526
+ if (args.status === "customer_canceled") {
527
+ return {
528
+ status: "aborted",
529
+ errorCode: "client_disconnected",
530
+ providerLatencyMs: args.providerLatencyMs,
531
+ providerExecution: args.execution,
532
+ providerMessage: args.message,
533
+ responseBody: args.responseBody,
534
+ billingOutcome: "customer_canceled",
535
+ partialOutput: args.partialOutput ?? null,
536
+ };
537
+ }
538
+ if (args.status === "provider_error") {
539
+ return {
540
+ status: "provider_error",
541
+ errorCode: subscriptionUsageLimitFromFailure(args.message) === null
542
+ ? "provider_execution_failed"
543
+ : "subscription_usage_limit",
544
+ errorMessage: typeof args.message.errorMessage === "string"
545
+ ? args.message.errorMessage
546
+ : undefined,
547
+ providerMessage: args.message,
548
+ providerLatencyMs: args.providerLatencyMs,
549
+ providerExecution: args.execution,
550
+ };
551
+ }
552
+ return {
553
+ status: "completed",
554
+ interactionOutput: args.output,
555
+ providerMessage: args.message,
556
+ responseBody: args.responseBody,
557
+ providerLatencyMs: args.providerLatencyMs,
558
+ providerExecution: args.execution,
559
+ };
560
+ }
561
+ function selectorIngest(args) {
562
+ const { trace } = args;
563
+ return {
564
+ selector_model: trace.selectorModel,
565
+ candidate_models: trace.selectorInput.candidate_pairs.map((candidate) => ({
566
+ model: candidate.model,
567
+ thinking_level: candidate.thinking_level,
568
+ })),
569
+ evals: trace.selectorInput.imported_evals.flatMap((card) => {
570
+ const id = card.id;
571
+ const name = card.name;
572
+ return typeof id === "string" && typeof name === "string" ? [{ id, name }] : [];
573
+ }),
574
+ cost_estimates: trace.selectorInput.cost_estimates,
575
+ previous_model: trace.selectorInput.previous_decision?.model ?? null,
576
+ reason: args.reason,
577
+ prompt_json: JSON.stringify(trace.selectorRequest),
578
+ output_text: selectorOutputText(trace, args.outputAccepted),
579
+ usage: usageIngest(trace.usage, args.pricingKnown),
580
+ };
581
+ }
582
+ function selectorOutputText(trace, accepted) {
583
+ const rejected = trace.rejectedOutputTexts ?? [];
584
+ if (rejected.length === 0)
585
+ return trace.outputText;
586
+ return JSON.stringify({
587
+ schema_version: 1,
588
+ kind: "selector_output_attempts",
589
+ attempts: [
590
+ ...rejected.map((outputText) => ({
591
+ outcome: "rejected",
592
+ output_text: outputText,
593
+ })),
594
+ {
595
+ outcome: accepted ? "accepted" : "rejected",
596
+ output_text: trace.outputText,
597
+ },
598
+ ],
599
+ });
600
+ }
601
+ // Normalized usage -> RoutingUsageIngest. Costs ride along as strings so the
602
+ // backend's Decimal columns parse them exactly; pricing_known=false marks
603
+ // catalog-missing models whose normalized cost would otherwise read as $0.
604
+ function usageIngest(usage, pricingKnown) {
605
+ if (!usage)
606
+ return null;
607
+ const cost = isRecord(usage.cost) ? usage.cost : {};
608
+ return {
609
+ input_tokens: tokenCount(usage.input),
610
+ cache_read_tokens: tokenCount(usage.cacheRead),
611
+ cache_write_tokens: tokenCount(usage.cacheWrite),
612
+ output_tokens: tokenCount(usage.output),
613
+ total_tokens: tokenCount(usage.totalTokens),
614
+ input_cost_usd: pricingKnown ? costString(cost.input) : null,
615
+ cache_read_cost_usd: pricingKnown ? costString(cost.cacheRead) : null,
616
+ cache_write_cost_usd: pricingKnown ? costString(cost.cacheWrite) : null,
617
+ output_cost_usd: pricingKnown ? costString(cost.output) : null,
618
+ total_cost_usd: pricingKnown ? costString(cost.total) : null,
619
+ pricing_known: pricingKnown,
620
+ };
621
+ }
@@ -0,0 +1,45 @@
1
+ import { type AgentRequestContext, type InteractionContent, type InteractionRequest, type ProviderContinuationState } from "../../interaction/types.js";
2
+ declare const SCHEMA_VERSION: 1;
3
+ type CountBy<T extends string> = Record<T, number>;
4
+ type ItemKind = "hosted_tool_call" | "hosted_tool_result" | "message" | "reasoning" | "tool_call" | "tool_result";
5
+ type MessageRole = "assistant" | "developer" | "system" | "user";
6
+ type ToolKind = "custom" | "function" | "hosted";
7
+ type ContentModality = "image" | "text";
8
+ type ContinuationKind = ProviderContinuationState["kind"];
9
+ export type InteractionObservabilityMetadata = {
10
+ schema_version: typeof SCHEMA_VERSION;
11
+ agent_context?: AgentRequestContext;
12
+ item_counts: CountBy<ItemKind> & {
13
+ total: number;
14
+ };
15
+ message_role_counts: CountBy<MessageRole>;
16
+ tool_call_counts: CountBy<ToolKind>;
17
+ declared_tool_counts: CountBy<ToolKind> & {
18
+ total: number;
19
+ };
20
+ item_content_modality_counts: CountBy<ContentModality>;
21
+ continuation_state_counts: CountBy<ContinuationKind> & {
22
+ total: number;
23
+ };
24
+ instructions_present: boolean;
25
+ reasoning_history_present: boolean;
26
+ prompt_cache_key_present: boolean;
27
+ user_present: boolean;
28
+ controls: {
29
+ reasoning_effort?: NonNullable<InteractionRequest["reasoning"]>["effort"];
30
+ reasoning_enabled?: boolean;
31
+ response_format?: NonNullable<InteractionRequest["responseFormat"]>["type"];
32
+ tool_choice?: "any" | "auto" | "custom" | "function" | "hosted" | "none" | "required";
33
+ parallel_tool_calls?: boolean;
34
+ };
35
+ };
36
+ export type InteractionRequestKind = "other" | "tool_continuation" | "user_message";
37
+ export type InteractionObservability = {
38
+ requestKind: InteractionRequestKind;
39
+ messageCount: number;
40
+ hasTools: boolean;
41
+ metadata: InteractionObservabilityMetadata;
42
+ };
43
+ export declare function extractInteractionObservability(request: InteractionRequest): InteractionObservability;
44
+ export declare function hasMeaningfulUserContent(content: InteractionContent[] | string | null): boolean;
45
+ export {};