cartethyia 2.7.4 → 2.7.6
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 +5 -0
- package/README.md +18 -6
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.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-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.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/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
- package/dist/dashboard/assets/settings-BxslsErn.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-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.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 +60 -59
- 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/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -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/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +794 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +19 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +33 -6
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +56 -24
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- 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 +37 -31
- 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 +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- 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 +43 -299
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/driver.ts +150 -247
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +176 -153
- package/src/console/cli-tools/routes.ts +7 -1
- package/src/console/cli-tools/service.ts +8 -7
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- 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 +7 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +23 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- 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 +259 -66
- 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/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +11 -13
- 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 +4 -4
- 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 +252 -97
- 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 +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +9 -2
- package/src/observability/event-query.ts +61 -291
- 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 +19 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- 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 +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +121 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +29 -2
- package/src/persistence/stores/share-store.ts +20 -8
- 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 +6 -10
- package/src/protocol/dsml.ts +8 -125
- package/src/protocol/image-clamp.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 +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- 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 +6 -6
- 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/providers/adapters/compatible-adapter.ts +71 -4
- 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/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +35 -37
- 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 +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +22 -13
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- 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-identity.ts +86 -0
- 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 +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- 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 +35 -3
- 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 +9 -9
- package/src/providers/integrations/github/github-discovery.ts +70 -12
- package/src/providers/integrations/github/github-oauth.ts +184 -14
- package/src/providers/integrations/github/github.ts +134 -26
- 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 +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- 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 +80 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
- 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 +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +422 -71
- 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/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 +51 -8
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- 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 +198 -133
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- 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 +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- 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-usage.ts +50 -19
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +13 -6
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +17 -3
- 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/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +70 -22
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- 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 +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +21 -1
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- 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 +7 -13
- 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 +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +159 -37
- package/src/transport/shutdown-notice.ts +2 -2
- 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 +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/reasoning-summary.ts +9 -14
- 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 +431 -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-ChG5svFb.js +0 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.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-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-BayTTy01.js +0 -9
- package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.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-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
- 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/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,6 +18,7 @@
|
|
|
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
23
|
import { apiKeys, telemetryEvents, telemetryUsageTotals } from "../persistence/schema";
|
|
23
24
|
import type { TelemetryEventRecord } from "./event-log";
|
|
@@ -33,6 +34,20 @@ import { gatewayErrorSql } from "./telemetry-status";
|
|
|
33
34
|
*/
|
|
34
35
|
export const IS_GATEWAY_ERROR_SQL = gatewayErrorSql(telemetryEvents.status, telemetryEvents.httpStatus);
|
|
35
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;
|
|
50
|
+
|
|
36
51
|
/** One bounded page of events, newest first. */
|
|
37
52
|
export interface EventPage {
|
|
38
53
|
readonly events: TelemetryEventRecord[];
|
|
@@ -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;
|
|
@@ -176,6 +184,7 @@ export function eventRecordFromRow(row: {
|
|
|
176
184
|
readonly firstContentDeltaAtMs: number | null;
|
|
177
185
|
readonly lastEventAtMs: number | null;
|
|
178
186
|
readonly payloadCaptureState: string | null;
|
|
187
|
+
readonly degenerateStop: boolean | null;
|
|
179
188
|
}): TelemetryEventRecord {
|
|
180
189
|
return {
|
|
181
190
|
id: row.id ?? row.requestId,
|
|
@@ -211,6 +220,7 @@ export function eventRecordFromRow(row: {
|
|
|
211
220
|
firstContentDeltaAtMs: row.firstContentDeltaAtMs,
|
|
212
221
|
lastEventAtMs: row.lastEventAtMs,
|
|
213
222
|
payloadCaptureState: row.payloadCaptureState,
|
|
223
|
+
...(row.degenerateStop === true ? { degenerateStop: true } : {}),
|
|
214
224
|
};
|
|
215
225
|
}
|
|
216
226
|
|
|
@@ -225,11 +235,28 @@ export function eventRecordFromRow(row: {
|
|
|
225
235
|
* renders the SQL, so it is the only answer that cannot disagree with it.
|
|
226
236
|
*/
|
|
227
237
|
function isPostgres(db: CartethyiaDatabase): boolean {
|
|
228
|
-
const dialect = (db as unknown as { dialect?:
|
|
229
|
-
|
|
238
|
+
const dialect = (db as unknown as { dialect?: unknown }).dialect;
|
|
239
|
+
if (dialect instanceof PgDialect) return true;
|
|
240
|
+
const name = (dialect as { constructor?: { name?: string } } | undefined)?.constructor?.name;
|
|
230
241
|
return name === "PostgresDialect" || name === "PgDialect";
|
|
231
242
|
}
|
|
232
243
|
|
|
244
|
+
/**
|
|
245
|
+
* The epoch-bucket SQL expression for one time column.
|
|
246
|
+
*
|
|
247
|
+
* Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
|
|
248
|
+
* expressions as identical: parameter numbers differing between the two
|
|
249
|
+
* clauses made Postgres treat them as unrelated expressions referencing
|
|
250
|
+
* `created_at`, which 42803-rejected the query. The width is a raw SQL
|
|
251
|
+
* literal for the same reason.
|
|
252
|
+
*/
|
|
253
|
+
function bucketExpression(db: CartethyiaDatabase, column: typeof telemetryEvents.createdAt, widthSeconds: number): SQL {
|
|
254
|
+
const w = sql.raw(String(Math.max(1, Math.trunc(widthSeconds))));
|
|
255
|
+
return isPostgres(db)
|
|
256
|
+
? sql<number>`(cast(extract(epoch from ${column}) as bigint) / ${w}) * ${w}`
|
|
257
|
+
: sql<number>`(cast(strftime('%s', ${column}) as integer) / ${w}) * ${w}`;
|
|
258
|
+
}
|
|
259
|
+
|
|
233
260
|
/**
|
|
234
261
|
* The tenant predicate — the scope every read shares — plus the window when
|
|
235
262
|
* one is given.
|
|
@@ -400,90 +427,9 @@ export async function queryTelemetryEvents(
|
|
|
400
427
|
};
|
|
401
428
|
}
|
|
402
429
|
|
|
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
430
|
/**
|
|
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.
|
|
431
|
+
* The SQL column one breakdown dimension groups by.
|
|
426
432
|
*/
|
|
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
433
|
|
|
488
434
|
type Percentiles = { p50: number | null; p95: number | null; p99: number | null };
|
|
489
435
|
|
|
@@ -546,9 +492,6 @@ async function latencyPercentiles(
|
|
|
546
492
|
};
|
|
547
493
|
}
|
|
548
494
|
|
|
549
|
-
/**
|
|
550
|
-
* The SQL column one breakdown dimension groups by.
|
|
551
|
-
*/
|
|
552
495
|
function dimensionColumn(dimension: string) {
|
|
553
496
|
if (dimension === "model") return telemetryEvents.requestedModel;
|
|
554
497
|
if (dimension === "provider") return telemetryEvents.providerId;
|
|
@@ -587,9 +530,7 @@ export async function breakdownTelemetry(
|
|
|
587
530
|
.select({
|
|
588
531
|
key: column,
|
|
589
532
|
requests: sql<number>`count(*)`,
|
|
590
|
-
|
|
591
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
592
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
533
|
+
...AGGREGATE_SELECT,
|
|
593
534
|
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
594
535
|
costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
595
536
|
avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
|
|
@@ -626,32 +567,23 @@ export async function chartTelemetry(
|
|
|
626
567
|
since: Date,
|
|
627
568
|
bucketSeconds: number,
|
|
628
569
|
): Promise<ChartBucket[]> {
|
|
629
|
-
const
|
|
630
|
-
// Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
|
|
631
|
-
// expressions as identical: parameter numbers differing between the two
|
|
632
|
-
// clauses made Postgres treat them as unrelated expressions referencing
|
|
633
|
-
// `created_at`, which 42803-rejected the query.
|
|
634
|
-
const w = sql.raw(String(width));
|
|
635
|
-
const bucketExpression = isPostgres(db)
|
|
636
|
-
? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
|
|
637
|
-
: sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
|
|
570
|
+
const bucketExpr = bucketExpression(db, telemetryEvents.createdAt, bucketSeconds);
|
|
638
571
|
const rows = await db
|
|
639
572
|
.select({
|
|
640
|
-
at:
|
|
573
|
+
at: bucketExpr,
|
|
641
574
|
requests: sql<number>`count(*)`,
|
|
642
|
-
|
|
643
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
644
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
575
|
+
...AGGREGATE_SELECT,
|
|
645
576
|
})
|
|
646
577
|
.from(telemetryEvents)
|
|
647
578
|
.where(scoped(tenantId, since))
|
|
648
|
-
.groupBy(
|
|
579
|
+
.groupBy(bucketExpr);
|
|
649
580
|
return rows.map((row) => ({
|
|
650
581
|
at: numeric(row.at) * 1000,
|
|
651
582
|
requests: numeric(row.requests),
|
|
652
583
|
errors: numeric(row.errors),
|
|
653
584
|
inputTokens: numeric(row.inputTokens),
|
|
654
585
|
outputTokens: numeric(row.outputTokens),
|
|
586
|
+
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
655
587
|
}));
|
|
656
588
|
}
|
|
657
589
|
|
|
@@ -681,20 +613,15 @@ export async function providerUsageMatrix(
|
|
|
681
613
|
providerId: string,
|
|
682
614
|
since: Date,
|
|
683
615
|
): Promise<ProviderUsageDayRow[]> {
|
|
684
|
-
|
|
685
|
-
// identical expression or Postgres 42803-rejects the grouped reference.
|
|
686
|
-
const w = sql.raw(String(86400));
|
|
687
|
-
const dayExpression = isPostgres(db)
|
|
688
|
-
? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
|
|
689
|
-
: sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
|
|
616
|
+
const dayExpression = bucketExpression(db, telemetryEvents.createdAt, 86400);
|
|
690
617
|
const rows = await db
|
|
691
618
|
.select({
|
|
692
619
|
day: dayExpression,
|
|
693
620
|
model: telemetryEvents.requestedModel,
|
|
694
621
|
requests: sql<number>`count(*)`,
|
|
695
|
-
inputTokens:
|
|
622
|
+
inputTokens: AGGREGATE_SELECT.inputTokens,
|
|
696
623
|
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
697
|
-
outputTokens:
|
|
624
|
+
outputTokens: AGGREGATE_SELECT.outputTokens,
|
|
698
625
|
costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
699
626
|
})
|
|
700
627
|
.from(telemetryEvents)
|
|
@@ -712,43 +639,6 @@ export async function providerUsageMatrix(
|
|
|
712
639
|
}));
|
|
713
640
|
}
|
|
714
641
|
|
|
715
|
-
/** Overview/health totals for one tenant window. */
|
|
716
|
-
export async function healthTelemetry(
|
|
717
|
-
db: CartethyiaDatabase,
|
|
718
|
-
tenantId: string,
|
|
719
|
-
since: Date,
|
|
720
|
-
): Promise<HealthTotals> {
|
|
721
|
-
const [row] = await db
|
|
722
|
-
.select({
|
|
723
|
-
requests: sql<number>`count(*)`,
|
|
724
|
-
errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
|
|
725
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
726
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
727
|
-
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
728
|
-
latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
|
|
729
|
-
})
|
|
730
|
-
.from(telemetryEvents)
|
|
731
|
-
.where(scoped(tenantId, since));
|
|
732
|
-
if (!row) {
|
|
733
|
-
return {
|
|
734
|
-
requests: 0,
|
|
735
|
-
errors: 0,
|
|
736
|
-
inputTokens: 0,
|
|
737
|
-
outputTokens: 0,
|
|
738
|
-
cachedInputTokens: 0,
|
|
739
|
-
latencyAvgMs: null,
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
return {
|
|
743
|
-
requests: numeric(row.requests),
|
|
744
|
-
errors: numeric(row.errors),
|
|
745
|
-
inputTokens: numeric(row.inputTokens),
|
|
746
|
-
outputTokens: numeric(row.outputTokens),
|
|
747
|
-
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
748
|
-
latencyAvgMs: numericNullable(row.latencyAvg),
|
|
749
|
-
};
|
|
750
|
-
}
|
|
751
|
-
|
|
752
642
|
/**
|
|
753
643
|
* Summed input+output tokens for a set of API keys over one window — the
|
|
754
644
|
* family bucket spend query.
|
|
@@ -783,9 +673,6 @@ export async function sumTelemetryTokens(
|
|
|
783
673
|
return numeric(row?.total ?? 0);
|
|
784
674
|
}
|
|
785
675
|
|
|
786
|
-
/** One hour, in milliseconds — the share page's hourly bucket width. */
|
|
787
|
-
const HOUR_MS = 3_600_000;
|
|
788
|
-
|
|
789
676
|
/**
|
|
790
677
|
* All-time family counts from the durable rollup, not from a scan of the
|
|
791
678
|
* retained telemetry window.
|
|
@@ -798,7 +685,7 @@ const HOUR_MS = 3_600_000;
|
|
|
798
685
|
* event on every tick. The rollup is the same definition as the read-side
|
|
799
686
|
* counts, so the two cannot drift.
|
|
800
687
|
*/
|
|
801
|
-
|
|
688
|
+
interface FamilyKpiTotals {
|
|
802
689
|
readonly requests: number;
|
|
803
690
|
readonly errors: number;
|
|
804
691
|
readonly tokens: number;
|
|
@@ -830,43 +717,6 @@ export async function familyKpiTotals(
|
|
|
830
717
|
};
|
|
831
718
|
}
|
|
832
719
|
|
|
833
|
-
/**
|
|
834
|
-
* All-time family counts: the numbers a share page's KPI card shows.
|
|
835
|
-
*
|
|
836
|
-
* These must be computed in SQL, not derived from the bounded page read — the
|
|
837
|
-
* page caps at {@link MAX_LIMIT}, so a busy family would freeze at 500
|
|
838
|
-
* requests/errors/last-hour regardless of real traffic.
|
|
839
|
-
*/
|
|
840
|
-
export interface FamilyUsageTotals {
|
|
841
|
-
readonly requests: number;
|
|
842
|
-
readonly errors: number;
|
|
843
|
-
readonly lastHourRequests: number;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
/** Counts one share family's requests, errors, and last-hour traffic in SQL. */
|
|
847
|
-
export async function familyUsageTotals(
|
|
848
|
-
db: CartethyiaDatabase,
|
|
849
|
-
tenantId: string,
|
|
850
|
-
familyKeyId: string,
|
|
851
|
-
now: Date,
|
|
852
|
-
): Promise<FamilyUsageTotals> {
|
|
853
|
-
const lastHourStart = new Date(now.getTime() - HOUR_MS);
|
|
854
|
-
const conditions = [eq(telemetryEvents.tenantId, tenantId), familyKeyCondition(familyKeyId)];
|
|
855
|
-
const [row] = await db
|
|
856
|
-
.select({
|
|
857
|
-
requests: sql<number>`count(*)`,
|
|
858
|
-
errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
|
|
859
|
-
lastHourRequests: sql<number>`sum(case when ${telemetryEvents.createdAt} >= ${lastHourStart} then 1 else 0 end)`,
|
|
860
|
-
})
|
|
861
|
-
.from(telemetryEvents)
|
|
862
|
-
.where(and(...conditions));
|
|
863
|
-
return {
|
|
864
|
-
requests: numeric(row?.requests ?? 0),
|
|
865
|
-
errors: numeric(row?.errors ?? 0),
|
|
866
|
-
lastHourRequests: numeric(row?.lastHourRequests ?? 0),
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
|
|
870
720
|
/** Counts one share family's requests in a bounded window — a range scan, not a full-window aggregate. */
|
|
871
721
|
export async function familyWindowRequests(
|
|
872
722
|
db: CartethyiaDatabase,
|
|
@@ -945,83 +795,36 @@ export async function familyHourlyTelemetry(
|
|
|
945
795
|
since: Date,
|
|
946
796
|
bucketSeconds: number,
|
|
947
797
|
): Promise<ChartBucket[]> {
|
|
948
|
-
const
|
|
949
|
-
// Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
|
|
950
|
-
// expressions as identical — see chartTelemetry for the failure this avoids.
|
|
951
|
-
const w = sql.raw(String(width));
|
|
952
|
-
const bucketExpression = isPostgres(db)
|
|
953
|
-
? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
|
|
954
|
-
: sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
|
|
798
|
+
const bucketExpr = bucketExpression(db, telemetryEvents.createdAt, bucketSeconds);
|
|
955
799
|
const conditions = [scoped(tenantId, since), familyKeyCondition(familyKeyId)];
|
|
956
800
|
const rows = await db
|
|
957
801
|
.select({
|
|
958
|
-
at:
|
|
802
|
+
at: bucketExpr,
|
|
959
803
|
requests: sql<number>`count(*)`,
|
|
960
|
-
|
|
961
|
-
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
962
|
-
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
804
|
+
...AGGREGATE_SELECT,
|
|
963
805
|
})
|
|
964
806
|
.from(telemetryEvents)
|
|
965
807
|
.where(and(...conditions))
|
|
966
|
-
.groupBy(
|
|
808
|
+
.groupBy(bucketExpr);
|
|
967
809
|
return rows.map((row) => ({
|
|
968
810
|
at: numeric(row.at) * 1000,
|
|
969
811
|
requests: numeric(row.requests),
|
|
970
812
|
errors: numeric(row.errors),
|
|
971
813
|
inputTokens: numeric(row.inputTokens),
|
|
972
814
|
outputTokens: numeric(row.outputTokens),
|
|
815
|
+
cachedInputTokens: numeric(row.cachedInputTokens),
|
|
973
816
|
}));
|
|
974
817
|
}
|
|
975
818
|
|
|
976
|
-
/**
|
|
977
|
-
* The health card's latency and throughput figures for one window.
|
|
978
|
-
*
|
|
979
|
-
* Same principle as {@link summarizeTelemetry}: `avg(tokens_per_sec)` and the
|
|
980
|
-
* latency percentiles are computed by the database. The console's health card
|
|
981
|
-
* used to pull every retained event into an array to average `tokensPerSec`
|
|
982
|
-
* and sort latencies in JavaScript.
|
|
983
|
-
*/
|
|
984
|
-
export interface HealthPerformance {
|
|
985
|
-
readonly latencyAvgMs: number | null;
|
|
986
|
-
readonly latencyP50Ms: number | null;
|
|
987
|
-
readonly latencyP95Ms: number | null;
|
|
988
|
-
readonly latencyP99Ms: number | null;
|
|
989
|
-
readonly avgTokensPerSec: number;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
export async function summarizeHealthPerformance(
|
|
993
|
-
db: CartethyiaDatabase,
|
|
994
|
-
tenantId: string,
|
|
995
|
-
since: Date,
|
|
996
|
-
): Promise<HealthPerformance> {
|
|
997
|
-
const [row] = await db
|
|
998
|
-
.select({
|
|
999
|
-
latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
|
|
1000
|
-
sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
|
|
1001
|
-
avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
|
|
1002
|
-
})
|
|
1003
|
-
.from(telemetryEvents)
|
|
1004
|
-
.where(scoped(tenantId, since));
|
|
1005
|
-
const sampled = numeric(row?.sampled ?? 0);
|
|
1006
|
-
const percentiles = await latencyPercentiles(db, tenantId, since, sampled);
|
|
1007
|
-
return {
|
|
1008
|
-
latencyAvgMs: numericNullable(row?.latencyAvg ?? null),
|
|
1009
|
-
latencyP50Ms: percentiles.p50,
|
|
1010
|
-
latencyP95Ms: percentiles.p95,
|
|
1011
|
-
latencyP99Ms: percentiles.p99,
|
|
1012
|
-
avgTokensPerSec: numeric(row?.avgTokensPerSec ?? 0),
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
819
|
/**
|
|
1017
820
|
* The Usage summary's performance block, computed in SQL.
|
|
1018
821
|
*
|
|
1019
822
|
* Replaces `summarizeUsagePerformance` (which sorted a latency array in
|
|
1020
823
|
* JavaScript): `avg(resolve_ms)` and `avg(tokens_per_sec)` are SQL averages,
|
|
1021
824
|
* and the latency percentiles come from the same database-side percentile
|
|
1022
|
-
* path {@link
|
|
825
|
+
* path {@link summarizeUsageWithPerformance} uses.
|
|
1023
826
|
*/
|
|
1024
|
-
|
|
827
|
+
interface UsagePerformanceTotals {
|
|
1025
828
|
readonly avgResolveMs: number;
|
|
1026
829
|
readonly avgDurationMs: number;
|
|
1027
830
|
/**
|
|
@@ -1038,42 +841,9 @@ export interface UsagePerformanceTotals {
|
|
|
1038
841
|
readonly avgTokensPerSec: number;
|
|
1039
842
|
}
|
|
1040
843
|
|
|
1041
|
-
export async function summarizeUsagePerformanceSql(
|
|
1042
|
-
db: CartethyiaDatabase,
|
|
1043
|
-
tenantId: string,
|
|
1044
|
-
since: Date,
|
|
1045
|
-
): Promise<UsagePerformanceTotals> {
|
|
1046
|
-
const [row] = await db
|
|
1047
|
-
.select({
|
|
1048
|
-
sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
|
|
1049
|
-
latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
|
|
1050
|
-
resolveSum: sql<number>`coalesce(sum(${telemetryEvents.resolveMs}), 0)`,
|
|
1051
|
-
resolveCount: sql<number>`count(${telemetryEvents.resolveMs})`,
|
|
1052
|
-
avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
|
|
1053
|
-
})
|
|
1054
|
-
.from(telemetryEvents)
|
|
1055
|
-
.where(scoped(tenantId, since));
|
|
1056
|
-
const sampled = numeric(row?.sampled ?? 0);
|
|
1057
|
-
const resolveCount = numeric(row?.resolveCount ?? 0);
|
|
1058
|
-
const percentiles = await latencyPercentiles(db, tenantId, since, sampled);
|
|
1059
|
-
return {
|
|
1060
|
-
avgResolveMs: resolveCount > 0 ? numeric(row?.resolveSum ?? 0) / resolveCount : 0,
|
|
1061
|
-
avgDurationMs: numericNullable(row?.latencyAvg ?? null) ?? 0,
|
|
1062
|
-
latencyP50Ms: percentiles.p50 ?? 0,
|
|
1063
|
-
latencyP95Ms: percentiles.p95 ?? 0,
|
|
1064
|
-
latencyP99Ms: percentiles.p99 ?? 0,
|
|
1065
|
-
latencySampleCount: sampled,
|
|
1066
|
-
resolveSampleCount: resolveCount,
|
|
1067
|
-
avgTokensPerSec: numeric(row?.avgTokensPerSec ?? 0),
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
844
|
/**
|
|
1072
845
|
* Combined usage totals + performance in ONE window scan.
|
|
1073
846
|
*
|
|
1074
|
-
* `summarizeTelemetry` and `summarizeUsagePerformanceSql` both scan the same
|
|
1075
|
-
* tenant window and each additionally run the percentile path, so the Usage
|
|
1076
|
-
* summary endpoint paid two scans plus two percentile passes for one response.
|
|
1077
847
|
* This runs the combined SELECT once and one percentile pass, returning both
|
|
1078
848
|
* shapes so the caller needs no second query.
|
|
1079
849
|
*/
|
|
@@ -1086,11 +856,11 @@ export async function summarizeUsageWithPerformance(
|
|
|
1086
856
|
const [row] = await db
|
|
1087
857
|
.select({
|
|
1088
858
|
requests: sql<number>`count(*)`,
|
|
1089
|
-
errors:
|
|
859
|
+
errors: AGGREGATE_SELECT.errors,
|
|
1090
860
|
cancelled: sql<number>`sum(case when ${telemetryEvents.status} = 'cancelled' then 1 else 0 end)`,
|
|
1091
861
|
truncated: sql<number>`sum(case when ${telemetryEvents.status} = 'truncated' then 1 else 0 end)`,
|
|
1092
|
-
inputTokens:
|
|
1093
|
-
outputTokens:
|
|
862
|
+
inputTokens: AGGREGATE_SELECT.inputTokens,
|
|
863
|
+
outputTokens: AGGREGATE_SELECT.outputTokens,
|
|
1094
864
|
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
1095
865
|
reasoningTokens: sql<number>`coalesce(sum(${telemetryEvents.reasoningTokens}), 0)`,
|
|
1096
866
|
estimatedCostUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
@@ -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;
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
* `render()` emits the Prometheus exposition format on demand.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
type MetricLabels = Readonly<Record<string, string | number>>;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface CounterMetric {
|
|
11
11
|
inc(value?: number, labels?: MetricLabels): void;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
interface GaugeMetric extends CounterMetric {
|
|
15
15
|
set(value: number, labels?: MetricLabels): void;
|
|
16
16
|
dec(value?: number, labels?: MetricLabels): void;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
interface HistogramMetric {
|
|
20
20
|
observe(value: number, labels?: MetricLabels): void;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
TelemetryPayloadDepth,
|
|
9
9
|
TelemetryPayloadMode,
|
|
10
10
|
} from "../console/settings/contracts";
|
|
11
|
-
|
|
11
|
+
type CaptureScope = "tenant" | "debug_session" | "operator_flag";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* How long a captured payload frame lives when the deployment has not chosen.
|
|
@@ -203,13 +203,13 @@ export interface PayloadCaptureInput {
|
|
|
203
203
|
operatorFlagOptIn?: boolean;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
interface PayloadSignals {
|
|
207
207
|
readonly toolCalls: number;
|
|
208
208
|
readonly images: number;
|
|
209
209
|
readonly attachments: number;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
interface StoredPayload {
|
|
213
213
|
id: string;
|
|
214
214
|
/**
|
|
215
215
|
* JSON text of the five body surfaces, in drawer order, built while the
|
|
@@ -412,8 +412,9 @@ export function buildPayloadRecord(
|
|
|
412
412
|
|
|
413
413
|
/**
|
|
414
414
|
* Postgres-backed redacted request/response payload capture. Payloads live for
|
|
415
|
-
* the tenant's chosen retention —
|
|
416
|
-
*
|
|
415
|
+
* the tenant's chosen retention — an hour by default (see
|
|
416
|
+
* {@link payloadRetentionMs} / `DEFAULT_PAYLOAD_RETENTION_MS`) — and are pruned
|
|
417
|
+
* by the sweep.
|
|
417
418
|
|
|
418
419
|
*/
|
|
419
420
|
export class TelemetryPayloadCapture {
|
|
@@ -22,7 +22,7 @@ interface PayloadFrame {
|
|
|
22
22
|
readonly payload: unknown;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
function isPayloadFileReference(value: unknown): value is PayloadFileReference {
|
|
26
26
|
if (typeof value !== "object" || value === null) return false;
|
|
27
27
|
const record = value as Record<string, unknown>;
|
|
28
28
|
return (
|
|
@@ -32,7 +32,7 @@ export interface RuntimeMemorySnapshot {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/** Live collection sizes sampled alongside process memory. */
|
|
35
|
-
|
|
35
|
+
interface RuntimeMetricSources {
|
|
36
36
|
readonly routingRoundRobinEntries: () => { readonly combo: number; readonly provider: number };
|
|
37
37
|
/**
|
|
38
38
|
* Tracked keys in the in-memory IP abuse store, or 0 when the store is
|
|
@@ -65,6 +65,12 @@ export interface TelemetryEventInput {
|
|
|
65
65
|
* list instead of needing the server's logs.
|
|
66
66
|
*/
|
|
67
67
|
readonly payloadCaptureState?: string;
|
|
68
|
+
/**
|
|
69
|
+
* True for a completed request whose output was far smaller than the model's
|
|
70
|
+
* ceiling and which ended with an ordinary `stop` (a "degenerate stop").
|
|
71
|
+
* Carried onto the telemetry row so this pattern can be measured.
|
|
72
|
+
*/
|
|
73
|
+
readonly degenerateStop?: boolean;
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
function tokenCountToInt(value: number | "unavailable" | undefined): number | undefined {
|
|
@@ -155,10 +161,11 @@ function telemetryEventRow(event: TelemetryEventInput) {
|
|
|
155
161
|
firstContentDeltaAtMs: event.firstContentDeltaAtMs ?? null,
|
|
156
162
|
lastEventAtMs: event.lastEventAtMs ?? null,
|
|
157
163
|
payloadCaptureState: event.payloadCaptureState ?? null,
|
|
164
|
+
degenerateStop: event.degenerateStop ?? null,
|
|
158
165
|
};
|
|
159
166
|
}
|
|
160
167
|
|
|
161
|
-
|
|
168
|
+
interface TelemetryBatchBufferOptions {
|
|
162
169
|
readonly flushIntervalMs?: number;
|
|
163
170
|
readonly maxBatch?: number;
|
|
164
171
|
readonly maxItems?: number;
|
|
@@ -184,8 +191,17 @@ function drainRetryDelayMs(attempt: number, baseDelayMs = DRAIN_RETRY_BASE_MS):
|
|
|
184
191
|
return exponentialBackoff(attempt, options);
|
|
185
192
|
}
|
|
186
193
|
|
|
187
|
-
/**
|
|
188
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Floor for the adaptive active interval so a full queue still drains promptly.
|
|
196
|
+
*
|
|
197
|
+
* Raised to 200ms (from 50ms) so a flush does not collide with console reads:
|
|
198
|
+
* under load the queue saturates `maxBatch`, the interval fell to 50ms, and a
|
|
199
|
+
* drain landed in the middle of the console's own telemetry reads — the GROUP BY
|
|
200
|
+
* behind the Usage/ConsoleLog pages — making the page's queries wait on the
|
|
201
|
+
* drain's insert lock. 200ms still drains a 500-row batch ~5x/sec, which is a
|
|
202
|
+
* steady 2500 rows/sec ceiling; the adaptive curve otherwise keeps its shape.
|
|
203
|
+
*/
|
|
204
|
+
const ADAPTIVE_FLUSH_MIN_MS = 200;
|
|
189
205
|
/** Multiplier applied to the base interval while the queue is empty. */
|
|
190
206
|
const ADAPTIVE_IDLE_MULTIPLIER = 4;
|
|
191
207
|
|