@srouterhq/server 0.0.0-stage → 0.9.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/LICENSE +21 -0
- package/client/dist/assets/am-TQ7Jmdqe.js +1 -0
- package/client/dist/assets/ar-Bf5a7yfC.js +1 -0
- package/client/dist/assets/az-CrosE1xH.js +1 -0
- package/client/dist/assets/bg-BvWnmcUz.js +1 -0
- package/client/dist/assets/bn-ams5TsJp.js +1 -0
- package/client/dist/assets/cs-Dh67CaJ_.js +1 -0
- package/client/dist/assets/da-9WlT8Iq7.js +1 -0
- package/client/dist/assets/de-lWhJJzz7.js +1 -0
- package/client/dist/assets/el-Cu7_GhNq.js +1 -0
- package/client/dist/assets/es-BZuzsBcP.js +1 -0
- package/client/dist/assets/fa-Dwdn-jKS.js +1 -0
- package/client/dist/assets/fi-BFrFyOTy.js +1 -0
- package/client/dist/assets/fr-XhbtmPpj.js +1 -0
- package/client/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/client/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/client/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/client/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/client/dist/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
- package/client/dist/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
- package/client/dist/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
- package/client/dist/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
- package/client/dist/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
- package/client/dist/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
- package/client/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/client/dist/assets/gu-D0uhLUB8.js +1 -0
- package/client/dist/assets/ha-CVlm9pqp.js +1 -0
- package/client/dist/assets/he-CMIFeoWr.js +1 -0
- package/client/dist/assets/hi-ENTwhBpn.js +1 -0
- package/client/dist/assets/hr-fc03vMy_.js +1 -0
- package/client/dist/assets/hu-YBo2BIYt.js +1 -0
- package/client/dist/assets/id-BEWXYX69.js +1 -0
- package/client/dist/assets/ig-BKLZFKka.js +1 -0
- package/client/dist/assets/index-D9I4KtvR.js +145 -0
- package/client/dist/assets/index-txhEmCdb.css +2 -0
- package/client/dist/assets/it-Bzmei1Y2.js +1 -0
- package/client/dist/assets/ja-BfcpuqEl.js +1 -0
- package/client/dist/assets/ka-CnWjRgLo.js +1 -0
- package/client/dist/assets/km-BAjPR3Qj.js +1 -0
- package/client/dist/assets/kn-B15WKU5m.js +1 -0
- package/client/dist/assets/ko-C61fAB-w.js +1 -0
- package/client/dist/assets/lt-CYTr4_YW.js +1 -0
- package/client/dist/assets/ml-CtqxgyH3.js +1 -0
- package/client/dist/assets/mr-X5TMY7aR.js +1 -0
- package/client/dist/assets/ms-DSg8RqPS.js +1 -0
- package/client/dist/assets/my-D7RqR8uE.js +1 -0
- package/client/dist/assets/ne-DaflxkDj.js +1 -0
- package/client/dist/assets/nl-BOqqtsC8.js +1 -0
- package/client/dist/assets/no-BSHp_StB.js +1 -0
- package/client/dist/assets/or-BkLR9Vub.js +1 -0
- package/client/dist/assets/pa-CarJ2XgI.js +1 -0
- package/client/dist/assets/pl-C2n0V-0k.js +1 -0
- package/client/dist/assets/pt-BR-CqJdNhg2.js +1 -0
- package/client/dist/assets/pt-PT-C_HpMyKz.js +1 -0
- package/client/dist/assets/ro-DVRZqSAS.js +1 -0
- package/client/dist/assets/ru-DwAJG7VC.js +1 -0
- package/client/dist/assets/si-BxUezXm0.js +1 -0
- package/client/dist/assets/sk-eI1VcIfY.js +1 -0
- package/client/dist/assets/sr-CJF3auCr.js +1 -0
- package/client/dist/assets/srouter-logo-C6ZfjGIi.svg +14 -0
- package/client/dist/assets/sv-nqvwHAq_.js +1 -0
- package/client/dist/assets/sw-DWynE2pW.js +1 -0
- package/client/dist/assets/ta-BVM12knC.js +1 -0
- package/client/dist/assets/te-oyzEkF7_.js +1 -0
- package/client/dist/assets/th-COVeE0ZQ.js +1 -0
- package/client/dist/assets/tl-DsJANnQz.js +1 -0
- package/client/dist/assets/tr-B-Fl-urO.js +1 -0
- package/client/dist/assets/uk-C5qketAO.js +1 -0
- package/client/dist/assets/ur-QU9KugfV.js +1 -0
- package/client/dist/assets/uz-DL9cG_mY.js +1 -0
- package/client/dist/assets/vi-CQiVy3Pu.js +1 -0
- package/client/dist/assets/yo-DqazMdrZ.js +1 -0
- package/client/dist/assets/zh-CN-DnYL_Kio.js +1 -0
- package/client/dist/assets/zh-TW-5bdbosKj.js +1 -0
- package/client/dist/favicon.svg +14 -0
- package/client/dist/icons.svg +24 -0
- package/client/dist/index.html +37 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +372 -0
- package/dist/app.js.map +1 -0
- package/dist/db/index.d.ts +49 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +203 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate/TEMPLATE.d.ts +4 -0
- package/dist/db/migrate/TEMPLATE.d.ts.map +1 -0
- package/dist/db/migrate/TEMPLATE.js +18 -0
- package/dist/db/migrate/TEMPLATE.js.map +1 -0
- package/dist/db/migrate/cli.d.ts +2 -0
- package/dist/db/migrate/cli.d.ts.map +1 -0
- package/dist/db/migrate/cli.js +177 -0
- package/dist/db/migrate/cli.js.map +1 -0
- package/dist/db/migrate/defaults.d.ts +52 -0
- package/dist/db/migrate/defaults.d.ts.map +1 -0
- package/dist/db/migrate/defaults.js +126 -0
- package/dist/db/migrate/defaults.js.map +1 -0
- package/dist/db/migrate/runner.d.ts +16 -0
- package/dist/db/migrate/runner.d.ts.map +1 -0
- package/dist/db/migrate/runner.js +178 -0
- package/dist/db/migrate/runner.js.map +1 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts +4 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts.map +1 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.js +2240 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.js.map +1 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts +4 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts.map +1 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.js +27 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.js.map +1 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts +4 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts.map +1 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.js +30 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.js.map +1 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.d.ts +6 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.d.ts.map +1 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.js +104 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.js.map +1 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts +9 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts.map +1 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.js +22 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.js.map +1 -0
- package/dist/db/migrations/20260706_000001_request_client_info.d.ts +4 -0
- package/dist/db/migrations/20260706_000001_request_client_info.d.ts.map +1 -0
- package/dist/db/migrations/20260706_000001_request_client_info.js +30 -0
- package/dist/db/migrations/20260706_000001_request_client_info.js.map +1 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts +17 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts.map +1 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.js +20 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.js.map +1 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts +12 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts.map +1 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.js +45 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.js.map +1 -0
- package/dist/db/migrations/20260720_000001_key_health_error.d.ts +5 -0
- package/dist/db/migrations/20260720_000001_key_health_error.d.ts.map +1 -0
- package/dist/db/migrations/20260720_000001_key_health_error.js +16 -0
- package/dist/db/migrations/20260720_000001_key_health_error.js.map +1 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts +4 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js +38 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js.map +1 -0
- package/dist/db/migrations/20260726_000002_request_attempts.d.ts +4 -0
- package/dist/db/migrations/20260726_000002_request_attempts.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000002_request_attempts.js +43 -0
- package/dist/db/migrations/20260726_000002_request_attempts.js.map +1 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts +4 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.js +80 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.js.map +1 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.d.ts +4 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.js +34 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.js.map +1 -0
- package/dist/db/migrations/20260726_000005_request_served_model.d.ts +4 -0
- package/dist/db/migrations/20260726_000005_request_served_model.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000005_request_served_model.js +31 -0
- package/dist/db/migrations/20260726_000005_request_served_model.js.map +1 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts +4 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.js +29 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.js.map +1 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts +4 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts.map +1 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.js +41 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.js.map +1 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts +12 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts.map +1 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.js +37 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.js.map +1 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts +4 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts.map +1 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js +145 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js.map +1 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts +6 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts.map +1 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js +52 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js.map +1 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.d.ts +6 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.d.ts.map +1 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.js +17 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.js.map +1 -0
- package/dist/db/migrations/20260805_000002_client_profiles.d.ts +4 -0
- package/dist/db/migrations/20260805_000002_client_profiles.d.ts.map +1 -0
- package/dist/db/migrations/20260805_000002_client_profiles.js +33 -0
- package/dist/db/migrations/20260805_000002_client_profiles.js.map +1 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts +4 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts.map +1 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.js +33 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.js.map +1 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts +4 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts.map +1 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.js +19 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.js.map +1 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.d.ts +4 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.d.ts.map +1 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.js +47 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.js.map +1 -0
- package/dist/db/migrations/20260823_000001_server_logs.d.ts +4 -0
- package/dist/db/migrations/20260823_000001_server_logs.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000001_server_logs.js +53 -0
- package/dist/db/migrations/20260823_000001_server_logs.js.map +1 -0
- package/dist/db/migrations/20260823_000002_backups_table.d.ts +8 -0
- package/dist/db/migrations/20260823_000002_backups_table.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000002_backups_table.js +22 -0
- package/dist/db/migrations/20260823_000002_backups_table.js.map +1 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts +19 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.js +28 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.js.map +1 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts +14 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.js +25 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.js.map +1 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts +4 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts.map +1 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.js +46 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.js.map +1 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts +4 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts.map +1 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.js +37 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.js.map +1 -0
- package/dist/db/migrations/20260901_000003_request_caller.d.ts +4 -0
- package/dist/db/migrations/20260901_000003_request_caller.d.ts.map +1 -0
- package/dist/db/migrations/20260901_000003_request_caller.js +27 -0
- package/dist/db/migrations/20260901_000003_request_caller.js.map +1 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts +4 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts.map +1 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js +35 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js.map +1 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts +4 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts.map +1 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.js +37 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.js.map +1 -0
- package/dist/db/migrations/20260903_000002_response_cache.d.ts +4 -0
- package/dist/db/migrations/20260903_000002_response_cache.d.ts.map +1 -0
- package/dist/db/migrations/20260903_000002_response_cache.js +53 -0
- package/dist/db/migrations/20260903_000002_response_cache.js.map +1 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts +13 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts.map +1 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.js +35 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.js.map +1 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts +4 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts.map +1 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.js +68 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.js.map +1 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts +5 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts.map +1 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.js +37 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.js.map +1 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts +4 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts.map +1 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js +28 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js.map +1 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts +4 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts.map +1 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js +25 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js.map +1 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.d.ts +10 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.d.ts.map +1 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.js +78 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.js.map +1 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts +21 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts.map +1 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.js +41 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.js.map +1 -0
- package/dist/db/model-pricing.d.ts +39 -0
- package/dist/db/model-pricing.d.ts.map +1 -0
- package/dist/db/model-pricing.js +274 -0
- package/dist/db/model-pricing.js.map +1 -0
- package/dist/db/node-sqlite.d.ts +9 -0
- package/dist/db/node-sqlite.d.ts.map +1 -0
- package/dist/db/node-sqlite.js +87 -0
- package/dist/db/node-sqlite.js.map +1 -0
- package/dist/db/types.d.ts +22 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/docs/docs-page.d.ts +2 -0
- package/dist/docs/docs-page.d.ts.map +1 -0
- package/dist/docs/docs-page.js +319 -0
- package/dist/docs/docs-page.js.map +1 -0
- package/dist/docs/openapi.d.ts +1896 -0
- package/dist/docs/openapi.d.ts.map +1 -0
- package/dist/docs/openapi.js +1096 -0
- package/dist/docs/openapi.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +9 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +144 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/anthropic-documents.d.ts +41 -0
- package/dist/lib/anthropic-documents.d.ts.map +1 -0
- package/dist/lib/anthropic-documents.js +132 -0
- package/dist/lib/anthropic-documents.js.map +1 -0
- package/dist/lib/app-version.d.ts +5 -0
- package/dist/lib/app-version.d.ts.map +1 -0
- package/dist/lib/app-version.js +61 -0
- package/dist/lib/app-version.js.map +1 -0
- package/dist/lib/attempt-trace.d.ts +23 -0
- package/dist/lib/attempt-trace.d.ts.map +1 -0
- package/dist/lib/attempt-trace.js +32 -0
- package/dist/lib/attempt-trace.js.map +1 -0
- package/dist/lib/budget.d.ts +6 -0
- package/dist/lib/budget.d.ts.map +1 -0
- package/dist/lib/budget.js +40 -0
- package/dist/lib/budget.js.map +1 -0
- package/dist/lib/client-classifier.d.ts +10 -0
- package/dist/lib/client-classifier.d.ts.map +1 -0
- package/dist/lib/client-classifier.js +126 -0
- package/dist/lib/client-classifier.js.map +1 -0
- package/dist/lib/client-context.d.ts +10 -0
- package/dist/lib/client-context.d.ts.map +1 -0
- package/dist/lib/client-context.js +39 -0
- package/dist/lib/client-context.js.map +1 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +94 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/content.d.ts +33 -0
- package/dist/lib/content.d.ts.map +1 -0
- package/dist/lib/content.js +201 -0
- package/dist/lib/content.js.map +1 -0
- package/dist/lib/credential.d.ts +11 -0
- package/dist/lib/credential.d.ts.map +1 -0
- package/dist/lib/credential.js +18 -0
- package/dist/lib/credential.js.map +1 -0
- package/dist/lib/crypto.d.ts +31 -0
- package/dist/lib/crypto.d.ts.map +1 -0
- package/dist/lib/crypto.js +206 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/custom-provider-cleanup.d.ts +13 -0
- package/dist/lib/custom-provider-cleanup.d.ts.map +1 -0
- package/dist/lib/custom-provider-cleanup.js +43 -0
- package/dist/lib/custom-provider-cleanup.js.map +1 -0
- package/dist/lib/db-backup.d.ts +31 -0
- package/dist/lib/db-backup.d.ts.map +1 -0
- package/dist/lib/db-backup.js +255 -0
- package/dist/lib/db-backup.js.map +1 -0
- package/dist/lib/endpoint-scope.d.ts +42 -0
- package/dist/lib/endpoint-scope.d.ts.map +1 -0
- package/dist/lib/endpoint-scope.js +95 -0
- package/dist/lib/endpoint-scope.js.map +1 -0
- package/dist/lib/env-drift.d.ts +17 -0
- package/dist/lib/env-drift.d.ts.map +1 -0
- package/dist/lib/env-drift.js +108 -0
- package/dist/lib/env-drift.js.map +1 -0
- package/dist/lib/error-classify.d.ts +40 -0
- package/dist/lib/error-classify.d.ts.map +1 -0
- package/dist/lib/error-classify.js +687 -0
- package/dist/lib/error-classify.js.map +1 -0
- package/dist/lib/error-redaction.d.ts +8 -0
- package/dist/lib/error-redaction.d.ts.map +1 -0
- package/dist/lib/error-redaction.js +45 -0
- package/dist/lib/error-redaction.js.map +1 -0
- package/dist/lib/fallback-loop.d.ts +306 -0
- package/dist/lib/fallback-loop.d.ts.map +1 -0
- package/dist/lib/fallback-loop.js +1336 -0
- package/dist/lib/fallback-loop.js.map +1 -0
- package/dist/lib/file-permissions.d.ts +98 -0
- package/dist/lib/file-permissions.d.ts.map +1 -0
- package/dist/lib/file-permissions.js +159 -0
- package/dist/lib/file-permissions.js.map +1 -0
- package/dist/lib/gemini-wire.d.ts +76 -0
- package/dist/lib/gemini-wire.d.ts.map +1 -0
- package/dist/lib/gemini-wire.js +392 -0
- package/dist/lib/gemini-wire.js.map +1 -0
- package/dist/lib/guardrails.d.ts +37 -0
- package/dist/lib/guardrails.d.ts.map +1 -0
- package/dist/lib/guardrails.js +106 -0
- package/dist/lib/guardrails.js.map +1 -0
- package/dist/lib/header-value.d.ts +8 -0
- package/dist/lib/header-value.d.ts.map +1 -0
- package/dist/lib/header-value.js +55 -0
- package/dist/lib/header-value.js.map +1 -0
- package/dist/lib/image-normalize.d.ts +21 -0
- package/dist/lib/image-normalize.d.ts.map +1 -0
- package/dist/lib/image-normalize.js +218 -0
- package/dist/lib/image-normalize.js.map +1 -0
- package/dist/lib/inbound-chat.d.ts +48 -0
- package/dist/lib/inbound-chat.d.ts.map +1 -0
- package/dist/lib/inbound-chat.js +406 -0
- package/dist/lib/inbound-chat.js.map +1 -0
- package/dist/lib/key-parser.d.ts +79 -0
- package/dist/lib/key-parser.d.ts.map +1 -0
- package/dist/lib/key-parser.js +701 -0
- package/dist/lib/key-parser.js.map +1 -0
- package/dist/lib/key-proxy.d.ts +49 -0
- package/dist/lib/key-proxy.d.ts.map +1 -0
- package/dist/lib/key-proxy.js +92 -0
- package/dist/lib/key-proxy.js.map +1 -0
- package/dist/lib/log-redaction.d.ts +28 -0
- package/dist/lib/log-redaction.d.ts.map +1 -0
- package/dist/lib/log-redaction.js +166 -0
- package/dist/lib/log-redaction.js.map +1 -0
- package/dist/lib/model-scope.d.ts +9 -0
- package/dist/lib/model-scope.d.ts.map +1 -0
- package/dist/lib/model-scope.js +29 -0
- package/dist/lib/model-scope.js.map +1 -0
- package/dist/lib/one-time-code.d.ts +15 -0
- package/dist/lib/one-time-code.d.ts.map +1 -0
- package/dist/lib/one-time-code.js +41 -0
- package/dist/lib/one-time-code.js.map +1 -0
- package/dist/lib/output-cap.d.ts +24 -0
- package/dist/lib/output-cap.d.ts.map +1 -0
- package/dist/lib/output-cap.js +80 -0
- package/dist/lib/output-cap.js.map +1 -0
- package/dist/lib/password.d.ts +3 -0
- package/dist/lib/password.d.ts.map +1 -0
- package/dist/lib/password.js +27 -0
- package/dist/lib/password.js.map +1 -0
- package/dist/lib/process-safety-net.d.ts +32 -0
- package/dist/lib/process-safety-net.d.ts.map +1 -0
- package/dist/lib/process-safety-net.js +123 -0
- package/dist/lib/process-safety-net.js.map +1 -0
- package/dist/lib/provider-identity.d.ts +37 -0
- package/dist/lib/provider-identity.d.ts.map +1 -0
- package/dist/lib/provider-identity.js +110 -0
- package/dist/lib/provider-identity.js.map +1 -0
- package/dist/lib/provider-size-parser.d.ts +6 -0
- package/dist/lib/provider-size-parser.d.ts.map +1 -0
- package/dist/lib/provider-size-parser.js +72 -0
- package/dist/lib/provider-size-parser.js.map +1 -0
- package/dist/lib/provider-timeout.d.ts +17 -0
- package/dist/lib/provider-timeout.d.ts.map +1 -0
- package/dist/lib/provider-timeout.js +70 -0
- package/dist/lib/provider-timeout.js.map +1 -0
- package/dist/lib/proxy.d.ts +180 -0
- package/dist/lib/proxy.d.ts.map +1 -0
- package/dist/lib/proxy.js +1002 -0
- package/dist/lib/proxy.js.map +1 -0
- package/dist/lib/request-log.d.ts +4 -0
- package/dist/lib/request-log.d.ts.map +1 -0
- package/dist/lib/request-log.js +161 -0
- package/dist/lib/request-log.js.map +1 -0
- package/dist/lib/reset-code.d.ts +5 -0
- package/dist/lib/reset-code.d.ts.map +1 -0
- package/dist/lib/reset-code.js +39 -0
- package/dist/lib/reset-code.js.map +1 -0
- package/dist/lib/retry-hint.d.ts +14 -0
- package/dist/lib/retry-hint.d.ts.map +1 -0
- package/dist/lib/retry-hint.js +36 -0
- package/dist/lib/retry-hint.js.map +1 -0
- package/dist/lib/route-live.d.ts +29 -0
- package/dist/lib/route-live.d.ts.map +1 -0
- package/dist/lib/route-live.js +97 -0
- package/dist/lib/route-live.js.map +1 -0
- package/dist/lib/sampling-params.d.ts +183 -0
- package/dist/lib/sampling-params.d.ts.map +1 -0
- package/dist/lib/sampling-params.js +386 -0
- package/dist/lib/sampling-params.js.map +1 -0
- package/dist/lib/scheduler.d.ts +13 -0
- package/dist/lib/scheduler.d.ts.map +1 -0
- package/dist/lib/scheduler.js +11 -0
- package/dist/lib/scheduler.js.map +1 -0
- package/dist/lib/served-model.d.ts +16 -0
- package/dist/lib/served-model.d.ts.map +1 -0
- package/dist/lib/served-model.js +0 -0
- package/dist/lib/served-model.js.map +1 -0
- package/dist/lib/server-logs.d.ts +132 -0
- package/dist/lib/server-logs.d.ts.map +1 -0
- package/dist/lib/server-logs.js +473 -0
- package/dist/lib/server-logs.js.map +1 -0
- package/dist/lib/setup-code.d.ts +5 -0
- package/dist/lib/setup-code.d.ts.map +1 -0
- package/dist/lib/setup-code.js +35 -0
- package/dist/lib/setup-code.js.map +1 -0
- package/dist/lib/structured-output.d.ts +9 -0
- package/dist/lib/structured-output.d.ts.map +1 -0
- package/dist/lib/structured-output.js +72 -0
- package/dist/lib/structured-output.js.map +1 -0
- package/dist/lib/system-prompt.d.ts +30 -0
- package/dist/lib/system-prompt.d.ts.map +1 -0
- package/dist/lib/system-prompt.js +66 -0
- package/dist/lib/system-prompt.js.map +1 -0
- package/dist/lib/task-type.d.ts +18 -0
- package/dist/lib/task-type.d.ts.map +1 -0
- package/dist/lib/task-type.js +92 -0
- package/dist/lib/task-type.js.map +1 -0
- package/dist/lib/think-tags.d.ts +37 -0
- package/dist/lib/think-tags.d.ts.map +1 -0
- package/dist/lib/think-tags.js +203 -0
- package/dist/lib/think-tags.js.map +1 -0
- package/dist/lib/tool-args.d.ts +36 -0
- package/dist/lib/tool-args.d.ts.map +1 -0
- package/dist/lib/tool-args.js +190 -0
- package/dist/lib/tool-args.js.map +1 -0
- package/dist/lib/tool-call-rescue.d.ts +62 -0
- package/dist/lib/tool-call-rescue.d.ts.map +1 -0
- package/dist/lib/tool-call-rescue.js +258 -0
- package/dist/lib/tool-call-rescue.js.map +1 -0
- package/dist/lib/tool-capability.d.ts +13 -0
- package/dist/lib/tool-capability.d.ts.map +1 -0
- package/dist/lib/tool-capability.js +70 -0
- package/dist/lib/tool-capability.js.map +1 -0
- package/dist/lib/tool-validate.d.ts +44 -0
- package/dist/lib/tool-validate.d.ts.map +1 -0
- package/dist/lib/tool-validate.js +165 -0
- package/dist/lib/tool-validate.js.map +1 -0
- package/dist/lib/ttfb-budget.d.ts +13 -0
- package/dist/lib/ttfb-budget.d.ts.map +1 -0
- package/dist/lib/ttfb-budget.js +153 -0
- package/dist/lib/ttfb-budget.js.map +1 -0
- package/dist/lib/url-guard.d.ts +47 -0
- package/dist/lib/url-guard.d.ts.map +1 -0
- package/dist/lib/url-guard.js +229 -0
- package/dist/lib/url-guard.js.map +1 -0
- package/dist/lib/wake-detect.d.ts +12 -0
- package/dist/lib/wake-detect.d.ts.map +1 -0
- package/dist/lib/wake-detect.js +99 -0
- package/dist/lib/wake-detect.js.map +1 -0
- package/dist/middleware/errorHandler.d.ts +3 -0
- package/dist/middleware/errorHandler.d.ts.map +1 -0
- package/dist/middleware/errorHandler.js +45 -0
- package/dist/middleware/errorHandler.js.map +1 -0
- package/dist/middleware/rateLimit.d.ts +4 -0
- package/dist/middleware/rateLimit.d.ts.map +1 -0
- package/dist/middleware/rateLimit.js +125 -0
- package/dist/middleware/rateLimit.js.map +1 -0
- package/dist/middleware/requireAuth.d.ts +3 -0
- package/dist/middleware/requireAuth.d.ts.map +1 -0
- package/dist/middleware/requireAuth.js +17 -0
- package/dist/middleware/requireAuth.js.map +1 -0
- package/dist/providers/aclide.d.ts +18 -0
- package/dist/providers/aclide.d.ts.map +1 -0
- package/dist/providers/aclide.js +174 -0
- package/dist/providers/aclide.js.map +1 -0
- package/dist/providers/aihorde.d.ts +42 -0
- package/dist/providers/aihorde.d.ts.map +1 -0
- package/dist/providers/aihorde.js +192 -0
- package/dist/providers/aihorde.js.map +1 -0
- package/dist/providers/airforce.d.ts +11 -0
- package/dist/providers/airforce.d.ts.map +1 -0
- package/dist/providers/airforce.js +32 -0
- package/dist/providers/airforce.js.map +1 -0
- package/dist/providers/base.d.ts +177 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +354 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/blaze.d.ts +11 -0
- package/dist/providers/blaze.d.ts.map +1 -0
- package/dist/providers/blaze.js +33 -0
- package/dist/providers/blaze.js.map +1 -0
- package/dist/providers/blockrun.d.ts +14 -0
- package/dist/providers/blockrun.d.ts.map +1 -0
- package/dist/providers/blockrun.js +36 -0
- package/dist/providers/blockrun.js.map +1 -0
- package/dist/providers/clod.d.ts +11 -0
- package/dist/providers/clod.d.ts.map +1 -0
- package/dist/providers/clod.js +39 -0
- package/dist/providers/clod.js.map +1 -0
- package/dist/providers/cloudflare.d.ts +15 -0
- package/dist/providers/cloudflare.d.ts.map +1 -0
- package/dist/providers/cloudflare.js +175 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/cohere.d.ts +11 -0
- package/dist/providers/cohere.d.ts.map +1 -0
- package/dist/providers/cohere.js +117 -0
- package/dist/providers/cohere.js.map +1 -0
- package/dist/providers/dreamprompting.d.ts +11 -0
- package/dist/providers/dreamprompting.d.ts.map +1 -0
- package/dist/providers/dreamprompting.js +41 -0
- package/dist/providers/dreamprompting.js.map +1 -0
- package/dist/providers/electronhub.d.ts +10 -0
- package/dist/providers/electronhub.d.ts.map +1 -0
- package/dist/providers/electronhub.js +56 -0
- package/dist/providers/electronhub.js.map +1 -0
- package/dist/providers/experiential.d.ts +10 -0
- package/dist/providers/experiential.d.ts.map +1 -0
- package/dist/providers/experiential.js +33 -0
- package/dist/providers/experiential.js.map +1 -0
- package/dist/providers/gizmo.d.ts +15 -0
- package/dist/providers/gizmo.d.ts.map +1 -0
- package/dist/providers/gizmo.js +54 -0
- package/dist/providers/gizmo.js.map +1 -0
- package/dist/providers/google.d.ts +30 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +766 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +561 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/llmtr.d.ts +15 -0
- package/dist/providers/llmtr.d.ts.map +1 -0
- package/dist/providers/llmtr.js +51 -0
- package/dist/providers/llmtr.js.map +1 -0
- package/dist/providers/logfare.d.ts +11 -0
- package/dist/providers/logfare.d.ts.map +1 -0
- package/dist/providers/logfare.js +33 -0
- package/dist/providers/logfare.js.map +1 -0
- package/dist/providers/lucidity.d.ts +11 -0
- package/dist/providers/lucidity.d.ts.map +1 -0
- package/dist/providers/lucidity.js +37 -0
- package/dist/providers/lucidity.js.map +1 -0
- package/dist/providers/modelscope.d.ts +50 -0
- package/dist/providers/modelscope.d.ts.map +1 -0
- package/dist/providers/modelscope.js +143 -0
- package/dist/providers/modelscope.js.map +1 -0
- package/dist/providers/moondream.d.ts +17 -0
- package/dist/providers/moondream.d.ts.map +1 -0
- package/dist/providers/moondream.js +140 -0
- package/dist/providers/moondream.js.map +1 -0
- package/dist/providers/openai-compat.d.ts +140 -0
- package/dist/providers/openai-compat.d.ts.map +1 -0
- package/dist/providers/openai-compat.js +537 -0
- package/dist/providers/openai-compat.js.map +1 -0
- package/dist/providers/opencode-free.d.ts +24 -0
- package/dist/providers/opencode-free.d.ts.map +1 -0
- package/dist/providers/opencode-free.js +262 -0
- package/dist/providers/opencode-free.js.map +1 -0
- package/dist/providers/pollinations.d.ts +32 -0
- package/dist/providers/pollinations.d.ts.map +1 -0
- package/dist/providers/pollinations.js +65 -0
- package/dist/providers/pollinations.js.map +1 -0
- package/dist/providers/router9.d.ts +16 -0
- package/dist/providers/router9.d.ts.map +1 -0
- package/dist/providers/router9.js +42 -0
- package/dist/providers/router9.js.map +1 -0
- package/dist/providers/sail.d.ts +44 -0
- package/dist/providers/sail.d.ts.map +1 -0
- package/dist/providers/sail.js +302 -0
- package/dist/providers/sail.js.map +1 -0
- package/dist/providers/septor.d.ts +10 -0
- package/dist/providers/septor.d.ts.map +1 -0
- package/dist/providers/septor.js +36 -0
- package/dist/providers/septor.js.map +1 -0
- package/dist/providers/speechify.d.ts +14 -0
- package/dist/providers/speechify.d.ts.map +1 -0
- package/dist/providers/speechify.js +28 -0
- package/dist/providers/speechify.js.map +1 -0
- package/dist/providers/speka.d.ts +12 -0
- package/dist/providers/speka.d.ts.map +1 -0
- package/dist/providers/speka.js +37 -0
- package/dist/providers/speka.js.map +1 -0
- package/dist/providers/waterfall.d.ts +11 -0
- package/dist/providers/waterfall.d.ts.map +1 -0
- package/dist/providers/waterfall.js +33 -0
- package/dist/providers/waterfall.js.map +1 -0
- package/dist/providers/xkiro.d.ts +71 -0
- package/dist/providers/xkiro.d.ts.map +1 -0
- package/dist/providers/xkiro.js +119 -0
- package/dist/providers/xkiro.js.map +1 -0
- package/dist/providers/zhipu.d.ts +43 -0
- package/dist/providers/zhipu.d.ts.map +1 -0
- package/dist/providers/zhipu.js +95 -0
- package/dist/providers/zhipu.js.map +1 -0
- package/dist/routes/analytics.d.ts +2 -0
- package/dist/routes/analytics.d.ts.map +1 -0
- package/dist/routes/analytics.js +738 -0
- package/dist/routes/analytics.js.map +1 -0
- package/dist/routes/anthropic.d.ts +7 -0
- package/dist/routes/anthropic.d.ts.map +1 -0
- package/dist/routes/anthropic.js +1076 -0
- package/dist/routes/anthropic.js.map +1 -0
- package/dist/routes/auth.d.ts +2 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +310 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/backups.d.ts +2 -0
- package/dist/routes/backups.d.ts.map +1 -0
- package/dist/routes/backups.js +127 -0
- package/dist/routes/backups.js.map +1 -0
- package/dist/routes/cache.d.ts +2 -0
- package/dist/routes/cache.d.ts.map +1 -0
- package/dist/routes/cache.js +40 -0
- package/dist/routes/cache.js.map +1 -0
- package/dist/routes/client-profiles.d.ts +2 -0
- package/dist/routes/client-profiles.d.ts.map +1 -0
- package/dist/routes/client-profiles.js +128 -0
- package/dist/routes/client-profiles.js.map +1 -0
- package/dist/routes/compression.d.ts +2 -0
- package/dist/routes/compression.d.ts.map +1 -0
- package/dist/routes/compression.js +82 -0
- package/dist/routes/compression.js.map +1 -0
- package/dist/routes/conversations.d.ts +3 -0
- package/dist/routes/conversations.d.ts.map +1 -0
- package/dist/routes/conversations.js +215 -0
- package/dist/routes/conversations.js.map +1 -0
- package/dist/routes/docs.d.ts +2 -0
- package/dist/routes/docs.d.ts.map +1 -0
- package/dist/routes/docs.js +21 -0
- package/dist/routes/docs.js.map +1 -0
- package/dist/routes/embeddings.d.ts +2 -0
- package/dist/routes/embeddings.d.ts.map +1 -0
- package/dist/routes/embeddings.js +255 -0
- package/dist/routes/embeddings.js.map +1 -0
- package/dist/routes/fallback.d.ts +2 -0
- package/dist/routes/fallback.d.ts.map +1 -0
- package/dist/routes/fallback.js +631 -0
- package/dist/routes/fallback.js.map +1 -0
- package/dist/routes/free-tier.d.ts +22 -0
- package/dist/routes/free-tier.d.ts.map +1 -0
- package/dist/routes/free-tier.js +175 -0
- package/dist/routes/free-tier.js.map +1 -0
- package/dist/routes/gemini.d.ts +2 -0
- package/dist/routes/gemini.d.ts.map +1 -0
- package/dist/routes/gemini.js +218 -0
- package/dist/routes/gemini.js.map +1 -0
- package/dist/routes/health.d.ts +2 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +72 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/keys.d.ts +17 -0
- package/dist/routes/keys.d.ts.map +1 -0
- package/dist/routes/keys.js +1787 -0
- package/dist/routes/keys.js.map +1 -0
- package/dist/routes/logs.d.ts +2 -0
- package/dist/routes/logs.d.ts.map +1 -0
- package/dist/routes/logs.js +88 -0
- package/dist/routes/logs.js.map +1 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +563 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/media.d.ts +2 -0
- package/dist/routes/media.d.ts.map +1 -0
- package/dist/routes/media.js +180 -0
- package/dist/routes/media.js.map +1 -0
- package/dist/routes/models.d.ts +2 -0
- package/dist/routes/models.d.ts.map +1 -0
- package/dist/routes/models.js +439 -0
- package/dist/routes/models.js.map +1 -0
- package/dist/routes/ollama.d.ts +7 -0
- package/dist/routes/ollama.d.ts.map +1 -0
- package/dist/routes/ollama.js +595 -0
- package/dist/routes/ollama.js.map +1 -0
- package/dist/routes/profiles.d.ts +3 -0
- package/dist/routes/profiles.d.ts.map +1 -0
- package/dist/routes/profiles.js +431 -0
- package/dist/routes/profiles.js.map +1 -0
- package/dist/routes/proxy.d.ts +32 -0
- package/dist/routes/proxy.d.ts.map +1 -0
- package/dist/routes/proxy.js +2793 -0
- package/dist/routes/proxy.js.map +1 -0
- package/dist/routes/responses.d.ts +1161 -0
- package/dist/routes/responses.d.ts.map +1 -0
- package/dist/routes/responses.js +1521 -0
- package/dist/routes/responses.js.map +1 -0
- package/dist/routes/settings.d.ts +2 -0
- package/dist/routes/settings.d.ts.map +1 -0
- package/dist/routes/settings.js +549 -0
- package/dist/routes/settings.js.map +1 -0
- package/dist/routes/status.d.ts +3 -0
- package/dist/routes/status.d.ts.map +1 -0
- package/dist/routes/status.js +214 -0
- package/dist/routes/status.js.map +1 -0
- package/dist/routes/update.d.ts +31 -0
- package/dist/routes/update.d.ts.map +1 -0
- package/dist/routes/update.js +536 -0
- package/dist/routes/update.js.map +1 -0
- package/dist/routes/url-tokens.d.ts +2 -0
- package/dist/routes/url-tokens.d.ts.map +1 -0
- package/dist/routes/url-tokens.js +31 -0
- package/dist/routes/url-tokens.js.map +1 -0
- package/dist/scripts/export-catalog.d.ts +2 -0
- package/dist/scripts/export-catalog.d.ts.map +1 -0
- package/dist/scripts/export-catalog.js +114 -0
- package/dist/scripts/export-catalog.js.map +1 -0
- package/dist/scripts/rotate-encryption-key.d.ts +81 -0
- package/dist/scripts/rotate-encryption-key.d.ts.map +1 -0
- package/dist/scripts/rotate-encryption-key.js +232 -0
- package/dist/scripts/rotate-encryption-key.js.map +1 -0
- package/dist/scripts/routing-sim.d.ts +2 -0
- package/dist/scripts/routing-sim.d.ts.map +1 -0
- package/dist/scripts/routing-sim.js +130 -0
- package/dist/scripts/routing-sim.js.map +1 -0
- package/dist/scripts/test-all-models.d.ts +2 -0
- package/dist/scripts/test-all-models.d.ts.map +1 -0
- package/dist/scripts/test-all-models.js +56 -0
- package/dist/scripts/test-all-models.js.map +1 -0
- package/dist/services/anthropic-map.d.ts +39 -0
- package/dist/services/anthropic-map.d.ts.map +1 -0
- package/dist/services/anthropic-map.js +138 -0
- package/dist/services/anthropic-map.js.map +1 -0
- package/dist/services/auth.d.ts +30 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +124 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/services/auto-discover.d.ts +77 -0
- package/dist/services/auto-discover.d.ts.map +1 -0
- package/dist/services/auto-discover.js +1047 -0
- package/dist/services/auto-discover.js.map +1 -0
- package/dist/services/backups.d.ts +67 -0
- package/dist/services/backups.d.ts.map +1 -0
- package/dist/services/backups.js +444 -0
- package/dist/services/backups.js.map +1 -0
- package/dist/services/builtin-model-discovery.d.ts +107 -0
- package/dist/services/builtin-model-discovery.d.ts.map +1 -0
- package/dist/services/builtin-model-discovery.js +296 -0
- package/dist/services/builtin-model-discovery.js.map +1 -0
- package/dist/services/cache.d.ts +190 -0
- package/dist/services/cache.d.ts.map +1 -0
- package/dist/services/cache.js +617 -0
- package/dist/services/cache.js.map +1 -0
- package/dist/services/catalog-sync.d.ts +257 -0
- package/dist/services/catalog-sync.d.ts.map +1 -0
- package/dist/services/catalog-sync.js +1041 -0
- package/dist/services/catalog-sync.js.map +1 -0
- package/dist/services/compression/config.d.ts +33 -0
- package/dist/services/compression/config.d.ts.map +1 -0
- package/dist/services/compression/config.js +158 -0
- package/dist/services/compression/config.js.map +1 -0
- package/dist/services/compression/engines/aging.d.ts +2 -0
- package/dist/services/compression/engines/aging.d.ts.map +1 -0
- package/dist/services/compression/engines/aging.js +53 -0
- package/dist/services/compression/engines/aging.js.map +1 -0
- package/dist/services/compression/engines/custom-filters.d.ts +4 -0
- package/dist/services/compression/engines/custom-filters.d.ts.map +1 -0
- package/dist/services/compression/engines/custom-filters.js +49 -0
- package/dist/services/compression/engines/custom-filters.js.map +1 -0
- package/dist/services/compression/engines/dedup.d.ts +2 -0
- package/dist/services/compression/engines/dedup.d.ts.map +1 -0
- package/dist/services/compression/engines/dedup.js +61 -0
- package/dist/services/compression/engines/dedup.js.map +1 -0
- package/dist/services/compression/engines/filter-definitions.d.ts +75 -0
- package/dist/services/compression/engines/filter-definitions.d.ts.map +1 -0
- package/dist/services/compression/engines/filter-definitions.js +45 -0
- package/dist/services/compression/engines/filter-definitions.js.map +1 -0
- package/dist/services/compression/engines/hard-budget.d.ts +2 -0
- package/dist/services/compression/engines/hard-budget.d.ts.map +1 -0
- package/dist/services/compression/engines/hard-budget.js +52 -0
- package/dist/services/compression/engines/hard-budget.js.map +1 -0
- package/dist/services/compression/engines/index.d.ts +9 -0
- package/dist/services/compression/engines/index.d.ts.map +1 -0
- package/dist/services/compression/engines/index.js +9 -0
- package/dist/services/compression/engines/index.js.map +1 -0
- package/dist/services/compression/engines/jsoncompact.d.ts +3 -0
- package/dist/services/compression/engines/jsoncompact.d.ts.map +1 -0
- package/dist/services/compression/engines/jsoncompact.js +145 -0
- package/dist/services/compression/engines/jsoncompact.js.map +1 -0
- package/dist/services/compression/engines/lite.d.ts +2 -0
- package/dist/services/compression/engines/lite.d.ts.map +1 -0
- package/dist/services/compression/engines/lite.js +34 -0
- package/dist/services/compression/engines/lite.js.map +1 -0
- package/dist/services/compression/engines/read-lifecycle.d.ts +2 -0
- package/dist/services/compression/engines/read-lifecycle.d.ts.map +1 -0
- package/dist/services/compression/engines/read-lifecycle.js +70 -0
- package/dist/services/compression/engines/read-lifecycle.js.map +1 -0
- package/dist/services/compression/engines/relevance.d.ts +2 -0
- package/dist/services/compression/engines/relevance.d.ts.map +1 -0
- package/dist/services/compression/engines/relevance.js +74 -0
- package/dist/services/compression/engines/relevance.js.map +1 -0
- package/dist/services/compression/engines/toolfilter.d.ts +2 -0
- package/dist/services/compression/engines/toolfilter.d.ts.map +1 -0
- package/dist/services/compression/engines/toolfilter.js +195 -0
- package/dist/services/compression/engines/toolfilter.js.map +1 -0
- package/dist/services/compression/fidelity-gate.d.ts +12 -0
- package/dist/services/compression/fidelity-gate.d.ts.map +1 -0
- package/dist/services/compression/fidelity-gate.js +90 -0
- package/dist/services/compression/fidelity-gate.js.map +1 -0
- package/dist/services/compression/helpers.d.ts +9 -0
- package/dist/services/compression/helpers.d.ts.map +1 -0
- package/dist/services/compression/helpers.js +52 -0
- package/dist/services/compression/helpers.js.map +1 -0
- package/dist/services/compression/pipeline.d.ts +7 -0
- package/dist/services/compression/pipeline.d.ts.map +1 -0
- package/dist/services/compression/pipeline.js +245 -0
- package/dist/services/compression/pipeline.js.map +1 -0
- package/dist/services/compression/preservation.d.ts +27 -0
- package/dist/services/compression/preservation.d.ts.map +1 -0
- package/dist/services/compression/preservation.js +109 -0
- package/dist/services/compression/preservation.js.map +1 -0
- package/dist/services/compression/registry.d.ts +6 -0
- package/dist/services/compression/registry.d.ts.map +1 -0
- package/dist/services/compression/registry.js +16 -0
- package/dist/services/compression/registry.js.map +1 -0
- package/dist/services/compression/stats.d.ts +29 -0
- package/dist/services/compression/stats.d.ts.map +1 -0
- package/dist/services/compression/stats.js +55 -0
- package/dist/services/compression/stats.js.map +1 -0
- package/dist/services/compression/types.d.ts +90 -0
- package/dist/services/compression/types.d.ts.map +1 -0
- package/dist/services/compression/types.js +2 -0
- package/dist/services/compression/types.js.map +1 -0
- package/dist/services/context-handoff.d.ts +22 -0
- package/dist/services/context-handoff.d.ts.map +1 -0
- package/dist/services/context-handoff.js +164 -0
- package/dist/services/context-handoff.js.map +1 -0
- package/dist/services/cooldown-probe.d.ts +24 -0
- package/dist/services/cooldown-probe.d.ts.map +1 -0
- package/dist/services/cooldown-probe.js +181 -0
- package/dist/services/cooldown-probe.js.map +1 -0
- package/dist/services/custom-endpoint.d.ts +58 -0
- package/dist/services/custom-endpoint.d.ts.map +1 -0
- package/dist/services/custom-endpoint.js +167 -0
- package/dist/services/custom-endpoint.js.map +1 -0
- package/dist/services/custom-media-register.d.ts +22 -0
- package/dist/services/custom-media-register.d.ts.map +1 -0
- package/dist/services/custom-media-register.js +45 -0
- package/dist/services/custom-media-register.js.map +1 -0
- package/dist/services/custom-model-register.d.ts +50 -0
- package/dist/services/custom-model-register.d.ts.map +1 -0
- package/dist/services/custom-model-register.js +103 -0
- package/dist/services/custom-model-register.js.map +1 -0
- package/dist/services/custom-model-seed.d.ts +18 -0
- package/dist/services/custom-model-seed.d.ts.map +1 -0
- package/dist/services/custom-model-seed.js +42 -0
- package/dist/services/custom-model-seed.js.map +1 -0
- package/dist/services/custom-model-sync.d.ts +37 -0
- package/dist/services/custom-model-sync.d.ts.map +1 -0
- package/dist/services/custom-model-sync.js +118 -0
- package/dist/services/custom-model-sync.js.map +1 -0
- package/dist/services/custom-model-tombstone.d.ts +5 -0
- package/dist/services/custom-model-tombstone.d.ts.map +1 -0
- package/dist/services/custom-model-tombstone.js +30 -0
- package/dist/services/custom-model-tombstone.js.map +1 -0
- package/dist/services/declarative-config.d.ts +356 -0
- package/dist/services/declarative-config.d.ts.map +1 -0
- package/dist/services/declarative-config.js +427 -0
- package/dist/services/declarative-config.js.map +1 -0
- package/dist/services/degradation.d.ts +40 -0
- package/dist/services/degradation.d.ts.map +1 -0
- package/dist/services/degradation.js +119 -0
- package/dist/services/degradation.js.map +1 -0
- package/dist/services/embeddings.d.ts +76 -0
- package/dist/services/embeddings.d.ts.map +1 -0
- package/dist/services/embeddings.js +319 -0
- package/dist/services/embeddings.js.map +1 -0
- package/dist/services/fusion.d.ts +162 -0
- package/dist/services/fusion.d.ts.map +1 -0
- package/dist/services/fusion.js +789 -0
- package/dist/services/fusion.js.map +1 -0
- package/dist/services/gemini-map.d.ts +14 -0
- package/dist/services/gemini-map.d.ts.map +1 -0
- package/dist/services/gemini-map.js +78 -0
- package/dist/services/gemini-map.js.map +1 -0
- package/dist/services/health.d.ts +52 -0
- package/dist/services/health.d.ts.map +1 -0
- package/dist/services/health.js +352 -0
- package/dist/services/health.js.map +1 -0
- package/dist/services/idempotency.d.ts +49 -0
- package/dist/services/idempotency.d.ts.map +1 -0
- package/dist/services/idempotency.js +140 -0
- package/dist/services/idempotency.js.map +1 -0
- package/dist/services/key-budget.d.ts +74 -0
- package/dist/services/key-budget.d.ts.map +1 -0
- package/dist/services/key-budget.js +200 -0
- package/dist/services/key-budget.js.map +1 -0
- package/dist/services/media.d.ts +123 -0
- package/dist/services/media.d.ts.map +1 -0
- package/dist/services/media.js +954 -0
- package/dist/services/media.js.map +1 -0
- package/dist/services/model-discovery.d.ts +126 -0
- package/dist/services/model-discovery.d.ts.map +1 -0
- package/dist/services/model-discovery.js +662 -0
- package/dist/services/model-discovery.js.map +1 -0
- package/dist/services/model-groups.d.ts +149 -0
- package/dist/services/model-groups.d.ts.map +1 -0
- package/dist/services/model-groups.js +314 -0
- package/dist/services/model-groups.js.map +1 -0
- package/dist/services/model-listing.d.ts +18 -0
- package/dist/services/model-listing.d.ts.map +1 -0
- package/dist/services/model-listing.js +94 -0
- package/dist/services/model-listing.js.map +1 -0
- package/dist/services/model-retirement.d.ts +29 -0
- package/dist/services/model-retirement.d.ts.map +1 -0
- package/dist/services/model-retirement.js +86 -0
- package/dist/services/model-retirement.js.map +1 -0
- package/dist/services/model-state.d.ts +90 -0
- package/dist/services/model-state.d.ts.map +1 -0
- package/dist/services/model-state.js +342 -0
- package/dist/services/model-state.js.map +1 -0
- package/dist/services/model-weight-overrides.d.ts +48 -0
- package/dist/services/model-weight-overrides.d.ts.map +1 -0
- package/dist/services/model-weight-overrides.js +127 -0
- package/dist/services/model-weight-overrides.js.map +1 -0
- package/dist/services/notification-emitter.d.ts +50 -0
- package/dist/services/notification-emitter.d.ts.map +1 -0
- package/dist/services/notification-emitter.js +177 -0
- package/dist/services/notification-emitter.js.map +1 -0
- package/dist/services/opencode-free-sync.d.ts +29 -0
- package/dist/services/opencode-free-sync.d.ts.map +1 -0
- package/dist/services/opencode-free-sync.js +206 -0
- package/dist/services/opencode-free-sync.js.map +1 -0
- package/dist/services/penalty-inspector.d.ts +56 -0
- package/dist/services/penalty-inspector.d.ts.map +1 -0
- package/dist/services/penalty-inspector.js +167 -0
- package/dist/services/penalty-inspector.js.map +1 -0
- package/dist/services/profile-models.d.ts +5 -0
- package/dist/services/profile-models.d.ts.map +1 -0
- package/dist/services/profile-models.js +62 -0
- package/dist/services/profile-models.js.map +1 -0
- package/dist/services/provider-credential.d.ts +19 -0
- package/dist/services/provider-credential.d.ts.map +1 -0
- package/dist/services/provider-credential.js +51 -0
- package/dist/services/provider-credential.js.map +1 -0
- package/dist/services/provider-quota.d.ts +64 -0
- package/dist/services/provider-quota.d.ts.map +1 -0
- package/dist/services/provider-quota.js +563 -0
- package/dist/services/provider-quota.js.map +1 -0
- package/dist/services/quirks.d.ts +0 -0
- package/dist/services/quirks.d.ts.map +1 -0
- package/dist/services/quirks.js +0 -0
- package/dist/services/quirks.js.map +1 -0
- package/dist/services/quota-forecast.d.ts +50 -0
- package/dist/services/quota-forecast.d.ts.map +1 -0
- package/dist/services/quota-forecast.js +150 -0
- package/dist/services/quota-forecast.js.map +1 -0
- package/dist/services/quota-outlook.d.ts +4 -0
- package/dist/services/quota-outlook.d.ts.map +1 -0
- package/dist/services/quota-outlook.js +92 -0
- package/dist/services/quota-outlook.js.map +1 -0
- package/dist/services/ratelimit.d.ts +249 -0
- package/dist/services/ratelimit.d.ts.map +1 -0
- package/dist/services/ratelimit.js +1182 -0
- package/dist/services/ratelimit.js.map +1 -0
- package/dist/services/request-retention.d.ts +60 -0
- package/dist/services/request-retention.d.ts.map +1 -0
- package/dist/services/request-retention.js +229 -0
- package/dist/services/request-retention.js.map +1 -0
- package/dist/services/router.d.ts +384 -0
- package/dist/services/router.d.ts.map +1 -0
- package/dist/services/router.js +1986 -0
- package/dist/services/router.js.map +1 -0
- package/dist/services/scoring.d.ts +156 -0
- package/dist/services/scoring.d.ts.map +1 -0
- package/dist/services/scoring.js +435 -0
- package/dist/services/scoring.js.map +1 -0
- package/dist/services/url-tokens.d.ts +15 -0
- package/dist/services/url-tokens.d.ts.map +1 -0
- package/dist/services/url-tokens.js +55 -0
- package/dist/services/url-tokens.js.map +1 -0
- package/package.json +77 -4
- package/README.md +0 -3
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { getDb } from '../db/index.js';
|
|
3
|
+
import { FALLBACK_INPUT_PER_M, FALLBACK_OUTPUT_PER_M } from '../db/model-pricing.js';
|
|
4
|
+
import { providerIdFor, providerDisplayName } from '../lib/provider-identity.js';
|
|
5
|
+
import { getLiveRoutes } from '../lib/route-live.js';
|
|
6
|
+
import { normalizeBaseUrl } from '../lib/endpoint-scope.js';
|
|
7
|
+
export const analyticsRouter = Router();
|
|
8
|
+
// The dashboard's live routing strip: what the router is serving right now and
|
|
9
|
+
// what it finished most recently. Backed by the in-memory tracker in
|
|
10
|
+
// lib/route-live (fed by traceRouteEvent), so a poll costs no SQL and the
|
|
11
|
+
// state resets honestly with the process.
|
|
12
|
+
analyticsRouter.get('/live', (_req, res) => {
|
|
13
|
+
res.json(getLiveRoutes());
|
|
14
|
+
});
|
|
15
|
+
// The endpoint identity of a request, in SQL: the serving key's base_url, ''
|
|
16
|
+
// when the key is gone or carries none (every catalog key). Custom endpoints
|
|
17
|
+
// all share the platform id 'custom' (services/custom-endpoint.ts), so any
|
|
18
|
+
// view that groups by `platform` alone collapses every relay into one row and
|
|
19
|
+
// the operator cannot tell which endpoint did what (#889) — this expression is
|
|
20
|
+
// the second half of the grouping key everywhere that matters.
|
|
21
|
+
//
|
|
22
|
+
// rtrim/trim mirror lib/endpoint-scope.normalizeBaseUrl so the SQL side agrees
|
|
23
|
+
// with the ids providerIdFor() builds: keys.ts normalizes base_url on write,
|
|
24
|
+
// but rows stored before it did would otherwise split one endpoint in two.
|
|
25
|
+
// Requires the query to LEFT JOIN api_keys as `k`.
|
|
26
|
+
const ENDPOINT_ID_SQL = "COALESCE(rtrim(trim(k.base_url), '/'), '')";
|
|
27
|
+
// Format UTC timestamps the same way SQLite stores created_at text values.
|
|
28
|
+
const toSqliteDateTime = (timestamp) => new Date(timestamp).toISOString().slice(0, 19).replace('T', ' ');
|
|
29
|
+
// Return the rolling cutoff timestamp for the selected analytics range.
|
|
30
|
+
function getSinceTimestamp(range) {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
switch (range) {
|
|
33
|
+
case '24h':
|
|
34
|
+
return toSqliteDateTime(now - 24 * 60 * 60 * 1000);
|
|
35
|
+
case '30d':
|
|
36
|
+
return toSqliteDateTime(now - 30 * 24 * 60 * 60 * 1000);
|
|
37
|
+
case '90d':
|
|
38
|
+
return toSqliteDateTime(now - 90 * 24 * 60 * 60 * 1000);
|
|
39
|
+
case '7d':
|
|
40
|
+
default:
|
|
41
|
+
return toSqliteDateTime(now - 7 * 24 * 60 * 60 * 1000);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Range-based window read from the durable `request_hourly` aggregate. The raw
|
|
45
|
+
// `requests` table is pruned by REQUEST_ANALYTICS_MAX_ROWS, so any analytics
|
|
46
|
+
// count that depends on a >=7d window must read from the hourly table to stay
|
|
47
|
+
// accurate. Hourly resolution is fine for any UI range the dashboard exposes.
|
|
48
|
+
function readAggregateSince(since) {
|
|
49
|
+
const db = getDb();
|
|
50
|
+
// Hour keys are created_at truncated to the hour, so they share SQLite's
|
|
51
|
+
// canonical 'YYYY-MM-DD HH:00:00' text (space separator). The range cutoff is
|
|
52
|
+
// already in that format — floor it to the hour and compare the strings
|
|
53
|
+
// directly. No separator conversion: the writer (logRequest) and the timeline
|
|
54
|
+
// reader both compare on the space form, so this must too.
|
|
55
|
+
const aggregateSince = since.slice(0, 13) + ':00:00';
|
|
56
|
+
const rows = db.prepare(`
|
|
57
|
+
SELECT
|
|
58
|
+
COALESCE(SUM(total_requests), 0) as total_requests,
|
|
59
|
+
COALESCE(SUM(success_count), 0) as success_count,
|
|
60
|
+
COALESCE(SUM(error_count), 0) as error_count,
|
|
61
|
+
COALESCE(SUM(input_tokens), 0) as total_input_tokens,
|
|
62
|
+
COALESCE(SUM(output_tokens), 0) as total_output_tokens,
|
|
63
|
+
MIN(hour) as first_request_at
|
|
64
|
+
FROM request_hourly
|
|
65
|
+
WHERE hour >= ?
|
|
66
|
+
`).get(aggregateSince);
|
|
67
|
+
return rows;
|
|
68
|
+
}
|
|
69
|
+
function readLifetimeSettings() {
|
|
70
|
+
const db = getDb();
|
|
71
|
+
const row = db.prepare(`
|
|
72
|
+
SELECT value FROM settings WHERE key = 'first_request_at'
|
|
73
|
+
`).get();
|
|
74
|
+
return row?.value ?? null;
|
|
75
|
+
}
|
|
76
|
+
// Summary stats
|
|
77
|
+
analyticsRouter.get('/summary', (req, res) => {
|
|
78
|
+
const range = req.query.range ?? '7d';
|
|
79
|
+
const since = getSinceTimestamp(range);
|
|
80
|
+
const db = getDb();
|
|
81
|
+
// Totals (request count, token sums, success rate, lifetime first_request_at)
|
|
82
|
+
// come from the durable `request_hourly` aggregate so they stay accurate even
|
|
83
|
+
// after the raw `requests` table is pruned. Per-model pin-honor rate and
|
|
84
|
+
// estimated savings still need the raw table because they're broken down by
|
|
85
|
+
// (platform, model_id); for those we fall back to the raw rows but they're
|
|
86
|
+
// only reported for ranges where recent activity still exists. The aggregate
|
|
87
|
+
// is the source of truth for headline numbers.
|
|
88
|
+
const aggregate = readAggregateSince(since);
|
|
89
|
+
const totalRequests = aggregate.total_requests ?? 0;
|
|
90
|
+
// Success rate over success+error only: a 'canceled' request (#752 — client
|
|
91
|
+
// hung up) still counts in the totals but is neither a success nor a
|
|
92
|
+
// failure, so it must not dilute the rate.
|
|
93
|
+
const decidedRequests = (aggregate.success_count ?? 0) + (aggregate.error_count ?? 0);
|
|
94
|
+
const successRate = decidedRequests > 0 ? (aggregate.success_count / decidedRequests) * 100 : 0;
|
|
95
|
+
// Avg latency is only meaningful at the raw row level; the hourly bucket
|
|
96
|
+
// doesn't preserve it. Fall back to a 0/null when no recent raw rows exist.
|
|
97
|
+
const latencyRow = db.prepare(`
|
|
98
|
+
SELECT AVG(latency_ms) as avg_latency_ms FROM requests WHERE created_at >= ?
|
|
99
|
+
`).get(since);
|
|
100
|
+
// Estimated savings is a per-request priced value, so it lives on the raw
|
|
101
|
+
// rows. For ranges where the raw table is empty we report 0 (no recent
|
|
102
|
+
// activity to price).
|
|
103
|
+
const savings = db.prepare(`
|
|
104
|
+
SELECT COALESCE(SUM(
|
|
105
|
+
CASE WHEN r.status = 'success' THEN
|
|
106
|
+
r.input_tokens * COALESCE(m.paid_input_per_m, ?) / 1000000.0 +
|
|
107
|
+
r.output_tokens * COALESCE(m.paid_output_per_m, ?) / 1000000.0
|
|
108
|
+
ELSE 0 END
|
|
109
|
+
), 0) as est_savings
|
|
110
|
+
FROM requests r
|
|
111
|
+
LEFT JOIN models m ON m.platform = r.platform AND m.model_id = r.model_id
|
|
112
|
+
WHERE r.created_at >= ?
|
|
113
|
+
`).get(FALLBACK_INPUT_PER_M, FALLBACK_OUTPUT_PER_M, since);
|
|
114
|
+
// Pin-honor stats are also raw-row scoped. We still report them when present
|
|
115
|
+
// (typically 24h/7d) and gracefully drop them when the raw window is empty.
|
|
116
|
+
const pinRow = db.prepare(`
|
|
117
|
+
SELECT
|
|
118
|
+
SUM(CASE WHEN requested_model IS NOT NULL THEN 1 ELSE 0 END) as pinned_count,
|
|
119
|
+
SUM(CASE WHEN requested_model = model_id THEN 1 ELSE 0 END) as pin_honored_count
|
|
120
|
+
FROM requests WHERE created_at >= ?
|
|
121
|
+
`).get(since);
|
|
122
|
+
// Latency percentiles, time-to-first-token, and the chat/embedding split all
|
|
123
|
+
// live on the raw rows (the hourly aggregate keeps neither latency nor a
|
|
124
|
+
// per-type breakdown). When the raw window is empty (older than the prune
|
|
125
|
+
// horizon) we report null, not 0, so the UI can render a placeholder instead
|
|
126
|
+
// of a misleading zero. Percentiles use nearest-rank via ORDER BY/OFFSET.
|
|
127
|
+
// Only rows that actually recorded a latency participate in the percentile.
|
|
128
|
+
// The IS NOT NULL guard must be on BOTH the offset-denominator count and the
|
|
129
|
+
// ordered selection so they range over the same set: a NULL sorts first under
|
|
130
|
+
// ORDER BY latency_ms ASC, so if it were counted but not filtered the offset
|
|
131
|
+
// math would shift and a NULL could be selected (rendered as 0).
|
|
132
|
+
const rawCount = db.prepare(`SELECT COUNT(*) as c FROM requests WHERE created_at >= ? AND latency_ms IS NOT NULL`).get(since).c;
|
|
133
|
+
const percentileAt = (fraction) => {
|
|
134
|
+
if (rawCount === 0)
|
|
135
|
+
return null;
|
|
136
|
+
const offset = Math.floor((rawCount - 1) * fraction);
|
|
137
|
+
const row = db.prepare(`
|
|
138
|
+
SELECT latency_ms FROM requests
|
|
139
|
+
WHERE created_at >= ? AND latency_ms IS NOT NULL
|
|
140
|
+
ORDER BY latency_ms ASC
|
|
141
|
+
LIMIT 1 OFFSET ?
|
|
142
|
+
`).get(since, offset);
|
|
143
|
+
return row ? Math.round(row.latency_ms) : null;
|
|
144
|
+
};
|
|
145
|
+
const p50LatencyMs = percentileAt(0.5);
|
|
146
|
+
const p95LatencyMs = percentileAt(0.95);
|
|
147
|
+
const ttfbRow = db.prepare(`
|
|
148
|
+
SELECT AVG(ttfb_ms) as avg_ttfb_ms FROM requests
|
|
149
|
+
WHERE created_at >= ? AND ttfb_ms IS NOT NULL
|
|
150
|
+
`).get(since);
|
|
151
|
+
const avgTtfbMs = ttfbRow?.avg_ttfb_ms != null ? Math.round(ttfbRow.avg_ttfb_ms) : null;
|
|
152
|
+
const typeRows = db.prepare(`
|
|
153
|
+
SELECT request_type, COUNT(*) as count FROM requests
|
|
154
|
+
WHERE created_at >= ?
|
|
155
|
+
GROUP BY request_type
|
|
156
|
+
`).all(since);
|
|
157
|
+
const requestTypeCounts = { chat: 0, embedding: 0 };
|
|
158
|
+
for (const row of typeRows) {
|
|
159
|
+
if (row.request_type === 'embedding')
|
|
160
|
+
requestTypeCounts.embedding = row.count;
|
|
161
|
+
else if (row.request_type === 'chat')
|
|
162
|
+
requestTypeCounts.chat = row.count;
|
|
163
|
+
}
|
|
164
|
+
const lifetimeFirst = readLifetimeSettings();
|
|
165
|
+
res.json({
|
|
166
|
+
totalRequests,
|
|
167
|
+
successRate: Math.round(successRate * 10) / 10,
|
|
168
|
+
totalInputTokens: aggregate.total_input_tokens ?? 0,
|
|
169
|
+
totalOutputTokens: aggregate.total_output_tokens ?? 0,
|
|
170
|
+
avgLatencyMs: Math.round(latencyRow?.avg_latency_ms ?? 0),
|
|
171
|
+
// Latency spread (raw rows): p50 typical, p95 tail. Null when the raw
|
|
172
|
+
// window is empty.
|
|
173
|
+
p50LatencyMs,
|
|
174
|
+
p95LatencyMs,
|
|
175
|
+
// Average streaming time-to-first-token over rows that recorded it; null
|
|
176
|
+
// when none did (non-streaming traffic or pruned window).
|
|
177
|
+
avgTtfbMs,
|
|
178
|
+
// Chat vs embedding request split for the selected window.
|
|
179
|
+
requestTypeCounts,
|
|
180
|
+
estimatedCostSavings: Math.round((savings.est_savings ?? 0) * 100) / 100,
|
|
181
|
+
// Pinned = requests where the client named a specific model (not 'auto').
|
|
182
|
+
// Honored = the pinned model actually served it; the difference is
|
|
183
|
+
// failovers that overrode the pin.
|
|
184
|
+
pinnedRequests: pinRow.pinned_count ?? 0,
|
|
185
|
+
pinHonoredRequests: pinRow.pin_honored_count ?? 0,
|
|
186
|
+
// First-ever request timestamp (lifetime, never pruned). Falls back to
|
|
187
|
+
// the oldest hour in the current window when lifetime is not yet seeded.
|
|
188
|
+
firstRequestAt: lifetimeFirst ?? aggregate.first_request_at ?? null,
|
|
189
|
+
// Lifetime total since install — useful when the user wants to see "all
|
|
190
|
+
// time" alongside the selected range window. Sourced from settings so it
|
|
191
|
+
// survives the raw-row prune entirely.
|
|
192
|
+
lifetimeTotalRequests: Number(db.prepare(`SELECT value FROM settings WHERE key='total_requests'`).get()?.value ?? 0) || 0,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
// Stats grouped by model.
|
|
196
|
+
//
|
|
197
|
+
// The grouping key is (platform, endpoint, model_id), not (platform, model_id):
|
|
198
|
+
// the same model id served by two different custom relays is two different
|
|
199
|
+
// things — different latency, different failure modes — and merging them into
|
|
200
|
+
// one row labelled "custom" is the #889 collision in its most misleading form,
|
|
201
|
+
// because the merged row's numbers describe neither endpoint.
|
|
202
|
+
//
|
|
203
|
+
// The models join is endpoint-scoped for the same reason. `models` is unique on
|
|
204
|
+
// (platform, model_id, endpoint_scope) since #651, so joining on
|
|
205
|
+
// (platform, model_id) alone matches ONE row per relay that registered the
|
|
206
|
+
// model and multiplies every request row by that count. Adding endpoint_scope
|
|
207
|
+
// to the ON clause picks the row belonging to the endpoint that actually served
|
|
208
|
+
// the request — the only one whose display name and pricing apply.
|
|
209
|
+
analyticsRouter.get('/by-model', (req, res) => {
|
|
210
|
+
const range = req.query.range ?? '7d';
|
|
211
|
+
const since = getSinceTimestamp(range);
|
|
212
|
+
const db = getDb();
|
|
213
|
+
const rows = db.prepare(`
|
|
214
|
+
SELECT
|
|
215
|
+
r.platform,
|
|
216
|
+
${ENDPOINT_ID_SQL} as base_url,
|
|
217
|
+
r.model_id,
|
|
218
|
+
m.display_name,
|
|
219
|
+
COUNT(*) as requests,
|
|
220
|
+
-- Rate over success+error only: 'canceled' (#752) is neither.
|
|
221
|
+
SUM(CASE WHEN r.status = 'success' THEN 1 ELSE 0 END) * 100.0 / NULLIF(SUM(CASE WHEN r.status <> 'canceled' THEN 1 ELSE 0 END), 0) as success_rate,
|
|
222
|
+
AVG(r.latency_ms) as avg_latency_ms,
|
|
223
|
+
SUM(r.input_tokens) as total_input_tokens,
|
|
224
|
+
SUM(r.output_tokens) as total_output_tokens,
|
|
225
|
+
SUM(CASE WHEN r.requested_model = r.model_id THEN 1 ELSE 0 END) as pinned_requests,
|
|
226
|
+
SUM(CASE WHEN r.status = 'success' THEN
|
|
227
|
+
r.input_tokens * COALESCE(m.paid_input_per_m, ?) / 1000000.0 +
|
|
228
|
+
r.output_tokens * COALESCE(m.paid_output_per_m, ?) / 1000000.0
|
|
229
|
+
ELSE 0 END) as est_cost
|
|
230
|
+
FROM requests r
|
|
231
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
232
|
+
LEFT JOIN models m
|
|
233
|
+
ON m.platform = r.platform AND m.model_id = r.model_id
|
|
234
|
+
AND m.endpoint_scope = ${ENDPOINT_ID_SQL}
|
|
235
|
+
WHERE r.created_at >= ?
|
|
236
|
+
GROUP BY r.platform, ${ENDPOINT_ID_SQL}, r.model_id
|
|
237
|
+
ORDER BY requests DESC
|
|
238
|
+
`).all(FALLBACK_INPUT_PER_M, FALLBACK_OUTPUT_PER_M, since);
|
|
239
|
+
res.json(rows.map(r => ({
|
|
240
|
+
platform: r.platform,
|
|
241
|
+
// Same row identity as /by-platform, so a model row and a provider row for
|
|
242
|
+
// one endpoint carry the same id and the same operator-readable name.
|
|
243
|
+
providerId: providerIdFor(r.platform, r.base_url || null),
|
|
244
|
+
endpoint: providerDisplayName(r.platform, r.base_url || null),
|
|
245
|
+
modelId: r.model_id,
|
|
246
|
+
displayName: r.display_name ?? r.model_id,
|
|
247
|
+
requests: r.requests,
|
|
248
|
+
// success_rate is NULL when every row in the group was canceled.
|
|
249
|
+
successRate: Math.round((r.success_rate ?? 0) * 10) / 10,
|
|
250
|
+
avgLatencyMs: Math.round(r.avg_latency_ms),
|
|
251
|
+
totalInputTokens: r.total_input_tokens ?? 0,
|
|
252
|
+
totalOutputTokens: r.total_output_tokens ?? 0,
|
|
253
|
+
// Requests this model served because the client pinned it by name.
|
|
254
|
+
pinnedRequests: r.pinned_requests ?? 0,
|
|
255
|
+
estimatedCost: Math.round((r.est_cost ?? 0) * 100) / 100,
|
|
256
|
+
})));
|
|
257
|
+
});
|
|
258
|
+
// Stats grouped by platform.
|
|
259
|
+
//
|
|
260
|
+
// Custom endpoints all share the platform id 'custom' (services/custom-
|
|
261
|
+
// endpoint.ts), so grouping by `platform` alone would collapse every custom
|
|
262
|
+
// relay into one row and the operator could not tell which endpoint did what
|
|
263
|
+
// (#889). We therefore also group by the serving key's base_url — the canonical
|
|
264
|
+
// endpoint identity (custom-endpoint.ts pools credentials by base_url, and the
|
|
265
|
+
// router treats every key sharing a base_url as one endpoint). Non-custom keys
|
|
266
|
+
// carry no base_url, so COALESCE(base_url,'') keeps each of them in a single
|
|
267
|
+
// per-platform group exactly as before.
|
|
268
|
+
analyticsRouter.get('/by-platform', (req, res) => {
|
|
269
|
+
const range = req.query.range ?? '7d';
|
|
270
|
+
const since = getSinceTimestamp(range);
|
|
271
|
+
const db = getDb();
|
|
272
|
+
const rows = db.prepare(`
|
|
273
|
+
SELECT
|
|
274
|
+
r.platform,
|
|
275
|
+
${ENDPOINT_ID_SQL} as base_url,
|
|
276
|
+
COUNT(*) as requests,
|
|
277
|
+
COUNT(r.latency_ms) as latency_count,
|
|
278
|
+
SUM(CASE WHEN r.status = 'success' THEN 1 ELSE 0 END) * 100.0 / NULLIF(SUM(CASE WHEN r.status <> 'canceled' THEN 1 ELSE 0 END), 0) as success_rate,
|
|
279
|
+
AVG(r.latency_ms) as avg_latency_ms,
|
|
280
|
+
AVG(r.ttfb_ms) as avg_ttfb_ms,
|
|
281
|
+
SUM(CASE WHEN r.status = 'error' THEN 1 ELSE 0 END) as error_count,
|
|
282
|
+
AVG(CASE WHEN r.output_tokens > 0 AND r.latency_ms > 0
|
|
283
|
+
THEN r.output_tokens / (r.latency_ms / 1000.0) ELSE NULL END) as avg_tokens_per_second,
|
|
284
|
+
SUM(r.input_tokens) as total_input_tokens,
|
|
285
|
+
SUM(r.output_tokens) as total_output_tokens
|
|
286
|
+
FROM requests r
|
|
287
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
288
|
+
WHERE r.created_at >= ?
|
|
289
|
+
GROUP BY r.platform, ${ENDPOINT_ID_SQL}
|
|
290
|
+
ORDER BY requests DESC
|
|
291
|
+
`).all(since);
|
|
292
|
+
// P95 latency is a per-group percentile; SQLite has no native percentile
|
|
293
|
+
// aggregate, so we take the nearest-rank value per group with a small
|
|
294
|
+
// ORDER BY/OFFSET query. The group count is tiny (one row per provider /
|
|
295
|
+
// custom endpoint), so the extra round-trips are negligible and keep the SQL
|
|
296
|
+
// readable. The WHERE must match the grouping exactly — platform AND the
|
|
297
|
+
// endpoint's base_url — or a custom endpoint's p95 would bleed in latency
|
|
298
|
+
// from every other custom endpoint.
|
|
299
|
+
const p95Stmt = db.prepare(`
|
|
300
|
+
SELECT r.latency_ms FROM requests r
|
|
301
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
302
|
+
WHERE r.created_at >= ? AND r.platform = ? AND ${ENDPOINT_ID_SQL} = ? AND r.latency_ms IS NOT NULL
|
|
303
|
+
ORDER BY r.latency_ms ASC
|
|
304
|
+
LIMIT 1 OFFSET ?
|
|
305
|
+
`);
|
|
306
|
+
res.json(rows.map(r => {
|
|
307
|
+
// Offset math and the ordered selection both range over the non-null
|
|
308
|
+
// latency rows (latency_count), so a NULL can neither be counted into the
|
|
309
|
+
// denominator nor selected as the p95 value.
|
|
310
|
+
const latencyCount = r.latency_count ?? 0;
|
|
311
|
+
const baseUrl = r.base_url || null;
|
|
312
|
+
const p95Row = latencyCount > 0
|
|
313
|
+
? p95Stmt.get(since, r.platform, r.base_url, Math.floor((latencyCount - 1) * 0.95))
|
|
314
|
+
: undefined;
|
|
315
|
+
return {
|
|
316
|
+
platform: r.platform,
|
|
317
|
+
// Stable, unique id for this row: the platform slug for catalog providers,
|
|
318
|
+
// 'custom:<base_url>' for custom endpoints (falls back to 'custom' when
|
|
319
|
+
// the key is gone). The client uses this as the chart key and the
|
|
320
|
+
// recent-calls filter value.
|
|
321
|
+
providerId: providerIdFor(r.platform, baseUrl),
|
|
322
|
+
// The short identifier the operator actually reads: the endpoint host for
|
|
323
|
+
// custom rows, the platform slug otherwise.
|
|
324
|
+
endpoint: providerDisplayName(r.platform, baseUrl),
|
|
325
|
+
requests: r.requests,
|
|
326
|
+
successRate: Math.round((r.success_rate ?? 0) * 10) / 10,
|
|
327
|
+
avgLatencyMs: Math.round(r.avg_latency_ms),
|
|
328
|
+
p95LatencyMs: p95Row ? Math.round(p95Row.latency_ms) : null,
|
|
329
|
+
avgTtfbMs: r.avg_ttfb_ms != null ? Math.round(r.avg_ttfb_ms) : null,
|
|
330
|
+
errorCount: r.error_count ?? 0,
|
|
331
|
+
avgTokensPerSecond: r.avg_tokens_per_second != null
|
|
332
|
+
? Math.round(r.avg_tokens_per_second * 10) / 10
|
|
333
|
+
: null,
|
|
334
|
+
totalInputTokens: r.total_input_tokens ?? 0,
|
|
335
|
+
totalOutputTokens: r.total_output_tokens ?? 0,
|
|
336
|
+
};
|
|
337
|
+
}));
|
|
338
|
+
});
|
|
339
|
+
analyticsRouter.get('/by-client', (req, res) => {
|
|
340
|
+
const range = req.query.range ?? '7d';
|
|
341
|
+
const since = getSinceTimestamp(range);
|
|
342
|
+
const rows = getDb().prepare(`
|
|
343
|
+
SELECT
|
|
344
|
+
COALESCE(client_agent, 'unknown') AS client_agent,
|
|
345
|
+
COUNT(*) AS requests,
|
|
346
|
+
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) * 100.0 / NULLIF(SUM(CASE WHEN status <> 'canceled' THEN 1 ELSE 0 END), 0) AS success_rate,
|
|
347
|
+
AVG(latency_ms) AS avg_latency_ms,
|
|
348
|
+
SUM(input_tokens) AS total_input_tokens,
|
|
349
|
+
SUM(output_tokens) AS total_output_tokens,
|
|
350
|
+
MAX(strftime('%Y-%m-%dT%H:%M:%SZ', created_at)) AS last_seen_at
|
|
351
|
+
FROM requests
|
|
352
|
+
WHERE created_at >= ?
|
|
353
|
+
GROUP BY client_agent
|
|
354
|
+
ORDER BY requests DESC
|
|
355
|
+
`).all(since);
|
|
356
|
+
res.json(rows.map(row => ({
|
|
357
|
+
clientAgent: row.client_agent,
|
|
358
|
+
requests: row.requests,
|
|
359
|
+
successRate: Math.round((row.success_rate ?? 0) * 10) / 10,
|
|
360
|
+
avgLatencyMs: Math.round(row.avg_latency_ms ?? 0),
|
|
361
|
+
totalInputTokens: row.total_input_tokens ?? 0,
|
|
362
|
+
totalOutputTokens: row.total_output_tokens ?? 0,
|
|
363
|
+
lastSeenAt: row.last_seen_at,
|
|
364
|
+
})));
|
|
365
|
+
});
|
|
366
|
+
// Stats grouped by API key. Raw-row scoped (the hourly aggregate has no key
|
|
367
|
+
// dimension), LEFT JOINed to api_keys so a request whose key was later deleted
|
|
368
|
+
// still shows up with a null label — the keyId is always returned.
|
|
369
|
+
analyticsRouter.get('/by-key', (req, res) => {
|
|
370
|
+
const range = req.query.range ?? '7d';
|
|
371
|
+
const since = getSinceTimestamp(range);
|
|
372
|
+
const db = getDb();
|
|
373
|
+
const rows = db.prepare(`
|
|
374
|
+
SELECT
|
|
375
|
+
r.key_id as key_id,
|
|
376
|
+
k.label as label,
|
|
377
|
+
k.platform as platform,
|
|
378
|
+
COUNT(*) as requests,
|
|
379
|
+
SUM(CASE WHEN r.status = 'success' THEN 1 ELSE 0 END) * 100.0 / NULLIF(SUM(CASE WHEN r.status <> 'canceled' THEN 1 ELSE 0 END), 0) as success_rate,
|
|
380
|
+
AVG(r.latency_ms) as avg_latency_ms,
|
|
381
|
+
SUM(r.input_tokens) as total_input_tokens,
|
|
382
|
+
SUM(r.output_tokens) as total_output_tokens
|
|
383
|
+
FROM requests r
|
|
384
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
385
|
+
WHERE r.key_id IS NOT NULL AND r.created_at >= ?
|
|
386
|
+
GROUP BY r.key_id
|
|
387
|
+
ORDER BY requests DESC
|
|
388
|
+
LIMIT 50
|
|
389
|
+
`).all(since);
|
|
390
|
+
res.json(rows.map(r => ({
|
|
391
|
+
keyId: r.key_id,
|
|
392
|
+
// Null when the key row was deleted, or the empty string when the key
|
|
393
|
+
// exists but was never labelled; the client falls back to "Key #<id>".
|
|
394
|
+
label: r.label ?? null,
|
|
395
|
+
platform: r.platform ?? null,
|
|
396
|
+
requests: r.requests,
|
|
397
|
+
successRate: Math.round((r.success_rate ?? 0) * 10) / 10,
|
|
398
|
+
avgLatencyMs: Math.round(r.avg_latency_ms),
|
|
399
|
+
totalInputTokens: r.total_input_tokens ?? 0,
|
|
400
|
+
totalOutputTokens: r.total_output_tokens ?? 0,
|
|
401
|
+
})));
|
|
402
|
+
});
|
|
403
|
+
// Timeline data
|
|
404
|
+
analyticsRouter.get('/timeline', (req, res) => {
|
|
405
|
+
const range = req.query.range ?? '7d';
|
|
406
|
+
const interval = req.query.interval ?? (range === '24h' ? 'hour' : 'day');
|
|
407
|
+
const since = getSinceTimestamp(range);
|
|
408
|
+
const db = getDb();
|
|
409
|
+
// dateFormat is a hardcoded whitelist — never user-controlled.
|
|
410
|
+
const dateFormat = interval === 'hour' ? '%Y-%m-%dT%H:00:00' : '%Y-%m-%d';
|
|
411
|
+
// tzOffset: viewer's local offset from UTC in minutes (480 = UTC+8), sent by
|
|
412
|
+
// the browser so hour/day bucket boundaries follow the viewer's wall clock
|
|
413
|
+
// instead of UTC. Whitelisted to a sane integer range; bound as a parameter,
|
|
414
|
+
// never interpolated into SQL.
|
|
415
|
+
const rawOffset = Number(req.query.tzOffset);
|
|
416
|
+
const tzOffset = Number.isInteger(rawOffset) && rawOffset >= -720 && rawOffset <= 840 ? rawOffset : 0;
|
|
417
|
+
// The single current offset applies to the whole range (SQLite has no tz
|
|
418
|
+
// database), so buckets on the far side of a DST transition sit 1h off.
|
|
419
|
+
const tzModifier = `${tzOffset >= 0 ? '+' : '-'}${Math.abs(tzOffset)} minutes`;
|
|
420
|
+
// Read from request_hourly (hour-bucketed) for both 'hour' and 'day'
|
|
421
|
+
// intervals. Day buckets are rolled up via strftime on the hour column,
|
|
422
|
+
// which keeps the timeline accurate past the raw-row prune window.
|
|
423
|
+
const rows = db.prepare(`
|
|
424
|
+
SELECT
|
|
425
|
+
strftime(?, hour, ?) as timestamp,
|
|
426
|
+
SUM(total_requests) as requests,
|
|
427
|
+
SUM(success_count) as success_count,
|
|
428
|
+
SUM(error_count) as failure_count,
|
|
429
|
+
SUM(input_tokens) as input_tokens,
|
|
430
|
+
SUM(output_tokens) as output_tokens
|
|
431
|
+
FROM request_hourly
|
|
432
|
+
WHERE hour >= ?
|
|
433
|
+
GROUP BY timestamp
|
|
434
|
+
ORDER BY timestamp ASC
|
|
435
|
+
`).all(dateFormat, tzModifier, since);
|
|
436
|
+
res.json(rows.map(r => ({
|
|
437
|
+
timestamp: r.timestamp,
|
|
438
|
+
requests: r.requests,
|
|
439
|
+
successCount: r.success_count,
|
|
440
|
+
failureCount: r.failure_count,
|
|
441
|
+
inputTokens: r.input_tokens ?? 0,
|
|
442
|
+
outputTokens: r.output_tokens ?? 0,
|
|
443
|
+
})));
|
|
444
|
+
});
|
|
445
|
+
// Error distribution (grouped by error type and platform)
|
|
446
|
+
analyticsRouter.get('/error-distribution', (req, res) => {
|
|
447
|
+
const range = req.query.range ?? '7d';
|
|
448
|
+
const since = getSinceTimestamp(range);
|
|
449
|
+
const db = getDb();
|
|
450
|
+
// Group errors by category (extract the key part of the error message).
|
|
451
|
+
// LOWER() first: LIKE is case-sensitive in SQLite, and the error strings we
|
|
452
|
+
// persist are dominated by capitalized upstream messages ("Invalid API key",
|
|
453
|
+
// "Too Many Requests", "Timeout"), which all landed in 'Other' before. The
|
|
454
|
+
// old '%invalid.*key%' pattern was regex syntax pasted into LIKE, where '.'
|
|
455
|
+
// and '*' are literal characters, so it matched nothing real.
|
|
456
|
+
const rows = db.prepare(`
|
|
457
|
+
SELECT
|
|
458
|
+
platform,
|
|
459
|
+
model_id,
|
|
460
|
+
CASE
|
|
461
|
+
WHEN lower(error) LIKE '%429%' OR lower(error) LIKE '%rate limit%' OR lower(error) LIKE '%too many%' OR lower(error) LIKE '%quota%' THEN 'Rate Limited (429)'
|
|
462
|
+
WHEN lower(error) LIKE '%401%' OR lower(error) LIKE '%unauthorized%' OR lower(error) LIKE '%invalid api key%' OR lower(error) LIKE '%invalid_api_key%' OR lower(error) LIKE '%incorrect api key%' THEN 'Auth Error (401)'
|
|
463
|
+
WHEN lower(error) LIKE '%403%' OR lower(error) LIKE '%forbidden%' THEN 'Forbidden (403)'
|
|
464
|
+
WHEN lower(error) LIKE '%404%' OR lower(error) LIKE '%not found%' THEN 'Not Found (404)'
|
|
465
|
+
WHEN lower(error) LIKE '%timeout%' OR lower(error) LIKE '%timed out%' OR lower(error) LIKE '%etimedout%' OR lower(error) LIKE '%econnrefused%' THEN 'Timeout/Connection'
|
|
466
|
+
WHEN lower(error) LIKE '%500%' OR lower(error) LIKE '%internal server%' THEN 'Server Error (500)'
|
|
467
|
+
WHEN lower(error) LIKE '%503%' OR lower(error) LIKE '%unavailable%' THEN 'Unavailable (503)'
|
|
468
|
+
ELSE 'Other'
|
|
469
|
+
END as error_category,
|
|
470
|
+
COUNT(*) as count
|
|
471
|
+
FROM requests
|
|
472
|
+
WHERE status = 'error' AND created_at >= ?
|
|
473
|
+
GROUP BY platform, error_category
|
|
474
|
+
ORDER BY count DESC
|
|
475
|
+
`).all(since);
|
|
476
|
+
// Also get totals by category
|
|
477
|
+
const byCategory = db.prepare(`
|
|
478
|
+
SELECT
|
|
479
|
+
CASE
|
|
480
|
+
WHEN lower(error) LIKE '%429%' OR lower(error) LIKE '%rate limit%' OR lower(error) LIKE '%too many%' OR lower(error) LIKE '%quota%' THEN 'Rate Limited (429)'
|
|
481
|
+
WHEN lower(error) LIKE '%401%' OR lower(error) LIKE '%unauthorized%' OR lower(error) LIKE '%invalid api key%' OR lower(error) LIKE '%invalid_api_key%' OR lower(error) LIKE '%incorrect api key%' THEN 'Auth Error (401)'
|
|
482
|
+
WHEN lower(error) LIKE '%403%' OR lower(error) LIKE '%forbidden%' THEN 'Forbidden (403)'
|
|
483
|
+
WHEN lower(error) LIKE '%404%' OR lower(error) LIKE '%not found%' THEN 'Not Found (404)'
|
|
484
|
+
WHEN lower(error) LIKE '%timeout%' OR lower(error) LIKE '%timed out%' OR lower(error) LIKE '%etimedout%' OR lower(error) LIKE '%econnrefused%' THEN 'Timeout/Connection'
|
|
485
|
+
WHEN lower(error) LIKE '%500%' OR lower(error) LIKE '%internal server%' THEN 'Server Error (500)'
|
|
486
|
+
WHEN lower(error) LIKE '%503%' OR lower(error) LIKE '%unavailable%' THEN 'Unavailable (503)'
|
|
487
|
+
ELSE 'Other'
|
|
488
|
+
END as category,
|
|
489
|
+
COUNT(*) as count
|
|
490
|
+
FROM requests
|
|
491
|
+
WHERE status = 'error' AND created_at >= ?
|
|
492
|
+
GROUP BY category
|
|
493
|
+
ORDER BY count DESC
|
|
494
|
+
`).all(since);
|
|
495
|
+
// Errors by provider. Endpoint-scoped like /by-platform: one bar per custom
|
|
496
|
+
// relay, not one bar pooling every relay's failures under "custom" (#889) —
|
|
497
|
+
// a bar the operator cannot act on, because it never says which endpoint is
|
|
498
|
+
// the one failing.
|
|
499
|
+
const byPlatformRows = db.prepare(`
|
|
500
|
+
SELECT r.platform, ${ENDPOINT_ID_SQL} as base_url, COUNT(*) as count
|
|
501
|
+
FROM requests r
|
|
502
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
503
|
+
WHERE r.status = 'error' AND r.created_at >= ?
|
|
504
|
+
GROUP BY r.platform, ${ENDPOINT_ID_SQL}
|
|
505
|
+
ORDER BY count DESC
|
|
506
|
+
`).all(since);
|
|
507
|
+
const byPlatform = byPlatformRows.map(r => {
|
|
508
|
+
const baseUrl = r.base_url || null;
|
|
509
|
+
return {
|
|
510
|
+
// `platform` stays the raw slug: it is what the chart colors by.
|
|
511
|
+
platform: r.platform,
|
|
512
|
+
providerId: providerIdFor(r.platform, baseUrl),
|
|
513
|
+
endpoint: providerDisplayName(r.platform, baseUrl),
|
|
514
|
+
count: r.count,
|
|
515
|
+
};
|
|
516
|
+
});
|
|
517
|
+
res.json({
|
|
518
|
+
byCategory,
|
|
519
|
+
byPlatform,
|
|
520
|
+
detailed: rows,
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
// Recent errors
|
|
524
|
+
analyticsRouter.get('/errors', (req, res) => {
|
|
525
|
+
const range = req.query.range ?? '7d';
|
|
526
|
+
const since = getSinceTimestamp(range);
|
|
527
|
+
const db = getDb();
|
|
528
|
+
// Joined to the serving key so each error names the endpoint it came from.
|
|
529
|
+
// Without it every custom relay's failures read as a bare "custom" in the
|
|
530
|
+
// panel (#889) and the operator has to guess which one broke.
|
|
531
|
+
const rows = db.prepare(`
|
|
532
|
+
SELECT r.id, r.platform, ${ENDPOINT_ID_SQL} as base_url, r.model_id, r.error,
|
|
533
|
+
r.latency_ms, r.created_at
|
|
534
|
+
FROM requests r
|
|
535
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
536
|
+
WHERE r.status = 'error' AND r.created_at >= ?
|
|
537
|
+
ORDER BY r.created_at DESC
|
|
538
|
+
LIMIT 50
|
|
539
|
+
`).all(since);
|
|
540
|
+
res.json(rows.map(r => {
|
|
541
|
+
const baseUrl = r.base_url || null;
|
|
542
|
+
return {
|
|
543
|
+
id: r.id,
|
|
544
|
+
platform: r.platform,
|
|
545
|
+
providerId: providerIdFor(r.platform, baseUrl),
|
|
546
|
+
endpoint: providerDisplayName(r.platform, baseUrl),
|
|
547
|
+
modelId: r.model_id,
|
|
548
|
+
error: r.error,
|
|
549
|
+
latencyMs: r.latency_ms,
|
|
550
|
+
createdAt: r.created_at,
|
|
551
|
+
};
|
|
552
|
+
}));
|
|
553
|
+
});
|
|
554
|
+
// Recent calls — one row per proxied request, newest first, with the caller's
|
|
555
|
+
// IP and User-Agent (all local clients share the unified key, so client_ip is
|
|
556
|
+
// the only per-caller discriminator; UA disambiguates tunneled loopback calls).
|
|
557
|
+
// Reads the raw `requests` table, so history is bounded by the retention prune.
|
|
558
|
+
analyticsRouter.get('/requests', (req, res) => {
|
|
559
|
+
const range = req.query.range ?? '7d';
|
|
560
|
+
const since = getSinceTimestamp(range);
|
|
561
|
+
const limit = Math.min(Math.max(parseInt(req.query.limit, 10) || 100, 1), 500);
|
|
562
|
+
const offset = Math.max(parseInt(req.query.offset, 10) || 0, 0);
|
|
563
|
+
// Optional filters. All are validated (whitelist / shape) and applied as
|
|
564
|
+
// bound parameters; absent filters keep the default behavior identical.
|
|
565
|
+
const status = req.query.status;
|
|
566
|
+
if (status !== undefined && status !== 'success' && status !== 'error' && status !== 'canceled') {
|
|
567
|
+
res.status(400).json({ error: "invalid status filter (expected 'success', 'error' or 'canceled')" });
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
// Provider filter. The `provider` param carries the stable row id returned by
|
|
571
|
+
// /by-platform: a platform slug ('groq') or 'custom:<base_url>' for a custom
|
|
572
|
+
// endpoint (#889 — every custom relay shares the 'custom' platform id, so the
|
|
573
|
+
// endpoint's base_url is what actually selects one). The legacy `platform`
|
|
574
|
+
// param still works for old clients. Both are bound parameters, never
|
|
575
|
+
// interpolated, so the only validation needed is shape.
|
|
576
|
+
const provider = req.query.provider;
|
|
577
|
+
const platform = req.query.platform;
|
|
578
|
+
let providerFilterSql = '';
|
|
579
|
+
const providerFilterParams = [];
|
|
580
|
+
if (provider !== undefined) {
|
|
581
|
+
if (provider.length > 256 || /[\r\n]/.test(provider)) {
|
|
582
|
+
res.status(400).json({ error: 'invalid provider filter' });
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (provider === 'custom') {
|
|
586
|
+
// The BARE 'custom' id is the orphan bucket, not "all custom traffic":
|
|
587
|
+
// /by-platform emits it only for rows whose endpoint is unknown (the key
|
|
588
|
+
// was deleted, or never carried a base_url). Falling through to the slug
|
|
589
|
+
// branch below would filter on `platform = 'custom'` and return every
|
|
590
|
+
// relay's traffic — a list that contradicts the very row the user
|
|
591
|
+
// clicked, which counted the orphans alone. Match what that row counted.
|
|
592
|
+
providerFilterSql = ` AND r.platform = 'custom' AND ${ENDPOINT_ID_SQL} = ''`;
|
|
593
|
+
}
|
|
594
|
+
else if (provider.startsWith('custom:')) {
|
|
595
|
+
// Select one custom endpoint by its base_url. Normalized on the way in
|
|
596
|
+
// for the same reason ENDPOINT_ID_SQL normalizes on the way out.
|
|
597
|
+
providerFilterSql = ` AND r.platform = 'custom' AND ${ENDPOINT_ID_SQL} = ?`;
|
|
598
|
+
providerFilterParams.push(normalizeBaseUrl(provider.slice('custom:'.length)));
|
|
599
|
+
}
|
|
600
|
+
else if (/^[A-Za-z0-9_-]{1,64}$/.test(provider)) {
|
|
601
|
+
providerFilterSql = ' AND r.platform = ?';
|
|
602
|
+
providerFilterParams.push(provider);
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
res.status(400).json({ error: 'invalid provider filter' });
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else if (platform !== undefined) {
|
|
610
|
+
// The legacy param keeps its pre-#889 meaning: `platform=custom` is every
|
|
611
|
+
// custom relay's traffic. Only the `provider` ids are endpoint-scoped.
|
|
612
|
+
// Platform ids are short slugs ('groq', 'pt-custom_1'); anything else is a
|
|
613
|
+
// client bug, not a filter.
|
|
614
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(platform)) {
|
|
615
|
+
res.status(400).json({ error: 'invalid platform filter' });
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
providerFilterSql = ' AND r.platform = ?';
|
|
619
|
+
providerFilterParams.push(platform);
|
|
620
|
+
}
|
|
621
|
+
const db = getDb();
|
|
622
|
+
const filterSql = (status !== undefined ? ' AND r.status = ?' : '') +
|
|
623
|
+
providerFilterSql;
|
|
624
|
+
const filterParams = [
|
|
625
|
+
...(status !== undefined ? [status] : []),
|
|
626
|
+
...providerFilterParams,
|
|
627
|
+
];
|
|
628
|
+
const total = db.prepare(`SELECT COUNT(*) as c FROM requests r
|
|
629
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
630
|
+
WHERE r.created_at >= ?${filterSql}`).get(since, ...filterParams).c;
|
|
631
|
+
const rows = db.prepare(`
|
|
632
|
+
SELECT r.id, r.platform, r.model_id, r.requested_model, r.request_type, r.status,
|
|
633
|
+
r.input_tokens, r.output_tokens, r.latency_ms, r.error,
|
|
634
|
+
r.client_ip, r.client_user_agent, r.client_agent,
|
|
635
|
+
strftime('%Y-%m-%dT%H:%M:%SZ', r.created_at) as created_at_iso,
|
|
636
|
+
(SELECT COUNT(*) FROM request_attempts a WHERE a.request_id = r.id) as attempt_count,
|
|
637
|
+
k.label as key_label
|
|
638
|
+
FROM requests r
|
|
639
|
+
LEFT JOIN api_keys k ON k.id = r.key_id
|
|
640
|
+
WHERE r.created_at >= ?${filterSql}
|
|
641
|
+
ORDER BY r.created_at DESC, r.id DESC
|
|
642
|
+
LIMIT ? OFFSET ?
|
|
643
|
+
`).all(since, ...filterParams, limit, offset);
|
|
644
|
+
res.json({
|
|
645
|
+
total,
|
|
646
|
+
rows: rows.map(r => ({
|
|
647
|
+
id: r.id,
|
|
648
|
+
platform: r.platform,
|
|
649
|
+
modelId: r.model_id,
|
|
650
|
+
requestedModel: r.requested_model,
|
|
651
|
+
requestType: r.request_type,
|
|
652
|
+
status: r.status,
|
|
653
|
+
inputTokens: r.input_tokens,
|
|
654
|
+
outputTokens: r.output_tokens,
|
|
655
|
+
latencyMs: r.latency_ms,
|
|
656
|
+
error: r.error,
|
|
657
|
+
clientIp: r.client_ip,
|
|
658
|
+
clientUserAgent: r.client_user_agent,
|
|
659
|
+
clientAgent: r.client_agent,
|
|
660
|
+
createdAt: r.created_at_iso,
|
|
661
|
+
// #785: custom endpoints all share the generic 'custom' platform id, so
|
|
662
|
+
// the user's key label ("Ollama box") rides along to name the real
|
|
663
|
+
// provider in the recent-calls list. Null when the key was deleted or
|
|
664
|
+
// never labelled.
|
|
665
|
+
keyLabel: r.key_label ?? null,
|
|
666
|
+
// Failover-ladder length for this row. Attempts hang off the TERMINAL
|
|
667
|
+
// row of a proxied request; mid-ladder failure rows report 0.
|
|
668
|
+
attemptCount: r.attempt_count,
|
|
669
|
+
})),
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
// Per-request detail: the row plus its durable failover ladder — one entry per
|
|
673
|
+
// dispatched attempt (including the successful final one), ordinal-ordered,
|
|
674
|
+
// with the failure class and timing of each hop. keyOrdinal is the per-request
|
|
675
|
+
// key ordinal (key1, key2…), same anonymization as X-Fallback-Trail — internal
|
|
676
|
+
// key ids are never exposed. Attempts are keyed to the ladder's terminal row
|
|
677
|
+
// (the success row, or the last failure row when it exhausted), so mid-ladder
|
|
678
|
+
// error rows legitimately return an empty attempts array.
|
|
679
|
+
analyticsRouter.get('/requests/:id', (req, res) => {
|
|
680
|
+
const id = Number.parseInt(req.params.id, 10);
|
|
681
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
682
|
+
res.status(400).json({ error: 'invalid request id' });
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const db = getDb();
|
|
686
|
+
const r = db.prepare(`
|
|
687
|
+
SELECT id, platform, model_id, requested_model, served_model, request_type, status,
|
|
688
|
+
input_tokens, output_tokens, latency_ms, ttfb_ms, error,
|
|
689
|
+
client_ip, client_user_agent, client_agent,
|
|
690
|
+
strftime('%Y-%m-%dT%H:%M:%SZ', created_at) as created_at_iso
|
|
691
|
+
FROM requests
|
|
692
|
+
WHERE id = ?
|
|
693
|
+
`).get(id);
|
|
694
|
+
if (!r) {
|
|
695
|
+
res.status(404).json({ error: 'request not found' });
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const attempts = db.prepare(`
|
|
699
|
+
SELECT ordinal, platform, model_id, key_ordinal, key_label, outcome, start_offset_ms, duration_ms, error_summary
|
|
700
|
+
FROM request_attempts
|
|
701
|
+
WHERE request_id = ?
|
|
702
|
+
ORDER BY ordinal ASC
|
|
703
|
+
`).all(id);
|
|
704
|
+
res.json({
|
|
705
|
+
id: r.id,
|
|
706
|
+
platform: r.platform,
|
|
707
|
+
modelId: r.model_id,
|
|
708
|
+
requestedModel: r.requested_model,
|
|
709
|
+
// Upstream-reported model when it genuinely differed from the routed
|
|
710
|
+
// model_id (#534 served-model drift guard); null in the healthy case.
|
|
711
|
+
servedModel: r.served_model,
|
|
712
|
+
requestType: r.request_type,
|
|
713
|
+
status: r.status,
|
|
714
|
+
inputTokens: r.input_tokens,
|
|
715
|
+
outputTokens: r.output_tokens,
|
|
716
|
+
latencyMs: r.latency_ms,
|
|
717
|
+
ttfbMs: r.ttfb_ms,
|
|
718
|
+
error: r.error,
|
|
719
|
+
clientIp: r.client_ip,
|
|
720
|
+
clientUserAgent: r.client_user_agent,
|
|
721
|
+
clientAgent: r.client_agent,
|
|
722
|
+
createdAt: r.created_at_iso,
|
|
723
|
+
attempts: attempts.map(a => ({
|
|
724
|
+
ordinal: a.ordinal,
|
|
725
|
+
platform: a.platform,
|
|
726
|
+
modelId: a.model_id,
|
|
727
|
+
keyOrdinal: a.key_ordinal,
|
|
728
|
+
keyLabel: a.key_label ?? null,
|
|
729
|
+
outcome: a.outcome,
|
|
730
|
+
startOffsetMs: a.start_offset_ms,
|
|
731
|
+
durationMs: a.duration_ms,
|
|
732
|
+
// Short, redacted per-hop error text (null for successful hops and for
|
|
733
|
+
// rows written before the error_summary migration).
|
|
734
|
+
errorSummary: a.error_summary ?? null,
|
|
735
|
+
})),
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
//# sourceMappingURL=analytics.js.map
|