cartethyia 2.5.10 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +8 -0
- package/dist/dashboard/assets/{BannedPage-Cd30Z0b9.js → BannedPage-l6XMnfr9.js} +1 -1
- package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-wf5u-ww6.js → CliToolDetailPage-DwOzAW45.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DykPvuck.js → CliToolsPage-BT3Up3QA.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-DwvSD3vs.js → CombosPage-6kYIUpzR.js} +1 -1
- package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CIr7KDex.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-82X0DDdH.js → CustomizationPage-C7m-H1Rz.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-CS5mPuVF.js → LoginPage-CbphLvjg.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C0UCUsVJ.js → ModelPicker-DVny4S5_.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-vdja5ZPK.js → ProvidersPage-Dfb2896L.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-BB4CNm4-.js → ProxyPage-BaCIdAUt.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-FadUOl9S.js → SetupPage-D4g6vOGc.js} +1 -1
- package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-Cfy6_Khv.js → SortableList-C93i14BD.js} +1 -1
- package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-CM92fgJf.js → StudioPage-SgQpymjm.js} +1 -1
- package/dist/dashboard/assets/{StudioPage-CM92fgJf.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
- package/dist/dashboard/assets/{UsagePage-BT4MyQeT.js → UsagePage-BFME-eba.js} +2 -2
- package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
- package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
- package/dist/dashboard/assets/{app-B7pVRhJ1.js → app-2KhtHCv9.js} +1 -1
- package/dist/dashboard/assets/{app-B7pVRhJ1.js.gz → app-2KhtHCv9.js.gz} +0 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
- package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
- package/dist/dashboard/assets/{badge-Bvyr7D0u.js → badge-CKsF123d.js} +1 -1
- package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DIvKAZih.js → banner-8j7uChA9.js} +1 -1
- package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
- package/dist/dashboard/assets/{card-D5_gYIS_.js → card-CdsSlWA3.js} +1 -1
- package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
- package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-B6vDBtbm.js → clipboard-button-uL5aYLfW.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-CpjrgdwJ.js → dialog-C07hr5_G.js} +1 -1
- package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BWmt3b5y.js → drawer-BPO1TcKH.js} +1 -1
- package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
- package/dist/dashboard/assets/{entry-C_oakV3z.js → entry-Bz_CtDfC.js} +1 -1
- package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-RRHHWGxh.js → github-badge-DmQreSDd.js} +1 -1
- package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
- package/dist/dashboard/assets/{index-Bhey_rg_.js → index-Bblu6erR.js} +2 -2
- package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
- package/dist/dashboard/assets/{inline-C2_EQ5Uf.js → inline-C9dCYaBZ.js} +1 -1
- package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
- package/dist/dashboard/assets/{input-BMRlQ450.js → input-CjCnXweJ.js} +1 -1
- package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
- package/dist/dashboard/assets/{layout-XR5xP8ew.js → layout-V6y8DAkl.js} +1 -1
- package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Cw07dioc.js → list-row-CM1AaG_y.js} +1 -1
- package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
- package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
- package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
- package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
- package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-DrdA9oSM.js → numbers-BOevJzdz.js} +1 -1
- package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
- package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
- package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
- package/dist/dashboard/assets/{select-CxVVB__N.js → select-C980c4cz.js} +1 -1
- package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
- package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
- package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
- package/dist/dashboard/assets/{stack-uPOwM9Hs.js → stack-_gfTdLVB.js} +1 -1
- package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
- package/dist/dashboard/assets/{state-D1Yz79iL.js → state-2c5V4zo7.js} +1 -1
- package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
- package/dist/dashboard/assets/{switch-sevsRrRo.js → switch-Dkke7Naf.js} +1 -1
- package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
- package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -51
- package/migrations/README.md +52 -52
- package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
- package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
- package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/package.json +96 -95
- package/scripts/cli/admin.ts +126 -128
- package/scripts/cli/autostart.ts +237 -237
- package/scripts/cli/background.ts +120 -110
- package/scripts/cli/bin/carte.cjs +8 -8
- package/scripts/cli/bin/cartethyia.cjs +167 -167
- package/scripts/cli/console-menu.ts +39 -30
- package/scripts/cli/controller.ts +145 -0
- package/scripts/cli/env.ts +99 -99
- package/scripts/cli/gateway.ts +264 -260
- package/scripts/cli/instance.ts +234 -224
- package/scripts/cli/main.ts +188 -167
- package/scripts/cli/platform.ts +64 -64
- package/scripts/cli/prompt.ts +217 -79
- package/scripts/cli/services.ts +88 -88
- package/scripts/cli/setup.ts +445 -445
- package/scripts/cli/start.ts +85 -85
- package/scripts/cli/startup-signal.ts +23 -23
- package/scripts/cli/telemetry.ts +70 -0
- package/scripts/cli/tray/index.ts +263 -263
- package/scripts/cli/tray/windows.ps1 +107 -107
- package/scripts/cli/tray.ts +29 -4
- package/scripts/cli/update.ts +39 -39
- package/scripts/cli/version.ts +67 -67
- package/scripts/cli/wordmark.ts +58 -58
- package/src/boot/app.ts +16 -1
- package/src/boot/dependencies.ts +128 -25
- package/src/config/resources.ts +137 -0
- package/src/config/runtime.ts +133 -76
- package/src/config/spec.ts +30 -29
- package/src/config/telemetry.ts +0 -25
- package/src/console/api-keys/bucket-spend.ts +14 -17
- package/src/console/api-keys/contracts.ts +410 -410
- package/src/console/api-keys/routes.ts +12 -0
- package/src/console/audit/store.ts +2 -3
- package/src/console/auth/setup-service.ts +8 -1
- package/src/console/backup/routes.ts +7 -0
- package/src/console/backup/service.ts +463 -463
- package/src/console/backup/store.ts +16 -11
- package/src/console/backup/validate.ts +1 -4
- package/src/console/cli-tools/fs-ops.ts +18 -2
- package/src/console/data-management/contracts.ts +1 -8
- package/src/console/data-management/operations.ts +61 -48
- package/src/console/data-management/routes.ts +4 -2
- package/src/console/domain-registration.ts +25 -3
- package/src/console/inferhub/client.ts +139 -0
- package/src/console/inferhub/key-store.ts +129 -0
- package/src/console/inferhub/normalize.ts +347 -0
- package/src/console/inferhub/routes.ts +411 -0
- package/src/console/inferhub/store.ts +109 -0
- package/src/console/inferhub/types.ts +183 -0
- package/src/console/observability/contracts.ts +2 -41
- package/src/console/observability/store.ts +176 -371
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/store.ts +23 -36
- package/src/console/quota/account-quota-shared.ts +52 -52
- package/src/console/quota/account-quota-tenant.ts +16 -24
- package/src/console/quota/refresh.ts +441 -441
- package/src/console/routing/pools/import-parse.ts +206 -206
- package/src/console/routing/pools/operations.ts +543 -543
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/routes.ts +181 -181
- package/src/console/settings/contracts.ts +42 -40
- package/src/console/settings/store.ts +25 -25
- package/src/console/share/share-stats.ts +22 -4
- package/src/console/share/share-usage.ts +30 -4
- package/src/console/shared/cursor.ts +15 -0
- package/src/console/updates/routes.ts +12 -0
- package/src/main.ts +13 -5
- package/src/network/pool/resolver.ts +1104 -1104
- package/src/observability/client-name.ts +22 -0
- package/src/observability/event-log.ts +5 -29
- package/src/observability/event-query.ts +690 -0
- package/src/observability/log-ring.ts +26 -2
- package/src/observability/log-sanitize.ts +118 -0
- package/src/observability/logger.ts +17 -13
- package/src/observability/metrics.ts +35 -0
- package/src/observability/payload-capture.ts +114 -41
- package/src/observability/payload-store.ts +249 -24
- package/src/observability/raw-event-writer.ts +118 -0
- package/src/observability/runtime-metrics.ts +71 -1
- package/src/observability/telemetry-buffer.ts +100 -17
- package/src/observability/telemetry-reconciliation.ts +417 -0
- package/src/persistence/boot.ts +20 -9
- package/src/persistence/db-pg.ts +13 -0
- package/src/persistence/db-sqlite.ts +569 -497
- package/src/persistence/dialect.ts +3 -2
- package/src/persistence/migrate.ts +181 -181
- package/src/persistence/schema.ts +1496 -1406
- package/src/persistence/shared-cache.ts +5 -1
- package/src/persistence/stores/api-key-store.ts +581 -581
- package/src/persistence/stores/telemetry-store.ts +124 -63
- package/src/protocol/reasoning-text.ts +30 -0
- package/src/protocol/response/chat.ts +33 -7
- package/src/protocol/response/codex.ts +14 -2
- package/src/protocol/response/messages.ts +5 -2
- package/src/providers/authentication/oauth-flow-store.ts +3 -10
- package/src/providers/bundled-discovery-providers.ts +122 -122
- package/src/providers/default-registry.ts +10 -0
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +43 -7
- package/src/providers/discovery/probing-service.ts +70 -32
- package/src/providers/integrations/antigravity/antigravity.ts +9 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
- package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
- package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
- package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
- package/src/providers/integrations/claude/claude-quota.ts +2 -2
- package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
- package/src/providers/integrations/claude/claude.ts +3 -4
- package/src/providers/integrations/cline/cline.ts +94 -8
- package/src/providers/integrations/codex/codex.ts +61 -37
- package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
- package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
- package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
- package/src/providers/integrations/cursor/cursor-request.ts +11 -0
- package/src/providers/integrations/cursor/cursor.ts +29 -17
- package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
- package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
- package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
- package/src/providers/integrations/grok/grok-quota.ts +279 -279
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
- package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
- package/src/providers/integrations/hermes.ts +7 -1
- package/src/providers/integrations/inferhub.ts +201 -73
- package/src/providers/integrations/kilo/kilo.ts +152 -152
- package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
- package/src/providers/integrations/openrouter/provider.ts +129 -129
- package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
- package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
- package/src/providers/integrations/trae/trae-oauth.ts +4 -26
- package/src/providers/integrations/trae/trae-quota.ts +389 -384
- package/src/providers/integrations/trae/trae-realm.ts +15 -2
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/account-health-service.ts +605 -279
- package/src/providers/provider-metadata.ts +12 -1
- package/src/providers/provider-model-definition.ts +373 -373
- package/src/providers/quota/quota-contracts.ts +3 -7
- package/src/providers/search/search-provider.ts +2 -6
- package/src/security/admission/contracts.ts +10 -0
- package/src/security/admission/in-memory-store.ts +89 -14
- package/src/security/admission/lua.ts +191 -182
- package/src/security/admission/redis-store.ts +30 -0
- package/src/security/admission/service.ts +18 -0
- package/src/security/api-key-auth-cache.ts +96 -0
- package/src/security/api-key-auth.ts +11 -100
- package/src/security/client-router-fingerprint.ts +3 -7
- package/src/security/ip-boundary.ts +7 -3
- package/src/shared/coerce.ts +67 -0
- package/src/shared/hash.ts +6 -0
- package/src/shared/time.ts +7 -0
- package/src/transport/dispatch/attempt-finalize.ts +26 -138
- package/src/transport/dispatch/attempt-loop.ts +103 -81
- package/src/transport/dispatch/capture-policy.ts +78 -0
- package/src/transport/dispatch/proxy-request.ts +4 -2
- package/src/transport/dispatch/streaming/encoder.ts +111 -5
- package/src/transport/dispatch/streaming/stream.ts +19 -2
- package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
- package/src/transport/failure-policy.ts +27 -0
- package/src/transport/guards/abuse-guard.ts +2 -2
- package/src/transport/guards/auth-guard.ts +17 -0
- package/src/transport/guards/body-policy.ts +3 -2
- package/src/transport/guards/concurrency-guard.ts +89 -13
- package/src/transport/guards/early-rejection-capture.ts +43 -3
- package/src/transport/guards/error-lifecycle.ts +10 -2
- package/src/transport/guards/guard-errors.ts +23 -0
- package/src/transport/guards/headroom-guard.ts +41 -7
- package/src/transport/guards/pipeline.ts +24 -0
- package/src/transport/guards/request-context.ts +2 -1
- package/src/transport/routing/admission.ts +51 -9
- package/src/transport/routing/bounded-lru.ts +49 -0
- package/src/transport/routing/candidate-index.ts +88 -0
- package/src/transport/routing/reservations.ts +121 -7
- package/src/transport/routing/round-robin.ts +10 -22
- package/src/transport/routing/route-catalog.ts +26 -13
- package/src/transport/routing/route-model.ts +22 -2
- package/src/transport/routing/router.ts +53 -31
- package/src/transport/surface/chat/encode.ts +2 -3
- package/src/transport/surface/chat/parse.ts +2 -1
- package/src/transport/surface/dialects.ts +11 -3
- package/src/transport/surface/messages/encode.ts +2 -1
- package/src/transport/surface/messages/parse.ts +3 -1
- package/src/transport/surface/messages/stream.ts +2 -6
- package/src/workers/maintenance-lease.ts +200 -0
- package/src/workers/scheduler.ts +101 -1
- package/dist/dashboard/assets/BannedPage-Cd30Z0b9.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-wf5u-ww6.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DykPvuck.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DwvSD3vs.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CIr7KDex.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-82X0DDdH.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CS5mPuVF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C0UCUsVJ.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-vdja5ZPK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BB4CNm4-.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js +0 -1
- package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js +0 -1
- package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-FadUOl9S.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-Cfy6_Khv.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BT4MyQeT.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/badge-Bvyr7D0u.js.gz +0 -0
- package/dist/dashboard/assets/banner-DIvKAZih.js.gz +0 -0
- package/dist/dashboard/assets/card-D5_gYIS_.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-B6vDBtbm.js.gz +0 -0
- package/dist/dashboard/assets/dialog-CpjrgdwJ.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BWmt3b5y.js.gz +0 -0
- package/dist/dashboard/assets/entry-B-gvimrg.js +0 -1
- package/dist/dashboard/assets/entry-B-gvimrg.js.gz +0 -0
- package/dist/dashboard/assets/entry-C_oakV3z.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dj0cHfEc.js +0 -4
- package/dist/dashboard/assets/entry-Dj0cHfEc.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-RRHHWGxh.js.gz +0 -0
- package/dist/dashboard/assets/index-Bhey_rg_.js.gz +0 -0
- package/dist/dashboard/assets/inline-C2_EQ5Uf.js.gz +0 -0
- package/dist/dashboard/assets/input-BMRlQ450.js.gz +0 -0
- package/dist/dashboard/assets/layout-XR5xP8ew.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Cw07dioc.js.gz +0 -0
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DrdA9oSM.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/select-CxVVB__N.js.gz +0 -0
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/stack-uPOwM9Hs.js.gz +0 -0
- package/dist/dashboard/assets/state-D1Yz79iL.js.gz +0 -0
- package/dist/dashboard/assets/switch-sevsRrRo.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/src/observability/event-compaction.ts +0 -317
- package/src/observability/event-day-index.ts +0 -439
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InferHub market normalization.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions over already-parsed remote data: no I/O, so every derivation
|
|
5
|
+
* here is unit-testable with a fixture. All inputs are untrusted — a malformed
|
|
6
|
+
* or hostile payload must degrade to `null`/empty, never throw into a route.
|
|
7
|
+
*
|
|
8
|
+
* Two rules run through everything:
|
|
9
|
+
* - **Null means unknown.** InferHub nulls any metric below a volume floor, and
|
|
10
|
+
* a null is not a zero. A model with no reported success rate is unverified,
|
|
11
|
+
* not broken; coerce it to 0 and every quiet model looks like an outage.
|
|
12
|
+
* - **The cheapest ask is not capacity.** The lowest tier frequently has
|
|
13
|
+
* `avail: 1`, so both the minimum and a capacity-weighted price are exposed.
|
|
14
|
+
*/
|
|
15
|
+
import type {
|
|
16
|
+
FamilyState,
|
|
17
|
+
ModelMarket,
|
|
18
|
+
RawCatalogModel,
|
|
19
|
+
RawMarketModel,
|
|
20
|
+
RawStatusFamily,
|
|
21
|
+
RawStatusModel,
|
|
22
|
+
RawUpstreamCard,
|
|
23
|
+
Tier,
|
|
24
|
+
} from "./types";
|
|
25
|
+
|
|
26
|
+
/** Ask buckets are keyed to this many decimals so float noise cannot split one price into two tiers. */
|
|
27
|
+
const PRICE_PRECISION = 6;
|
|
28
|
+
/** Default number of live providers a capacity-weighted price must have behind it. */
|
|
29
|
+
export const DEFAULT_MIN_CAPACITY = 2;
|
|
30
|
+
|
|
31
|
+
const FAMILY_STATES: readonly FamilyState[] = [
|
|
32
|
+
"operational",
|
|
33
|
+
"degraded",
|
|
34
|
+
"major_outage",
|
|
35
|
+
"low_traffic",
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/** Reads a finite number, or null. Never coerces a missing metric to zero. */
|
|
39
|
+
function num(value: unknown): number | null {
|
|
40
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
41
|
+
// Catalog and budgets carry decimal strings; market carries numbers. Both
|
|
42
|
+
// are read here so one helper covers the wire's own inconsistency.
|
|
43
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
44
|
+
const parsed = Number(value);
|
|
45
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Reads a finite number, or null. Never coerces a missing metric to zero. */
|
|
51
|
+
export function numOrNull(value: unknown): number | null {
|
|
52
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
53
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
54
|
+
const parsed = Number(value);
|
|
55
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function str(value: unknown): string | null {
|
|
61
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function bool(value: unknown): boolean {
|
|
65
|
+
return value === true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function stringArray(value: unknown): readonly string[] {
|
|
69
|
+
return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Percentage below the official price, rounded to one decimal.
|
|
74
|
+
*
|
|
75
|
+
* Null when the official price is missing or zero: a discount against an
|
|
76
|
+
* unknown baseline is not a number, and dividing by zero would advertise
|
|
77
|
+
* every such model as infinitely cheap.
|
|
78
|
+
*/
|
|
79
|
+
export function discountPct(ask: number | null, official: number | null): number | null {
|
|
80
|
+
if (ask === null || official === null || official <= 0) return null;
|
|
81
|
+
return Math.round((1 - ask / official) * 1000) / 10;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Groups one side's asks into ascending price tiers.
|
|
86
|
+
*
|
|
87
|
+
* A model with no asks at all yields an empty list, which the caller reports as
|
|
88
|
+
* `providersLive: 0` — the model stays listed but is never routable.
|
|
89
|
+
*/
|
|
90
|
+
export function groupTiers(
|
|
91
|
+
asks: readonly number[],
|
|
92
|
+
official: number | null,
|
|
93
|
+
): readonly Tier[] {
|
|
94
|
+
const counts = new Map<number, number>();
|
|
95
|
+
for (const ask of asks) {
|
|
96
|
+
if (!Number.isFinite(ask)) continue;
|
|
97
|
+
const key = Math.round(ask * 10 ** PRICE_PRECISION) / 10 ** PRICE_PRECISION;
|
|
98
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
99
|
+
}
|
|
100
|
+
return [...counts.entries()]
|
|
101
|
+
.sort((a, b) => a[0] - b[0])
|
|
102
|
+
.map(([price, avail]) => ({ price, avail, discountPct: discountPct(price, official) }));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Reads `[price, providerCount]` tuples from the catalog.
|
|
107
|
+
*
|
|
108
|
+
* InferHub pre-aggregates tiers, so this is a read rather than a bucket-and-
|
|
109
|
+
* count: one tuple is one distinct price and how many live providers offer it.
|
|
110
|
+
* Money is numeric here even though `officialIn`/`officialOut` are decimal
|
|
111
|
+
* strings.
|
|
112
|
+
*
|
|
113
|
+
* Malformed tuples are dropped rather than defaulted — a tier with an unknown
|
|
114
|
+
* provider count would make `providersLive` wrong, and a wrong availability
|
|
115
|
+
* number is worse than an absent one.
|
|
116
|
+
*/
|
|
117
|
+
export function pricePointsToTiers(
|
|
118
|
+
points: unknown,
|
|
119
|
+
official: number | null,
|
|
120
|
+
): readonly Tier[] {
|
|
121
|
+
if (!Array.isArray(points)) return [];
|
|
122
|
+
const seen = new Map<number, Tier>();
|
|
123
|
+
for (const point of points) {
|
|
124
|
+
if (!Array.isArray(point) || point.length < 2) continue;
|
|
125
|
+
const price = num(point[0]);
|
|
126
|
+
const avail = num(point[1]);
|
|
127
|
+
if (price === null || avail === null) continue;
|
|
128
|
+
const count = Math.max(0, Math.trunc(avail));
|
|
129
|
+
const key = Math.round(price * 10 ** PRICE_PRECISION) / 10 ** PRICE_PRECISION;
|
|
130
|
+
const existing = seen.get(key);
|
|
131
|
+
if (existing !== undefined) {
|
|
132
|
+
// Same price from two tuples: capacities add. `Tier` is readonly, so the
|
|
133
|
+
// merged tier is a new object rather than a mutation.
|
|
134
|
+
seen.set(key, {
|
|
135
|
+
price: existing.price,
|
|
136
|
+
avail: existing.avail + count,
|
|
137
|
+
discountPct: existing.discountPct,
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
seen.set(key, { price, avail: count, discountPct: discountPct(price, official) });
|
|
142
|
+
}
|
|
143
|
+
return [...seen.values()].sort((a, b) => a.price - b.price);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Cheapest price with at least `minAvail` live providers behind it. */
|
|
147
|
+
export function capacityWeightedPrice(
|
|
148
|
+
tiers: readonly Tier[],
|
|
149
|
+
minAvail: number = DEFAULT_MIN_CAPACITY,
|
|
150
|
+
): number | null {
|
|
151
|
+
const sustainable = tiers.find((tier) => tier.avail >= minAvail);
|
|
152
|
+
return sustainable?.price ?? tiers[0]?.price ?? null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Cross-prefix comparison key.
|
|
157
|
+
*
|
|
158
|
+
* The same model is sold under different ids per upstream (`cb/claude-opus-4.6`
|
|
159
|
+
* vs `cc/claude-opus-4-6`), so comparison needs a normalized form: the last
|
|
160
|
+
* path segment, lowercased, dots folded to dashes.
|
|
161
|
+
*/
|
|
162
|
+
export function aliasKeyOf(modelId: string): string {
|
|
163
|
+
const segment = modelId.includes("/") ? (modelId.split("/").pop() ?? modelId) : modelId;
|
|
164
|
+
const stripped = segment.replace(/\s*\(Kiro\)/gi, "").trim().toLowerCase();
|
|
165
|
+
const folded = stripped.replace(/\./g, "-");
|
|
166
|
+
return MANUAL_ALIASES[folded] ?? folded;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Known id pairs the mechanical fold does not unify.
|
|
171
|
+
*
|
|
172
|
+
* InferHub publishes its own aliases at `/api/budgets/aliases` and
|
|
173
|
+
* `/api/keys/allowlist-options`; prefer those once verified against the live
|
|
174
|
+
* API, and only then remove this map.
|
|
175
|
+
*/
|
|
176
|
+
const MANUAL_ALIASES: Readonly<Record<string, string>> = {
|
|
177
|
+
"kimi-k2-7": "kimi-k2-7-code",
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/** Splits `prefix/model`, tolerating a bare id. */
|
|
181
|
+
export function splitId(id: string): { prefix: string; modelId: string } {
|
|
182
|
+
const separator = id.indexOf("/");
|
|
183
|
+
if (separator === -1) return { prefix: "", modelId: id };
|
|
184
|
+
return { prefix: id.slice(0, separator), modelId: id.slice(separator + 1) };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function familyStateOf(value: unknown): FamilyState | null {
|
|
188
|
+
const raw = str(value);
|
|
189
|
+
const state = raw === null ? null : raw.toLowerCase();
|
|
190
|
+
return state !== null && (FAMILY_STATES as readonly string[]).includes(state)
|
|
191
|
+
? (state as FamilyState)
|
|
192
|
+
: null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Normalizes one catalog model into the market shape.
|
|
197
|
+
* Returns null when the row has no usable id — an unnamed row cannot be routed
|
|
198
|
+
* to or compared, and inventing one would put a phantom in the catalog.
|
|
199
|
+
*/
|
|
200
|
+
export function normalizeCatalogModel(
|
|
201
|
+
raw: RawCatalogModel,
|
|
202
|
+
upstream: Pick<RawUpstreamCard, "prefix" | "label" | "systemPromptNote">,
|
|
203
|
+
): ModelMarket | null {
|
|
204
|
+
// `id` is a UUID and `upstreamModelId` is the routable slug
|
|
205
|
+
// (`claude-haiku-4-5`). Only the slug is usable as a route id, so a row with
|
|
206
|
+
// no slug is dropped rather than given a UUID that cannot be requested.
|
|
207
|
+
const modelSlug = str(raw.upstreamModelId);
|
|
208
|
+
const prefix = str(upstream.prefix) ?? "";
|
|
209
|
+
if (modelSlug === null || prefix.length === 0) return null;
|
|
210
|
+
const id = `${prefix}/${modelSlug}`;
|
|
211
|
+
const officialIn = num(raw.officialIn);
|
|
212
|
+
const officialOut = num(raw.officialOut);
|
|
213
|
+
const tiersIn = pricePointsToTiers(raw.pricePointsIn, officialIn);
|
|
214
|
+
const tiersOut = pricePointsToTiers(raw.pricePointsOut, officialOut);
|
|
215
|
+
// Live providers is the capacity behind the cheapest tier, not a row count:
|
|
216
|
+
// InferHub reports it per price, so the largest tier is the true ceiling.
|
|
217
|
+
const providersLive = Math.max(
|
|
218
|
+
0,
|
|
219
|
+
...tiersIn.map((tier) => tier.avail),
|
|
220
|
+
...tiersOut.map((tier) => tier.avail),
|
|
221
|
+
);
|
|
222
|
+
const outputModality = str(raw.outputModality);
|
|
223
|
+
return {
|
|
224
|
+
id,
|
|
225
|
+
prefix,
|
|
226
|
+
family: str(upstream.label) ?? prefix,
|
|
227
|
+
modelId: modelSlug,
|
|
228
|
+
aliasKey: aliasKeyOf(id),
|
|
229
|
+
officialIn,
|
|
230
|
+
officialOut,
|
|
231
|
+
tiersIn,
|
|
232
|
+
tiersOut,
|
|
233
|
+
minAskIn: tiersIn[0]?.price ?? null,
|
|
234
|
+
minAskOut: tiersOut[0]?.price ?? null,
|
|
235
|
+
maxAskIn: tiersIn.at(-1)?.price ?? null,
|
|
236
|
+
maxAskOut: tiersOut.at(-1)?.price ?? null,
|
|
237
|
+
providersLive,
|
|
238
|
+
// Not carried by the catalog; merged from `/api/market` by the store.
|
|
239
|
+
lastRate: null,
|
|
240
|
+
cacheHitRate: num(raw.cacheHitRate),
|
|
241
|
+
health: {
|
|
242
|
+
successRatePct: null,
|
|
243
|
+
avgTtftMs: null,
|
|
244
|
+
avgTps: null,
|
|
245
|
+
familyState: null,
|
|
246
|
+
},
|
|
247
|
+
capabilities: {
|
|
248
|
+
contextWindow: num(raw.contextWindow),
|
|
249
|
+
maxOutputTokens: num(raw.maxOutputTokens),
|
|
250
|
+
modalities: stringArray(raw.modalities),
|
|
251
|
+
outputModality:
|
|
252
|
+
outputModality === "image" || outputModality === "video" ? outputModality : "text",
|
|
253
|
+
reasoningLevels: stringArray(raw.reasoningLevels),
|
|
254
|
+
supportsCache: bool(raw.supportsCache),
|
|
255
|
+
},
|
|
256
|
+
systemPromptNote: str(upstream.systemPromptNote),
|
|
257
|
+
// A model with no live provider is never routable; health refines this.
|
|
258
|
+
routable: providersLive > 0,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Merges `/api/market` numbers (lastRate, min/max asks) into catalog-derived models. */
|
|
263
|
+
export function mergeMarketPrices(
|
|
264
|
+
models: readonly ModelMarket[],
|
|
265
|
+
market: readonly RawMarketModel[],
|
|
266
|
+
): readonly ModelMarket[] {
|
|
267
|
+
const byId = new Map<string, RawMarketModel>();
|
|
268
|
+
for (const entry of market) {
|
|
269
|
+
const slug = str(entry.slug);
|
|
270
|
+
if (slug !== null) byId.set(slug, entry);
|
|
271
|
+
}
|
|
272
|
+
return models.map((model) => {
|
|
273
|
+
const entry = byId.get(model.id);
|
|
274
|
+
if (entry === undefined) return model;
|
|
275
|
+
return {
|
|
276
|
+
...model,
|
|
277
|
+
lastRate: num(entry.lastRate),
|
|
278
|
+
// Market is the cheaper source for the extremes, but the catalog's tier
|
|
279
|
+
// grouping is what gives them capacity, so only the nulls are filled.
|
|
280
|
+
minAskIn: model.minAskIn ?? num(entry.minAskIn),
|
|
281
|
+
minAskOut: model.minAskOut ?? num(entry.minAskOut),
|
|
282
|
+
maxAskIn: model.maxAskIn ?? num(entry.maxAskIn),
|
|
283
|
+
maxAskOut: model.maxAskOut ?? num(entry.maxAskOut),
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Merges `/api/status` per-model health into catalog-derived models. */
|
|
289
|
+
export function mergeModelHealth(
|
|
290
|
+
models: readonly ModelMarket[],
|
|
291
|
+
status: readonly RawStatusModel[],
|
|
292
|
+
): readonly ModelMarket[] {
|
|
293
|
+
const byId = new Map<string, RawStatusModel>();
|
|
294
|
+
for (const entry of status) {
|
|
295
|
+
// `/api/status` keys per-model health by `id` (`ag/claude-opus-4-6-thinking`),
|
|
296
|
+
// not `slug`. Reading `slug` matched nothing, so every model reported a null
|
|
297
|
+
// success rate — which the UI renders as "unknown" and sorts last, making
|
|
298
|
+
// the quality ranking useless. `slug` is kept as a fallback.
|
|
299
|
+
const slug = str(entry.id) ?? str(entry.slug);
|
|
300
|
+
if (slug !== null) byId.set(slug, entry);
|
|
301
|
+
}
|
|
302
|
+
return models.map((model) => {
|
|
303
|
+
const entry = byId.get(model.id);
|
|
304
|
+
if (entry === undefined) return model;
|
|
305
|
+
return {
|
|
306
|
+
...model,
|
|
307
|
+
health: {
|
|
308
|
+
...model.health,
|
|
309
|
+
successRatePct: num(entry.successRatePct),
|
|
310
|
+
avgTtftMs: num(entry.avgTtftMs),
|
|
311
|
+
avgTps: num(entry.avgTps),
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Applies family state and recomputes routability.
|
|
319
|
+
*
|
|
320
|
+
* A null success rate is unknown, not bad — only a *reported* rate below the
|
|
321
|
+
* threshold, a `major_outage` family, or zero live providers makes a model
|
|
322
|
+
* unroutable.
|
|
323
|
+
*/
|
|
324
|
+
export function applyFamilyHealth(
|
|
325
|
+
models: readonly ModelMarket[],
|
|
326
|
+
families: readonly RawStatusFamily[],
|
|
327
|
+
minSuccessRatePct: number,
|
|
328
|
+
): readonly ModelMarket[] {
|
|
329
|
+
const byPrefix = new Map<string, FamilyState | null>();
|
|
330
|
+
for (const family of families) {
|
|
331
|
+
const prefix = str(family.prefix) ?? str(family.slug);
|
|
332
|
+
if (prefix !== null) byPrefix.set(prefix, familyStateOf(family.state));
|
|
333
|
+
}
|
|
334
|
+
return models.map((model) => {
|
|
335
|
+
const familyState = byPrefix.get(model.prefix) ?? null;
|
|
336
|
+
const successRate = model.health.successRatePct;
|
|
337
|
+
const healthyEnough = successRate === null || successRate >= minSuccessRatePct;
|
|
338
|
+
// `undefined` (family absent from the status feed entirely) must not be
|
|
339
|
+
// read as "healthy" — an unknown family is not a confirmed-good one.
|
|
340
|
+
const familyUsable = familyState === null || familyState !== "major_outage";
|
|
341
|
+
return {
|
|
342
|
+
...model,
|
|
343
|
+
health: { ...model.health, familyState },
|
|
344
|
+
routable: model.providersLive > 0 && healthyEnough && familyUsable,
|
|
345
|
+
};
|
|
346
|
+
});
|
|
347
|
+
}
|