cartethyia 2.7.3 → 2.7.5
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.
- package/.env.example +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +59 -57
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -5
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +67 -10
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +8 -4
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +92 -31
- package/src/console/api-keys/routes.ts +131 -6
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +5 -5
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +40 -34
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +11 -9
- package/src/console/backup/export-endpoint.ts +11 -12
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +32 -36
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +4 -4
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +36 -4
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +18 -4
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +5 -1
- package/src/console/observability/live.ts +34 -21
- package/src/console/observability/logs.ts +4 -4
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +36 -20
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +82 -22
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +110 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +30 -6
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1662 -1292
- package/src/console/providers/detail/routes.ts +4 -4
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +95 -16
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +285 -67
- package/src/console/quota/account-test-egress.ts +4 -4
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +13 -10
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +12 -12
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/routes.ts +3 -3
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +112 -44
- package/src/console/share/share-stats.ts +109 -39
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +42 -6
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +256 -98
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +76 -5
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +55 -51
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +22 -2
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +24 -75
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +286 -214
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/telemetry-status.ts +1 -1
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +10 -5
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +19 -7
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +177 -45
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +109 -4
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/stores/telemetry-store.ts +1 -1
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +12 -16
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/image-generation.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +27 -12
- package/src/protocol/response/codex.ts +2 -2
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +6 -6
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +3 -3
- package/src/protocol/sse-decoder.ts +64 -4
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +8 -8
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +44 -5
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +50 -15
- package/src/providers/discovery/probing-service.ts +18 -20
- package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +15 -26
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +76 -116
- package/src/providers/integrations/commandcode/quota.ts +132 -48
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +53 -20
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +13 -13
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +47 -20
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +88 -42
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +91 -78
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
- package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
- package/src/providers/integrations/qoder/qoder.ts +521 -63
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +59 -19
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +22 -6
- package/src/providers/operations/provider-credential-service.ts +35 -6
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +65 -10
- package/src/providers/provider-credential-resolver.ts +1 -1
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-registry.ts +4 -4
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +28 -10
- package/src/providers/quota/refresh.ts +29 -6
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +6 -8
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +8 -8
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +20 -8
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/coerce.ts +19 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +5 -4
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +28 -25
- package/src/transport/dispatch/generation.ts +33 -53
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +10 -8
- package/src/transport/dispatch/responses-compact.ts +35 -56
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +11 -10
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +27 -55
- package/src/transport/dispatch/token-count.ts +49 -46
- package/src/transport/dispatch/websearch.ts +24 -50
- package/src/transport/drain-endpoint.ts +3 -3
- package/src/transport/failure-policy.ts +19 -6
- package/src/transport/guards/auth-guard.ts +6 -11
- package/src/transport/guards/body-policy.ts +7 -7
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +2 -2
- package/src/transport/guards/pipeline.ts +7 -7
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +15 -20
- package/src/transport/request/preparer.ts +306 -27
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +5 -20
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +113 -19
- package/src/transport/routing/route-model.ts +131 -38
- package/src/transport/routing/router.ts +154 -39
- package/src/transport/shutdown-notice.ts +21 -9
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +3 -3
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/encode.ts +1 -1
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
* - `isGatewayError()` semantics live in one shared SQL predicate.
|
|
19
19
|
*/
|
|
20
20
|
import { and, desc, eq, gte, inArray, isNotNull, or, sql, type SQL } from "drizzle-orm";
|
|
21
|
+
import { PgDialect } from "drizzle-orm/pg-core";
|
|
21
22
|
import type { CartethyiaDatabase } from "../persistence/db-handle";
|
|
22
|
-
import { apiKeys, telemetryEvents } from "../persistence/schema";
|
|
23
|
+
import { apiKeys, telemetryEvents, telemetryUsageTotals } from "../persistence/schema";
|
|
23
24
|
import type { TelemetryEventRecord } from "./event-log";
|
|
24
25
|
import { gatewayErrorSql } from "./telemetry-status";
|
|
25
26
|
|
|
@@ -31,7 +32,21 @@ import { gatewayErrorSql } from "./telemetry-status";
|
|
|
31
32
|
* totals, and the durable usage rollup cannot drift apart — an unknown status
|
|
32
33
|
* must be classified identically in all three.
|
|
33
34
|
*/
|
|
34
|
-
const IS_GATEWAY_ERROR_SQL = gatewayErrorSql(telemetryEvents.status, telemetryEvents.httpStatus);
|
|
35
|
+
export const IS_GATEWAY_ERROR_SQL = gatewayErrorSql(telemetryEvents.status, telemetryEvents.httpStatus);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The aggregate select fragments shared by every tenant-window read. The
|
|
39
|
+
* `errors` count uses the single shared gateway-error predicate (see
|
|
40
|
+
* {@link IS_GATEWAY_ERROR_SQL}) so the row-level rule and the SQL aggregate
|
|
41
|
+
* cannot drift; the sum fragments coalesce to 0 so an empty window reads as
|
|
42
|
+
* zero rather than null.
|
|
43
|
+
*/
|
|
44
|
+
const AGGREGATE_SELECT = {
|
|
45
|
+
errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
|
|
46
|
+
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
47
|
+
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
48
|
+
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
49
|
+
} as const;
|
|
35
50
|
|
|
36
51
|
/** One bounded page of events, newest first. */
|
|
37
52
|
export interface EventPage {
|
|
@@ -40,7 +55,7 @@ export interface EventPage {
|
|
|
40
55
|
readonly nextCursor?: string;
|
|
41
56
|
}
|
|
42
57
|
|
|
43
|
-
|
|
58
|
+
interface EventListQuery {
|
|
44
59
|
readonly tenantId: string;
|
|
45
60
|
/**
|
|
46
61
|
* Lower bound. Omitting it means "the whole retained window" — the SQL table
|
|
@@ -96,7 +111,7 @@ export interface UsageSummary {
|
|
|
96
111
|
readonly unpriced: number;
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
|
|
114
|
+
interface BreakdownGroup {
|
|
100
115
|
readonly key: string;
|
|
101
116
|
readonly requests: number;
|
|
102
117
|
readonly errors: number;
|
|
@@ -116,15 +131,8 @@ export interface ChartBucket {
|
|
|
116
131
|
readonly errors: number;
|
|
117
132
|
readonly inputTokens: number;
|
|
118
133
|
readonly outputTokens: number;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
export interface HealthTotals {
|
|
122
|
-
readonly requests: number;
|
|
123
|
-
readonly errors: number;
|
|
124
|
-
readonly inputTokens: number;
|
|
125
|
-
readonly outputTokens: number;
|
|
134
|
+
/** Cached-input share of the prompt, summed for the bucket. */
|
|
126
135
|
readonly cachedInputTokens: number;
|
|
127
|
-
readonly latencyAvgMs: number | null;
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
const DEFAULT_LIMIT = 50;
|
|
@@ -141,7 +149,7 @@ function numericNullable(value: unknown): number | null {
|
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
/** Maps one `telemetry_events` row to the console's record shape. */
|
|
144
|
-
|
|
152
|
+
function eventRecordFromRow(row: {
|
|
145
153
|
readonly id: string | null;
|
|
146
154
|
readonly createdAt: Date;
|
|
147
155
|
readonly tenantId: string;
|
|
@@ -225,11 +233,28 @@ export function eventRecordFromRow(row: {
|
|
|
225
233
|
* renders the SQL, so it is the only answer that cannot disagree with it.
|
|
226
234
|
*/
|
|
227
235
|
function isPostgres(db: CartethyiaDatabase): boolean {
|
|
228
|
-
const dialect = (db as unknown as { dialect?:
|
|
229
|
-
|
|
236
|
+
const dialect = (db as unknown as { dialect?: unknown }).dialect;
|
|
237
|
+
if (dialect instanceof PgDialect) return true;
|
|
238
|
+
const name = (dialect as { constructor?: { name?: string } } | undefined)?.constructor?.name;
|
|
230
239
|
return name === "PostgresDialect" || name === "PgDialect";
|
|
231
240
|
}
|
|
232
241
|
|
|
242
|
+
/**
|
|
243
|
+
* The epoch-bucket SQL expression for one time column.
|
|
244
|
+
*
|
|
245
|
+
* Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
|
|
246
|
+
* expressions as identical: parameter numbers differing between the two
|
|
247
|
+
* clauses made Postgres treat them as unrelated expressions referencing
|
|
248
|
+
* `created_at`, which 42803-rejected the query. The width is a raw SQL
|
|
249
|
+
* literal for the same reason.
|
|
250
|
+
*/
|
|
251
|
+
function bucketExpression(db: CartethyiaDatabase, column: typeof telemetryEvents.createdAt, widthSeconds: number): SQL {
|
|
252
|
+
const w = sql.raw(String(Math.max(1, Math.trunc(widthSeconds))));
|
|
253
|
+
return isPostgres(db)
|
|
254
|
+
? sql<number>`(cast(extract(epoch from ${column}) as bigint) / ${w}) * ${w}`
|
|
255
|
+
: sql<number>`(cast(strftime('%s', ${column}) as integer) / ${w}) * ${w}`;
|
|
256
|
+
}
|
|
257
|
+
|
|
233
258
|
/**
|
|
234
259
|
* The tenant predicate — the scope every read shares — plus the window when
|
|
235
260
|
* one is given.
|
|
@@ -400,90 +425,9 @@ export async function queryTelemetryEvents(
|
|
|
400
425
|
};
|
|
401
426
|
}
|
|
402
427
|
|
|
403
|
-
/** One event by request id — a single indexed row, never a window scan. */
|
|
404
|
-
export async function eventByRequestId(
|
|
405
|
-
db: CartethyiaDatabase,
|
|
406
|
-
tenantId: string,
|
|
407
|
-
requestId: string,
|
|
408
|
-
): Promise<TelemetryEventRecord | undefined> {
|
|
409
|
-
const [row] = await db
|
|
410
|
-
.select()
|
|
411
|
-
.from(telemetryEvents)
|
|
412
|
-
.where(and(eq(telemetryEvents.tenantId, tenantId), eq(telemetryEvents.requestId, requestId)))
|
|
413
|
-
.limit(1);
|
|
414
|
-
if (!row) return undefined;
|
|
415
|
-
return eventRecordFromRow(row as Parameters<typeof eventRecordFromRow>[0]);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
428
|
/**
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
* Percentiles are computed by the database. Postgres uses `percentile_cont`;
|
|
422
|
-
* SQLite (no ordered-set aggregates) computes nearest-rank percentiles from
|
|
423
|
-
* the ordered latency column via OFFSET/LIMIT. Either way, latency values are
|
|
424
|
-
* never returned to JavaScript — the array that the old raw reader built to
|
|
425
|
-
* sort in JS was the memory cost this replaces.
|
|
429
|
+
* The SQL column one breakdown dimension groups by.
|
|
426
430
|
*/
|
|
427
|
-
export async function summarizeTelemetry(
|
|
428
|
-
db: CartethyiaDatabase,
|
|
429
|
-
tenantId: string,
|
|
430
|
-
since: Date,
|
|
431
|
-
): Promise<UsageSummary> {
|
|
432
|
-
const condition = scoped(tenantId, since);
|
|
433
|
-
const [row] = await db
|
|
434
|
-
.select({
|
|
435
|
-
requests: sql<number>`count(*)`,
|
|
436
|
-
errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
|
|
437
|
-
cancelled: sql<number>`sum(case when ${telemetryEvents.status} = 'cancelled' then 1 else 0 end)`,
|
|
438
|
-
truncated: sql<number>`sum(case when ${telemetryEvents.status} = 'truncated' then 1 else 0 end)`,
|
|
439
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
440
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
441
|
-
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
442
|
-
reasoningTokens: sql<number>`coalesce(sum(${telemetryEvents.reasoningTokens}), 0)`,
|
|
443
|
-
estimatedCostUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
444
|
-
sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
|
|
445
|
-
latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
|
|
446
|
-
unpriced: sql<number>`sum(case when ${telemetryEvents.estimatedCostUsd} is null then 1 else 0 end)`,
|
|
447
|
-
})
|
|
448
|
-
.from(telemetryEvents)
|
|
449
|
-
.where(condition);
|
|
450
|
-
if (!row) {
|
|
451
|
-
return {
|
|
452
|
-
requests: 0,
|
|
453
|
-
errors: 0,
|
|
454
|
-
cancelled: 0,
|
|
455
|
-
truncated: 0,
|
|
456
|
-
inputTokens: 0,
|
|
457
|
-
outputTokens: 0,
|
|
458
|
-
cachedInputTokens: 0,
|
|
459
|
-
reasoningTokens: 0,
|
|
460
|
-
estimatedCostUsd: 0,
|
|
461
|
-
sampled: 0,
|
|
462
|
-
latencyMs: { avg: null, p50: null, p95: null, p99: null },
|
|
463
|
-
unpriced: 0,
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const sampled = numeric(row.sampled);
|
|
468
|
-
const percentiles = await latencyPercentiles(db, tenantId, since, sampled);
|
|
469
|
-
return {
|
|
470
|
-
requests: numeric(row.requests),
|
|
471
|
-
errors: numeric(row.errors),
|
|
472
|
-
cancelled: numeric(row.cancelled),
|
|
473
|
-
truncated: numeric(row.truncated),
|
|
474
|
-
inputTokens: numeric(row.inputTokens),
|
|
475
|
-
outputTokens: numeric(row.outputTokens),
|
|
476
|
-
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
477
|
-
reasoningTokens: numeric(row.reasoningTokens),
|
|
478
|
-
estimatedCostUsd: numeric(row.estimatedCostUsd),
|
|
479
|
-
sampled,
|
|
480
|
-
latencyMs: {
|
|
481
|
-
avg: numericNullable(row.latencyAvg),
|
|
482
|
-
...percentiles,
|
|
483
|
-
},
|
|
484
|
-
unpriced: numeric(row.unpriced),
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
431
|
|
|
488
432
|
type Percentiles = { p50: number | null; p95: number | null; p99: number | null };
|
|
489
433
|
|
|
@@ -516,23 +460,36 @@ async function latencyPercentiles(
|
|
|
516
460
|
}
|
|
517
461
|
|
|
518
462
|
// SQLite: nearest-rank percentile = the value at ceil(p * n) rows into the
|
|
519
|
-
// ordered latency column
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
463
|
+
// ordered latency column. One query computes all three ranks via a window
|
|
464
|
+
// function, instead of three separate OFFSET/LIMIT passes over the same
|
|
465
|
+
// ordered column (one per percentile).
|
|
466
|
+
const n = Math.max(1, sampled);
|
|
467
|
+
const ranks = [
|
|
468
|
+
Math.ceil(0.5 * n),
|
|
469
|
+
Math.ceil(0.95 * n),
|
|
470
|
+
Math.ceil(0.99 * n),
|
|
471
|
+
];
|
|
472
|
+
const [row] = await db
|
|
473
|
+
.select({
|
|
474
|
+
p50: sql<number | null>`max(case when rn = ${ranks[0]} then value end)`,
|
|
475
|
+
p95: sql<number | null>`max(case when rn = ${ranks[1]} then value end)`,
|
|
476
|
+
p99: sql<number | null>`max(case when rn = ${ranks[2]} then value end)`,
|
|
477
|
+
})
|
|
478
|
+
.from(
|
|
479
|
+
sql`(
|
|
480
|
+
select ${telemetryEvents.latencyMs} as value,
|
|
481
|
+
row_number() over (order by ${telemetryEvents.latencyMs}) as rn
|
|
482
|
+
from ${telemetryEvents}
|
|
483
|
+
where ${and(...base)}
|
|
484
|
+
)`,
|
|
485
|
+
);
|
|
486
|
+
return {
|
|
487
|
+
p50: numericNullable(row?.p50 ?? null),
|
|
488
|
+
p95: numericNullable(row?.p95 ?? null),
|
|
489
|
+
p99: numericNullable(row?.p99 ?? null),
|
|
530
490
|
};
|
|
531
|
-
const [p50, p95, p99] = await Promise.all([rankAt(0.5), rankAt(0.95), rankAt(0.99)]);
|
|
532
|
-
return { p50, p95, p99 };
|
|
533
491
|
}
|
|
534
492
|
|
|
535
|
-
/** The SQL column one breakdown dimension groups by. */
|
|
536
493
|
function dimensionColumn(dimension: string) {
|
|
537
494
|
if (dimension === "model") return telemetryEvents.requestedModel;
|
|
538
495
|
if (dimension === "provider") return telemetryEvents.providerId;
|
|
@@ -571,9 +528,7 @@ export async function breakdownTelemetry(
|
|
|
571
528
|
.select({
|
|
572
529
|
key: column,
|
|
573
530
|
requests: sql<number>`count(*)`,
|
|
574
|
-
|
|
575
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
576
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
531
|
+
...AGGREGATE_SELECT,
|
|
577
532
|
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
578
533
|
costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
579
534
|
avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
|
|
@@ -610,32 +565,23 @@ export async function chartTelemetry(
|
|
|
610
565
|
since: Date,
|
|
611
566
|
bucketSeconds: number,
|
|
612
567
|
): Promise<ChartBucket[]> {
|
|
613
|
-
const
|
|
614
|
-
// Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
|
|
615
|
-
// expressions as identical: parameter numbers differing between the two
|
|
616
|
-
// clauses made Postgres treat them as unrelated expressions referencing
|
|
617
|
-
// `created_at`, which 42803-rejected the query.
|
|
618
|
-
const w = sql.raw(String(width));
|
|
619
|
-
const bucketExpression = isPostgres(db)
|
|
620
|
-
? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
|
|
621
|
-
: sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
|
|
568
|
+
const bucketExpr = bucketExpression(db, telemetryEvents.createdAt, bucketSeconds);
|
|
622
569
|
const rows = await db
|
|
623
570
|
.select({
|
|
624
|
-
at:
|
|
571
|
+
at: bucketExpr,
|
|
625
572
|
requests: sql<number>`count(*)`,
|
|
626
|
-
|
|
627
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
628
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
573
|
+
...AGGREGATE_SELECT,
|
|
629
574
|
})
|
|
630
575
|
.from(telemetryEvents)
|
|
631
576
|
.where(scoped(tenantId, since))
|
|
632
|
-
.groupBy(
|
|
577
|
+
.groupBy(bucketExpr);
|
|
633
578
|
return rows.map((row) => ({
|
|
634
579
|
at: numeric(row.at) * 1000,
|
|
635
580
|
requests: numeric(row.requests),
|
|
636
581
|
errors: numeric(row.errors),
|
|
637
582
|
inputTokens: numeric(row.inputTokens),
|
|
638
583
|
outputTokens: numeric(row.outputTokens),
|
|
584
|
+
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
639
585
|
}));
|
|
640
586
|
}
|
|
641
587
|
|
|
@@ -665,20 +611,15 @@ export async function providerUsageMatrix(
|
|
|
665
611
|
providerId: string,
|
|
666
612
|
since: Date,
|
|
667
613
|
): Promise<ProviderUsageDayRow[]> {
|
|
668
|
-
|
|
669
|
-
// identical expression or Postgres 42803-rejects the grouped reference.
|
|
670
|
-
const w = sql.raw(String(86400));
|
|
671
|
-
const dayExpression = isPostgres(db)
|
|
672
|
-
? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
|
|
673
|
-
: sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
|
|
614
|
+
const dayExpression = bucketExpression(db, telemetryEvents.createdAt, 86400);
|
|
674
615
|
const rows = await db
|
|
675
616
|
.select({
|
|
676
617
|
day: dayExpression,
|
|
677
618
|
model: telemetryEvents.requestedModel,
|
|
678
619
|
requests: sql<number>`count(*)`,
|
|
679
|
-
inputTokens:
|
|
620
|
+
inputTokens: AGGREGATE_SELECT.inputTokens,
|
|
680
621
|
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
681
|
-
outputTokens:
|
|
622
|
+
outputTokens: AGGREGATE_SELECT.outputTokens,
|
|
682
623
|
costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
683
624
|
})
|
|
684
625
|
.from(telemetryEvents)
|
|
@@ -696,43 +637,6 @@ export async function providerUsageMatrix(
|
|
|
696
637
|
}));
|
|
697
638
|
}
|
|
698
639
|
|
|
699
|
-
/** Overview/health totals for one tenant window. */
|
|
700
|
-
export async function healthTelemetry(
|
|
701
|
-
db: CartethyiaDatabase,
|
|
702
|
-
tenantId: string,
|
|
703
|
-
since: Date,
|
|
704
|
-
): Promise<HealthTotals> {
|
|
705
|
-
const [row] = await db
|
|
706
|
-
.select({
|
|
707
|
-
requests: sql<number>`count(*)`,
|
|
708
|
-
errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
|
|
709
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
710
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
711
|
-
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
712
|
-
latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
|
|
713
|
-
})
|
|
714
|
-
.from(telemetryEvents)
|
|
715
|
-
.where(scoped(tenantId, since));
|
|
716
|
-
if (!row) {
|
|
717
|
-
return {
|
|
718
|
-
requests: 0,
|
|
719
|
-
errors: 0,
|
|
720
|
-
inputTokens: 0,
|
|
721
|
-
outputTokens: 0,
|
|
722
|
-
cachedInputTokens: 0,
|
|
723
|
-
latencyAvgMs: null,
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
return {
|
|
727
|
-
requests: numeric(row.requests),
|
|
728
|
-
errors: numeric(row.errors),
|
|
729
|
-
inputTokens: numeric(row.inputTokens),
|
|
730
|
-
outputTokens: numeric(row.outputTokens),
|
|
731
|
-
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
732
|
-
latencyAvgMs: numericNullable(row.latencyAvg),
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
|
|
736
640
|
/**
|
|
737
641
|
* Summed input+output tokens for a set of API keys over one window — the
|
|
738
642
|
* family bucket spend query.
|
|
@@ -768,54 +672,157 @@ export async function sumTelemetryTokens(
|
|
|
768
672
|
}
|
|
769
673
|
|
|
770
674
|
/**
|
|
771
|
-
*
|
|
675
|
+
* All-time family counts from the durable rollup, not from a scan of the
|
|
676
|
+
* retained telemetry window.
|
|
772
677
|
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
*
|
|
678
|
+
* The share KPI cards need lifetime numbers. `telemetry_usage_totals` is
|
|
679
|
+
* charged at write time and accumulates per identity, so a family's lifetime
|
|
680
|
+
* requests/errors/tokens are a SUM over its rows (keyed by
|
|
681
|
+
* `family_template_key_id`) — sub-millisecond on a small table, versus a
|
|
682
|
+
* full-window aggregate over `telemetry_events` that reads every retained
|
|
683
|
+
* event on every tick. The rollup is the same definition as the read-side
|
|
684
|
+
* counts, so the two cannot drift.
|
|
777
685
|
*/
|
|
778
|
-
|
|
779
|
-
readonly
|
|
780
|
-
readonly
|
|
781
|
-
readonly
|
|
782
|
-
readonly latencyP99Ms: number | null;
|
|
783
|
-
readonly avgTokensPerSec: number;
|
|
686
|
+
interface FamilyKpiTotals {
|
|
687
|
+
readonly requests: number;
|
|
688
|
+
readonly errors: number;
|
|
689
|
+
readonly tokens: number;
|
|
784
690
|
}
|
|
785
691
|
|
|
786
|
-
|
|
692
|
+
/** Sums one share family's durable lifetime totals. */
|
|
693
|
+
export async function familyKpiTotals(
|
|
787
694
|
db: CartethyiaDatabase,
|
|
788
695
|
tenantId: string,
|
|
789
|
-
|
|
790
|
-
): Promise<
|
|
696
|
+
familyKeyId: string,
|
|
697
|
+
): Promise<FamilyKpiTotals> {
|
|
791
698
|
const [row] = await db
|
|
792
699
|
.select({
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
700
|
+
requests: sql<number>`coalesce(sum(${telemetryUsageTotals.requests}), 0)`,
|
|
701
|
+
errors: sql<number>`coalesce(sum(${telemetryUsageTotals.errors}), 0)`,
|
|
702
|
+
tokens: sql<number>`coalesce(sum(coalesce(${telemetryUsageTotals.inputTokens}, 0) + coalesce(${telemetryUsageTotals.outputTokens}, 0)), 0)`,
|
|
796
703
|
})
|
|
797
|
-
.from(
|
|
798
|
-
.where(
|
|
799
|
-
|
|
800
|
-
|
|
704
|
+
.from(telemetryUsageTotals)
|
|
705
|
+
.where(
|
|
706
|
+
and(
|
|
707
|
+
eq(telemetryUsageTotals.tenantId, tenantId),
|
|
708
|
+
eq(telemetryUsageTotals.familyTemplateKeyId, familyKeyId),
|
|
709
|
+
),
|
|
710
|
+
);
|
|
801
711
|
return {
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
latencyP99Ms: percentiles.p99,
|
|
806
|
-
avgTokensPerSec: numeric(row?.avgTokensPerSec ?? 0),
|
|
712
|
+
requests: numeric(row?.requests ?? 0),
|
|
713
|
+
errors: numeric(row?.errors ?? 0),
|
|
714
|
+
tokens: numeric(row?.tokens ?? 0),
|
|
807
715
|
};
|
|
808
716
|
}
|
|
809
717
|
|
|
718
|
+
/** Counts one share family's requests in a bounded window — a range scan, not a full-window aggregate. */
|
|
719
|
+
export async function familyWindowRequests(
|
|
720
|
+
db: CartethyiaDatabase,
|
|
721
|
+
tenantId: string,
|
|
722
|
+
familyKeyId: string,
|
|
723
|
+
since: Date,
|
|
724
|
+
): Promise<number> {
|
|
725
|
+
const [row] = await db
|
|
726
|
+
.select({
|
|
727
|
+
requests: sql<number>`count(*)`,
|
|
728
|
+
})
|
|
729
|
+
.from(telemetryEvents)
|
|
730
|
+
.where(
|
|
731
|
+
and(
|
|
732
|
+
eq(telemetryEvents.tenantId, tenantId),
|
|
733
|
+
familyKeyCondition(familyKeyId),
|
|
734
|
+
gte(telemetryEvents.createdAt, since),
|
|
735
|
+
),
|
|
736
|
+
);
|
|
737
|
+
return numeric(row?.requests ?? 0);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* One share family's client-IP breakdown, computed in SQL.
|
|
742
|
+
*
|
|
743
|
+
* Unlike the 24-hour page read, this covers a bounded history so an address
|
|
744
|
+
* that stopped before the last day still appears — marked inactive. The
|
|
745
|
+
* `active` flag is decided in the caller from `lastSeenAt` vs a cutoff; here
|
|
746
|
+
* we only aggregate. The window is bounded (`since`, default the whole
|
|
747
|
+
* retained window) so an unbounded scan is never the default for callers
|
|
748
|
+
* that only need recent addresses.
|
|
749
|
+
*/
|
|
750
|
+
export async function familyIpBreakdown(
|
|
751
|
+
db: CartethyiaDatabase,
|
|
752
|
+
tenantId: string,
|
|
753
|
+
familyKeyId: string,
|
|
754
|
+
limit = 50,
|
|
755
|
+
since?: Date,
|
|
756
|
+
): Promise<{ ip: string; requests: number; tokens: number; lastSeenAt: Date | null }[]> {
|
|
757
|
+
const conditions = [
|
|
758
|
+
eq(telemetryEvents.tenantId, tenantId),
|
|
759
|
+
familyKeyCondition(familyKeyId),
|
|
760
|
+
...(since === undefined ? [] : [gte(telemetryEvents.createdAt, since)]),
|
|
761
|
+
];
|
|
762
|
+
const rows = await db
|
|
763
|
+
.select({
|
|
764
|
+
ip: telemetryEvents.clientIp,
|
|
765
|
+
requests: sql<number>`count(*)`,
|
|
766
|
+
tokens: sql<number>`coalesce(sum(coalesce(${telemetryEvents.inputTokens}, 0) + coalesce(${telemetryEvents.outputTokens}, 0)), 0)`,
|
|
767
|
+
lastSeenAt: sql<Date>`max(${telemetryEvents.createdAt})`,
|
|
768
|
+
})
|
|
769
|
+
.from(telemetryEvents)
|
|
770
|
+
.where(and(...conditions))
|
|
771
|
+
.groupBy(telemetryEvents.clientIp)
|
|
772
|
+
// Highest volume first: the table answers "who has used the link most",
|
|
773
|
+
// not just who is active right now.
|
|
774
|
+
.orderBy(desc(sql`count(*)`))
|
|
775
|
+
.limit(limit);
|
|
776
|
+
return rows.flatMap((row) =>
|
|
777
|
+
row.ip === null ? [] : [{ ip: row.ip, requests: numeric(row.requests), tokens: numeric(row.tokens), lastSeenAt: row.lastSeenAt }],
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* One share family's time-series buckets, computed in SQL with the family
|
|
783
|
+
* membership filter.
|
|
784
|
+
*
|
|
785
|
+
* Mirrors {@link chartTelemetry} but scoped to a share family. The share page's
|
|
786
|
+
* 24-hour strip must not be truncated to the bounded page (500 events), or a
|
|
787
|
+
* busy family's chart undercounts every hour bucket.
|
|
788
|
+
*/
|
|
789
|
+
export async function familyHourlyTelemetry(
|
|
790
|
+
db: CartethyiaDatabase,
|
|
791
|
+
tenantId: string,
|
|
792
|
+
familyKeyId: string,
|
|
793
|
+
since: Date,
|
|
794
|
+
bucketSeconds: number,
|
|
795
|
+
): Promise<ChartBucket[]> {
|
|
796
|
+
const bucketExpr = bucketExpression(db, telemetryEvents.createdAt, bucketSeconds);
|
|
797
|
+
const conditions = [scoped(tenantId, since), familyKeyCondition(familyKeyId)];
|
|
798
|
+
const rows = await db
|
|
799
|
+
.select({
|
|
800
|
+
at: bucketExpr,
|
|
801
|
+
requests: sql<number>`count(*)`,
|
|
802
|
+
...AGGREGATE_SELECT,
|
|
803
|
+
})
|
|
804
|
+
.from(telemetryEvents)
|
|
805
|
+
.where(and(...conditions))
|
|
806
|
+
.groupBy(bucketExpr);
|
|
807
|
+
return rows.map((row) => ({
|
|
808
|
+
at: numeric(row.at) * 1000,
|
|
809
|
+
requests: numeric(row.requests),
|
|
810
|
+
errors: numeric(row.errors),
|
|
811
|
+
inputTokens: numeric(row.inputTokens),
|
|
812
|
+
outputTokens: numeric(row.outputTokens),
|
|
813
|
+
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
814
|
+
}));
|
|
815
|
+
}
|
|
816
|
+
|
|
810
817
|
/**
|
|
811
818
|
* The Usage summary's performance block, computed in SQL.
|
|
812
819
|
*
|
|
813
820
|
* Replaces `summarizeUsagePerformance` (which sorted a latency array in
|
|
814
821
|
* JavaScript): `avg(resolve_ms)` and `avg(tokens_per_sec)` are SQL averages,
|
|
815
822
|
* and the latency percentiles come from the same database-side percentile
|
|
816
|
-
* path {@link
|
|
823
|
+
* path {@link summarizeUsageWithPerformance} uses.
|
|
817
824
|
*/
|
|
818
|
-
|
|
825
|
+
interface UsagePerformanceTotals {
|
|
819
826
|
readonly avgResolveMs: number;
|
|
820
827
|
readonly avgDurationMs: number;
|
|
821
828
|
/**
|
|
@@ -832,32 +839,97 @@ export interface UsagePerformanceTotals {
|
|
|
832
839
|
readonly avgTokensPerSec: number;
|
|
833
840
|
}
|
|
834
841
|
|
|
835
|
-
|
|
842
|
+
/**
|
|
843
|
+
* Combined usage totals + performance in ONE window scan.
|
|
844
|
+
*
|
|
845
|
+
* This runs the combined SELECT once and one percentile pass, returning both
|
|
846
|
+
* shapes so the caller needs no second query.
|
|
847
|
+
*/
|
|
848
|
+
export async function summarizeUsageWithPerformance(
|
|
836
849
|
db: CartethyiaDatabase,
|
|
837
850
|
tenantId: string,
|
|
838
851
|
since: Date,
|
|
839
|
-
): Promise<UsagePerformanceTotals> {
|
|
852
|
+
): Promise<{ summary: UsageSummary; performance: UsagePerformanceTotals }> {
|
|
853
|
+
const condition = scoped(tenantId, since);
|
|
840
854
|
const [row] = await db
|
|
841
855
|
.select({
|
|
856
|
+
requests: sql<number>`count(*)`,
|
|
857
|
+
errors: AGGREGATE_SELECT.errors,
|
|
858
|
+
cancelled: sql<number>`sum(case when ${telemetryEvents.status} = 'cancelled' then 1 else 0 end)`,
|
|
859
|
+
truncated: sql<number>`sum(case when ${telemetryEvents.status} = 'truncated' then 1 else 0 end)`,
|
|
860
|
+
inputTokens: AGGREGATE_SELECT.inputTokens,
|
|
861
|
+
outputTokens: AGGREGATE_SELECT.outputTokens,
|
|
862
|
+
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
863
|
+
reasoningTokens: sql<number>`coalesce(sum(${telemetryEvents.reasoningTokens}), 0)`,
|
|
864
|
+
estimatedCostUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
842
865
|
sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
|
|
843
866
|
latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
|
|
867
|
+
unpriced: sql<number>`sum(case when ${telemetryEvents.estimatedCostUsd} is null then 1 else 0 end)`,
|
|
844
868
|
resolveSum: sql<number>`coalesce(sum(${telemetryEvents.resolveMs}), 0)`,
|
|
845
869
|
resolveCount: sql<number>`count(${telemetryEvents.resolveMs})`,
|
|
846
870
|
avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
|
|
847
871
|
})
|
|
848
872
|
.from(telemetryEvents)
|
|
849
|
-
.where(
|
|
850
|
-
|
|
851
|
-
|
|
873
|
+
.where(condition);
|
|
874
|
+
|
|
875
|
+
if (!row) {
|
|
876
|
+
const empty: UsageSummary = {
|
|
877
|
+
requests: 0,
|
|
878
|
+
errors: 0,
|
|
879
|
+
cancelled: 0,
|
|
880
|
+
truncated: 0,
|
|
881
|
+
inputTokens: 0,
|
|
882
|
+
outputTokens: 0,
|
|
883
|
+
cachedInputTokens: 0,
|
|
884
|
+
reasoningTokens: 0,
|
|
885
|
+
estimatedCostUsd: 0,
|
|
886
|
+
sampled: 0,
|
|
887
|
+
latencyMs: { avg: null, p50: null, p95: null, p99: null },
|
|
888
|
+
unpriced: 0,
|
|
889
|
+
};
|
|
890
|
+
const emptyPerformance: UsagePerformanceTotals = {
|
|
891
|
+
avgResolveMs: 0,
|
|
892
|
+
avgDurationMs: 0,
|
|
893
|
+
latencyP50Ms: 0,
|
|
894
|
+
latencyP95Ms: 0,
|
|
895
|
+
latencyP99Ms: 0,
|
|
896
|
+
latencySampleCount: 0,
|
|
897
|
+
resolveSampleCount: 0,
|
|
898
|
+
avgTokensPerSec: 0,
|
|
899
|
+
};
|
|
900
|
+
return { summary: empty, performance: emptyPerformance };
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const sampled = numeric(row.sampled);
|
|
852
904
|
const percentiles = await latencyPercentiles(db, tenantId, since, sampled);
|
|
905
|
+
const resolveCount = numeric(row.resolveCount);
|
|
853
906
|
return {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
907
|
+
summary: {
|
|
908
|
+
requests: numeric(row.requests),
|
|
909
|
+
errors: numeric(row.errors),
|
|
910
|
+
cancelled: numeric(row.cancelled),
|
|
911
|
+
truncated: numeric(row.truncated),
|
|
912
|
+
inputTokens: numeric(row.inputTokens),
|
|
913
|
+
outputTokens: numeric(row.outputTokens),
|
|
914
|
+
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
915
|
+
reasoningTokens: numeric(row.reasoningTokens),
|
|
916
|
+
estimatedCostUsd: numeric(row.estimatedCostUsd),
|
|
917
|
+
sampled,
|
|
918
|
+
latencyMs: {
|
|
919
|
+
avg: numericNullable(row.latencyAvg),
|
|
920
|
+
...percentiles,
|
|
921
|
+
},
|
|
922
|
+
unpriced: numeric(row.unpriced),
|
|
923
|
+
},
|
|
924
|
+
performance: {
|
|
925
|
+
avgResolveMs: resolveCount > 0 ? numeric(row.resolveSum) / resolveCount : 0,
|
|
926
|
+
avgDurationMs: numericNullable(row.latencyAvg) ?? 0,
|
|
927
|
+
latencyP50Ms: percentiles.p50 ?? 0,
|
|
928
|
+
latencyP95Ms: percentiles.p95 ?? 0,
|
|
929
|
+
latencyP99Ms: percentiles.p99 ?? 0,
|
|
930
|
+
latencySampleCount: sampled,
|
|
931
|
+
resolveSampleCount: resolveCount,
|
|
932
|
+
avgTokensPerSec: numeric(row.avgTokensPerSec),
|
|
933
|
+
},
|
|
862
934
|
};
|
|
863
935
|
}
|
|
@@ -36,7 +36,7 @@ export const LOG_RING_MAX_CODE_UNITS = 8_192;
|
|
|
36
36
|
|
|
37
37
|
/** Marker appended wherever content was dropped. */
|
|
38
38
|
export const LOG_TRUNCATED_MARKER = "[truncated]";
|
|
39
|
-
|
|
39
|
+
const LOG_CIRCULAR_MARKER = "[circular]";
|
|
40
40
|
|
|
41
41
|
function isHighSurrogate(code: number): boolean {
|
|
42
42
|
return code >= 0xd800 && code <= 0xdbff;
|