@cartanova/qgrid-cli 2.10.9 → 2.11.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 (318) hide show
  1. package/bundle/dist/_virtual/_rolldown/runtime.js +1 -10
  2. package/bundle/dist/application/monit/log-buffer.js +12 -6
  3. package/bundle/dist/application/monit/monit.frame.js +11 -3
  4. package/bundle/dist/application/monit/monit.types.js +1 -1
  5. package/bundle/dist/application/qgrid/conv-routing.js +9 -6
  6. package/bundle/dist/application/qgrid/expired-token-reminder.js +2 -2
  7. package/bundle/dist/application/qgrid/oauth.js +31 -4
  8. package/bundle/dist/application/qgrid/qgrid-image-generation.js +1 -1
  9. package/bundle/dist/application/qgrid/qgrid-response-format.js +22 -20
  10. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +43 -34
  11. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +128 -93
  12. package/bundle/dist/application/qgrid/qgrid.frame.js +930 -49
  13. package/bundle/dist/application/qgrid/qgrid.types.js +212 -5
  14. package/bundle/dist/application/qgrid/schema-prompt.js +75 -48
  15. package/bundle/dist/application/qgrid/slack-user-map.js +1 -1
  16. package/bundle/dist/application/qgrid/token-death.js +1 -1
  17. package/bundle/dist/application/qgrid/token-subscriber.js +83 -30
  18. package/bundle/dist/application/qgrid/token-trigger-setup.js +1 -1
  19. package/bundle/dist/application/qgrid/token-window-keepalive.js +8 -6
  20. package/bundle/dist/application/qgrid/tool-emulation-schema.js +13 -13
  21. package/bundle/dist/application/qgrid/tool-emulation.js +19 -15
  22. package/bundle/dist/application/request-log/json-schema-type-text.js +29 -12
  23. package/bundle/dist/application/request-log/request-log.model.js +83 -69
  24. package/bundle/dist/application/request-log/request-log.types.js +8 -2
  25. package/bundle/dist/application/request-log/zod-code-format.js +1 -1
  26. package/bundle/dist/application/request-log-step/request-log-step.model.js +1 -1
  27. package/bundle/dist/application/request-log-step/request-log-step.types.js +1 -1
  28. package/bundle/dist/application/setting/setting.constant.js +1 -1
  29. package/bundle/dist/application/setting/setting.model.js +2 -2
  30. package/bundle/dist/application/setting/setting.store.js +2 -2
  31. package/bundle/dist/application/setting/setting.types.js +1 -1
  32. package/bundle/dist/application/sonamu.generated.js +40 -16
  33. package/bundle/dist/application/sonamu.generated.sso.js +2 -1
  34. package/bundle/dist/application/token/token.model.js +26 -9
  35. package/bundle/dist/application/token/token.types.js +20 -3
  36. package/bundle/dist/i18n/en.js +15 -1
  37. package/bundle/dist/i18n/ko.js +15 -1
  38. package/bundle/dist/i18n/sd.generated.js +2 -1
  39. package/bundle/dist/migrations/20260807133553_manual__backfill_request_log_cost.js +8 -6
  40. package/bundle/dist/migrations/20261018215031_alter_request_logs_add1_alter1.js +15 -0
  41. package/bundle/dist/server-bootstrap.js +4 -2
  42. package/bundle/dist/server-error-handler.js +1 -1
  43. package/bundle/dist/sonamu.config.js +37 -8
  44. package/bundle/dist/startup-migrations.js +17 -7
  45. package/bundle/dist/testing/global.js +1 -1
  46. package/bundle/dist/testing/schema-bench.js +0 -0
  47. package/bundle/dist/testing/test-database-name.js +1 -1
  48. package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +1 -1
  49. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +11 -5
  50. package/bundle/dist/utils/providers/anthropic/anthropic-quota.js +4 -4
  51. package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +1 -1
  52. package/bundle/dist/utils/providers/anthropic/claude-session.js +20 -19
  53. package/bundle/dist/utils/providers/anthropic/fence-strip.js +1 -1
  54. package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +49 -23
  55. package/bundle/dist/utils/providers/common/credentials.js +5 -1
  56. package/bundle/dist/utils/providers/common/effort.js +3 -3
  57. package/bundle/dist/utils/providers/common/model-cost.js +69 -14
  58. package/bundle/dist/utils/providers/common/provider-types.js +29 -1
  59. package/bundle/dist/utils/providers/common/schema-validation.js +78 -40
  60. package/bundle/dist/utils/providers/common/smooth-weighted-round-robin.js +1 -1
  61. package/bundle/dist/utils/providers/common/strictifier.js +62 -43
  62. package/bundle/dist/utils/providers/common/thinking.js +3 -2
  63. package/bundle/dist/utils/providers/common/ttft.js +1 -1
  64. package/bundle/dist/utils/providers/grok/grok-backend-protocol.js +427 -0
  65. package/bundle/dist/utils/providers/grok/grok-constants.js +55 -0
  66. package/bundle/dist/utils/providers/grok/grok-direct-client.js +196 -0
  67. package/bundle/dist/utils/providers/grok/grok-dispatcher.js +1313 -0
  68. package/bundle/dist/utils/providers/grok/grok-headers.js +118 -0
  69. package/bundle/dist/utils/providers/grok/grok-media-client.js +420 -0
  70. package/bundle/dist/utils/providers/grok/grok-models.js +114 -0
  71. package/bundle/dist/utils/providers/grok/grok-oauth.js +455 -0
  72. package/bundle/dist/utils/providers/grok/grok-quota.js +119 -0
  73. package/bundle/dist/utils/providers/grok/grok-refresh.js +192 -0
  74. package/bundle/dist/utils/providers/grok/grok-types.js +186 -0
  75. package/bundle/dist/utils/providers/grok/grok-voice-client.js +199 -0
  76. package/bundle/dist/utils/providers/grok/index.js +11 -0
  77. package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +111 -83
  78. package/bundle/dist/utils/providers/openai/openai-callback-relay.js +3 -2
  79. package/bundle/dist/utils/providers/openai/openai-constants.js +1 -1
  80. package/bundle/dist/utils/providers/openai/openai-direct-client.js +23 -7
  81. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +143 -106
  82. package/bundle/dist/utils/providers/openai/openai-image-generation.js +48 -33
  83. package/bundle/dist/utils/providers/openai/openai-oauth.js +28 -13
  84. package/bundle/dist/utils/providers/openai/openai-quota.js +38 -27
  85. package/bundle/dist/utils/providers/openai/openai-refresh.js +17 -9
  86. package/bundle/dist/utils/providers/openai/openai-sse.js +1 -1
  87. package/bundle/dist/utils/providers/openai/openai-transport-config.js +1 -1
  88. package/bundle/dist/utils/quiet-hours.js +1 -1
  89. package/bundle/dist/utils/slack-notify.js +14 -8
  90. package/bundle/dist/utils/subset-loaders.js +4 -5
  91. package/bundle/src/application/monit/log-buffer.ts +23 -9
  92. package/bundle/src/application/monit/monit.frame.ts +22 -5
  93. package/bundle/src/application/monit/monit.types.ts +6 -0
  94. package/bundle/src/application/qgrid/conv-routing.test.ts +76 -4
  95. package/bundle/src/application/qgrid/conv-routing.ts +35 -9
  96. package/bundle/src/application/qgrid/expired-token-reminder.ts +9 -1
  97. package/bundle/src/application/qgrid/oauth.ts +51 -15
  98. package/bundle/src/application/qgrid/qgrid-image-generation.ts +9 -0
  99. package/bundle/src/application/qgrid/qgrid-response-format.ts +50 -25
  100. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +33 -0
  101. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +87 -40
  102. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +97 -0
  103. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +274 -126
  104. package/bundle/src/application/qgrid/qgrid.frame.test.ts +1013 -3
  105. package/bundle/src/application/qgrid/qgrid.frame.ts +1415 -60
  106. package/bundle/src/application/qgrid/qgrid.types.ts +335 -0
  107. package/bundle/src/application/qgrid/schema-prompt.ts +105 -41
  108. package/bundle/src/application/qgrid/slack-user-map.ts +5 -0
  109. package/bundle/src/application/qgrid/token-death.ts +4 -0
  110. package/bundle/src/application/qgrid/token-subscriber.test.ts +21 -19
  111. package/bundle/src/application/qgrid/token-subscriber.ts +186 -53
  112. package/bundle/src/application/qgrid/token-trigger-setup.ts +2 -0
  113. package/bundle/src/application/qgrid/token-window-keepalive.ts +49 -6
  114. package/bundle/src/application/qgrid/tool-emulation-schema.ts +33 -29
  115. package/bundle/src/application/qgrid/tool-emulation.ts +52 -27
  116. package/bundle/src/application/request-log/json-schema-type-text.test.ts +5 -5
  117. package/bundle/src/application/request-log/json-schema-type-text.ts +81 -26
  118. package/bundle/src/application/request-log/request-log.entity.json +8 -0
  119. package/bundle/src/application/request-log/request-log.model.test.ts +111 -4
  120. package/bundle/src/application/request-log/request-log.model.ts +279 -145
  121. package/bundle/src/application/request-log/request-log.types.ts +17 -0
  122. package/bundle/src/application/request-log/zod-code-format.ts +11 -0
  123. package/bundle/src/application/request-log-step/request-log-step.model.ts +1 -0
  124. package/bundle/src/application/request-log-step/request-log-step.types.ts +2 -0
  125. package/bundle/src/application/setting/setting.constant.ts +9 -0
  126. package/bundle/src/application/setting/setting.model.ts +12 -2
  127. package/bundle/src/application/setting/setting.store.ts +5 -1
  128. package/bundle/src/application/setting/setting.types.ts +8 -0
  129. package/bundle/src/application/sonamu.generated.http +135 -0
  130. package/bundle/src/application/sonamu.generated.sso.ts +3 -0
  131. package/bundle/src/application/sonamu.generated.ts +29 -0
  132. package/bundle/src/application/token/token.identity.test.ts +20 -0
  133. package/bundle/src/application/token/token.model.ts +66 -7
  134. package/bundle/src/application/token/token.types.ts +24 -1
  135. package/bundle/src/i18n/en.ts +19 -0
  136. package/bundle/src/i18n/ko.ts +21 -0
  137. package/bundle/src/i18n/sd.generated.ts +24 -2
  138. package/bundle/src/migrations/20260807133553_manual__backfill_request_log_cost.ts +16 -8
  139. package/bundle/src/migrations/20261018215031_alter_request_logs_add1_alter1.ts +15 -0
  140. package/bundle/src/server-bootstrap.ts +4 -2
  141. package/bundle/src/server-error-handler.ts +1 -0
  142. package/bundle/src/sonamu.config.ts +69 -7
  143. package/bundle/src/startup-migrations.ts +27 -9
  144. package/bundle/src/testing/global.ts +20 -0
  145. package/bundle/src/testing/schema-bench.ts +105 -30
  146. package/bundle/src/testing/test-database-name.ts +2 -0
  147. package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +8 -0
  148. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +41 -6
  149. package/bundle/src/utils/providers/anthropic/anthropic-quota.ts +7 -3
  150. package/bundle/src/utils/providers/anthropic/anthropic-thinking.ts +6 -0
  151. package/bundle/src/utils/providers/anthropic/claude-session.ts +58 -24
  152. package/bundle/src/utils/providers/anthropic/fence-strip.ts +11 -0
  153. package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +110 -45
  154. package/bundle/src/utils/providers/common/credentials.ts +8 -0
  155. package/bundle/src/utils/providers/common/effort.ts +9 -2
  156. package/bundle/src/utils/providers/common/model-cost.test.ts +48 -1
  157. package/bundle/src/utils/providers/common/model-cost.ts +259 -158
  158. package/bundle/src/utils/providers/common/provider-dispatcher.ts +98 -2
  159. package/bundle/src/utils/providers/common/provider-types.ts +46 -3
  160. package/bundle/src/utils/providers/common/schema-validation.test.ts +5 -1
  161. package/bundle/src/utils/providers/common/schema-validation.ts +124 -56
  162. package/bundle/src/utils/providers/common/smooth-weighted-round-robin.ts +4 -0
  163. package/bundle/src/utils/providers/common/strictifier.ts +185 -59
  164. package/bundle/src/utils/providers/common/thinking.ts +2 -2
  165. package/bundle/src/utils/providers/common/ttft.ts +4 -2
  166. package/bundle/src/utils/providers/grok/__fixtures__/golden-responses-1.0.60.json +164 -0
  167. package/bundle/src/utils/providers/grok/grok-backend-protocol.test.ts +325 -0
  168. package/bundle/src/utils/providers/grok/grok-backend-protocol.ts +721 -0
  169. package/bundle/src/utils/providers/grok/grok-constants.ts +105 -0
  170. package/bundle/src/utils/providers/grok/grok-direct-client.test.ts +179 -0
  171. package/bundle/src/utils/providers/grok/grok-direct-client.ts +313 -0
  172. package/bundle/src/utils/providers/grok/grok-dispatcher.test.ts +1395 -0
  173. package/bundle/src/utils/providers/grok/grok-dispatcher.ts +2105 -0
  174. package/bundle/src/utils/providers/grok/grok-headers.test.ts +149 -0
  175. package/bundle/src/utils/providers/grok/grok-headers.ts +199 -0
  176. package/bundle/src/utils/providers/grok/grok-media-client.test.ts +446 -0
  177. package/bundle/src/utils/providers/grok/grok-media-client.ts +703 -0
  178. package/bundle/src/utils/providers/grok/grok-models.test.ts +96 -0
  179. package/bundle/src/utils/providers/grok/grok-models.ts +161 -0
  180. package/bundle/src/utils/providers/grok/grok-oauth.test.ts +406 -0
  181. package/bundle/src/utils/providers/grok/grok-oauth.ts +771 -0
  182. package/bundle/src/utils/providers/grok/grok-quota.test.ts +142 -0
  183. package/bundle/src/utils/providers/grok/grok-quota.ts +198 -0
  184. package/bundle/src/utils/providers/grok/grok-refresh.test.ts +239 -0
  185. package/bundle/src/utils/providers/grok/grok-refresh.ts +325 -0
  186. package/bundle/src/utils/providers/grok/grok-types.test.ts +252 -0
  187. package/bundle/src/utils/providers/grok/grok-types.ts +374 -0
  188. package/bundle/src/utils/providers/grok/grok-voice-client.test.ts +321 -0
  189. package/bundle/src/utils/providers/grok/grok-voice-client.ts +284 -0
  190. package/bundle/src/utils/providers/grok/index.ts +20 -0
  191. package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +221 -137
  192. package/bundle/src/utils/providers/openai/openai-callback-relay.ts +12 -1
  193. package/bundle/src/utils/providers/openai/openai-constants.ts +2 -0
  194. package/bundle/src/utils/providers/openai/openai-direct-client.ts +111 -21
  195. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +322 -169
  196. package/bundle/src/utils/providers/openai/openai-image-generation.ts +116 -36
  197. package/bundle/src/utils/providers/openai/openai-oauth.ts +48 -20
  198. package/bundle/src/utils/providers/openai/openai-quota.ts +77 -46
  199. package/bundle/src/utils/providers/openai/openai-refresh.ts +42 -18
  200. package/bundle/src/utils/providers/openai/openai-sse.ts +27 -3
  201. package/bundle/src/utils/providers/openai/openai-transport-config.ts +2 -0
  202. package/bundle/src/utils/quiet-hours.ts +12 -1
  203. package/bundle/src/utils/slack-notify.ts +44 -13
  204. package/bundle/src/utils/subset-loaders.ts +7 -7
  205. package/bundle/web-dist/client/assets/clsx-5XM_pHvN.js +1 -0
  206. package/bundle/web-dist/client/assets/index-BA7uQ-6c.js +31 -0
  207. package/bundle/web-dist/client/assets/index-Be2qCZmI.css +1 -0
  208. package/bundle/web-dist/client/assets/logs-C47OJGEI.js +1 -0
  209. package/bundle/web-dist/client/assets/monit-BS9no0dU.js +4 -0
  210. package/bundle/web-dist/client/assets/plus-sdACTfH0.js +1 -0
  211. package/bundle/web-dist/client/assets/refresh-cw-CY8o_1cT.js +1 -0
  212. package/bundle/web-dist/client/assets/request-model-BRtFuZvj.js +1 -0
  213. package/bundle/web-dist/client/assets/routes-kqgkUxE8.js +5 -0
  214. package/bundle/web-dist/client/assets/{services.generated-BsieU37H.js → services.generated-CefiDXEv.js} +5 -5
  215. package/bundle/web-dist/client/assets/settings-DNDvFGSw.js +1 -0
  216. package/bundle/web-dist/client/assets/show-u8Np69q3.js +2 -0
  217. package/bundle/web-dist/client/assets/tokens-QvP410NW.js +1 -0
  218. package/bundle/web-dist/client/assets/total-cost-MnfKU8IN.js +1 -0
  219. package/bundle/web-dist/client/assets/use-oauth-login-flow-Bzdh28Ws.js +1 -0
  220. package/bundle/web-dist/client/assets/use-update-token-mutation-CfJKaxmR.js +1 -0
  221. package/bundle/web-dist/client/assets/vendor-react-CUFFrVN9.js +244 -0
  222. package/bundle/web-dist/client/assets/vendor-tanstack-Bf_jag9G.js +1 -0
  223. package/bundle/web-dist/client/index.html +6 -6
  224. package/bundle/web-dist/server/assets/clsx-D_N5PN_2.js +16 -0
  225. package/bundle/web-dist/server/assets/{lazyRouteComponent-Dtd0NR4r.js → lazyRouteComponent-D8iTIHyn.js} +129 -2
  226. package/bundle/web-dist/server/assets/logs-B3OTd9hy.js +825 -0
  227. package/bundle/web-dist/server/assets/{monit-DvzribSE.js → monit-B2hqorPe.js} +385 -126
  228. package/bundle/web-dist/server/assets/plus-DwG7h9kq.js +28 -0
  229. package/bundle/web-dist/server/assets/{react-dom-B4a6-uuR.js → react-dom-BIYQzBqG.js} +2 -129
  230. package/bundle/web-dist/server/assets/refresh-cw-mMBkwOq0.js +44 -0
  231. package/bundle/web-dist/server/assets/{request-model-CbqfnQOJ.js → request-model-I6OdODSH.js} +15 -6
  232. package/bundle/web-dist/server/assets/{routes-BAPWTizT.js → routes-B95YlUv2.js} +760 -213
  233. package/bundle/web-dist/server/assets/{services.generated-CEbGjsI0.js → services.generated-DQATJ6qf.js} +12821 -12194
  234. package/bundle/web-dist/server/assets/settings-C3gbGk0m.js +830 -0
  235. package/bundle/web-dist/server/assets/{show-CxVZTAdv.js → show-Bxaj3rhH.js} +1355 -419
  236. package/bundle/web-dist/server/assets/tokens-CZjH3cB7.js +1038 -0
  237. package/bundle/web-dist/server/assets/total-cost-fTE9Xy82.js +18 -0
  238. package/bundle/web-dist/server/assets/use-oauth-login-flow-CKDHgc3U.js +2379 -0
  239. package/bundle/web-dist/server/assets/{use-update-token-mutation-CfYmk_4_.js → use-update-token-mutation-BEbgOzaa.js} +1 -1
  240. package/bundle/web-dist/server/entry-server.generated.js +4303 -22810
  241. package/dist/cli.js +9 -9
  242. package/package.json +4 -11
  243. package/postinstall.mjs +12 -0
  244. package/skills/qgrid/SKILL.md +1 -0
  245. package/skills/qgrid/references/cli-env-and-server-boot.md +4 -1
  246. package/skills/qgrid/references/decision-rationale.md +24 -1
  247. package/skills/qgrid/references/grok-runtime.md +89 -0
  248. package/skills/qgrid/references/provider-runtime-differences.md +30 -29
  249. package/skills/qgrid/references/repo-map.md +10 -0
  250. package/skills/qgrid/references/request-log-run-lifecycle.md +1 -1
  251. package/skills/qgrid/references/sonamu-api-web-flow.md +1 -1
  252. package/skills/qgrid/references/token-auth-quota-lifecycle.md +56 -3
  253. package/bundle/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +0 -406
  254. package/bundle/dist/node_modules/.pnpm/@vitest_expect@4.1.10/node_modules/@vitest/expect/dist/index.js +0 -1507
  255. package/bundle/dist/node_modules/.pnpm/@vitest_pretty-format@4.1.10/node_modules/@vitest/pretty-format/dist/index.js +0 -887
  256. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/chunk-artifact.js +0 -2756
  257. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/index.js +0 -8
  258. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/utils.js +0 -8
  259. package/bundle/dist/node_modules/.pnpm/@vitest_snapshot@4.1.10/node_modules/@vitest/snapshot/dist/index.js +0 -922
  260. package/bundle/dist/node_modules/.pnpm/@vitest_spy@4.1.10/node_modules/@vitest/spy/dist/index.js +0 -386
  261. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +0 -121
  262. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/constants.js +0 -51
  263. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/diff.js +0 -1317
  264. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/display.js +0 -561
  265. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/error.js +0 -40
  266. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/helpers.js +0 -272
  267. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/offset.js +0 -27
  268. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/serialize.js +0 -77
  269. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/source-map.js +0 -381
  270. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/timers.js +0 -51
  271. package/bundle/dist/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.js +0 -2986
  272. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js +0 -10
  273. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.js +0 -109
  274. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js +0 -10
  275. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js +0 -10
  276. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js +0 -10
  277. package/bundle/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +0 -1126
  278. package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +0 -21
  279. package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +0 -432
  280. package/bundle/dist/node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.js +0 -517
  281. package/bundle/dist/node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js +0 -101
  282. package/bundle/dist/node_modules/.pnpm/vite@8.0.3_@emnapi_core@1.11.2_@emnapi_runtime@1.11.2_@types_node@25.0.7_esbuild@0.27.4_5b35fc1fb85d2af4d105e0d74f940f50/node_modules/vite/dist/node/module-runner.js +0 -1265
  283. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +0 -9
  284. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/benchmark.CX_oY03V.js +0 -41
  285. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/evaluatedModules.Dg1zASAC.js +0 -19
  286. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.Chj8NDwU.js +0 -202
  287. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.DdgEv5B1.js +0 -46
  288. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +0 -119
  289. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/test.DNmyFkvJ.js +0 -3044
  290. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +0 -59
  291. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/index.js +0 -28
  292. package/bundle/dist/testing/setup-mocks.js +0 -16
  293. package/bundle/src/testing/setup-mocks.ts +0 -44
  294. package/bundle/web-dist/client/assets/index-Bw2s88Ot.css +0 -1
  295. package/bundle/web-dist/client/assets/index-CrXKu_J8.js +0 -106
  296. package/bundle/web-dist/client/assets/logs-B2SmWJXA.js +0 -1
  297. package/bundle/web-dist/client/assets/monit-D-JwXXoA.js +0 -1
  298. package/bundle/web-dist/client/assets/plus-B62CuqT8.js +0 -1
  299. package/bundle/web-dist/client/assets/refresh-cw-NNcU0Cmq.js +0 -1
  300. package/bundle/web-dist/client/assets/request-model-CojtinqK.js +0 -1
  301. package/bundle/web-dist/client/assets/routes-C5r4gTvY.js +0 -5
  302. package/bundle/web-dist/client/assets/settings-D3tDSAck.js +0 -1
  303. package/bundle/web-dist/client/assets/show-Cah_kUKC.js +0 -2
  304. package/bundle/web-dist/client/assets/tokens-DU2Iyxxd.js +0 -1
  305. package/bundle/web-dist/client/assets/use-oauth-login-flow-D0UaR9_e.js +0 -1
  306. package/bundle/web-dist/client/assets/use-update-token-mutation-DAq0F8p-.js +0 -1
  307. package/bundle/web-dist/client/assets/vendor-react-DH4kUfK2.js +0 -9
  308. package/bundle/web-dist/client/assets/vendor-tanstack-DJRQAbrY.js +0 -1
  309. package/bundle/web-dist/server/assets/logs-l91avYif.js +0 -509
  310. package/bundle/web-dist/server/assets/plus-KZrECewT.js +0 -19
  311. package/bundle/web-dist/server/assets/refresh-cw-BND93JrF.js +0 -23
  312. package/bundle/web-dist/server/assets/settings-BZR5HSAL.js +0 -461
  313. package/bundle/web-dist/server/assets/tokens-D1hb4eKk.js +0 -448
  314. package/bundle/web-dist/server/assets/use-oauth-login-flow-Cvdy0zF8.js +0 -178
  315. package/dist/cli.d.ts +0 -10
  316. /package/bundle/web-dist/client/assets/{cost-DH2jZbrw.js → cost-DB3HbU7T.js} +0 -0
  317. /package/bundle/web-dist/client/assets/{preload-helper-ca-nBW7U.js → preload-helper-DWTEM3RW.js} +0 -0
  318. /package/bundle/web-dist/server/assets/{cost-eEHeTRT5.js → cost-BC9yzyA9.js} +0 -0
@@ -10,19 +10,27 @@ import {
10
10
  Puri,
11
11
  transactional,
12
12
  } from "sonamu";
13
+ import { z } from "zod";
13
14
 
14
15
  import { SD } from "../../i18n/sd.generated";
15
16
  import { calculateCostUsd } from "../../utils/providers/common/model-cost";
17
+ import {
18
+ isJsonObject,
19
+ isProviderId,
20
+ type JsonValue,
21
+ jsonValueSchema,
22
+ type ProviderId,
23
+ } from "../../utils/providers/common/provider-types";
24
+ import { type RequestLogStepSaveParams } from "../request-log-step/request-log-step.types";
16
25
  import { type RequestLogSubsetKey, type RequestLogSubsetMapping } from "../sonamu.generated";
17
26
  import { requestLogLoaderQueries, requestLogSubsetQueries } from "../sonamu.generated.sso";
27
+ import { renderJsonSchemaZodText, renderParsedJsonSchemaTypeText } from "./json-schema-type-text";
18
28
  import {
19
- renderJsonSchemaZodShapeText,
20
- renderParsedJsonSchemaTypeText,
21
- } from "./json-schema-type-text";
22
- import {
29
+ type HistoryItems,
23
30
  type RequestLogListParams,
24
31
  type RequestLogSaveParams,
25
- type ToolDefinitions,
32
+ type InputImages,
33
+ ToolDefinitions,
26
34
  type ToolView,
27
35
  } from "./request-log.types";
28
36
  import { formatZodCode } from "./zod-code-format";
@@ -32,11 +40,28 @@ export const MICRO_USD = 1_000_000;
32
40
  const REQUEST_LOG_RUN_LOCK_CLASS_ID = 718;
33
41
  const TOOL_ENUM_VALUE_LIMIT = 6;
34
42
 
35
- function decodeStoredTools(value: unknown): ToolDefinitions | null {
43
+ /**
44
+ * 비용을 알 수 없는 non-token 요청(Grok STT/TTS, ticks 없는 video). provider 비용 없이
45
+ * 토큰 0 으로 성공 저장되므로 SUM 은 건너뛰고 legacy 재계산도 $0 으로 매긴다 — 합계에
46
+ * 빠졌다는 신호를 따로 센다. 실패/중단/진행 중 row 는 과금 대상이 아니라 제외하고,
47
+ * 이미지 row 는 `unknownImageCostCount` 가 이미 센다.
48
+ */
49
+ export const UNKNOWN_MEDIA_COST_PREDICATE =
50
+ "request_logs.status = 'succeeded' AND request_logs.cost_usd IS NULL AND request_logs.cost_source IS NULL" +
51
+ " AND request_logs.is_image_generation = false AND request_logs.input_tokens = 0 AND request_logs.output_tokens = 0";
52
+
53
+ /** Numeric aggregate column that node-postgres may deliver as bigint text. */
54
+ const bigintAggregate = z.coerce.number();
55
+
56
+ function decodeStoredTools(
57
+ value: ToolDefinitions | string | null | undefined,
58
+ ): ToolDefinitions | null {
36
59
  if (!value) return null;
37
- if (typeof value !== "string") return value as ToolDefinitions;
60
+
61
+ if (Array.isArray(value)) return value;
62
+
38
63
  try {
39
- return JSON.parse(value) as ToolDefinitions;
64
+ return ToolDefinitions.safeParse(JSON.parse(value)).data ?? null;
40
65
  } catch {
41
66
  return null;
42
67
  }
@@ -48,6 +73,47 @@ type ToolResultContinuation = {
48
73
  isError?: boolean;
49
74
  };
50
75
 
76
+ type RequestLogRunRow = Partial<RequestLogSaveParams>;
77
+
78
+ export type RequestLogStatus = NonNullable<RequestLogSaveParams["status"]>;
79
+
80
+ /** Step columns the caller supplies; `request_log_id` is bound by `appendStep`. */
81
+ export type RequestLogStepFields = Partial<Omit<RequestLogStepSaveParams, "request_log_id">> &
82
+ Pick<RequestLogStepSaveParams, "step_index" | "type">;
83
+
84
+ export interface FinishRunParams {
85
+ status: RequestLogStatus;
86
+ response?: string;
87
+ token_name?: string;
88
+ input_tokens?: number;
89
+ output_tokens?: number;
90
+ cache_read_tokens?: number;
91
+ cache_creation_tokens?: number;
92
+ cache_creation_5m_tokens?: number | null;
93
+ cache_creation_1h_tokens?: number | null;
94
+ duration_ms?: number;
95
+ requested_model_name?: string | null;
96
+ model_name?: string | null;
97
+ service_tier?: string | null;
98
+ fallback_count?: number | null;
99
+ cost_usd?: number | null;
100
+ cost_source?: string | null;
101
+ history?: HistoryItems;
102
+ error_message?: string;
103
+ tool_call_count?: number;
104
+ image_cost_usd?: number | null;
105
+ image_cost_method?: string | null;
106
+ is_image_generation?: boolean;
107
+ response_json_ok?: boolean | null;
108
+ }
109
+
110
+ type FinishRunUpdate = Partial<FinishRunParams> & { id: number; ttft_ms?: number };
111
+
112
+ /** Copies `key` only when the caller supplied it, so omitted columns stay untouched. */
113
+ function copyDefined<T, K extends keyof T>(target: Partial<T>, source: T, key: K): void {
114
+ if (source[key] !== undefined) target[key] = source[key];
115
+ }
116
+
51
117
  type RequestLogStepAggregate = {
52
118
  input_tokens: number;
53
119
  output_tokens: number;
@@ -74,23 +140,24 @@ const STEP_AGGREGATE_SELECT = {
74
140
  cost_source: "cost_source",
75
141
  } as const;
76
142
 
77
- function aggregateGenerateStepRows(rows: Array<Record<string, unknown>>): RequestLogStepAggregate {
78
- const sum = (field: string): number =>
79
- rows.reduce((total, row) => total + Number(row[field] ?? 0), 0);
80
- const optionalSum = (field: string): number | undefined =>
81
- rows.some((row) => row[field] !== null && row[field] !== undefined) ? sum(field) : undefined;
82
- const collapse = (field: string): string | undefined => {
83
- const values = [
84
- ...new Set(
85
- rows
86
- .map((row) => row[field])
87
- .filter((value): value is string => typeof value === "string" && value.length > 0),
88
- ),
89
- ];
90
- if (values.length === 0) return undefined;
91
- return values.length === 1 ? values[0] : "mixed";
92
- };
93
- return {
143
+ type StepAggregateNumberField = Exclude<keyof typeof STEP_AGGREGATE_SELECT, "cost_source">;
144
+
145
+ type StepAggregateRow = { [K in StepAggregateNumberField]: number | null } & {
146
+ cost_source: string | null;
147
+ };
148
+
149
+ function aggregateGenerateStepRows(rows: StepAggregateRow[]): RequestLogStepAggregate {
150
+ const sum = (field: StepAggregateNumberField): number =>
151
+ rows.reduce((total, row) => total + (row[field] ?? 0), 0);
152
+
153
+ const optionalSum = (field: StepAggregateNumberField): number | undefined =>
154
+ rows.some((row) => row[field] !== null) ? sum(field) : undefined;
155
+
156
+ const costSources = [
157
+ ...new Set(rows.flatMap((row) => (row.cost_source ? [row.cost_source] : []))),
158
+ ];
159
+
160
+ const aggregate: RequestLogStepAggregate = {
94
161
  input_tokens: sum("input_tokens"),
95
162
  output_tokens: sum("output_tokens"),
96
163
  cache_read_tokens: sum("cache_read_tokens"),
@@ -100,8 +167,13 @@ function aggregateGenerateStepRows(rows: Array<Record<string, unknown>>): Reques
100
167
  duration_ms: sum("duration_ms"),
101
168
  fallback_count: sum("fallback_count"),
102
169
  cost_usd: sum("cost_usd"),
103
- cost_source: collapse("cost_source"),
104
170
  };
171
+
172
+ if (costSources.length > 0) {
173
+ aggregate.cost_source = costSources.length === 1 ? costSources[0] : "mixed";
174
+ }
175
+
176
+ return aggregate;
105
177
  }
106
178
 
107
179
  type RequestLogUsageRow = {
@@ -125,11 +197,19 @@ function isAnthropicUsageRow(row: Pick<RequestLogUsageRow, "token_name" | "model
125
197
  );
126
198
  }
127
199
 
200
+ /** Provider of a provider-qualified model id (`openai/`, `anthropic/`, `grok/`); legacy ids are unknown. */
201
+ export function providerOfModelName(modelName: string | null | undefined): ProviderId | "unknown" {
202
+ const slash = modelName?.indexOf("/") ?? -1;
203
+ const prefix = slash > 0 ? modelName?.slice(0, slash) : undefined;
204
+
205
+ return isProviderId(prefix) ? prefix : "unknown";
206
+ }
207
+
128
208
  function canonicalModelName(modelName: string): string {
129
209
  return modelName.includes("/") ? modelName.split("/").pop()! : modelName;
130
210
  }
131
211
 
132
- function normalizedUsageForCost(row: RequestLogUsageRow): {
212
+ interface UsageForCost {
133
213
  model: string | null;
134
214
  inputTokens: number;
135
215
  outputTokens: number;
@@ -137,36 +217,47 @@ function normalizedUsageForCost(row: RequestLogUsageRow): {
137
217
  cacheCreationInputTokens: number;
138
218
  cacheCreationInputTokens5m?: number;
139
219
  cacheCreationInputTokens1h?: number;
140
- } {
220
+ }
221
+
222
+ function normalizedUsageForCost(row: RequestLogUsageRow): UsageForCost {
141
223
  const cacheRead = row.cache_read_tokens;
142
224
  const cacheCreation = row.cache_creation_tokens;
143
225
  const storedInput = row.input_tokens;
226
+
144
227
  const legacyAnthropicSplitInput =
145
228
  isAnthropicUsageRow(row) && storedInput < cacheRead + cacheCreation;
146
- return {
229
+
230
+ const usage: UsageForCost = {
147
231
  model: row.model_name ? canonicalModelName(row.model_name) : null,
148
232
  inputTokens: legacyAnthropicSplitInput ? storedInput + cacheRead + cacheCreation : storedInput,
149
233
  outputTokens: row.output_tokens,
150
234
  cachedInputTokens: cacheRead,
151
235
  cacheCreationInputTokens: cacheCreation,
152
- ...(row.cache_creation_5m_tokens !== null && row.cache_creation_5m_tokens !== undefined
153
- ? { cacheCreationInputTokens5m: row.cache_creation_5m_tokens }
154
- : {}),
155
- ...(row.cache_creation_1h_tokens !== null && row.cache_creation_1h_tokens !== undefined
156
- ? { cacheCreationInputTokens1h: row.cache_creation_1h_tokens }
157
- : {}),
158
236
  };
237
+
238
+ if (row.cache_creation_5m_tokens !== null && row.cache_creation_5m_tokens !== undefined) {
239
+ usage.cacheCreationInputTokens5m = row.cache_creation_5m_tokens;
240
+ }
241
+
242
+ if (row.cache_creation_1h_tokens !== null && row.cache_creation_1h_tokens !== undefined) {
243
+ usage.cacheCreationInputTokens1h = row.cache_creation_1h_tokens;
244
+ }
245
+
246
+ return usage;
159
247
  }
160
248
 
161
249
  function normalizeLegacyAnthropicRow<T extends RequestLogUsageRow>(row: T): T {
162
250
  // 새 row 는 당시의 provider/가격표 비용을 확정 저장한다. 현재 가격표로 덮어쓰지 않는다.
163
251
  if (row.cost_source) return row;
164
252
  const usage = normalizedUsageForCost(row);
253
+
165
254
  if (usage.inputTokens === row.input_tokens) return row;
166
255
  const normalized = { ...row, input_tokens: usage.inputTokens };
256
+
167
257
  if (usage.model) {
168
258
  normalized.cost_usd = Math.round(calculateCostUsd(usage.model, usage) * MICRO_USD);
169
259
  }
260
+
170
261
  return normalized;
171
262
  }
172
263
 
@@ -189,20 +280,25 @@ const ORDERABLE_COLUMNS = {
189
280
  cost_usd: "request_logs.cost_usd",
190
281
  } as const;
191
282
 
283
+ const ORDERABLE_COLUMN_BY_KEY = new Map(Object.entries(ORDERABLE_COLUMNS));
284
+
192
285
  function applyOrderBy(
193
286
  qb: ReturnType<RequestLogModelClass["getSubsetQueries"]>["qb"],
194
287
  orderBy: string,
195
288
  ): void {
196
289
  const separator = orderBy.lastIndexOf("-");
197
- const key = orderBy.slice(0, separator) as keyof typeof ORDERABLE_COLUMNS;
290
+ const key = orderBy.slice(0, separator);
198
291
  const direction = orderBy.slice(separator + 1) === "asc" ? "asc" : "desc";
199
- const column = ORDERABLE_COLUMNS[key];
292
+ const column = ORDERABLE_COLUMN_BY_KEY.get(key);
200
293
 
201
294
  if (!column) {
202
295
  qb.orderBy("request_logs.id", "desc");
296
+
203
297
  return;
204
298
  }
299
+
205
300
  qb.orderBy(column, direction);
301
+
206
302
  // 동점 행의 순서가 페이지마다 흔들리지 않게 고유 컬럼으로 tie-break 한다.
207
303
  if (key !== "id") qb.orderBy("request_logs.id", "desc");
208
304
  }
@@ -231,6 +327,7 @@ class RequestLogModelClass extends BaseModelClass<
231
327
  page: 1,
232
328
  queryMode: "list",
233
329
  });
330
+
234
331
  if (!rows[0]) {
235
332
  throw new NotFoundException(SD("error.entityNotFound")("RequestLog", id));
236
333
  }
@@ -331,9 +428,7 @@ class RequestLogModelClass extends BaseModelClass<
331
428
  const wdb = this.getPuri("w");
332
429
 
333
430
  // register
334
- spa.forEach((sp) => {
335
- wdb.ubRegister("request_logs", sp);
336
- });
431
+ for (const sp of spa) wdb.ubRegister("request_logs", sp);
337
432
 
338
433
  // transaction
339
434
  return wdb.transaction(async (trx) => {
@@ -359,7 +454,8 @@ class RequestLogModelClass extends BaseModelClass<
359
454
  }>
360
455
  > {
361
456
  const rdb = this.getPuri("r");
362
- const rows = (await rdb
457
+
458
+ const rows = await rdb
363
459
  .from("request_logs")
364
460
  .select({
365
461
  requested_model_name: "request_logs.requested_model_name",
@@ -367,36 +463,31 @@ class RequestLogModelClass extends BaseModelClass<
367
463
  input_tokens: "request_logs.input_tokens",
368
464
  cache_read_tokens: "request_logs.cache_read_tokens",
369
465
  })
370
- .where("request_logs.created_at", ">=", since)) as unknown as Array<{
371
- requested_model_name: string | null;
372
- status: string;
373
- input_tokens: number;
374
- cache_read_tokens: number;
375
- }>;
466
+ .where("request_logs.created_at", ">=", since);
376
467
 
377
468
  const byProvider = new Map<
378
469
  string,
379
470
  { requests: number; errors: number; inputTokens: number; cacheReadTokens: number }
380
471
  >();
381
- rows.forEach((row) => {
382
- const model = row.requested_model_name ?? "";
383
- const provider = model.startsWith("openai/")
384
- ? "openai"
385
- : model.startsWith("anthropic/")
386
- ? "anthropic"
387
- : "unknown";
472
+
473
+ for (const row of rows) {
474
+ const provider = providerOfModelName(row.requested_model_name);
475
+
388
476
  const agg = byProvider.get(provider) ?? {
389
477
  requests: 0,
390
478
  errors: 0,
391
479
  inputTokens: 0,
392
480
  cacheReadTokens: 0,
393
481
  };
482
+
394
483
  agg.requests += 1;
484
+
395
485
  if (row.status === "error") agg.errors += 1;
396
486
  agg.inputTokens += row.input_tokens;
397
487
  agg.cacheReadTokens += row.cache_read_tokens;
398
488
  byProvider.set(provider, agg);
399
- });
489
+ }
490
+
400
491
  return [...byProvider.entries()]
401
492
  .map(([provider, agg]) => ({ provider, ...agg }))
402
493
  .toSorted((a, b) => a.provider.localeCompare(b.provider));
@@ -414,27 +505,35 @@ class RequestLogModelClass extends BaseModelClass<
414
505
  zod: string | null;
415
506
  }> {
416
507
  const rdb = this.getPuri("r");
417
- const rows = (await rdb
508
+
509
+ const rows = await rdb
418
510
  .from("request_logs")
419
511
  .select({ json_schema: "request_logs.json_schema" })
420
- .where("request_logs.id", id)) as unknown as Array<{ json_schema: string | null }>;
512
+ .where("request_logs.id", id);
513
+
421
514
  const schema = rows[0]?.json_schema;
515
+
422
516
  if (!schema) return { typescript: null, zod: null };
423
517
 
424
- let parsed: unknown;
518
+ let parsed: JsonValue;
519
+
425
520
  try {
521
+ // JSON.parse without a reviver yields only JSON values.
426
522
  parsed = JSON.parse(schema);
427
523
  } catch {
428
524
  return { typescript: null, zod: null };
429
525
  }
526
+
430
527
  let zod: string | null = null;
431
- try {
432
- zod = formatZodCode(
433
- jsonSchemaToZod(parsed as Parameters<typeof jsonSchemaToZod>[0], { module: "none" }),
434
- );
435
- } catch {
436
- zod = null;
528
+
529
+ if (isJsonObject(parsed) || parsed === true || parsed === false) {
530
+ try {
531
+ zod = formatZodCode(jsonSchemaToZod(parsed, { module: "none" }));
532
+ } catch {
533
+ zod = null;
534
+ }
437
535
  }
536
+
438
537
  return { typescript: renderParsedJsonSchemaTypeText(parsed), zod };
439
538
  }
440
539
 
@@ -449,28 +548,38 @@ class RequestLogModelClass extends BaseModelClass<
449
548
  */
450
549
  @api({ httpMethod: "GET", clients: ["axios", "tanstack-query"] })
451
550
  async toolsView(id: number): Promise<ToolView[]> {
452
- const row = (await this.getPuri("r")
551
+ const row = await this.getPuri("r")
453
552
  .from("request_logs")
454
553
  .select({ tools: "request_logs.tools" })
455
554
  .where("request_logs.id", id)
456
- .first()) as { tools: unknown } | undefined;
555
+ .first();
556
+
457
557
  const tools = decodeStoredTools(row?.tools);
558
+
458
559
  if (!tools) return [];
459
560
 
460
561
  return tools.map((tool) => {
461
- const inputSchema = (tool.inputSchema ?? {}) as {
462
- properties?: Record<string, unknown>;
463
- };
464
- const inputZod = renderJsonSchemaZodShapeText(inputSchema, TOOL_ENUM_VALUE_LIMIT);
465
- const fullInputZod = renderJsonSchemaZodShapeText(inputSchema);
562
+ const inputSchema = jsonValueSchema.safeParse(tool.inputSchema ?? {}).data ?? {};
466
563
 
467
- return {
564
+ const inputZod = renderJsonSchemaZodText(inputSchema, TOOL_ENUM_VALUE_LIMIT);
565
+ const fullInputZod = renderJsonSchemaZodText(inputSchema);
566
+
567
+ const properties =
568
+ isJsonObject(inputSchema) && isJsonObject(inputSchema.properties)
569
+ ? inputSchema.properties
570
+ : {};
571
+
572
+ const view: ToolView = {
468
573
  name: tool.name,
469
- ...(tool.description !== undefined ? { description: tool.description } : {}),
470
- parameterCount: Object.keys(inputSchema.properties ?? {}).length,
574
+ parameterCount: Object.keys(properties).length,
471
575
  inputZod,
472
- ...(inputZod !== fullInputZod ? { fullInputZod } : {}),
473
576
  };
577
+
578
+ if (tool.description !== undefined) view.description = tool.description;
579
+
580
+ if (inputZod !== fullInputZod) view.fullInputZod = fullInputZod;
581
+
582
+ return view;
474
583
  });
475
584
  }
476
585
 
@@ -483,14 +592,16 @@ class RequestLogModelClass extends BaseModelClass<
483
592
  effort?: string | null;
484
593
  service_tier?: string | null;
485
594
  project_name?: string | null;
486
- history?: unknown;
595
+ history?: HistoryItems;
596
+ input_images?: InputImages;
487
597
  tools?: ToolDefinitions;
488
598
  is_image_generation?: boolean;
489
599
  is_structured?: boolean;
490
600
  json_schema?: string | null;
491
601
  }): Promise<number> {
492
602
  const wdb = this.getPuri("w");
493
- wdb.ubRegister("request_logs", {
603
+
604
+ const row: RequestLogRunRow = {
494
605
  user_prompt: params.user_prompt,
495
606
  system_prompt: params.system_prompt ?? null,
496
607
  // provider 실행 전이므로 serving model은 아직 알 수 없다.
@@ -512,59 +623,44 @@ class RequestLogModelClass extends BaseModelClass<
512
623
  json_schema: params.json_schema ?? null,
513
624
  is_structured:
514
625
  params.is_structured ?? (params.json_schema !== null && params.json_schema !== undefined),
515
- ...(params.history !== undefined ? { history: params.history as { type: string }[] } : {}),
516
- ...(params.tools !== undefined ? { tools: params.tools } : {}),
517
- });
626
+ };
627
+
628
+ if (params.history !== undefined) row.history = params.history;
629
+
630
+ if (params.input_images) row.input_images = params.input_images;
631
+
632
+ if (params.tools !== undefined) row.tools = params.tools;
633
+ wdb.ubRegister("request_logs", row);
634
+
518
635
  return wdb.transaction(async (trx) => {
519
636
  const ids = await trx.ubUpsert("request_logs");
637
+
520
638
  return ids[0]!;
521
639
  });
522
640
  }
523
641
 
524
- async appendStep(requestLogId: number, step: Record<string, unknown>): Promise<number> {
642
+ async appendStep(requestLogId: number, step: RequestLogStepFields): Promise<number> {
525
643
  const wdb = this.getPuri("w");
526
644
  wdb.ubRegister("request_log_steps", { request_log_id: requestLogId, ...step });
645
+
527
646
  return wdb.transaction(async (trx) => {
528
647
  const ids = await trx.ubUpsert("request_log_steps");
648
+
529
649
  if (step.type === "tool_call") {
530
650
  await trx.from("request_logs").where("id", requestLogId).increment("tool_call_count", 1);
531
651
  }
652
+
532
653
  return ids[0]!;
533
654
  });
534
655
  }
535
656
 
536
- async finishRun(
537
- requestLogId: number,
538
- params: {
539
- status: string;
540
- response?: string;
541
- token_name?: string;
542
- input_tokens?: number;
543
- output_tokens?: number;
544
- cache_read_tokens?: number;
545
- cache_creation_tokens?: number;
546
- cache_creation_5m_tokens?: number | null;
547
- cache_creation_1h_tokens?: number | null;
548
- duration_ms?: number;
549
- requested_model_name?: string | null;
550
- model_name?: string | null;
551
- service_tier?: string | null;
552
- fallback_count?: number | null;
553
- cost_usd?: number | null;
554
- cost_source?: string | null;
555
- history?: unknown;
556
- error_message?: string;
557
- tool_call_count?: number;
558
- image_cost_usd?: number | null;
559
- image_cost_method?: string | null;
560
- is_image_generation?: boolean;
561
- response_json_ok?: boolean | null;
562
- },
563
- ): Promise<void> {
657
+ async finishRun(requestLogId: number, params: FinishRunParams): Promise<void> {
564
658
  if (params.status === "succeeded" && !params.token_name) {
565
659
  throw new Error("tokenName is required for succeeded runs");
566
660
  }
567
- const update: Record<string, unknown> = { id: requestLogId, status: params.status };
661
+
662
+ const update: FinishRunUpdate = { id: requestLogId, status: params.status };
663
+
568
664
  const fields = [
569
665
  "response",
570
666
  "token_name",
@@ -588,9 +684,9 @@ class RequestLogModelClass extends BaseModelClass<
588
684
  "is_image_generation",
589
685
  "response_json_ok",
590
686
  ] as const;
591
- fields.forEach((key) => {
592
- if (params[key] !== undefined) update[key] = params[key];
593
- });
687
+
688
+ for (const key of fields) copyDefined(update, params, key);
689
+
594
690
  if (params.history !== undefined) update.history = params.history;
595
691
  update.ttft_ms = (await this.firstGenerateStepTtft(requestLogId)) ?? 0;
596
692
 
@@ -606,9 +702,12 @@ class RequestLogModelClass extends BaseModelClass<
606
702
  .select({ model_name: "model_name" })
607
703
  .where("id", requestLogId)
608
704
  .first();
705
+
609
706
  const effectiveModelName = params.model_name ?? row?.model_name;
707
+
610
708
  if (effectiveModelName) {
611
709
  const model = canonicalModelName(effectiveModelName);
710
+
612
711
  const usage = normalizedUsageForCost({
613
712
  token_name: params.token_name,
614
713
  model_name: effectiveModelName,
@@ -619,6 +718,7 @@ class RequestLogModelClass extends BaseModelClass<
619
718
  cache_creation_5m_tokens: params.cache_creation_5m_tokens,
620
719
  cache_creation_1h_tokens: params.cache_creation_1h_tokens,
621
720
  });
721
+
622
722
  update.cost_usd = Math.round(
623
723
  calculateCostUsd(model, {
624
724
  inputTokens: usage.inputTokens,
@@ -650,7 +750,8 @@ class RequestLogModelClass extends BaseModelClass<
650
750
  .orderBy("step_index", "asc")
651
751
  .orderBy("id", "asc")
652
752
  .first();
653
- return row?.ttft_ms === null || row?.ttft_ms === undefined ? null : Number(row.ttft_ms);
753
+
754
+ return row?.ttft_ms ?? null;
654
755
  }
655
756
 
656
757
  async aggregateStepUsage(requestLogId: number): Promise<RequestLogStepAggregate> {
@@ -659,7 +760,8 @@ class RequestLogModelClass extends BaseModelClass<
659
760
  .select(STEP_AGGREGATE_SELECT)
660
761
  .where("request_log_id", requestLogId)
661
762
  .where("type", "generate");
662
- return aggregateGenerateStepRows(rows as Array<Record<string, unknown>>);
763
+
764
+ return aggregateGenerateStepRows(rows);
663
765
  }
664
766
 
665
767
  /**
@@ -675,11 +777,13 @@ class RequestLogModelClass extends BaseModelClass<
675
777
  const threshold = new Date(Date.now() - thresholdMs);
676
778
  const candidateIds = await this.findStaleToolWaitingRunCandidates(threshold, limit);
677
779
  const expiredIds: number[] = [];
780
+
678
781
  for (const requestLogId of candidateIds) {
679
782
  if (await this.tryExpireStaleToolWaitingRun(requestLogId, threshold, errorMessage)) {
680
783
  expiredIds.push(requestLogId);
681
784
  }
682
785
  }
786
+
683
787
  return expiredIds;
684
788
  }
685
789
 
@@ -695,6 +799,7 @@ class RequestLogModelClass extends BaseModelClass<
695
799
  .where("request_log_steps.tool_result", null)
696
800
  .where("request_log_steps.error", null)
697
801
  .limit(limit);
802
+
698
803
  return rows.map(({ id }) => id);
699
804
  }
700
805
 
@@ -705,10 +810,12 @@ class RequestLogModelClass extends BaseModelClass<
705
810
  errorMessage: string,
706
811
  ): Promise<boolean> {
707
812
  const wdb = this.getPuri("w");
708
- const lockResult = (await wdb.knex.raw("SELECT pg_try_advisory_xact_lock(?, ?) AS acquired", [
709
- REQUEST_LOG_RUN_LOCK_CLASS_ID,
710
- requestLogId,
711
- ])) as { rows?: Array<{ acquired?: boolean }> };
813
+
814
+ const lockResult = await wdb.knex.raw<{ rows?: Array<{ acquired?: boolean }> }>(
815
+ "SELECT pg_try_advisory_xact_lock(?, ?) AS acquired",
816
+ [REQUEST_LOG_RUN_LOCK_CLASS_ID, requestLogId],
817
+ );
818
+
712
819
  if (lockResult.rows?.[0]?.acquired !== true) return false;
713
820
 
714
821
  // The follow-up path uses the same writer transaction lock. Recheck after
@@ -722,6 +829,7 @@ class RequestLogModelClass extends BaseModelClass<
722
829
  .where("tool_result", null)
723
830
  .where("error", null)
724
831
  .first();
832
+
725
833
  if (!unresolved) return false;
726
834
 
727
835
  const stepRows = await wdb
@@ -729,7 +837,9 @@ class RequestLogModelClass extends BaseModelClass<
729
837
  .select(STEP_AGGREGATE_SELECT)
730
838
  .where("request_log_id", requestLogId)
731
839
  .where("type", "generate");
732
- const aggregate = aggregateGenerateStepRows(stepRows as Array<Record<string, unknown>>);
840
+
841
+ const aggregate = aggregateGenerateStepRows(stepRows);
842
+
733
843
  const firstTtft = await wdb
734
844
  .from("request_log_steps")
735
845
  .select({ ttft_ms: "ttft_ms" })
@@ -740,32 +850,36 @@ class RequestLogModelClass extends BaseModelClass<
740
850
  .orderBy("id", "asc")
741
851
  .first();
742
852
 
853
+ const expiry: Partial<RequestLogSaveParams> = {
854
+ status: "error",
855
+ error_message: errorMessage,
856
+ input_tokens: aggregate.input_tokens,
857
+ output_tokens: aggregate.output_tokens,
858
+ cache_read_tokens: aggregate.cache_read_tokens,
859
+ cache_creation_tokens: aggregate.cache_creation_tokens,
860
+ };
861
+
862
+ if (aggregate.cache_creation_5m_tokens !== undefined) {
863
+ expiry.cache_creation_5m_tokens = aggregate.cache_creation_5m_tokens;
864
+ }
865
+
866
+ if (aggregate.cache_creation_1h_tokens !== undefined) {
867
+ expiry.cache_creation_1h_tokens = aggregate.cache_creation_1h_tokens;
868
+ }
869
+
870
+ expiry.duration_ms = aggregate.duration_ms;
871
+ expiry.fallback_count = aggregate.fallback_count;
872
+ expiry.cost_usd = aggregate.cost_usd;
873
+
874
+ if (aggregate.cost_source !== undefined) expiry.cost_source = aggregate.cost_source;
875
+ expiry.ttft_ms = firstTtft?.ttft_ms ?? 0;
876
+
743
877
  const updated = await wdb
744
878
  .from("request_logs")
745
879
  .where("id", requestLogId)
746
880
  .where("status", "running")
747
- .update({
748
- status: "error",
749
- error_message: errorMessage,
750
- input_tokens: aggregate.input_tokens,
751
- output_tokens: aggregate.output_tokens,
752
- cache_read_tokens: aggregate.cache_read_tokens,
753
- cache_creation_tokens: aggregate.cache_creation_tokens,
754
- ...(aggregate.cache_creation_5m_tokens !== undefined
755
- ? { cache_creation_5m_tokens: aggregate.cache_creation_5m_tokens }
756
- : {}),
757
- ...(aggregate.cache_creation_1h_tokens !== undefined
758
- ? { cache_creation_1h_tokens: aggregate.cache_creation_1h_tokens }
759
- : {}),
760
- duration_ms: aggregate.duration_ms,
761
- fallback_count: aggregate.fallback_count,
762
- cost_usd: aggregate.cost_usd,
763
- ...(aggregate.cost_source !== undefined ? { cost_source: aggregate.cost_source } : {}),
764
- ttft_ms:
765
- firstTtft?.ttft_ms === null || firstTtft?.ttft_ms === undefined
766
- ? 0
767
- : Number(firstTtft.ttft_ms),
768
- });
881
+ .update(expiry);
882
+
769
883
  return updated > 0;
770
884
  }
771
885
 
@@ -789,7 +903,9 @@ class RequestLogModelClass extends BaseModelClass<
789
903
  .select({ status: "status" })
790
904
  .where("id", requestLogId)
791
905
  .first();
906
+
792
907
  if (!run) throw new Error(`request log run ${requestLogId} not found`);
908
+
793
909
  if (run.status !== "running") {
794
910
  throw new Error(`request log run ${requestLogId} is already ${run.status}`);
795
911
  }
@@ -808,6 +924,7 @@ class RequestLogModelClass extends BaseModelClass<
808
924
  .select({ max_step: Puri.max("step_index") })
809
925
  .where("request_log_id", requestLogId)
810
926
  .first();
927
+
811
928
  return (row?.max_step ?? -1) + 1;
812
929
  }
813
930
 
@@ -826,7 +943,7 @@ class RequestLogModelClass extends BaseModelClass<
826
943
  */
827
944
  async totalCost(
828
945
  rawParams: RequestLogListParams = { num: 0, page: 1 },
829
- ): Promise<{ usd: number; unknownImageCostCount: number }> {
946
+ ): Promise<{ usd: number; unknownImageCostCount: number; unknownCostCount: number }> {
830
947
  const params = {
831
948
  num: 0,
832
949
  page: 1,
@@ -836,10 +953,13 @@ class RequestLogModelClass extends BaseModelClass<
836
953
 
837
954
  const { qb: totalQb } = this.getSubsetQueries("C");
838
955
  this.applyListFilters(totalQb, params);
956
+
839
957
  const sonamuFilter = params.sonamuFilter
840
958
  ? normalizeFilterQuery(params.sonamuFilter)
841
959
  : undefined;
960
+
842
961
  this.applySonamuFilters(totalQb, sonamuFilter);
962
+
843
963
  // subset qb 는 컬럼 목록이 이미 SELECT 에 박혀 있어 집계를 얹으면 GROUP BY 에러가 난다.
844
964
  // executeCountQuery 와 같은 방식으로 select 를 비우고 집계만 남긴다.
845
965
  const stored = await totalQb
@@ -852,12 +972,14 @@ class RequestLogModelClass extends BaseModelClass<
852
972
  unknownImageCostCount: Puri.rawNumber(
853
973
  "COUNT(*) FILTER (WHERE request_logs.is_image_generation = true AND request_logs.image_cost_usd IS NULL)",
854
974
  ),
975
+ unknownCostCount: Puri.rawNumber(`COUNT(*) FILTER (WHERE ${UNKNOWN_MEDIA_COST_PREDICATE})`),
855
976
  })
856
977
  .first();
857
978
 
858
979
  const { qb: legacyQb } = this.getSubsetQueries("C");
859
980
  this.applyListFilters(legacyQb, params);
860
981
  this.applySonamuFilters(legacyQb, sonamuFilter);
982
+
861
983
  const legacyRows = await legacyQb
862
984
  .whereRaw("request_logs.cost_source IS NULL OR request_logs.cost_usd IS NULL")
863
985
  .clear("select")
@@ -873,13 +995,17 @@ class RequestLogModelClass extends BaseModelClass<
873
995
  cost_usd: "request_logs.cost_usd",
874
996
  });
875
997
 
876
- const storedTotal = Number(stored?.total ?? 0) / MICRO_USD;
998
+ // Postgres returns SUM/COUNT as bigint, which node-postgres delivers as text
999
+ // even though `Puri.rawNumber` types it as number.
1000
+ const storedTotal = bigintAggregate.parse(stored?.total ?? 0) / MICRO_USD;
877
1001
 
878
1002
  // legacy row 는 위 SUM 에 저장값으로 이미 포함되어 있다. 재계산값과의 차액만 더한다.
879
1003
  const usd = legacyRows.reduce((sum, row) => {
880
1004
  const alreadySummed = Math.max(row.cost_usd ?? 0, 0) / MICRO_USD;
881
1005
  const usage = normalizedUsageForCost(row);
1006
+
882
1007
  if (!usage.model) return sum;
1008
+
883
1009
  return (
884
1010
  sum +
885
1011
  calculateCostUsd(usage.model, {
@@ -893,7 +1019,12 @@ class RequestLogModelClass extends BaseModelClass<
893
1019
  alreadySummed
894
1020
  );
895
1021
  }, storedTotal);
896
- return { usd, unknownImageCostCount: Number(stored?.unknownImageCostCount ?? 0) };
1022
+
1023
+ return {
1024
+ usd,
1025
+ unknownImageCostCount: bigintAggregate.parse(stored?.unknownImageCostCount ?? 0),
1026
+ unknownCostCount: bigintAggregate.parse(stored?.unknownCostCount ?? 0),
1027
+ };
897
1028
  }
898
1029
 
899
1030
  async distinctProjectNames(): Promise<string[]> {
@@ -905,6 +1036,7 @@ class RequestLogModelClass extends BaseModelClass<
905
1036
  .select({
906
1037
  project_name: "project_name",
907
1038
  });
1039
+
908
1040
  return rows.map((r) => r.project_name!);
909
1041
  }
910
1042
 
@@ -917,6 +1049,7 @@ class RequestLogModelClass extends BaseModelClass<
917
1049
  .select({
918
1050
  model_name: "model_name",
919
1051
  });
1052
+
920
1053
  return rows.map((r) => r.model_name!);
921
1054
  }
922
1055
 
@@ -930,6 +1063,7 @@ class RequestLogModelClass extends BaseModelClass<
930
1063
  .table("request_log_steps")
931
1064
  .whereIn("request_log_steps.request_log_id", ids)
932
1065
  .delete();
1066
+
933
1067
  return trx.table("request_logs").whereIn("request_logs.id", ids).delete();
934
1068
  });
935
1069