@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,1047 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auto-discover — Automatically discover and register ALL model types
|
|
3
|
+
* (chat, embeddings, media) when a provider key is added but the catalog
|
|
4
|
+
* hasn't shipped models for it yet.
|
|
5
|
+
*
|
|
6
|
+
* This bypasses the catalog gate: instead of telling users to "add as custom
|
|
7
|
+
* endpoint", we query the provider's own /v1/models endpoint with the user's
|
|
8
|
+
* key and register discovered models directly.
|
|
9
|
+
*
|
|
10
|
+
* All modalities are discovered:
|
|
11
|
+
* - Chat models → models table
|
|
12
|
+
* - Embedding models → embedding_models table (dimensions auto-guessed)
|
|
13
|
+
* - Image/audio/video/transcription → media_models table
|
|
14
|
+
* - Provider quirks auto-assigned from known patterns
|
|
15
|
+
*
|
|
16
|
+
* Models are inserted with source='user' so catalog syncs never delete them.
|
|
17
|
+
*/
|
|
18
|
+
import { getDb } from '../db/index.js';
|
|
19
|
+
import { resolveProvider } from '../providers/index.js';
|
|
20
|
+
import { OpenAICompatProvider } from '../providers/openai-compat.js';
|
|
21
|
+
import { parseModelCatalog } from './model-discovery.js';
|
|
22
|
+
import { ensureModelInProfiles } from './profile-models.js';
|
|
23
|
+
import { addNotifications, getSyncIntervalHours } from './catalog-sync.js';
|
|
24
|
+
// Large provider list — skip auto-discover for these well-known providers
|
|
25
|
+
// that either don't have /v1/models, are handled by the catalog already,
|
|
26
|
+
// or are 100% paid and we don't want to register any of their models.
|
|
27
|
+
const SKIP_AUTO_DISCOVER = new Set([
|
|
28
|
+
'ollama', // local, handled separately
|
|
29
|
+
'custom', // custom endpoints use their own flow
|
|
30
|
+
'aihorde', // uses its own API format
|
|
31
|
+
'bai', // 100% paid — "Deposit required to unlock premium models"
|
|
32
|
+
'cerebras', // paid/exhausted — returns 402
|
|
33
|
+
]);
|
|
34
|
+
/** How many models to register per platform before giving up */
|
|
35
|
+
const MAX_AUTO_MODELS = 300;
|
|
36
|
+
// How many model names one bell row may name before it elides the rest.
|
|
37
|
+
// A provider can gain dozens of models in one refresh; naming all of them
|
|
38
|
+
// turns the bell into a wall of text.
|
|
39
|
+
const DISCOVER_NOTIFICATION_MODELS = 5;
|
|
40
|
+
const PLATFORM_DEFAULTS = {
|
|
41
|
+
// ── Aggregators with published/observed limits ──
|
|
42
|
+
openrouter: { rpm: 5, rpd: 200, tpm: 200000, tpd: null }, // docs 20 rpm/200 rpd; live observed 5 rpm
|
|
43
|
+
routeway: { rpm: 5, rpd: 200, tpm: 200000, tpd: null }, // same pool as OpenRouter backend
|
|
44
|
+
kilo: { rpm: 3, rpd: 200, tpm: null, tpd: null }, // 200 req/hr keyless per IP
|
|
45
|
+
llm7: { rpm: 2, rpd: 100, tpm: null, tpd: null }, // 100 req/hr
|
|
46
|
+
unorouter: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // 1 req/min per account per model
|
|
47
|
+
bazaarlink: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // auto:free route
|
|
48
|
+
orcarouter: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // limits unpublished, tight free tier
|
|
49
|
+
nara: { rpm: 3, rpd: 100, tpm: null, tpd: null }, // free plan, conservative
|
|
50
|
+
'opencode-free': { rpm: 5, rpd: 200, tpm: null, tpd: null }, // zen free tier, unpublished limits
|
|
51
|
+
'opencode-go': { rpm: 10, rpd: 500, tpm: null, tpd: null }, // go tier, separate pool from zen
|
|
52
|
+
// ── First-party providers (documented free tier) ──
|
|
53
|
+
groq: { rpm: 30, rpd: 14400, tpm: 131072, tpd: 5000000 }, // free tier; varies by model
|
|
54
|
+
navy: { rpm: 20, rpd: null, tpm: null, tpd: 150000 }, // 150K tokens/day, 20 RPM
|
|
55
|
+
airforce: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // 1 req/min per account
|
|
56
|
+
ovh: { rpm: 2, rpd: null, tpm: null, tpd: null }, // 2 req/min keyless per model
|
|
57
|
+
modelscope: { rpm: 1, rpd: 2000, tpm: null, tpd: null }, // 2000 req/day
|
|
58
|
+
sealion: { rpm: 10, rpd: null, tpm: null, tpd: null }, // 10 RPM free tier
|
|
59
|
+
radeon: { rpm: 30, rpd: null, tpm: null, tpd: null }, // 30 RPM; $10/day credits
|
|
60
|
+
agnes: { rpm: 30, rpd: null, tpm: null, tpd: null }, // ~30 concurrent before 429
|
|
61
|
+
// ── Providers with generous/unpublished limits (conservative) ──
|
|
62
|
+
mistral: { rpm: 30, rpd: null, tpm: null, tpd: null }, // free plan, ~30 rpm observed
|
|
63
|
+
nvidia: { rpm: 40, rpd: null, tpm: null, tpd: null }, // generous free tier
|
|
64
|
+
google: { rpm: 15, rpd: null, tpm: 1000000, tpd: 50000000 }, // generous but bounded
|
|
65
|
+
anyapi: { rpm: null, rpd: null, tpm: null, tpd: 100000 }, // 100K tokens/day documented
|
|
66
|
+
xai: { rpm: null, rpd: null, tpm: null, tpd: null },
|
|
67
|
+
github: { rpm: null, rpd: null, tpm: null, tpd: null },
|
|
68
|
+
zhipu: { rpm: null, rpd: null, tpm: null, tpd: null },
|
|
69
|
+
huggingface: { rpm: null, rpd: null, tpm: null, tpd: null },
|
|
70
|
+
siliconflow: { rpm: null, rpd: null, tpm: null, tpd: null },
|
|
71
|
+
ollama: { rpm: null, rpd: null, tpm: null, tpd: null },
|
|
72
|
+
};
|
|
73
|
+
/** Conservative fallback when a platform is not in the map. */
|
|
74
|
+
const DEFAULT_FALLBACK = { rpm: 30, rpd: 1000, tpm: 100000, tpd: 1000000 };
|
|
75
|
+
/** Resolve the rate-limit defaults for a platform. */
|
|
76
|
+
function platformRates(platform) {
|
|
77
|
+
return PLATFORM_DEFAULTS[platform] ?? DEFAULT_FALLBACK;
|
|
78
|
+
}
|
|
79
|
+
function inferModelRanks(modelId) {
|
|
80
|
+
const lower = modelId.toLowerCase();
|
|
81
|
+
let intel = 100;
|
|
82
|
+
let spd = 100;
|
|
83
|
+
// Speed boosts (lower = faster / lighter)
|
|
84
|
+
if (/(?:^|-)(?:flash|mini|fast|lite|tiny|nano)(?:$|-)/i.test(lower))
|
|
85
|
+
spd = 80;
|
|
86
|
+
if (/(?:^|-)(?:turbo|instant|quick)(?:$|-)/i.test(lower))
|
|
87
|
+
spd = 85;
|
|
88
|
+
if (/(?:^|-)(?:haiku|small)(?:$|-)/i.test(lower))
|
|
89
|
+
spd = 85;
|
|
90
|
+
if (/(?:^|-)(?:medium)(?:$|-)/i.test(lower))
|
|
91
|
+
spd = 95;
|
|
92
|
+
// Speed penalties (slower / heavier)
|
|
93
|
+
if (/70b|405b|120b/i.test(lower))
|
|
94
|
+
spd = 120;
|
|
95
|
+
if (/(?:^|-)(?:large|ultra|thinking|reasoning)(?:$|-)/i.test(lower))
|
|
96
|
+
spd = 115;
|
|
97
|
+
if (/(?:^|-)(?:pro(?!mo)|max)(?:$|-)/i.test(lower))
|
|
98
|
+
spd = 110;
|
|
99
|
+
// Intelligence boosts (lower = smarter)
|
|
100
|
+
if (/405b/i.test(lower))
|
|
101
|
+
intel = 80;
|
|
102
|
+
if (/70b|120b/i.test(lower))
|
|
103
|
+
intel = 85;
|
|
104
|
+
if (/(?:^|-)(?:opus)(?:$|-)/i.test(lower))
|
|
105
|
+
intel = 82;
|
|
106
|
+
if (/(?:^|-)(?:sonnet)(?:$|-)/i.test(lower))
|
|
107
|
+
intel = 88;
|
|
108
|
+
if (/(?:^|-)(?:pro(?!mo)|max|ultra)(?:$|-)/i.test(lower))
|
|
109
|
+
intel = 85;
|
|
110
|
+
if (/(?:^|-)(?:plus)(?:$|-)/i.test(lower))
|
|
111
|
+
intel = 90;
|
|
112
|
+
// Intelligence penalties (weaker / distilled)
|
|
113
|
+
if (/(?:^|-)(?:flash|mini|tiny|nano|lite)(?:$|-)/i.test(lower))
|
|
114
|
+
intel = 120;
|
|
115
|
+
if (/(?:^|-)(?:small)(?:$|-)/i.test(lower))
|
|
116
|
+
intel = 115;
|
|
117
|
+
return { intelligence: intel, speed: spd };
|
|
118
|
+
}
|
|
119
|
+
// ── Embedding dimension guessing ──────────────────────────────────
|
|
120
|
+
// When the provider doesn't report dimensions, we estimate from the model id.
|
|
121
|
+
function guessEmbeddingDimensions(modelId) {
|
|
122
|
+
const lower = modelId.toLowerCase();
|
|
123
|
+
if (/3072/.test(lower))
|
|
124
|
+
return 3072;
|
|
125
|
+
if (/1536/.test(lower))
|
|
126
|
+
return 1536;
|
|
127
|
+
if (/1024/.test(lower))
|
|
128
|
+
return 1024;
|
|
129
|
+
if (/768/.test(lower))
|
|
130
|
+
return 768;
|
|
131
|
+
if (/512/.test(lower))
|
|
132
|
+
return 512;
|
|
133
|
+
if (/384/.test(lower))
|
|
134
|
+
return 384;
|
|
135
|
+
if (/256/.test(lower))
|
|
136
|
+
return 256;
|
|
137
|
+
if (/128/.test(lower))
|
|
138
|
+
return 128;
|
|
139
|
+
// Well-known model families
|
|
140
|
+
if (/text-embedding-3-small/.test(lower))
|
|
141
|
+
return 1536;
|
|
142
|
+
if (/text-embedding-3-large/.test(lower))
|
|
143
|
+
return 3072;
|
|
144
|
+
if (/text-embedding-ada/.test(lower))
|
|
145
|
+
return 1536;
|
|
146
|
+
if (/nomic/.test(lower))
|
|
147
|
+
return 768;
|
|
148
|
+
if (/bge-large/.test(lower))
|
|
149
|
+
return 1024;
|
|
150
|
+
if (/bge-base/.test(lower))
|
|
151
|
+
return 768;
|
|
152
|
+
if (/bge-small/.test(lower))
|
|
153
|
+
return 512;
|
|
154
|
+
if (/e5-large/.test(lower))
|
|
155
|
+
return 1024;
|
|
156
|
+
if (/e5-base/.test(lower))
|
|
157
|
+
return 768;
|
|
158
|
+
if (/e5-small/.test(lower))
|
|
159
|
+
return 512;
|
|
160
|
+
if (/cohere/.test(lower))
|
|
161
|
+
return 1024;
|
|
162
|
+
return 0; // unknown
|
|
163
|
+
}
|
|
164
|
+
/** Extract dimensions from the raw API response, or fall back to heuristics. */
|
|
165
|
+
function extractDimensions(modelId, rawModels) {
|
|
166
|
+
const raw = rawModels.find((m) => {
|
|
167
|
+
const id = m?.id ?? m?.name ?? m?.model ?? m?.model_id ?? m?.modelId ?? m?.slug;
|
|
168
|
+
return id === modelId;
|
|
169
|
+
});
|
|
170
|
+
if (raw) {
|
|
171
|
+
const dim = raw.dimensions ?? raw.dim ?? raw.embedding_dim ?? raw.dimension;
|
|
172
|
+
if (typeof dim === 'number' && dim > 0)
|
|
173
|
+
return dim;
|
|
174
|
+
}
|
|
175
|
+
return guessEmbeddingDimensions(modelId);
|
|
176
|
+
}
|
|
177
|
+
/** Derive embedding family name from model id using well-known patterns. */
|
|
178
|
+
function guessEmbeddingFamily(modelId) {
|
|
179
|
+
const lower = modelId.toLowerCase();
|
|
180
|
+
// Well-known embedding families — check specific patterns first
|
|
181
|
+
if (/^bge[-_]/.test(lower))
|
|
182
|
+
return 'bge';
|
|
183
|
+
if (/^e5[-_]/.test(lower))
|
|
184
|
+
return 'e5';
|
|
185
|
+
if (/^jina[-_]/.test(lower))
|
|
186
|
+
return 'jina';
|
|
187
|
+
if (/^gte[-_]/.test(lower))
|
|
188
|
+
return 'gte';
|
|
189
|
+
if (/^gte[-_]?embed/.test(lower))
|
|
190
|
+
return 'gte';
|
|
191
|
+
if (/^nomic[-_]/.test(lower))
|
|
192
|
+
return 'nomic';
|
|
193
|
+
if (/^cohere[-_]|^embed[-_]/.test(lower))
|
|
194
|
+
return 'cohere';
|
|
195
|
+
if (/^openai[-_]|^text-embedding[-_]/.test(lower))
|
|
196
|
+
return 'openai';
|
|
197
|
+
if (/^sentence[-_]transformer/.test(lower))
|
|
198
|
+
return 'sentence-transformer';
|
|
199
|
+
if (/^instructor[-_]/.test(lower))
|
|
200
|
+
return 'instructor';
|
|
201
|
+
if (/^sfr[-_]/.test(lower))
|
|
202
|
+
return 'sfr';
|
|
203
|
+
if (/^snowflake[-_]/.test(lower))
|
|
204
|
+
return 'snowflake';
|
|
205
|
+
if (/^mxbai[-_]/.test(lower))
|
|
206
|
+
return 'mxbai';
|
|
207
|
+
if (/^voyage[-_]/.test(lower))
|
|
208
|
+
return 'voyage';
|
|
209
|
+
if (/^arctic[-_]/.test(lower))
|
|
210
|
+
return 'arctic';
|
|
211
|
+
if (/^multilingual[-_]/.test(lower))
|
|
212
|
+
return 'multilingual-e5';
|
|
213
|
+
// Fallback: strip version suffixes
|
|
214
|
+
return lower.replace(/[-_]v?\d+$/, '').replace(/[-_]0\d+$/, '') || modelId;
|
|
215
|
+
}
|
|
216
|
+
// ── Quirk auto-detection ──────────────────────────────────────────
|
|
217
|
+
/** Known provider patterns we auto-detect from the /v1/models response. */
|
|
218
|
+
const AUTO_QUIRK_DETECTORS = [
|
|
219
|
+
{
|
|
220
|
+
detect: (_p, models) => models.length > 3 && models.every(m => !m.priceNote && !m.isFree),
|
|
221
|
+
slug: 'auto-no-pricing',
|
|
222
|
+
title: 'No pricing info from upstream',
|
|
223
|
+
body: 'This provider did not advertise pricing in /v1/models. Models may be free or paid — verify before relying on them.',
|
|
224
|
+
severity: 'info',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
detect: (_p, models) => {
|
|
228
|
+
const chat = models.filter(m => !m.kind);
|
|
229
|
+
if (chat.length < 3)
|
|
230
|
+
return false;
|
|
231
|
+
return chat.filter(m => (m.contextWindow ?? 128000) < 8192).length > chat.length * 0.5;
|
|
232
|
+
},
|
|
233
|
+
slug: 'auto-short-context',
|
|
234
|
+
title: 'Most chat models have short context',
|
|
235
|
+
body: 'More than half of discovered chat models have context windows under 8K tokens. Consider a different provider for long-context tasks.',
|
|
236
|
+
severity: 'warning',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
detect: (_p, models) => {
|
|
240
|
+
const hasChat = models.some(m => !m.kind);
|
|
241
|
+
const hasEmbed = models.some(m => m.kind === 'embedding');
|
|
242
|
+
return !hasChat && hasEmbed && models.length > 0;
|
|
243
|
+
},
|
|
244
|
+
slug: 'auto-embeddings-only',
|
|
245
|
+
title: 'Embeddings-only provider',
|
|
246
|
+
body: 'This provider only serves embedding models. It cannot be used for chat completions.',
|
|
247
|
+
severity: 'info',
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
detect: (_p, models) => models.length > 0 && models.every(m => m.isFree === true),
|
|
251
|
+
slug: 'auto-all-free',
|
|
252
|
+
title: 'All models reported as free',
|
|
253
|
+
body: 'Every model from this provider is advertised as free. Free tiers can change without notice.',
|
|
254
|
+
severity: 'info',
|
|
255
|
+
},
|
|
256
|
+
// ── P4: expanded quirk detectors ─────────────────────────────
|
|
257
|
+
// NOTE: auto-empty-response-prone is now evidence-based (see queryEmptyResponseRatio + autoAssignQuirks).
|
|
258
|
+
// It is NOT a static detector — it queries the requests table for actual empty-completion rates.
|
|
259
|
+
{
|
|
260
|
+
// Many models lack context window info
|
|
261
|
+
detect: (_p, models) => {
|
|
262
|
+
if (models.length < 5)
|
|
263
|
+
return false;
|
|
264
|
+
return models.filter(m => !m.contextWindow || m.contextWindow === 0).length > models.length * 0.7;
|
|
265
|
+
},
|
|
266
|
+
slug: 'auto-missing-context-info',
|
|
267
|
+
title: 'Most models lack context window info',
|
|
268
|
+
body: 'More than 70% of models from this provider have no context window data. Default 128K is used; actual limits may differ.',
|
|
269
|
+
severity: 'info',
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
// Large model catalog — likely an aggregator
|
|
273
|
+
detect: (_p, models) => models.length > 50,
|
|
274
|
+
slug: 'auto-many-models',
|
|
275
|
+
title: 'Large model catalog (50+)',
|
|
276
|
+
body: 'This provider offers 50+ models, suggesting it is an aggregator. Some models may be behind paid gates despite appearing free.',
|
|
277
|
+
severity: 'info',
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
// All models have small context windows (<=32K)
|
|
281
|
+
detect: (_p, models) => {
|
|
282
|
+
const chat = models.filter(m => !m.kind);
|
|
283
|
+
if (chat.length < 3)
|
|
284
|
+
return false;
|
|
285
|
+
return chat.every(m => (m.contextWindow ?? 128000) <= 32768);
|
|
286
|
+
},
|
|
287
|
+
slug: 'auto-small-context-only',
|
|
288
|
+
title: 'All chat models have small context (<=32K)',
|
|
289
|
+
body: 'All chat models from this provider have context windows of 32K or less. Not suitable for long-context tasks.',
|
|
290
|
+
severity: 'warning',
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
// Only media models (no chat, no embeddings)
|
|
294
|
+
detect: (_p, models) => {
|
|
295
|
+
const hasChat = models.some(m => !m.kind);
|
|
296
|
+
const hasEmb = models.some(m => m.kind === 'embedding');
|
|
297
|
+
const hasMedia = models.some(m => m.kind && m.kind !== 'embedding');
|
|
298
|
+
return !hasChat && !hasEmb && hasMedia && models.length > 0;
|
|
299
|
+
},
|
|
300
|
+
slug: 'auto-media-only',
|
|
301
|
+
title: 'Media-only provider',
|
|
302
|
+
body: 'This provider only serves generative media models (image/audio/video). It cannot be used for chat or embeddings.',
|
|
303
|
+
severity: 'info',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
// Only 1-2 models — limited provider
|
|
307
|
+
detect: (_p, models) => {
|
|
308
|
+
const chat = models.filter(m => !m.kind);
|
|
309
|
+
return chat.length >= 1 && chat.length <= 2;
|
|
310
|
+
},
|
|
311
|
+
slug: 'auto-few-models',
|
|
312
|
+
title: 'Very few chat models (1-2)',
|
|
313
|
+
body: 'This provider only offers 1-2 chat models. Consider using it only as a fallback, not a primary route.',
|
|
314
|
+
severity: 'info',
|
|
315
|
+
},
|
|
316
|
+
];
|
|
317
|
+
// ── Evidence-based empty response detection ─────────────────────
|
|
318
|
+
// Instead of hardcoding known-bad platforms, we query the requests table
|
|
319
|
+
// for actual empty-completion rates. A platform gets flagged when:
|
|
320
|
+
// - ≥50 requests in the last 7 days (enough data to judge)
|
|
321
|
+
// - >15% of those requests resulted in empty completions
|
|
322
|
+
// The quirk is automatically removed when the ratio drops below 10%
|
|
323
|
+
// (platform improved) or when there isn't enough data yet.
|
|
324
|
+
const EMPTY_RESPONSE_RATIO_ADD = 0.15; // 15% → add quirk
|
|
325
|
+
const EMPTY_RESPONSE_RATIO_REMOVE = 0.10; // 10% → remove quirk (improved)
|
|
326
|
+
const EMPTY_RESPONSE_MIN_REQUESTS = 50; // need at least this many to judge
|
|
327
|
+
const EMPTY_RESPONSE_WINDOW_DAYS = 7;
|
|
328
|
+
/**
|
|
329
|
+
* Query the requests table for empty-completion evidence on a platform.
|
|
330
|
+
* Returns { ratio, total, empty } or null if insufficient data.
|
|
331
|
+
*/
|
|
332
|
+
function queryEmptyResponseRatio(db, platform) {
|
|
333
|
+
const since = new Date(Date.now() - EMPTY_RESPONSE_WINDOW_DAYS * 86_400_000).toISOString();
|
|
334
|
+
const row = db.prepare(`
|
|
335
|
+
SELECT
|
|
336
|
+
COUNT(*) AS total,
|
|
337
|
+
SUM(CASE WHEN error LIKE '%empty completion%' THEN 1 ELSE 0 END) AS empty
|
|
338
|
+
FROM requests
|
|
339
|
+
WHERE platform = ? AND created_at >= ?
|
|
340
|
+
`).get(platform, since);
|
|
341
|
+
if (!row || row.total < EMPTY_RESPONSE_MIN_REQUESTS)
|
|
342
|
+
return null;
|
|
343
|
+
return { ratio: row.empty / row.total, total: row.total, empty: row.empty };
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Auto-assign quirks to a platform based on what we observe in its /v1/models.
|
|
347
|
+
* Only inserts quirks that don't already exist (operator or catalog quirks win).
|
|
348
|
+
*/
|
|
349
|
+
function autoAssignQuirks(db, platform, models, _rawModels) {
|
|
350
|
+
const insertQuirk = db.prepare(`
|
|
351
|
+
INSERT OR IGNORE INTO quirks (slug, title, body, severity, created_at_ms, updated_at_ms)
|
|
352
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
353
|
+
`);
|
|
354
|
+
const getQuirkId = db.prepare('SELECT id FROM quirks WHERE slug = ?');
|
|
355
|
+
const insertTarget = db.prepare('INSERT OR IGNORE INTO quirk_targets (quirk_id, platform, model_glob) VALUES (?, ?, ?)');
|
|
356
|
+
const now = Date.now();
|
|
357
|
+
let count = 0;
|
|
358
|
+
for (const detector of AUTO_QUIRK_DETECTORS) {
|
|
359
|
+
if (!detector.detect(platform, models))
|
|
360
|
+
continue;
|
|
361
|
+
// Scoped slug: platform prefix prevents cross-platform collision
|
|
362
|
+
const scopedSlug = `${detector.slug}--${platform}`;
|
|
363
|
+
// Skip if already exists (operator or catalog already set it)
|
|
364
|
+
const existing = db.prepare('SELECT 1 FROM quirks WHERE slug = ?').get(scopedSlug);
|
|
365
|
+
if (existing)
|
|
366
|
+
continue;
|
|
367
|
+
insertQuirk.run(scopedSlug, detector.title, detector.body, detector.severity, now, now);
|
|
368
|
+
const quirkRow = getQuirkId.get(scopedSlug);
|
|
369
|
+
if (quirkRow) {
|
|
370
|
+
insertTarget.run(quirkRow.id, platform, null);
|
|
371
|
+
count++;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// ── Evidence-based empty response detection ────────────────────
|
|
375
|
+
// Instead of hardcoding platform names, we query actual request logs.
|
|
376
|
+
// The quirk is added when empty-completion ratio >15% (min 50 requests
|
|
377
|
+
// in 7 days), and auto-removed when the ratio drops below 10%.
|
|
378
|
+
const emptySlug = `auto-empty-response-prone--${platform}`;
|
|
379
|
+
const emptyEvidence = queryEmptyResponseRatio(db, platform);
|
|
380
|
+
if (emptyEvidence) {
|
|
381
|
+
const { ratio, total, empty } = emptyEvidence;
|
|
382
|
+
if (ratio > EMPTY_RESPONSE_RATIO_ADD) {
|
|
383
|
+
// Ratio is high → add quirk if not already present, or update evidence
|
|
384
|
+
const existing = db.prepare('SELECT id FROM quirks WHERE slug = ?').get(emptySlug);
|
|
385
|
+
if (!existing) {
|
|
386
|
+
insertQuirk.run(emptySlug, 'Provider has high empty-completion rate (evidence-based)', `${Math.round(ratio * 100)}% of ${total} requests in the last ${EMPTY_RESPONSE_WINDOW_DAYS} days returned empty completions (${empty} empty). A retry/backoff strategy is recommended.`, 'warning', now, now);
|
|
387
|
+
const quirkRow = getQuirkId.get(emptySlug);
|
|
388
|
+
if (quirkRow) {
|
|
389
|
+
insertTarget.run(quirkRow.id, platform, null);
|
|
390
|
+
count++;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// Update the evidence body so stale hardcoded descriptions get refreshed
|
|
395
|
+
const newBody = `${Math.round(ratio * 100)}% of ${total} requests in the last ${EMPTY_RESPONSE_WINDOW_DAYS} days returned empty completions (${empty} empty). A retry/backoff strategy is recommended.`;
|
|
396
|
+
db.prepare('UPDATE quirks SET body = ?, updated_at_ms = ? WHERE id = ?').run(newBody, now, existing.id);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else if (ratio < EMPTY_RESPONSE_RATIO_REMOVE) {
|
|
400
|
+
// Ratio is low → remove the auto-detected quirk (platform improved)
|
|
401
|
+
const existing = db.prepare('SELECT id FROM quirks WHERE slug = ?').get(emptySlug);
|
|
402
|
+
if (existing) {
|
|
403
|
+
// Only remove if it was auto-detected (not operator/catalog-set)
|
|
404
|
+
// Auto-detected quirks are identified by the 'auto-' prefix
|
|
405
|
+
db.prepare('DELETE FROM quirk_targets WHERE quirk_id = ?').run(existing.id);
|
|
406
|
+
db.prepare('DELETE FROM quirks WHERE id = ?').run(existing.id);
|
|
407
|
+
count--; // signal that we removed a quirk (negative count convention)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// If ratio is between REMOVE and ADD thresholds (10-15%), keep current state
|
|
411
|
+
}
|
|
412
|
+
return count;
|
|
413
|
+
}
|
|
414
|
+
// ── Monthly token budget estimation (P4) ────────────────────────
|
|
415
|
+
// Auto-discovered models have no monthly_token_budget label. We derive a
|
|
416
|
+
// human-readable estimate from the platform's rate limits so analytics
|
|
417
|
+
// ("Est. savings") and the headroom guardrail work right out of the box.
|
|
418
|
+
function estimateMonthlyBudget(tpd, tpm, rpd) {
|
|
419
|
+
// tpd (tokens per day) is the strongest signal
|
|
420
|
+
if (tpd != null && tpd > 0) {
|
|
421
|
+
const monthly = tpd * 30;
|
|
422
|
+
if (monthly >= 1_000_000_000)
|
|
423
|
+
return `~${Math.round(monthly / 1_000_000_000)}B`;
|
|
424
|
+
if (monthly >= 1_000_000)
|
|
425
|
+
return `~${Math.round(monthly / 1_000_000)}M`;
|
|
426
|
+
if (monthly >= 1_000)
|
|
427
|
+
return `~${Math.round(monthly / 1_000)}K`;
|
|
428
|
+
return `~${monthly}`;
|
|
429
|
+
}
|
|
430
|
+
// tpm (tokens per minute) → extrapolate to 30-day month
|
|
431
|
+
if (tpm != null && tpm > 0) {
|
|
432
|
+
const monthly = tpm * 60 * 24 * 30;
|
|
433
|
+
if (monthly >= 1_000_000_000)
|
|
434
|
+
return `~${Math.round(monthly / 1_000_000_000)}B`;
|
|
435
|
+
if (monthly >= 1_000_000)
|
|
436
|
+
return `~${Math.round(monthly / 1_000_000)}M`;
|
|
437
|
+
if (monthly >= 1_000)
|
|
438
|
+
return `~${Math.round(monthly / 1_000)}K`;
|
|
439
|
+
return `~${monthly}`;
|
|
440
|
+
}
|
|
441
|
+
// rpd (requests per day) → estimate ~1K tokens per request on average
|
|
442
|
+
if (rpd != null && rpd > 0) {
|
|
443
|
+
const monthly = rpd * 30 * 1000;
|
|
444
|
+
if (monthly >= 1_000_000)
|
|
445
|
+
return `~${Math.round(monthly / 1_000_000)}M`;
|
|
446
|
+
if (monthly >= 1_000)
|
|
447
|
+
return `~${Math.round(monthly / 1_000)}K`;
|
|
448
|
+
return `~${monthly}`;
|
|
449
|
+
}
|
|
450
|
+
return '';
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Count enabled models for a platform across all model tables.
|
|
454
|
+
*/
|
|
455
|
+
export function enabledModelCountForPlatform(platform) {
|
|
456
|
+
const db = getDb();
|
|
457
|
+
const row = db.prepare(`
|
|
458
|
+
SELECT (SELECT COUNT(*) FROM models WHERE platform = ? AND enabled = 1)
|
|
459
|
+
+ (SELECT COUNT(*) FROM embedding_models WHERE platform = ? AND enabled = 1)
|
|
460
|
+
+ (SELECT COUNT(*) FROM media_models WHERE platform = ? AND enabled = 1) AS c
|
|
461
|
+
`).get(platform, platform, platform);
|
|
462
|
+
return row.c;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Discover and register models for a platform that has API keys but zero
|
|
466
|
+
* models. Uses the provider's /v1/models endpoint with the user's own key.
|
|
467
|
+
*
|
|
468
|
+
* Returns a DiscoverResult with counts and model-name lists, or an empty
|
|
469
|
+
* result if nothing was needed or discoverable.
|
|
470
|
+
*/
|
|
471
|
+
export async function autoDiscoverModels(platform, opts = {}) {
|
|
472
|
+
const empty = { registered: 0, refreshed: 0, retired: 0, embeddingsRegistered: 0, embeddingsRefreshed: 0, mediaRegistered: 0, mediaRefreshed: 0, quirksAssigned: 0, newModels: [], updatedModels: [], retiredModels: [] };
|
|
473
|
+
if (SKIP_AUTO_DISCOVER.has(platform))
|
|
474
|
+
return empty;
|
|
475
|
+
// Check if the platform already has all three model types — only skip if
|
|
476
|
+
// chat + embeddings + media are all populated. Platforms that have chat
|
|
477
|
+
// models but lack embeddings or media should still be discovered.
|
|
478
|
+
if (!opts.refresh) {
|
|
479
|
+
const dbPre = getDb();
|
|
480
|
+
const row = dbPre.prepare(`
|
|
481
|
+
SELECT
|
|
482
|
+
(SELECT COUNT(*) FROM models WHERE platform = ? AND enabled = 1) AS chat,
|
|
483
|
+
(SELECT COUNT(*) FROM embedding_models WHERE platform = ? AND enabled = 1) AS emb,
|
|
484
|
+
(SELECT COUNT(*) FROM media_models WHERE platform = ? AND enabled = 1) AS media
|
|
485
|
+
`).get(platform, platform, platform);
|
|
486
|
+
if (row.chat > 0 && row.emb > 0 && row.media > 0)
|
|
487
|
+
return empty;
|
|
488
|
+
}
|
|
489
|
+
const db = getDb();
|
|
490
|
+
// Find an enabled key for this platform
|
|
491
|
+
const keyRow = db.prepare('SELECT id, encrypted_key, iv, auth_tag FROM api_keys WHERE platform = ? AND enabled = 1 LIMIT 1').get(platform);
|
|
492
|
+
if (!keyRow)
|
|
493
|
+
return empty;
|
|
494
|
+
// Resolve the provider
|
|
495
|
+
const provider = resolveProvider(platform);
|
|
496
|
+
if (!provider || !(provider instanceof OpenAICompatProvider))
|
|
497
|
+
return empty;
|
|
498
|
+
// Decrypt the key (import dynamically to avoid circular deps)
|
|
499
|
+
const { decrypt } = await import('../lib/crypto.js');
|
|
500
|
+
let apiKey;
|
|
501
|
+
try {
|
|
502
|
+
apiKey = decrypt(keyRow.encrypted_key, keyRow.iv, keyRow.auth_tag);
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
return empty;
|
|
506
|
+
}
|
|
507
|
+
// Fetch the provider's model catalog
|
|
508
|
+
let models;
|
|
509
|
+
let rawBody;
|
|
510
|
+
try {
|
|
511
|
+
const res = await provider.fetchModelCatalog(apiKey);
|
|
512
|
+
if (!res.ok) {
|
|
513
|
+
console.log(`[auto-discover] ${platform}/models returned HTTP ${res.status} — skipping`);
|
|
514
|
+
return empty;
|
|
515
|
+
}
|
|
516
|
+
rawBody = await res.json();
|
|
517
|
+
models = parseModelCatalog(rawBody);
|
|
518
|
+
}
|
|
519
|
+
catch (err) {
|
|
520
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
521
|
+
console.warn(`[auto-discover] ${platform} discovery failed: ${msg}`);
|
|
522
|
+
return empty;
|
|
523
|
+
}
|
|
524
|
+
if (models.length === 0) {
|
|
525
|
+
console.log(`[auto-discover] ${platform} returned 0 models — skipping`);
|
|
526
|
+
return empty;
|
|
527
|
+
}
|
|
528
|
+
// ── Free-tier filter ──────────────────────────────────────────────
|
|
529
|
+
// We only want free models — loading paid ones causes 402/403 errors at
|
|
530
|
+
// request time and clutters the routing chain with unusable entries.
|
|
531
|
+
//
|
|
532
|
+
// Strategy (ordered by priority):
|
|
533
|
+
// 0. Providers that publish an explicit per-row `access_tier` (xkiro):
|
|
534
|
+
// keep access_tier === 'free'. This is the AUTHORITATIVE signal where it
|
|
535
|
+
// exists — xkiro's `:free` id suffix (rule 2) lists only 28 of its 58
|
|
536
|
+
// free rows, so the suffix filter silently dropped 30 free models, while
|
|
537
|
+
// no `:free` row is ever paid/premium. See providers/xkiro.ts.
|
|
538
|
+
// 1. Providers with a `tier` field (e.g. airforce): only keep tier='free'
|
|
539
|
+
// 2. Providers that use `:free` suffix convention (e.g. unorouter):
|
|
540
|
+
// only keep models whose id ends with ':free'
|
|
541
|
+
// 3. Everything else: pass through (assumed free)
|
|
542
|
+
let freeModels = models;
|
|
543
|
+
const body = rawBody;
|
|
544
|
+
const rawModels = Array.isArray(body?.data) ? body.data
|
|
545
|
+
: Array.isArray(body?.models) ? body.models
|
|
546
|
+
: Array.isArray(rawBody) ? rawBody
|
|
547
|
+
: [];
|
|
548
|
+
const hasAccessTierField = rawModels.some((m) => typeof m?.access_tier === 'string');
|
|
549
|
+
const hasTierField = rawModels.some((m) => typeof m?.tier === 'string');
|
|
550
|
+
if (hasAccessTierField) {
|
|
551
|
+
// Authoritative: the upstream tells us the tier per row. Match on the raw
|
|
552
|
+
// list (which carries the field) and project onto the parsed models.
|
|
553
|
+
const freeIds = new Set();
|
|
554
|
+
for (const m of rawModels) {
|
|
555
|
+
if (m.access_tier === 'free')
|
|
556
|
+
freeIds.add(m.id);
|
|
557
|
+
}
|
|
558
|
+
const beforeCount = freeModels.length;
|
|
559
|
+
freeModels = freeModels.filter(m => freeIds.has(m.id));
|
|
560
|
+
console.log(`[auto-discover] ${platform}: access_tier filter (${beforeCount} → ${freeModels.length} models)`);
|
|
561
|
+
}
|
|
562
|
+
else if (hasTierField) {
|
|
563
|
+
// Build a set of free model ids from the raw response
|
|
564
|
+
const freeIds = new Set();
|
|
565
|
+
for (const m of rawModels) {
|
|
566
|
+
if (m.tier === 'free')
|
|
567
|
+
freeIds.add(m.id);
|
|
568
|
+
}
|
|
569
|
+
freeModels = models.filter(m => freeIds.has(m.id));
|
|
570
|
+
console.log(`[auto-discover] ${platform}: ${freeIds.size}/${rawModels.length} models are free-tier (${models.length} parsed → ${freeModels.length} after filter)`);
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
// No tier field — check if models use the `:free` suffix convention.
|
|
574
|
+
// If ANY model id ends with ':free', assume this provider uses the pattern
|
|
575
|
+
// and only keep those. Otherwise pass everything through.
|
|
576
|
+
const hasColonFree = rawModels.some((m) => typeof m?.id === 'string' && m.id.endsWith(':free'));
|
|
577
|
+
if (hasColonFree) {
|
|
578
|
+
const beforeCount = freeModels.length;
|
|
579
|
+
freeModels = freeModels.filter(m => m.id.endsWith(':free'));
|
|
580
|
+
console.log(`[auto-discover] ${platform}: :free suffix filter (${beforeCount} → ${freeModels.length} models)`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// ── Split models by kind ──────────────────────────────────────────
|
|
584
|
+
const chatModels = freeModels.filter(m => !m.kind);
|
|
585
|
+
const embeddingModels = freeModels.filter(m => m.kind === 'embedding');
|
|
586
|
+
const mediaModels = freeModels.filter(m => m.kind && m.kind !== 'embedding');
|
|
587
|
+
if (chatModels.length === 0 && embeddingModels.length === 0 && mediaModels.length === 0) {
|
|
588
|
+
console.log(`[auto-discover] ${platform} has ${freeModels.length} free models but none are registerable — skipping`);
|
|
589
|
+
return empty;
|
|
590
|
+
}
|
|
591
|
+
// ── Verify free ACCESS for chat models only ─────────────────────
|
|
592
|
+
// A tier label is a claim; only a live request is evidence. xkiro labels all
|
|
593
|
+
// five `deepseek/*` rows `access_tier: 'free'` with zero pricing, yet every
|
|
594
|
+
// one answers a free-plan key with 403 "available to paying customers only".
|
|
595
|
+
let verifiedChatModels = chatModels;
|
|
596
|
+
if (hasAccessTierField) {
|
|
597
|
+
const { probeFreeAccess } = await import('../providers/xkiro.js');
|
|
598
|
+
verifiedChatModels = [];
|
|
599
|
+
let paid = 0;
|
|
600
|
+
let gone = 0;
|
|
601
|
+
for (const m of chatModels) {
|
|
602
|
+
const verdict = await probeFreeAccess(apiKey, m.id);
|
|
603
|
+
if (verdict === 'unauthorized') {
|
|
604
|
+
console.log(`[auto-discover] ${platform}: key rejected mid-verification — keeping remaining unverified`);
|
|
605
|
+
verifiedChatModels.push(m);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (verdict === 'paid') {
|
|
609
|
+
paid++;
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
if (verdict === 'gone') {
|
|
613
|
+
gone++;
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
verifiedChatModels.push(m);
|
|
617
|
+
}
|
|
618
|
+
console.log(`[auto-discover] ${platform}: chat access verification — ${verifiedChatModels.length} usable, ${paid} paid-gated, ${gone} gone`);
|
|
619
|
+
}
|
|
620
|
+
// ── Classify model size from name heuristics ──────────────────────
|
|
621
|
+
function classifySize(modelId) {
|
|
622
|
+
const lower = modelId.toLowerCase();
|
|
623
|
+
if (/70b|405b|120b|large|ultra|pro(?!mo)|max/i.test(lower))
|
|
624
|
+
return 'Large';
|
|
625
|
+
if (/(?:8|9|14|20|27|32|34)b|medium/i.test(lower))
|
|
626
|
+
return 'Medium';
|
|
627
|
+
if (/(?:3|4|7)b|tiny|nano|micro|small|lite|flash/i.test(lower))
|
|
628
|
+
return 'Small';
|
|
629
|
+
return 'Medium';
|
|
630
|
+
}
|
|
631
|
+
// ── Prepare statements (all types) ─────────────────────────────
|
|
632
|
+
// P0: rate limits are now platform-specific (see PLATFORM_DEFAULTS above)
|
|
633
|
+
const rates = platformRates(platform);
|
|
634
|
+
// P3: catalog metadata fallback — look up same model_id in catalog
|
|
635
|
+
const lookupCatalogMeta = db.prepare(`
|
|
636
|
+
SELECT rpm_limit, rpd_limit, tpm_limit, tpd_limit, intelligence_rank, speed_rank
|
|
637
|
+
FROM models WHERE model_id = ? AND source = 'catalog' AND enabled = 1
|
|
638
|
+
LIMIT 1
|
|
639
|
+
`);
|
|
640
|
+
const selectExistingChat = db.prepare('SELECT id, source, display_name, context_window, size_label, supports_vision, rpm_limit, rpd_limit, intelligence_rank, speed_rank FROM models WHERE platform = ? AND model_id = ?');
|
|
641
|
+
const insertModel = db.prepare(`
|
|
642
|
+
INSERT INTO models (platform, model_id, display_name, source, enabled,
|
|
643
|
+
intelligence_rank, speed_rank, size_label,
|
|
644
|
+
supports_vision, supports_tools, context_window,
|
|
645
|
+
rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget)
|
|
646
|
+
VALUES (?, ?, ?, 'user', 1,
|
|
647
|
+
?, ?, ?,
|
|
648
|
+
?, ?, ?,
|
|
649
|
+
?, ?, ?, ?, ?)
|
|
650
|
+
`);
|
|
651
|
+
const refreshModel = db.prepare('UPDATE models SET display_name = ?, size_label = ?, supports_vision = ?, supports_tools = ?, context_window = ? WHERE id = ?');
|
|
652
|
+
// P0+P2: upgrade limits/ranks for existing models still on old hardcoded defaults
|
|
653
|
+
const upgradeLimits = db.prepare('UPDATE models SET rpm_limit = ?, rpd_limit = ?, tpm_limit = ?, tpd_limit = ?, intelligence_rank = ?, speed_rank = ?, monthly_token_budget = ? WHERE id = ?');
|
|
654
|
+
const benchModel = db.prepare('UPDATE models SET enabled = 0 WHERE id = ?');
|
|
655
|
+
// Embedding statements
|
|
656
|
+
const selectExistingEmbedding = db.prepare('SELECT id, display_name, dimensions FROM embedding_models WHERE platform = ? AND model_id = ?');
|
|
657
|
+
const insertEmbedding = db.prepare(`
|
|
658
|
+
INSERT INTO embedding_models (family, platform, model_id, display_name, dimensions, max_input_tokens, priority, enabled, quota_label, key_id)
|
|
659
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 1, 'auto-discovered', ?)
|
|
660
|
+
`);
|
|
661
|
+
const refreshEmbedding = db.prepare('UPDATE embedding_models SET display_name = ?, dimensions = ?, max_input_tokens = ? WHERE id = ?');
|
|
662
|
+
// Media statements
|
|
663
|
+
const selectExistingMedia = db.prepare('SELECT id, display_name, modality FROM media_models WHERE platform = ? AND model_id = ?');
|
|
664
|
+
const insertMedia = db.prepare(`
|
|
665
|
+
INSERT INTO media_models (platform, model_id, display_name, modality, priority, enabled, quota_label, key_id)
|
|
666
|
+
VALUES (?, ?, ?, ?, ?, 1, 'auto-discovered', ?)
|
|
667
|
+
`);
|
|
668
|
+
const refreshMedia = db.prepare('UPDATE media_models SET display_name = ?, modality = ? WHERE id = ?');
|
|
669
|
+
const maxP = db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get().mx;
|
|
670
|
+
const insertFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
|
|
671
|
+
// ── Transaction: register all model types + quirks ──────────────
|
|
672
|
+
let registered = 0;
|
|
673
|
+
let refreshed = 0;
|
|
674
|
+
let benched = 0;
|
|
675
|
+
const newModelNames = [];
|
|
676
|
+
const updatedModelNames = [];
|
|
677
|
+
const retiredModelNames = [];
|
|
678
|
+
const tx = db.transaction(() => {
|
|
679
|
+
let idx = 0;
|
|
680
|
+
const stillOffered = new Set();
|
|
681
|
+
// ──── 1. CHAT MODELS ────────────────────────────────────────
|
|
682
|
+
for (const m of verifiedChatModels.slice(0, MAX_AUTO_MODELS)) {
|
|
683
|
+
const size = classifySize(m.id);
|
|
684
|
+
const vision = m.vision ? 1 : 0;
|
|
685
|
+
const tools = /gpt|gemini|claude|mistral|qwen|llama.*3|deepseek|command/i.test(m.id) ? 1 : 0;
|
|
686
|
+
const ctx = m.contextWindow ?? 128000;
|
|
687
|
+
const display = m.id.replace(/[-_]/g, ' ');
|
|
688
|
+
const ranks = inferModelRanks(m.id); // P2: name-based rank inference
|
|
689
|
+
const existing = selectExistingChat.get(platform, m.id);
|
|
690
|
+
if (existing) {
|
|
691
|
+
stillOffered.add(existing.id);
|
|
692
|
+
if (existing.source === 'user') {
|
|
693
|
+
// Basic metadata refresh
|
|
694
|
+
const needsRefresh = existing.display_name !== display
|
|
695
|
+
|| existing.context_window !== ctx
|
|
696
|
+
|| existing.size_label !== size
|
|
697
|
+
|| existing.supports_vision !== vision;
|
|
698
|
+
if (needsRefresh) {
|
|
699
|
+
refreshModel.run(display, size, vision, tools, ctx, existing.id);
|
|
700
|
+
refreshed++;
|
|
701
|
+
updatedModelNames.push(m.id);
|
|
702
|
+
}
|
|
703
|
+
// P0+P2: upgrade limits/ranks when still on old hardcoded defaults
|
|
704
|
+
// (rpm=60, rpd=1000 was the previous universal default)
|
|
705
|
+
const onOldDefaults = existing.rpm_limit === 60 && existing.rpd_limit === 1000;
|
|
706
|
+
const onFlatRanks = existing.intelligence_rank != null && existing.speed_rank != null
|
|
707
|
+
&& Math.abs(existing.intelligence_rank - existing.speed_rank) <= 2
|
|
708
|
+
&& existing.intelligence_rank >= 100 && existing.intelligence_rank <= 100 + 300;
|
|
709
|
+
if (onOldDefaults || onFlatRanks) {
|
|
710
|
+
// Prefer catalog metadata for ranks if available (P3)
|
|
711
|
+
const catMeta = lookupCatalogMeta.get(m.id);
|
|
712
|
+
const upRpm = catMeta?.rpm_limit ?? rates.rpm;
|
|
713
|
+
const upRpd = catMeta?.rpd_limit ?? rates.rpd;
|
|
714
|
+
const upTpm = catMeta?.tpm_limit ?? rates.tpm;
|
|
715
|
+
const upTpd = catMeta?.tpd_limit ?? rates.tpd;
|
|
716
|
+
const upIntel = catMeta?.intelligence_rank ?? ranks.intelligence;
|
|
717
|
+
const upSpeed = catMeta?.speed_rank ?? ranks.speed;
|
|
718
|
+
// P4: also backfill budget for old models that lack it
|
|
719
|
+
const upBudget = estimateMonthlyBudget(upTpd, upTpm, upRpd);
|
|
720
|
+
upgradeLimits.run(upRpm, upRpd, upTpm, upTpd, upIntel, upSpeed, upBudget, existing.id);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
// P3: use catalog metadata as fallback for limits and ranks
|
|
726
|
+
const catalogMeta = lookupCatalogMeta.get(m.id);
|
|
727
|
+
const finalRpm = catalogMeta?.rpm_limit ?? rates.rpm;
|
|
728
|
+
const finalRpd = catalogMeta?.rpd_limit ?? rates.rpd;
|
|
729
|
+
const finalTpm = catalogMeta?.tpm_limit ?? rates.tpm;
|
|
730
|
+
const finalTpd = catalogMeta?.tpd_limit ?? rates.tpd;
|
|
731
|
+
const finalIntel = catalogMeta?.intelligence_rank ?? ranks.intelligence;
|
|
732
|
+
const finalSpeed = catalogMeta?.speed_rank ?? ranks.speed;
|
|
733
|
+
// P4: auto-estimate monthly_token_budget from rate limits
|
|
734
|
+
const estBudget = estimateMonthlyBudget(finalTpd, finalTpm, finalRpd);
|
|
735
|
+
const info = insertModel.run(platform, m.id, display, finalIntel, finalSpeed, size, vision, tools, ctx, finalRpm, finalRpd, finalTpm, finalTpd, estBudget);
|
|
736
|
+
const modelDbId = info.lastInsertRowid;
|
|
737
|
+
insertFb.run(modelDbId, maxP + 1 + idx);
|
|
738
|
+
ensureModelInProfiles(db, modelDbId);
|
|
739
|
+
stillOffered.add(modelDbId);
|
|
740
|
+
registered++;
|
|
741
|
+
newModelNames.push(m.id);
|
|
742
|
+
idx++;
|
|
743
|
+
}
|
|
744
|
+
// Retire chat rows this platform's discovery created that upstream dropped
|
|
745
|
+
if (stillOffered.size > 0) {
|
|
746
|
+
const ownedRows = db.prepare("SELECT id, model_id FROM models WHERE platform = ? AND source = 'user' AND enabled = 1").all(platform);
|
|
747
|
+
for (const row of ownedRows) {
|
|
748
|
+
if (stillOffered.has(row.id))
|
|
749
|
+
continue;
|
|
750
|
+
benchModel.run(row.id);
|
|
751
|
+
benched++;
|
|
752
|
+
retiredModelNames.push(row.model_id);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
// ──── 2. EMBEDDING MODELS ───────────────────────────────────
|
|
756
|
+
let embRegistered = 0;
|
|
757
|
+
let embRefreshed = 0;
|
|
758
|
+
for (const m of embeddingModels.slice(0, MAX_AUTO_MODELS)) {
|
|
759
|
+
const dims = extractDimensions(m.id, rawModels);
|
|
760
|
+
const family = guessEmbeddingFamily(m.id);
|
|
761
|
+
const display = m.id.replace(/[-_]/g, ' ');
|
|
762
|
+
const maxInput = m.contextWindow ?? null;
|
|
763
|
+
const keyId = keyRow.id;
|
|
764
|
+
const existing = selectExistingEmbedding.get(platform, m.id);
|
|
765
|
+
if (existing) {
|
|
766
|
+
if (existing.display_name !== display || (dims > 0 && existing.dimensions !== dims)) {
|
|
767
|
+
refreshEmbedding.run(display, dims || existing.dimensions, maxInput, existing.id);
|
|
768
|
+
embRefreshed++;
|
|
769
|
+
updatedModelNames.push(m.id);
|
|
770
|
+
}
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
insertEmbedding.run(family, platform, m.id, display, dims || 0, maxInput, 100 + idx, keyId);
|
|
774
|
+
embRegistered++;
|
|
775
|
+
newModelNames.push(m.id);
|
|
776
|
+
idx++;
|
|
777
|
+
}
|
|
778
|
+
// ──── 3. MEDIA MODELS ───────────────────────────────────────
|
|
779
|
+
let mediaRegistered = 0;
|
|
780
|
+
let mediaRefreshed = 0;
|
|
781
|
+
for (const m of mediaModels.slice(0, MAX_AUTO_MODELS)) {
|
|
782
|
+
const modality = m.kind; // 'image' | 'audio' | 'video' | 'transcription'
|
|
783
|
+
const display = m.id.replace(/[-_]/g, ' ');
|
|
784
|
+
const keyId = keyRow.id;
|
|
785
|
+
const existing = selectExistingMedia.get(platform, m.id);
|
|
786
|
+
if (existing) {
|
|
787
|
+
if (existing.display_name !== display || existing.modality !== modality) {
|
|
788
|
+
refreshMedia.run(display, modality, existing.id);
|
|
789
|
+
mediaRefreshed++;
|
|
790
|
+
updatedModelNames.push(m.id);
|
|
791
|
+
}
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
insertMedia.run(platform, m.id, display, modality, 100 + idx, keyId);
|
|
795
|
+
mediaRegistered++;
|
|
796
|
+
newModelNames.push(m.id);
|
|
797
|
+
idx++;
|
|
798
|
+
}
|
|
799
|
+
// ──── 4. AUTO-ASSIGN QUIRKS ─────────────────────────────────
|
|
800
|
+
const quirkCount = autoAssignQuirks(db, platform, freeModels, rawModels);
|
|
801
|
+
// Store counts for return
|
|
802
|
+
txResult._embRegistered = embRegistered;
|
|
803
|
+
txResult._embRefreshed = embRefreshed;
|
|
804
|
+
txResult._mediaRegistered = mediaRegistered;
|
|
805
|
+
txResult._mediaRefreshed = mediaRefreshed;
|
|
806
|
+
txResult._quirkCount = quirkCount;
|
|
807
|
+
});
|
|
808
|
+
const txResult = {};
|
|
809
|
+
tx();
|
|
810
|
+
const embRegistered = txResult._embRegistered ?? 0;
|
|
811
|
+
const embRefreshed = txResult._embRefreshed ?? 0;
|
|
812
|
+
const mediaRegistered = txResult._mediaRegistered ?? 0;
|
|
813
|
+
const mediaRefreshed = txResult._mediaRefreshed ?? 0;
|
|
814
|
+
const quirkCount = txResult._quirkCount ?? 0;
|
|
815
|
+
// ── P1: Update auto-discovered models from runtime observations ──
|
|
816
|
+
// After insertion, check provider_quota_state for this platform. If
|
|
817
|
+
// we've observed real rate limits via response headers, use those to
|
|
818
|
+
// update models that still carry the default/initial values.
|
|
819
|
+
try {
|
|
820
|
+
const observed = db.prepare(`
|
|
821
|
+
SELECT metric, MIN(limit_value) AS lim
|
|
822
|
+
FROM provider_quota_state
|
|
823
|
+
WHERE platform = ? AND limit_value IS NOT NULL AND limit_value > 0
|
|
824
|
+
GROUP BY metric
|
|
825
|
+
`).all(platform);
|
|
826
|
+
if (observed.length > 0) {
|
|
827
|
+
const metricMap = {
|
|
828
|
+
rpm: { col: 'rpm_limit', defaultVal: rates.rpm },
|
|
829
|
+
rpd: { col: 'rpd_limit', defaultVal: rates.rpd },
|
|
830
|
+
tpm: { col: 'tpm_limit', defaultVal: rates.tpm },
|
|
831
|
+
tpd: { col: 'tpd_limit', defaultVal: rates.tpd },
|
|
832
|
+
};
|
|
833
|
+
let updated = 0;
|
|
834
|
+
for (const { metric, lim } of observed) {
|
|
835
|
+
const spec = metricMap[metric];
|
|
836
|
+
if (!spec)
|
|
837
|
+
continue;
|
|
838
|
+
// Only update models whose current value matches the initial default
|
|
839
|
+
// (or is NULL). This avoids overwriting catalog-authored or manually
|
|
840
|
+
// tuned limits.
|
|
841
|
+
if (spec.defaultVal != null) {
|
|
842
|
+
const res = db.prepare(`UPDATE models SET ${spec.col} = ? WHERE platform = ? AND source = 'user' AND ${spec.col} = ? AND enabled = 1`).run(lim, platform, spec.defaultVal);
|
|
843
|
+
updated += res.changes;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (updated > 0) {
|
|
847
|
+
console.log(`[auto-discover] ${platform}: P1 updated ${updated} model limit(s) from runtime observations`);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
// provider_quota_state may not exist yet or may be empty — non-fatal
|
|
853
|
+
}
|
|
854
|
+
const totalChanges = registered + refreshed + benched + embRegistered + embRefreshed + mediaRegistered + mediaRefreshed;
|
|
855
|
+
if (totalChanges > 0) {
|
|
856
|
+
console.log(`[auto-discover] ${platform}: chat ${registered}reg/${refreshed}ref/${benched}ret, ` +
|
|
857
|
+
`embed ${embRegistered}reg/${embRefreshed}ref, media ${mediaRegistered}reg/${mediaRefreshed}ref, ` +
|
|
858
|
+
`${quirkCount} quirks auto-assigned`);
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
registered, refreshed, retired: benched,
|
|
862
|
+
embeddingsRegistered: embRegistered, embeddingsRefreshed: embRefreshed,
|
|
863
|
+
mediaRegistered, mediaRefreshed: mediaRefreshed,
|
|
864
|
+
quirksAssigned: quirkCount,
|
|
865
|
+
newModels: newModelNames, updatedModels: updatedModelNames, retiredModels: retiredModelNames,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Discover models for ALL configured providers that have keys.
|
|
870
|
+
* Called on startup — handles initial registration for platforms with zero
|
|
871
|
+
* models, and also discovers embeddings/media for platforms that only have chat.
|
|
872
|
+
*/
|
|
873
|
+
export async function autoDiscoverAll() {
|
|
874
|
+
const db = getDb();
|
|
875
|
+
const configured = db.prepare(`
|
|
876
|
+
SELECT DISTINCT platform FROM api_keys WHERE enabled = 1
|
|
877
|
+
`).all();
|
|
878
|
+
for (const { platform } of configured) {
|
|
879
|
+
if (SKIP_AUTO_DISCOVER.has(platform))
|
|
880
|
+
continue;
|
|
881
|
+
// Skip platforms that already have all three model types
|
|
882
|
+
const counts = db.prepare(`
|
|
883
|
+
SELECT
|
|
884
|
+
(SELECT COUNT(*) FROM models WHERE platform = ? AND enabled = 1) AS chat,
|
|
885
|
+
(SELECT COUNT(*) FROM embedding_models WHERE platform = ? AND enabled = 1) AS emb,
|
|
886
|
+
(SELECT COUNT(*) FROM media_models WHERE platform = ? AND enabled = 1) AS media
|
|
887
|
+
`).get(platform, platform, platform);
|
|
888
|
+
if (counts.chat > 0 && counts.emb > 0 && counts.media > 0)
|
|
889
|
+
continue;
|
|
890
|
+
try {
|
|
891
|
+
await autoDiscoverModels(platform);
|
|
892
|
+
}
|
|
893
|
+
catch (err) {
|
|
894
|
+
console.warn(`[auto-discover] background discovery for ${platform} failed: ${err instanceof Error ? err.message : err}`);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Re-verify every configured platform's roster against its live upstream.
|
|
900
|
+
*
|
|
901
|
+
* A discovery run is a snapshot, and upstream rosters move: models are added,
|
|
902
|
+
* dropped, or flip behind a paid gate without any catalog noticing. This
|
|
903
|
+
* function sweeps ALL model types (chat, embeddings, media) for discovery-owned
|
|
904
|
+
* platforms.
|
|
905
|
+
*/
|
|
906
|
+
export async function refreshDiscoveredModels() {
|
|
907
|
+
const db = getDb();
|
|
908
|
+
// Find platforms where ANY table has source='user' rows (chat, embeddings, or media)
|
|
909
|
+
const discoveryOwned = db.prepare(`
|
|
910
|
+
SELECT DISTINCT platform FROM (
|
|
911
|
+
SELECT platform FROM models WHERE source = 'user'
|
|
912
|
+
UNION
|
|
913
|
+
SELECT platform FROM embedding_models WHERE key_id IS NOT NULL
|
|
914
|
+
UNION
|
|
915
|
+
SELECT platform FROM media_models WHERE key_id IS NOT NULL
|
|
916
|
+
)
|
|
917
|
+
`).all();
|
|
918
|
+
const targeted = new Set(discoveryOwned.map(r => r.platform));
|
|
919
|
+
// Platforms whose provider publishes an authoritative `access_tier` are
|
|
920
|
+
// discovery-first by design — xkiro's roster comes from discovery even while
|
|
921
|
+
// its only existing rows are catalog-authored, so a row-source scan alone
|
|
922
|
+
// would skip it forever (the exact gap that let the roster go stale).
|
|
923
|
+
for (const p of configuredPlatforms(db)) {
|
|
924
|
+
const provider = resolveProvider(p);
|
|
925
|
+
if (provider && provider.hasAccessTier)
|
|
926
|
+
targeted.add(p);
|
|
927
|
+
}
|
|
928
|
+
// One bell row per provider that changed, so the entry names WHICH provider
|
|
929
|
+
// gained or lost models. Discovery already runs per platform, so keeping the
|
|
930
|
+
// results keyed by platform is the natural shape — a single rolled-up line
|
|
931
|
+
// across every provider is what made these notifications unreadable.
|
|
932
|
+
const changesByPlatform = new Map();
|
|
933
|
+
const bucket = (platform) => {
|
|
934
|
+
const existing = changesByPlatform.get(platform);
|
|
935
|
+
if (existing)
|
|
936
|
+
return existing;
|
|
937
|
+
const fresh = { added: [], removed: [] };
|
|
938
|
+
changesByPlatform.set(platform, fresh);
|
|
939
|
+
return fresh;
|
|
940
|
+
};
|
|
941
|
+
for (const platform of targeted) {
|
|
942
|
+
if (SKIP_AUTO_DISCOVER.has(platform))
|
|
943
|
+
continue;
|
|
944
|
+
const hasKey = db.prepare('SELECT 1 FROM api_keys WHERE platform = ? AND enabled = 1 LIMIT 1').get(platform);
|
|
945
|
+
if (!hasKey)
|
|
946
|
+
continue;
|
|
947
|
+
try {
|
|
948
|
+
const result = await autoDiscoverModels(platform, { refresh: true });
|
|
949
|
+
if (result.registered === 0 && result.retired === 0)
|
|
950
|
+
continue;
|
|
951
|
+
const entry = bucket(platform);
|
|
952
|
+
entry.added.push(...result.newModels);
|
|
953
|
+
entry.removed.push(...result.retiredModels);
|
|
954
|
+
}
|
|
955
|
+
catch (err) {
|
|
956
|
+
console.warn(`[auto-discover] refresh for ${platform} failed: ${err instanceof Error ? err.message : err}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
if (changesByPlatform.size === 0)
|
|
960
|
+
return;
|
|
961
|
+
const now = Date.now();
|
|
962
|
+
const notifs = [];
|
|
963
|
+
const names = (list) => list.slice(0, DISCOVER_NOTIFICATION_MODELS).join(', ') + (list.length > DISCOVER_NOTIFICATION_MODELS ? '…' : '');
|
|
964
|
+
for (const [platform, change] of changesByPlatform) {
|
|
965
|
+
if (change.added.length > 0) {
|
|
966
|
+
notifs.push({
|
|
967
|
+
id: `discover-${now}-added-${platform}`,
|
|
968
|
+
type: 'added',
|
|
969
|
+
modelId: '*',
|
|
970
|
+
platform,
|
|
971
|
+
displayName: `${change.added.length} model baru`,
|
|
972
|
+
message: names(change.added),
|
|
973
|
+
data: { key: 'notifications.discoverAdded', vars: { count: change.added.length, platform } },
|
|
974
|
+
timestamp: now,
|
|
975
|
+
read: false,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
if (change.removed.length > 0) {
|
|
979
|
+
notifs.push({
|
|
980
|
+
id: `discover-${now}-removed-${platform}`,
|
|
981
|
+
type: 'removed',
|
|
982
|
+
modelId: '*',
|
|
983
|
+
platform,
|
|
984
|
+
displayName: `${change.removed.length} model dihapus`,
|
|
985
|
+
message: names(change.removed),
|
|
986
|
+
data: { key: 'notifications.discoverRemoved', vars: { count: change.removed.length, platform } },
|
|
987
|
+
timestamp: now,
|
|
988
|
+
read: false,
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
addNotifications(notifs);
|
|
993
|
+
}
|
|
994
|
+
function configuredPlatforms(db) {
|
|
995
|
+
return db.prepare('SELECT DISTINCT platform FROM api_keys WHERE enabled = 1').all()
|
|
996
|
+
.map((r) => r.platform);
|
|
997
|
+
}
|
|
998
|
+
/** First pass after boot: long enough for the DB and catalog sync to settle,
|
|
999
|
+
* short enough that a stale roster discovered before restart self-heals in
|
|
1000
|
+
* minutes rather than at the next tick. */
|
|
1001
|
+
export const MODEL_REFRESH_BOOT_DELAY_MS = 5 * 60 * 1000;
|
|
1002
|
+
let cancelRefresh;
|
|
1003
|
+
let cancelBootRefresh;
|
|
1004
|
+
let currentScheduler = null;
|
|
1005
|
+
/**
|
|
1006
|
+
* Schedule the recurring roster re-verification.
|
|
1007
|
+
* Uses the same configurable interval as catalog sync (sync_interval_hours).
|
|
1008
|
+
* When sync schedule is 'off', auto-discover also stops recurring (manual only).
|
|
1009
|
+
*/
|
|
1010
|
+
export function startModelRefresh(scheduler) {
|
|
1011
|
+
if (cancelRefresh && cancelBootRefresh)
|
|
1012
|
+
return;
|
|
1013
|
+
if (process.env.MODEL_REFRESH_DISABLED === '1') {
|
|
1014
|
+
console.log('[auto-discover] refresh disabled via MODEL_REFRESH_DISABLED=1');
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
currentScheduler = scheduler;
|
|
1018
|
+
cancelBootRefresh = scheduler.after(MODEL_REFRESH_BOOT_DELAY_MS, () => refreshDiscoveredModels());
|
|
1019
|
+
restartRefreshInterval();
|
|
1020
|
+
}
|
|
1021
|
+
/** Read the same sync_interval_hours setting used by catalog sync.
|
|
1022
|
+
* Delegates to getSyncIntervalHours() so the default (unset) matches
|
|
1023
|
+
* catalog sync's 12h; only an explicit 0/'off' disables the timer. */
|
|
1024
|
+
function getAutoDiscoverIntervalMs() {
|
|
1025
|
+
const hours = getSyncIntervalHours();
|
|
1026
|
+
if (hours === 'off')
|
|
1027
|
+
return 0;
|
|
1028
|
+
return hours * 60 * 60 * 1000;
|
|
1029
|
+
}
|
|
1030
|
+
/** Restart the recurring auto-discover timer (call when sync schedule changes). */
|
|
1031
|
+
export function restartRefreshInterval() {
|
|
1032
|
+
if (!currentScheduler)
|
|
1033
|
+
return;
|
|
1034
|
+
if (cancelRefresh) {
|
|
1035
|
+
cancelRefresh();
|
|
1036
|
+
cancelRefresh = undefined;
|
|
1037
|
+
}
|
|
1038
|
+
const ms = getAutoDiscoverIntervalMs();
|
|
1039
|
+
if (ms > 0) {
|
|
1040
|
+
cancelRefresh = currentScheduler.every(ms, () => { void refreshDiscoveredModels(); });
|
|
1041
|
+
console.log(`[auto-discover] re-verifying rosters every ${ms / 3600000}h (first pass in ${MODEL_REFRESH_BOOT_DELAY_MS / 60000}min)`);
|
|
1042
|
+
}
|
|
1043
|
+
else {
|
|
1044
|
+
console.log('[auto-discover] recurring refresh disabled (manual mode)');
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
//# sourceMappingURL=auto-discover.js.map
|