codexLB-up 1.25.0b10__py3-none-any.whl

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.
Files changed (827) hide show
  1. app/__init__.py +10 -0
  2. app/admin_cli.py +494 -0
  3. app/cli.py +339 -0
  4. app/codex_sessions_retag.py +487 -0
  5. app/core/__init__.py +0 -0
  6. app/core/audit/__init__.py +0 -0
  7. app/core/audit/service.py +216 -0
  8. app/core/audit/types.py +94 -0
  9. app/core/auth/__init__.py +236 -0
  10. app/core/auth/api_key_cache.py +65 -0
  11. app/core/auth/dashboard_access.py +429 -0
  12. app/core/auth/dashboard_mode.py +171 -0
  13. app/core/auth/dashboard_session_ttl.py +74 -0
  14. app/core/auth/dashboard_users_cache.py +120 -0
  15. app/core/auth/dependencies.py +710 -0
  16. app/core/auth/external_identity.py +78 -0
  17. app/core/auth/guardian.py +374 -0
  18. app/core/auth/models.py +49 -0
  19. app/core/auth/providers/__init__.py +129 -0
  20. app/core/auth/providers/oidc.py +838 -0
  21. app/core/auth/providers/registry.py +118 -0
  22. app/core/auth/refresh.py +394 -0
  23. app/core/auth/step_up.py +117 -0
  24. app/core/auth/totp.py +81 -0
  25. app/core/balancer/__init__.py +71 -0
  26. app/core/balancer/logic.py +1612 -0
  27. app/core/balancer/rendezvous_hash.py +21 -0
  28. app/core/balancer/types.py +21 -0
  29. app/core/bootstrap.py +172 -0
  30. app/core/cache/invalidation.py +426 -0
  31. app/core/clients/__init__.py +0 -0
  32. app/core/clients/codex.py +996 -0
  33. app/core/clients/codex_version.py +187 -0
  34. app/core/clients/codex_version_snapshot.py +2 -0
  35. app/core/clients/files.py +534 -0
  36. app/core/clients/headers.py +29 -0
  37. app/core/clients/http.py +511 -0
  38. app/core/clients/model_fetcher.py +276 -0
  39. app/core/clients/native_egress.py +1304 -0
  40. app/core/clients/oauth.py +477 -0
  41. app/core/clients/proxy.py +6089 -0
  42. app/core/clients/proxy_websocket.py +1411 -0
  43. app/core/clients/rate_limit_reset_credits.py +412 -0
  44. app/core/clients/stream_errors.py +12 -0
  45. app/core/clients/thread_cache_identity.py +347 -0
  46. app/core/clients/usage.py +467 -0
  47. app/core/clock.py +170 -0
  48. app/core/config/__init__.py +0 -0
  49. app/core/config/background_jobs.py +73 -0
  50. app/core/config/context_window_overrides.py +166 -0
  51. app/core/config/dashboard_overrides.py +145 -0
  52. app/core/config/inheritable.py +47 -0
  53. app/core/config/key_fingerprint.py +115 -0
  54. app/core/config/settings.py +872 -0
  55. app/core/config/settings_cache.py +100 -0
  56. app/core/config/spool_retention.py +217 -0
  57. app/core/config/tiers.py +174 -0
  58. app/core/conversation_archive.py +568 -0
  59. app/core/crypto.py +37 -0
  60. app/core/draining.py +11 -0
  61. app/core/errors.py +365 -0
  62. app/core/exceptions.py +155 -0
  63. app/core/handlers/__init__.py +3 -0
  64. app/core/handlers/exceptions.py +478 -0
  65. app/core/http_protocol.py +176 -0
  66. app/core/http_protocol_httptools.py +204 -0
  67. app/core/ingress_limits.py +17 -0
  68. app/core/metadata_store.py +18 -0
  69. app/core/metrics/__init__.py +0 -0
  70. app/core/metrics/middleware.py +74 -0
  71. app/core/metrics/prometheus.py +604 -0
  72. app/core/middleware/__init__.py +25 -0
  73. app/core/middleware/api_firewall.py +111 -0
  74. app/core/middleware/app_version.py +37 -0
  75. app/core/middleware/dashboard_auth_proxy.py +55 -0
  76. app/core/middleware/dashboard_csrf.py +177 -0
  77. app/core/middleware/dashboard_gzip.py +41 -0
  78. app/core/middleware/dashboard_overrides.py +59 -0
  79. app/core/middleware/firewall_cache.py +91 -0
  80. app/core/middleware/inflight.py +126 -0
  81. app/core/middleware/multipart_content_encoding.py +129 -0
  82. app/core/middleware/path_rewrite.py +148 -0
  83. app/core/middleware/request_body_limit.py +196 -0
  84. app/core/middleware/request_decompression.py +209 -0
  85. app/core/middleware/request_id.py +72 -0
  86. app/core/middleware/required_capability_http.py +116 -0
  87. app/core/middleware/trusted_proxy_headers.py +36 -0
  88. app/core/multipart.py +237 -0
  89. app/core/multipart_fields.py +91 -0
  90. app/core/openai/__init__.py +0 -0
  91. app/core/openai/chat_requests.py +493 -0
  92. app/core/openai/chat_responses.py +931 -0
  93. app/core/openai/contracts.py +46 -0
  94. app/core/openai/exceptions.py +24 -0
  95. app/core/openai/host_models.py +14 -0
  96. app/core/openai/images.py +413 -0
  97. app/core/openai/message_coercion.py +376 -0
  98. app/core/openai/model_refresh_scheduler.py +423 -0
  99. app/core/openai/model_registry.py +997 -0
  100. app/core/openai/model_registry_store.py +548 -0
  101. app/core/openai/models.py +172 -0
  102. app/core/openai/parsing.py +97 -0
  103. app/core/openai/requests.py +1980 -0
  104. app/core/openai/strict_schema.py +199 -0
  105. app/core/openai/tool_call_safety.py +99 -0
  106. app/core/openai/v1_requests.py +187 -0
  107. app/core/plan_types.py +90 -0
  108. app/core/prestop.py +202 -0
  109. app/core/rate_limiter/__init__.py +0 -0
  110. app/core/rate_limiter/db_rate_limiter.py +215 -0
  111. app/core/request_locality.py +473 -0
  112. app/core/resilience/__init__.py +0 -0
  113. app/core/resilience/backpressure.py +57 -0
  114. app/core/resilience/bulkhead.py +202 -0
  115. app/core/resilience/circuit_breaker.py +150 -0
  116. app/core/resilience/degradation.py +55 -0
  117. app/core/resilience/loop_lag_monitor.py +60 -0
  118. app/core/resilience/memory_monitor.py +137 -0
  119. app/core/resilience/network_recovery.py +256 -0
  120. app/core/resilience/overload.py +98 -0
  121. app/core/resilience/toggles.py +114 -0
  122. app/core/retention/__init__.py +0 -0
  123. app/core/retention/job.py +240 -0
  124. app/core/retention/scheduler.py +69 -0
  125. app/core/runtime_logging.py +601 -0
  126. app/core/scheduling/__init__.py +0 -0
  127. app/core/scheduling/leader_election.py +993 -0
  128. app/core/scheduling/leader_election_handle.py +25 -0
  129. app/core/server.py +140 -0
  130. app/core/shutdown.py +326 -0
  131. app/core/socket_peer.py +22 -0
  132. app/core/startup.py +41 -0
  133. app/core/timeout_invariants.py +347 -0
  134. app/core/tracing/__init__.py +0 -0
  135. app/core/tracing/otel.py +207 -0
  136. app/core/types.py +6 -0
  137. app/core/upstream_proxy/__init__.py +13 -0
  138. app/core/upstream_proxy/cache.py +130 -0
  139. app/core/upstream_proxy/resolver.py +189 -0
  140. app/core/upstream_proxy/types.py +104 -0
  141. app/core/usage/__init__.py +407 -0
  142. app/core/usage/depletion.py +109 -0
  143. app/core/usage/live_hub.py +41 -0
  144. app/core/usage/live_snapshots.py +174 -0
  145. app/core/usage/logs.py +212 -0
  146. app/core/usage/metadata_scheduler.py +118 -0
  147. app/core/usage/models.py +54 -0
  148. app/core/usage/pricing.py +576 -0
  149. app/core/usage/pricing_catalog.py +241 -0
  150. app/core/usage/pricing_snapshot.json +821 -0
  151. app/core/usage/quota.py +133 -0
  152. app/core/usage/refresh_policy.py +21 -0
  153. app/core/usage/refresh_scheduler.py +713 -0
  154. app/core/usage/reset_credits_refresh_scheduler.py +435 -0
  155. app/core/usage/types.py +168 -0
  156. app/core/utils/__init__.py +0 -0
  157. app/core/utils/json_guards.py +18 -0
  158. app/core/utils/locks.py +33 -0
  159. app/core/utils/masking.py +20 -0
  160. app/core/utils/proxy_env.py +104 -0
  161. app/core/utils/request_id.py +65 -0
  162. app/core/utils/retry.py +77 -0
  163. app/core/utils/shared_future.py +172 -0
  164. app/core/utils/sse.py +286 -0
  165. app/core/utils/time.py +24 -0
  166. app/db/__init__.py +0 -0
  167. app/db/account_identity_lock.py +56 -0
  168. app/db/alembic/__init__.py +1 -0
  169. app/db/alembic/env.py +70 -0
  170. app/db/alembic/revision_ids.py +43 -0
  171. app/db/alembic/versions/20260213_000000_base_schema.py +248 -0
  172. app/db/alembic/versions/20260213_000100_normalize_account_plan_types.py +45 -0
  173. app/db/alembic/versions/20260213_000200_add_request_logs_reasoning_effort.py +45 -0
  174. app/db/alembic/versions/20260213_000300_add_accounts_reset_at.py +45 -0
  175. app/db/alembic/versions/20260213_000400_add_accounts_chatgpt_account_id.py +45 -0
  176. app/db/alembic/versions/20260213_000500_add_dashboard_settings.py +70 -0
  177. app/db/alembic/versions/20260213_000600_add_dashboard_settings_totp.py +64 -0
  178. app/db/alembic/versions/20260213_000700_add_dashboard_settings_password.py +59 -0
  179. app/db/alembic/versions/20260213_000800_add_api_keys.py +87 -0
  180. app/db/alembic/versions/20260214_000000_add_api_key_limits.py +143 -0
  181. app/db/alembic/versions/20260215_000000_add_idx_logs_requested_at.py +38 -0
  182. app/db/alembic/versions/20260218_000000_add_api_key_usage_reservations.py +117 -0
  183. app/db/alembic/versions/20260218_000100_add_import_without_overwrite_and_drop_accounts_email_unique.py +198 -0
  184. app/db/alembic/versions/20260225_000000_add_dashboard_settings_routing_strategy.py +53 -0
  185. app/db/alembic/versions/20260228_020000_align_api_key_limit_enum_types.py +104 -0
  186. app/db/alembic/versions/20260228_030000_add_api_firewall_allowlist.py +42 -0
  187. app/db/alembic/versions/20260306_000000_add_request_logs_service_tier.py +45 -0
  188. app/db/alembic/versions/20260307_000000_add_api_key_enforcement_fields.py +56 -0
  189. app/db/alembic/versions/20260308_000000_add_sqlite_performance_indexes.py +41 -0
  190. app/db/alembic/versions/20260309_000000_add_additional_usage_history.py +71 -0
  191. app/db/alembic/versions/20260309_000000_request_logs_nullable_account_id.py +28 -0
  192. app/db/alembic/versions/20260310_000000_add_request_logs_transport.py +45 -0
  193. app/db/alembic/versions/20260310_000000_fix_postgresql_enum_value_casing.py +79 -0
  194. app/db/alembic/versions/20260310_120000_add_sticky_session_kinds_and_affinity_ttl.py +110 -0
  195. app/db/alembic/versions/20260312_000000_add_additional_usage_quota_key.py +193 -0
  196. app/db/alembic/versions/20260312_000000_split_sticky_sessions_primary_key_by_kind.py +96 -0
  197. app/db/alembic/versions/20260312_010000_merge_additional_usage_and_sticky_session_heads.py +25 -0
  198. app/db/alembic/versions/20260312_020000_merge_request_logs_transport_and_feature_heads.py +25 -0
  199. app/db/alembic/versions/20260312_020000_merge_request_logs_transport_and_sticky_heads.py +25 -0
  200. app/db/alembic/versions/20260312_120000_add_dashboard_upstream_stream_transport.py +62 -0
  201. app/db/alembic/versions/20260313_024500_merge_request_log_transport_heads.py +27 -0
  202. app/db/alembic/versions/20260319_100937_increase_prompt_cache_ttl_to_1800s.py +73 -0
  203. app/db/alembic/versions/20260319_111500_merge_prompt_cache_ttl_and_upstream_transport_heads.py +25 -0
  204. app/db/alembic/versions/20260319_183000_normalize_sqlite_account_status_casing.py +48 -0
  205. app/db/alembic/versions/20260320_000000_add_request_log_requested_actual_tiers.py +51 -0
  206. app/db/alembic/versions/20260321_190000_tighten_dashboard_db_indexes.py +64 -0
  207. app/db/alembic/versions/20260321_210000_merge_request_log_tiers_and_dashboard_index_heads.py +25 -0
  208. app/db/alembic/versions/20260325_000000_add_request_log_cost.py +193 -0
  209. app/db/alembic/versions/20260328_000000_add_rate_limit_attempts.py +48 -0
  210. app/db/alembic/versions/20260328_120000_add_request_log_ttft.py +37 -0
  211. app/db/alembic/versions/20260328_130000_add_audit_logs_table.py +45 -0
  212. app/db/alembic/versions/20260328_140000_add_scheduler_leader_table.py +39 -0
  213. app/db/alembic/versions/20260330_000000_add_cache_locality_settings.py +69 -0
  214. app/db/alembic/versions/20260330_010000_merge_scheduler_leader_and_cache_locality_heads.py +25 -0
  215. app/db/alembic/versions/20260330_020000_add_bridge_ring_members.py +46 -0
  216. app/db/alembic/versions/20260401_000000_add_api_key_enforced_service_tier.py +52 -0
  217. app/db/alembic/versions/20260401_000000_add_cache_invalidation.py +41 -0
  218. app/db/alembic/versions/20260401_020000_merge_cache_invalidation_and_api_key_service_tier_heads.py +22 -0
  219. app/db/alembic/versions/20260402_000000_switch_dashboard_routing_default_to_capacity_weighted.py +66 -0
  220. app/db/alembic/versions/20260403_000000_add_credit_api_key_limit_values.py +44 -0
  221. app/db/alembic/versions/20260406_000000_add_api_key_account_assignments.py +57 -0
  222. app/db/alembic/versions/20260406_010000_add_api_key_assignment_scope_flag.py +75 -0
  223. app/db/alembic/versions/20260407_000000_add_bridge_gateway_safe_mode.py +51 -0
  224. app/db/alembic/versions/20260407_010000_merge_api_key_assignment_and_bridge_gateway_heads.py +25 -0
  225. app/db/alembic/versions/20260408_000000_switch_import_without_overwrite_default_to_true.py +73 -0
  226. app/db/alembic/versions/20260408_010000_merge_import_without_overwrite_and_assignment_heads.py +25 -0
  227. app/db/alembic/versions/20260409_000000_add_http_bridge_sessions.py +130 -0
  228. app/db/alembic/versions/20260409_000000_switch_sticky_threads_and_prefer_earlier_reset_defaults_to_true.py +89 -0
  229. app/db/alembic/versions/20260409_010000_add_dashboard_settings_bootstrap_token.py +57 -0
  230. app/db/alembic/versions/20260409_010000_merge_http_bridge_and_import_without_overwrite_heads.py +25 -0
  231. app/db/alembic/versions/20260409_020000_fix_http_bridge_last_seen_index.py +46 -0
  232. app/db/alembic/versions/20260410_000000_backfill_pristine_dashboard_settings_defaults.py +77 -0
  233. app/db/alembic/versions/20260410_030000_restore_import_without_overwrite_default_true.py +73 -0
  234. app/db/alembic/versions/20260410_040000_merge_dashboard_defaults_and_import_default_heads.py +25 -0
  235. app/db/alembic/versions/20260413_000000_add_accounts_blocked_at.py +44 -0
  236. app/db/alembic/versions/20260415_160000_add_automations_tables.py +189 -0
  237. app/db/alembic/versions/20260415_160000_add_request_logs_response_lookup_index.py +67 -0
  238. app/db/alembic/versions/20260417_000000_add_request_log_plan_type.py +44 -0
  239. app/db/alembic/versions/20260418_190000_add_automation_runs_cycle_key.py +87 -0
  240. app/db/alembic/versions/20260419_000000_add_automation_run_cycle_metadata.py +56 -0
  241. app/db/alembic/versions/20260419_020000_add_automation_run_cycles_snapshot_tables.py +342 -0
  242. app/db/alembic/versions/20260421_000000_add_dashboard_session_ttl_setting.py +51 -0
  243. app/db/alembic/versions/20260421_120000_merge_request_log_lookup_and_plan_type_heads.py +25 -0
  244. app/db/alembic/versions/20260421_130000_merge_automation_and_request_log_heads.py +25 -0
  245. app/db/alembic/versions/20260422_103000_normalize_legacy_automation_run_cycle_keys.py +648 -0
  246. app/db/alembic/versions/20260423_120000_add_api_key_limit_reset_at_index.py +43 -0
  247. app/db/alembic/versions/20260424_000000_merge_dashboard_session_ttl_and_request_log_heads.py +25 -0
  248. app/db/alembic/versions/20260426_000000_add_dashboard_relative_availability_settings.py +68 -0
  249. app/db/alembic/versions/20260427_130000_add_warmup_model_and_request_kind.py +75 -0
  250. app/db/alembic/versions/20260506_000000_add_account_routing_policy.py +37 -0
  251. app/db/alembic/versions/20260509_000000_add_api_key_traffic_class.py +37 -0
  252. app/db/alembic/versions/20260509_010000_add_additional_quota_routing_policies.py +37 -0
  253. app/db/alembic/versions/20260513_000000_add_accounts_alias.py +44 -0
  254. app/db/alembic/versions/20260513_000000_add_api_key_apply_to_codex_model.py +59 -0
  255. app/db/alembic/versions/20260514_000000_add_request_logs_api_key_time_index.py +43 -0
  256. app/db/alembic/versions/20260515_000000_add_prefer_earlier_reset_window.py +47 -0
  257. app/db/alembic/versions/20260515_000000_soft_delete_request_logs_on_account_delete.py +83 -0
  258. app/db/alembic/versions/20260515_020000_add_split_sticky_budget_thresholds.py +62 -0
  259. app/db/alembic/versions/20260516_000000_add_sqlite_hot_path_indexes.py +75 -0
  260. app/db/alembic/versions/20260517_000000_merge_sqlite_hot_path_and_request_log_delete_heads.py +24 -0
  261. app/db/alembic/versions/20260518_000000_add_http_bridge_durable_input_prefix.py +44 -0
  262. app/db/alembic/versions/20260518_010000_merge_http_bridge_and_sqlite_recovery_heads.py +25 -0
  263. app/db/alembic/versions/20260518_120000_add_dashboard_guest_access.py +57 -0
  264. app/db/alembic/versions/20260520_000000_merge_api_key_and_http_bridge_heads.py +26 -0
  265. app/db/alembic/versions/20260520_010000_add_request_logs_api_key_account_index.py +45 -0
  266. app/db/alembic/versions/20260520_010000_merge_automation_and_api_key_heads.py +26 -0
  267. app/db/alembic/versions/20260520_030000_add_quota_planner.py +194 -0
  268. app/db/alembic/versions/20260520_040000_merge_quota_planner_and_request_log_index_heads.py +24 -0
  269. app/db/alembic/versions/20260521_000000_add_account_security_work_authorized.py +46 -0
  270. app/db/alembic/versions/20260521_000000_merge_dashboard_guest_and_api_key_heads.py +26 -0
  271. app/db/alembic/versions/20260521_000000_merge_prefer_reset_window_head.py +26 -0
  272. app/db/alembic/versions/20260521_000000_merge_warmup_and_request_log_index_heads.py +25 -0
  273. app/db/alembic/versions/20260522_000000_add_limit_warmup_trigger.py +195 -0
  274. app/db/alembic/versions/20260524_000000_merge_accounts_alias_and_reset_window_heads.py +24 -0
  275. app/db/alembic/versions/20260525_000000_add_usage_raw_window_latest_index.py +31 -0
  276. app/db/alembic/versions/20260526_000000_add_request_log_failure_metadata.py +100 -0
  277. app/db/alembic/versions/20260531_000000_add_account_workspace_identity.py +39 -0
  278. app/db/alembic/versions/20260531_000000_add_single_account_routing.py +42 -0
  279. app/db/alembic/versions/20260601_000000_merge_relative_availability_and_usage_raw_heads.py +24 -0
  280. app/db/alembic/versions/20260601_010000_add_api_key_usage_sections.py +58 -0
  281. app/db/alembic/versions/20260601_010000_add_routing_policy_persistence.py +67 -0
  282. app/db/alembic/versions/20260601_010000_merge_security_work_and_relative_availability_heads.py +25 -0
  283. app/db/alembic/versions/20260601_010000_merge_split_sticky_and_relative_usage_heads.py +25 -0
  284. app/db/alembic/versions/20260601_010000_merge_warmup_and_relative_availability_heads.py +25 -0
  285. app/db/alembic/versions/20260601_020000_merge_additional_quota_routing_and_relative_availability_heads.py +25 -0
  286. app/db/alembic/versions/20260601_020000_merge_quota_planner_and_relative_availability_heads.py +25 -0
  287. app/db/alembic/versions/20260601_020000_merge_reset_window_and_relative_availability_heads.py +25 -0
  288. app/db/alembic/versions/20260601_020000_merge_routing_policy_and_split_threshold_heads.py +24 -0
  289. app/db/alembic/versions/20260601_020000_merge_warmup_and_request_log_failure_heads.py +25 -0
  290. app/db/alembic/versions/20260601_030000_merge_single_account_and_routing_quota_heads.py +29 -0
  291. app/db/alembic/versions/20260601_040000_merge_security_work_and_routing_consolidation_heads.py +25 -0
  292. app/db/alembic/versions/20260601_050000_merge_quota_planner_and_routing_consolidation_heads.py +25 -0
  293. app/db/alembic/versions/20260602_050000_add_upstream_proxy_routing.py +164 -0
  294. app/db/alembic/versions/20260602_060000_merge_account_workspace_and_failure_heads.py +25 -0
  295. app/db/alembic/versions/20260602_070000_add_request_log_useragent_fields.py +86 -0
  296. app/db/alembic/versions/20260602_070000_merge_quota_planner_and_account_workspace_heads.py +25 -0
  297. app/db/alembic/versions/20260602_080000_merge_upstream_proxy_and_quota_planner_heads.py +25 -0
  298. app/db/alembic/versions/20260602_080000_merge_useragent_and_upstream_proxy_heads.py +24 -0
  299. app/db/alembic/versions/20260603_000000_add_weekly_pace_working_days.py +52 -0
  300. app/db/alembic/versions/20260603_000000_free_account_monthly_window.py +60 -0
  301. app/db/alembic/versions/20260604_000000_add_reauth_required_account_status.py +159 -0
  302. app/db/alembic/versions/20260604_010000_merge_automations_and_reauth_status_heads.py +25 -0
  303. app/db/alembic/versions/20260604_010000_merge_dashboard_guest_and_reauth_status_heads.py +25 -0
  304. app/db/alembic/versions/20260604_010000_merge_useragent_and_reauth_heads.py +24 -0
  305. app/db/alembic/versions/20260607_000000_merge_weekly_monthly_useragent_heads.py +28 -0
  306. app/db/alembic/versions/20260608_000000_add_hide_upstream_quota_from_api_keys.py +50 -0
  307. app/db/alembic/versions/20260611_000000_add_request_log_archive_request_id.py +47 -0
  308. app/db/alembic/versions/20260611_000000_merge_automations_and_weekly_monthly_useragent_heads.py +26 -0
  309. app/db/alembic/versions/20260611_000000_merge_dashboard_guest_and_weekly_useragent_heads.py +26 -0
  310. app/db/alembic/versions/20260611_010000_add_request_log_client_ip.py +57 -0
  311. app/db/alembic/versions/20260611_020000_merge_request_log_archive_and_client_ip_heads.py +26 -0
  312. app/db/alembic/versions/20260611_030000_merge_dashboard_guest_and_request_log_client_ip_heads.py +26 -0
  313. app/db/alembic/versions/20260613_000000_add_accounts_codex_installation_id.py +57 -0
  314. app/db/alembic/versions/20260613_010000_merge_installation_id_and_dashboard_weekly_heads.py +26 -0
  315. app/db/alembic/versions/20260615_000000_merge_automations_and_dashboard_guest_heads.py +26 -0
  316. app/db/alembic/versions/20260623_000000_add_limit_warmup_exhausted_threshold.py +51 -0
  317. app/db/alembic/versions/20260626_000000_add_http_downstream_transport_policy.py +46 -0
  318. app/db/alembic/versions/20260626_010000_add_request_logs_upstream_transport.py +31 -0
  319. app/db/alembic/versions/20260629_000000_add_dashboard_query_hot_path_indexes.py +53 -0
  320. app/db/alembic/versions/20260630_000000_add_limit_warmup_staggered_idle.py +47 -0
  321. app/db/alembic/versions/20260630_000000_merge_request_log_client_ip_and_dashboard_hot_path_heads.py +26 -0
  322. app/db/alembic/versions/20260630_010000_merge_warmup_and_request_log_dashboard_heads.py +26 -0
  323. app/db/alembic/versions/20260630_020000_merge_automations_and_main_heads.py +26 -0
  324. app/db/alembic/versions/20260630_020000_merge_warmup_threshold_and_main_heads.py +26 -0
  325. app/db/alembic/versions/20260630_030000_add_automation_run_model_snapshot.py +59 -0
  326. app/db/alembic/versions/20260630_040000_merge_automation_snapshot_and_warmup_threshold_heads.py +26 -0
  327. app/db/alembic/versions/20260630_050000_add_automation_run_prompt_snapshot.py +52 -0
  328. app/db/alembic/versions/20260701_000000_add_weekly_pace_smoothing_minutes.py +51 -0
  329. app/db/alembic/versions/20260702_000000_add_model_sources.py +176 -0
  330. app/db/alembic/versions/20260705_000000_add_model_source_audio_transcriptions.py +50 -0
  331. app/db/alembic/versions/20260705_000000_harden_dashboard_session_ttl.py +62 -0
  332. app/db/alembic/versions/20260706_000000_add_model_source_audio_per_minute.py +43 -0
  333. app/db/alembic/versions/20260707_000000_merge_model_sources_and_session_ttl.py +24 -0
  334. app/db/alembic/versions/20260707_010000_merge_automations_and_model_sources_heads.py +24 -0
  335. app/db/alembic/versions/20260707_020000_add_automation_job_account_scope.py +125 -0
  336. app/db/alembic/versions/20260709_000000_add_ttft_phase_observability.py +66 -0
  337. app/db/alembic/versions/20260711_000000_add_dashboard_prohibit_fast_mode.py +42 -0
  338. app/db/alembic/versions/20260711_010000_add_account_chatgpt_user_id.py +40 -0
  339. app/db/alembic/versions/20260711_020000_add_dashboard_account_concurrency_caps.py +71 -0
  340. app/db/alembic/versions/20260711_030000_add_limit_warmup_idle_threshold.py +51 -0
  341. app/db/alembic/versions/20260712_010000_add_account_usage_rollups.py +56 -0
  342. app/db/alembic/versions/20260712_020000_add_api_key_usage_rollups.py +53 -0
  343. app/db/alembic/versions/20260713_020000_add_model_registry_snapshot.py +42 -0
  344. app/db/alembic/versions/20260713_040000_add_account_refresh_claims.py +42 -0
  345. app/db/alembic/versions/20260713_040000_add_replica_guardrails.py +64 -0
  346. app/db/alembic/versions/20260713_070000_add_reset_credit_redeem_tables.py +58 -0
  347. app/db/alembic/versions/20260714_000000_add_oauth_flow_states.py +63 -0
  348. app/db/alembic/versions/20260715_000000_add_request_log_queue_latency.py +44 -0
  349. app/db/alembic/versions/20260716_000000_add_oauth_device_flow_slots.py +41 -0
  350. app/db/alembic/versions/20260716_010000_add_dashboard_retention_settings.py +48 -0
  351. app/db/alembic/versions/20260716_010000_add_reset_credit_display_settings.py +73 -0
  352. app/db/alembic/versions/20260717_000000_add_http_bridge_retry_circuits.py +47 -0
  353. app/db/alembic/versions/20260717_000000_merge_retention_and_reset_credit_display_heads.py +26 -0
  354. app/db/alembic/versions/20260717_000000_optimize_dashboard_hot_path_indexes.py +162 -0
  355. app/db/alembic/versions/20260717_000001_merge_retry_circuits_and_dashboard_indexes.py +22 -0
  356. app/db/alembic/versions/20260720_000000_add_request_log_conversation_id.py +57 -0
  357. app/db/alembic/versions/20260722_000000_backfill_request_log_useragent_families.py +40 -0
  358. app/db/alembic/versions/20260724_000000_add_request_usage_time_rollups.py +176 -0
  359. app/db/alembic/versions/20260724_000000_merge_request_log_schema_heads.py +33 -0
  360. app/db/alembic/versions/20260725_000000_add_http_bridge_pending_tool_calls.py +43 -0
  361. app/db/alembic/versions/20260726_000000_add_account_plan_downgrade_observations.py +44 -0
  362. app/db/alembic/versions/20260726_000000_repair_request_usage_rollups_after_merge.py +37 -0
  363. app/db/alembic/versions/20260727_000000_add_sticky_session_continuity_abandoned_at.py +45 -0
  364. app/db/alembic/versions/20260728_000000_merge_pending_tool_calls_and_rollup_repair_heads.py +31 -0
  365. app/db/alembic/versions/20260730_000000_add_api_key_fair_share_threshold.py +44 -0
  366. app/db/alembic/versions/20260730_000000_add_http_bridge_recovery_attempts.py +60 -0
  367. app/db/alembic/versions/20260731_000000_add_capability_lineage_markers.py +38 -0
  368. app/db/alembic/versions/20260803_000000_merge_http_bridge_recovery_and_capability_lineage_heads.py +30 -0
  369. app/db/alembic/versions/20260804_000000_add_http_bridge_operations.py +63 -0
  370. app/db/alembic/versions/20260804_000001_add_global_http_bridge_operation_fingerprint.py +53 -0
  371. app/db/alembic/versions/20260804_230000_add_request_log_connection_request_kind.py +43 -0
  372. app/db/alembic/versions/20260805_000000_add_http_bridge_operation_spool.py +75 -0
  373. app/db/alembic/versions/20260805_000001_finalize_http_bridge_operation_spool.py +65 -0
  374. app/db/alembic/versions/20260806_000000_add_additional_usage_alias_probe_indexes.py +84 -0
  375. app/db/alembic/versions/20260806_000000_add_anonymous_telemetry.py +49 -0
  376. app/db/alembic/versions/20260806_010000_add_conversation_presence_rollup.py +72 -0
  377. app/db/alembic/versions/20260806_020000_add_usage_history_bulk_covering_indexes.py +99 -0
  378. app/db/alembic/versions/20260806_030000_add_api_key_allowed_reasoning_efforts.py +65 -0
  379. app/db/alembic/versions/20260806_120000_add_http_bridge_owner_process_epoch.py +57 -0
  380. app/db/alembic/versions/20260807_000000_merge_http_bridge_operation_spool_and_latest_main.py +24 -0
  381. app/db/alembic/versions/20260808_000000_tune_usage_history_autovacuum.py +54 -0
  382. app/db/alembic/versions/20260810_000000_add_http_bridge_recovery_dispatch_count.py +44 -0
  383. app/db/alembic/versions/20260811_000000_add_hourly_rollup_cancelled_count.py +92 -0
  384. app/db/alembic/versions/20260812_000000_merge_recovery_dispatch_and_hourly_cancelled_heads.py +25 -0
  385. app/db/alembic/versions/20260812_120000_add_sticky_abandonment_scope.py +43 -0
  386. app/db/alembic/versions/20260813_000000_add_file_account_pins.py +40 -0
  387. app/db/alembic/versions/20260816_000000_add_account_pending_deletion.py +81 -0
  388. app/db/alembic/versions/20260816_000000_add_model_source_embeddings.py +50 -0
  389. app/db/alembic/versions/20260821_000000_add_retry_circuit_admission_generation.py +49 -0
  390. app/db/alembic/versions/20260826_000000_add_http_bridge_event_chunks.py +107 -0
  391. app/db/alembic/versions/20260828_000000_add_accounts_chatgpt_identity_index.py +55 -0
  392. app/db/alembic/versions/20260830_000000_add_quota_warmup_claim_expiry.py +83 -0
  393. app/db/alembic/versions/20260908_000000_add_guest_session_generation.py +37 -0
  394. app/db/alembic/versions/20260908_000000_add_subscription_overflow.py +140 -0
  395. app/db/alembic/versions/20260908_000000_replace_upstream_stream_transport_default_sentinel.py +50 -0
  396. app/db/alembic/versions/20260908_020000_merge_overflow_transport_heads.py +27 -0
  397. app/db/alembic/versions/20260909_000000_add_dashboard_roles.py +72 -0
  398. app/db/alembic/versions/20260909_010000_add_dashboard_users.py +183 -0
  399. app/db/alembic/versions/20260909_020000_reproject_compat_admin_credentials.py +105 -0
  400. app/db/alembic/versions/20260909_030000_add_audit_actor_columns.py +103 -0
  401. app/db/alembic/versions/20260909_030000_dashboard_resilience_toggle_settings.py +63 -0
  402. app/db/alembic/versions/20260909_040000_add_dashboard_user_invites.py +54 -0
  403. app/db/alembic/versions/20260909_040000_dashboard_timeout_settings.py +68 -0
  404. app/db/alembic/versions/20260909_050000_dashboard_routing_overload_settings.py +65 -0
  405. app/db/alembic/versions/20260909_060000_add_report_rollup.py +74 -0
  406. app/db/alembic/versions/20260909_070000_automation_run_claim_budget.py +50 -0
  407. app/db/alembic/versions/20260909_080000_dashboard_stream_bridge_budgets.py +62 -0
  408. app/db/alembic/versions/20260909_090000_dashboard_background_job_toggles.py +63 -0
  409. app/db/alembic/versions/20260909_100000_dashboard_codex_prewarm.py +49 -0
  410. app/db/alembic/versions/20260909_110000_model_context_window_overrides.py +45 -0
  411. app/db/alembic/versions/20260909_120000_dashboard_conversation_archive.py +48 -0
  412. app/db/alembic/versions/20260909_130000_add_request_logs_live_facet_indexes.py +96 -0
  413. app/db/alembic/versions/20260910_000000_add_totp_required_for_admin_role.py +44 -0
  414. app/db/alembic/versions/20260910_000000_request_logs_missing_cost_index.py +52 -0
  415. app/db/alembic/versions/20260910_010000_add_dashboard_auth_providers.py +134 -0
  416. app/db/alembic/versions/20260910_010000_dashboard_spool_retention.py +52 -0
  417. app/db/alembic/versions/20260910_020000_add_dashboard_role_mappings.py +74 -0
  418. app/db/alembic/versions/20260910_143000_request_log_affinity.py +28 -0
  419. app/db/alembic/versions/20260910_180000_merge_affinity_guest_heads.py +17 -0
  420. app/db/alembic/versions/20260910_200000_merge_affinity_identity_heads.py +17 -0
  421. app/db/alembic/versions/20260910_220000_merge_affinity_invite_heads.py +17 -0
  422. app/db/alembic/versions/20260911_000000_model_source_pins_kind_expires_index.py +43 -0
  423. app/db/alembic/versions/20260911_010000_merge_pin_index_and_affinity_heads.py +17 -0
  424. app/db/alembic/versions/20260911_020000_add_http_bridge_terminal_append_phase.py +54 -0
  425. app/db/alembic/versions/20260911_030000_add_local_login_policy.py +48 -0
  426. app/db/alembic/versions/20260911_040000_add_thread_cache_identity_mode.py +48 -0
  427. app/db/alembic/versions/20260911_060000_add_bridge_session_continuity_abandonment.py +40 -0
  428. app/db/alembic/versions/20260912_000000_merge_thread_cache_and_bridge_retirement_heads.py +22 -0
  429. app/db/alembic/versions/20260912_010000_drop_legacy_dashboard_credentials.py +140 -0
  430. app/db/alembic/versions/20260913_000000_add_oidc_provider_flow.py +134 -0
  431. app/db/alembic/versions/20260914_000000_add_scim_tokens.py +78 -0
  432. app/db/alembic/versions/20260914_000000_drop_subscription_overflow_schema.py +132 -0
  433. app/db/alembic/versions/20260918_000000_merge_scim_and_overflow_heads.py +17 -0
  434. app/db/alembic/versions/__init__.py +1 -0
  435. app/db/backup.py +64 -0
  436. app/db/migrate.py +1249 -0
  437. app/db/migration_lock.py +160 -0
  438. app/db/migration_url.py +18 -0
  439. app/db/models.py +3100 -0
  440. app/db/recover.py +244 -0
  441. app/db/session.py +1295 -0
  442. app/db/snapshot.py +46 -0
  443. app/db/sqlite_lock_retry.py +190 -0
  444. app/db/sqlite_utils.py +549 -0
  445. app/dependencies.py +462 -0
  446. app/main.py +1240 -0
  447. app/modules/__init__.py +0 -0
  448. app/modules/accounts/__init__.py +0 -0
  449. app/modules/accounts/api.py +426 -0
  450. app/modules/accounts/auth_manager.py +1439 -0
  451. app/modules/accounts/background_repository.py +160 -0
  452. app/modules/accounts/deletion.py +540 -0
  453. app/modules/accounts/mappers.py +654 -0
  454. app/modules/accounts/refresh_claims.py +352 -0
  455. app/modules/accounts/repository.py +1679 -0
  456. app/modules/accounts/schemas.py +308 -0
  457. app/modules/accounts/service.py +809 -0
  458. app/modules/accounts/usage_rollup.py +486 -0
  459. app/modules/accounts/usage_rollup_scheduler.py +75 -0
  460. app/modules/accounts/usage_time_rollup.py +1037 -0
  461. app/modules/accounts/usage_time_rollup_read.py +522 -0
  462. app/modules/api_keys/__init__.py +1 -0
  463. app/modules/api_keys/api.py +344 -0
  464. app/modules/api_keys/last_used_coalescer.py +216 -0
  465. app/modules/api_keys/limit_windows.py +41 -0
  466. app/modules/api_keys/repository.py +1200 -0
  467. app/modules/api_keys/reset_scheduler.py +85 -0
  468. app/modules/api_keys/schemas.py +135 -0
  469. app/modules/api_keys/service.py +2123 -0
  470. app/modules/audit/__init__.py +0 -0
  471. app/modules/audit/api.py +88 -0
  472. app/modules/audit/repository.py +74 -0
  473. app/modules/audit/schemas.py +33 -0
  474. app/modules/audit/service.py +71 -0
  475. app/modules/auth_providers/__init__.py +0 -0
  476. app/modules/auth_providers/api.py +127 -0
  477. app/modules/auth_providers/config.py +343 -0
  478. app/modules/auth_providers/repository.py +101 -0
  479. app/modules/auth_providers/schemas.py +88 -0
  480. app/modules/auth_providers/seed.py +105 -0
  481. app/modules/auth_providers/service.py +289 -0
  482. app/modules/automations/__init__.py +1 -0
  483. app/modules/automations/api.py +369 -0
  484. app/modules/automations/repository.py +1896 -0
  485. app/modules/automations/scheduler.py +107 -0
  486. app/modules/automations/schemas.py +158 -0
  487. app/modules/automations/service.py +2548 -0
  488. app/modules/cache_isolation_probe/__init__.py +0 -0
  489. app/modules/cache_isolation_probe/api.py +188 -0
  490. app/modules/cache_isolation_probe/prefix.py +144 -0
  491. app/modules/cache_isolation_probe/schemas.py +94 -0
  492. app/modules/cache_isolation_probe/sender.py +229 -0
  493. app/modules/cache_isolation_probe/service.py +525 -0
  494. app/modules/conversation_archive/__init__.py +1 -0
  495. app/modules/conversation_archive/api.py +101 -0
  496. app/modules/conversation_archive/schemas.py +28 -0
  497. app/modules/conversation_archive/service.py +165 -0
  498. app/modules/dashboard/__init__.py +1 -0
  499. app/modules/dashboard/api.py +106 -0
  500. app/modules/dashboard/builders.py +107 -0
  501. app/modules/dashboard/repository.py +304 -0
  502. app/modules/dashboard/schemas.py +148 -0
  503. app/modules/dashboard/service.py +534 -0
  504. app/modules/dashboard/timeframes.py +19 -0
  505. app/modules/dashboard/weekly_pace.py +617 -0
  506. app/modules/dashboard_auth/__init__.py +1 -0
  507. app/modules/dashboard_auth/api.py +1344 -0
  508. app/modules/dashboard_auth/oidc_api.py +753 -0
  509. app/modules/dashboard_auth/oidc_flows.py +330 -0
  510. app/modules/dashboard_auth/repository.py +452 -0
  511. app/modules/dashboard_auth/schemas.py +232 -0
  512. app/modules/dashboard_auth/service.py +1384 -0
  513. app/modules/dashboard_roles/__init__.py +0 -0
  514. app/modules/dashboard_roles/api.py +64 -0
  515. app/modules/dashboard_roles/repository.py +54 -0
  516. app/modules/dashboard_roles/schemas.py +29 -0
  517. app/modules/dashboard_roles/seed.py +80 -0
  518. app/modules/dashboard_roles/service.py +142 -0
  519. app/modules/dashboard_users/__init__.py +0 -0
  520. app/modules/dashboard_users/api.py +278 -0
  521. app/modules/dashboard_users/break_glass.py +169 -0
  522. app/modules/dashboard_users/credentials.py +18 -0
  523. app/modules/dashboard_users/identity_resolver.py +665 -0
  524. app/modules/dashboard_users/repository.py +725 -0
  525. app/modules/dashboard_users/schemas.py +117 -0
  526. app/modules/dashboard_users/service.py +1054 -0
  527. app/modules/firewall/__init__.py +1 -0
  528. app/modules/firewall/api.py +78 -0
  529. app/modules/firewall/repository.py +51 -0
  530. app/modules/firewall/schemas.py +28 -0
  531. app/modules/firewall/service.py +93 -0
  532. app/modules/fleet/__init__.py +1 -0
  533. app/modules/fleet/api.py +177 -0
  534. app/modules/fleet/mappers.py +56 -0
  535. app/modules/fleet/observability.py +372 -0
  536. app/modules/fleet/schemas.py +119 -0
  537. app/modules/health/__init__.py +0 -0
  538. app/modules/health/api.py +246 -0
  539. app/modules/health/schemas.py +27 -0
  540. app/modules/limit_warmup/__init__.py +1 -0
  541. app/modules/limit_warmup/repository.py +159 -0
  542. app/modules/limit_warmup/service.py +1001 -0
  543. app/modules/model_sources/__init__.py +1 -0
  544. app/modules/model_sources/api.py +99 -0
  545. app/modules/model_sources/catalog.py +307 -0
  546. app/modules/model_sources/forwarding.py +1435 -0
  547. app/modules/model_sources/projection.py +55 -0
  548. app/modules/model_sources/repository.py +201 -0
  549. app/modules/model_sources/schemas.py +79 -0
  550. app/modules/model_sources/selection.py +166 -0
  551. app/modules/model_sources/service.py +236 -0
  552. app/modules/oauth/__init__.py +0 -0
  553. app/modules/oauth/api.py +101 -0
  554. app/modules/oauth/repository.py +238 -0
  555. app/modules/oauth/schemas.py +49 -0
  556. app/modules/oauth/service.py +1196 -0
  557. app/modules/oauth/templates/oauth_success.html +122 -0
  558. app/modules/proxy/__init__.py +0 -0
  559. app/modules/proxy/_load_balancer/__init__.py +1 -0
  560. app/modules/proxy/_load_balancer/error_rate.py +116 -0
  561. app/modules/proxy/_load_balancer/latency_cohort.py +154 -0
  562. app/modules/proxy/_load_balancer/model_eligibility.py +250 -0
  563. app/modules/proxy/_load_balancer/opportunistic_admission.py +350 -0
  564. app/modules/proxy/_load_balancer/overload_backoff.py +394 -0
  565. app/modules/proxy/_load_balancer/sticky_selection.py +2417 -0
  566. app/modules/proxy/_load_balancer/throughput_cohort.py +233 -0
  567. app/modules/proxy/_load_balancer/ttft_cohort.py +214 -0
  568. app/modules/proxy/_load_balancer/tunables.py +97 -0
  569. app/modules/proxy/_load_balancer/types.py +96 -0
  570. app/modules/proxy/_load_balancer/unbound_selection.py +534 -0
  571. app/modules/proxy/_service/__init__.py +6 -0
  572. app/modules/proxy/_service/api_key_usage.py +745 -0
  573. app/modules/proxy/_service/clock_budget.py +38 -0
  574. app/modules/proxy/_service/codex_control.py +611 -0
  575. app/modules/proxy/_service/compact.py +2211 -0
  576. app/modules/proxy/_service/file_ops.py +749 -0
  577. app/modules/proxy/_service/http_bridge/__init__.py +3 -0
  578. app/modules/proxy/_service/http_bridge/accepted_replay.py +331 -0
  579. app/modules/proxy/_service/http_bridge/account_sessions.py +57 -0
  580. app/modules/proxy/_service/http_bridge/activity.py +178 -0
  581. app/modules/proxy/_service/http_bridge/helpers.py +3959 -0
  582. app/modules/proxy/_service/http_bridge/mixin.py +2435 -0
  583. app/modules/proxy/_service/http_bridge/owner_forwarding.py +609 -0
  584. app/modules/proxy/_service/http_bridge/protocol.py +167 -0
  585. app/modules/proxy/_service/http_bridge/proxy_failover.py +58 -0
  586. app/modules/proxy/_service/http_bridge/quarantine.py +443 -0
  587. app/modules/proxy/_service/http_bridge/request_submit.py +4582 -0
  588. app/modules/proxy/_service/http_bridge/retry_circuit.py +2442 -0
  589. app/modules/proxy/_service/http_bridge/service_stubs.py +492 -0
  590. app/modules/proxy/_service/http_bridge/session_registry.py +778 -0
  591. app/modules/proxy/_service/http_bridge/streaming.py +5328 -0
  592. app/modules/proxy/_service/http_bridge/upstream_events.py +4538 -0
  593. app/modules/proxy/_service/observability.py +410 -0
  594. app/modules/proxy/_service/rate_limit.py +361 -0
  595. app/modules/proxy/_service/realtime_live.py +655 -0
  596. app/modules/proxy/_service/refresh.py +154 -0
  597. app/modules/proxy/_service/request_log.py +585 -0
  598. app/modules/proxy/_service/response_create.py +930 -0
  599. app/modules/proxy/_service/streaming/__init__.py +3 -0
  600. app/modules/proxy/_service/streaming/helpers.py +1317 -0
  601. app/modules/proxy/_service/streaming/mixin.py +1100 -0
  602. app/modules/proxy/_service/streaming/protocol.py +32 -0
  603. app/modules/proxy/_service/streaming/retry.py +3762 -0
  604. app/modules/proxy/_service/support.py +2122 -0
  605. app/modules/proxy/_service/transcribe.py +434 -0
  606. app/modules/proxy/_service/warmup.py +483 -0
  607. app/modules/proxy/_service/websocket/__init__.py +3 -0
  608. app/modules/proxy/_service/websocket/helpers.py +2360 -0
  609. app/modules/proxy/_service/websocket/mixin.py +7285 -0
  610. app/modules/proxy/_service/websocket/protocol.py +74 -0
  611. app/modules/proxy/account_cache.py +229 -0
  612. app/modules/proxy/account_eligibility.py +87 -0
  613. app/modules/proxy/additional_model_limits.py +40 -0
  614. app/modules/proxy/affinity.py +830 -0
  615. app/modules/proxy/affinity_observation.py +101 -0
  616. app/modules/proxy/api.py +10254 -0
  617. app/modules/proxy/api_key_usage.py +113 -0
  618. app/modules/proxy/cap_partitioning.py +280 -0
  619. app/modules/proxy/capability_lineage.py +65 -0
  620. app/modules/proxy/capability_lineage_repository.py +82 -0
  621. app/modules/proxy/capability_routing.py +253 -0
  622. app/modules/proxy/continuity.py +112 -0
  623. app/modules/proxy/downstream_delivery.py +277 -0
  624. app/modules/proxy/durable_bridge_coordinator.py +1217 -0
  625. app/modules/proxy/durable_bridge_repository.py +4516 -0
  626. app/modules/proxy/durable_bridge_runtime.py +9 -0
  627. app/modules/proxy/durable_bridge_transcript_codec.py +122 -0
  628. app/modules/proxy/fair_share.py +131 -0
  629. app/modules/proxy/file_pin_repository.py +210 -0
  630. app/modules/proxy/helpers.py +439 -0
  631. app/modules/proxy/http_bridge_event_batcher.py +613 -0
  632. app/modules/proxy/http_bridge_forwarding.py +797 -0
  633. app/modules/proxy/http_continuation.py +76 -0
  634. app/modules/proxy/images_observability.py +62 -0
  635. app/modules/proxy/images_service.py +1054 -0
  636. app/modules/proxy/load_balancer.py +3016 -0
  637. app/modules/proxy/rate_limit_cache.py +46 -0
  638. app/modules/proxy/replay_safety.py +1063 -0
  639. app/modules/proxy/repo_bundle.py +31 -0
  640. app/modules/proxy/request_policy.py +1054 -0
  641. app/modules/proxy/ring_membership.py +251 -0
  642. app/modules/proxy/schemas.py +345 -0
  643. app/modules/proxy/selection_errors.py +41 -0
  644. app/modules/proxy/service.py +2600 -0
  645. app/modules/proxy/source_admission.py +125 -0
  646. app/modules/proxy/source_dispatch.py +999 -0
  647. app/modules/proxy/sticky_repository.py +831 -0
  648. app/modules/proxy/thread_anchors.py +491 -0
  649. app/modules/proxy/tool_call_dedupe.py +802 -0
  650. app/modules/proxy/types.py +54 -0
  651. app/modules/proxy/work_admission.py +121 -0
  652. app/modules/quota_planner/__init__.py +1 -0
  653. app/modules/quota_planner/api.py +276 -0
  654. app/modules/quota_planner/logic.py +743 -0
  655. app/modules/quota_planner/repository.py +722 -0
  656. app/modules/quota_planner/scheduler.py +212 -0
  657. app/modules/quota_planner/schemas.py +102 -0
  658. app/modules/quota_planner/warmup.py +693 -0
  659. app/modules/rate_limit_reset_credits/__init__.py +0 -0
  660. app/modules/rate_limit_reset_credits/api.py +601 -0
  661. app/modules/rate_limit_reset_credits/redeem_coordination.py +274 -0
  662. app/modules/rate_limit_reset_credits/store.py +114 -0
  663. app/modules/reports/__init__.py +1 -0
  664. app/modules/reports/api.py +134 -0
  665. app/modules/reports/cache.py +74 -0
  666. app/modules/reports/filters.py +27 -0
  667. app/modules/reports/repository.py +525 -0
  668. app/modules/reports/rollup.py +150 -0
  669. app/modules/reports/rollup_read.py +148 -0
  670. app/modules/reports/schemas.py +123 -0
  671. app/modules/reports/service.py +287 -0
  672. app/modules/reports/thread_identity.py +256 -0
  673. app/modules/request_logs/__init__.py +0 -0
  674. app/modules/request_logs/api.py +224 -0
  675. app/modules/request_logs/cost_backfill.py +154 -0
  676. app/modules/request_logs/mappers.py +97 -0
  677. app/modules/request_logs/repository.py +1765 -0
  678. app/modules/request_logs/schemas.py +145 -0
  679. app/modules/request_logs/service.py +348 -0
  680. app/modules/role_mappings/__init__.py +0 -0
  681. app/modules/role_mappings/api.py +206 -0
  682. app/modules/role_mappings/matching.py +55 -0
  683. app/modules/role_mappings/repository.py +106 -0
  684. app/modules/role_mappings/schemas.py +67 -0
  685. app/modules/role_mappings/service.py +334 -0
  686. app/modules/runtime/__init__.py +1 -0
  687. app/modules/runtime/api.py +18 -0
  688. app/modules/runtime/schemas.py +14 -0
  689. app/modules/runtime/service.py +218 -0
  690. app/modules/scim/__init__.py +1 -0
  691. app/modules/scim/api.py +194 -0
  692. app/modules/scim/dependencies.py +112 -0
  693. app/modules/scim/errors.py +77 -0
  694. app/modules/scim/management_api.py +156 -0
  695. app/modules/scim/repository.py +212 -0
  696. app/modules/scim/schemas.py +145 -0
  697. app/modules/scim/service.py +786 -0
  698. app/modules/scim/tokens.py +43 -0
  699. app/modules/settings/__init__.py +1 -0
  700. app/modules/settings/api.py +1677 -0
  701. app/modules/settings/repository.py +606 -0
  702. app/modules/settings/schemas.py +496 -0
  703. app/modules/settings/service.py +769 -0
  704. app/modules/shared/__init__.py +0 -0
  705. app/modules/shared/schemas.py +22 -0
  706. app/modules/sticky_sessions/__init__.py +1 -0
  707. app/modules/sticky_sessions/api.py +118 -0
  708. app/modules/sticky_sessions/cleanup_scheduler.py +487 -0
  709. app/modules/sticky_sessions/schemas.py +78 -0
  710. app/modules/sticky_sessions/service.py +196 -0
  711. app/modules/telemetry/__init__.py +5 -0
  712. app/modules/telemetry/api.py +115 -0
  713. app/modules/telemetry/clients.py +62 -0
  714. app/modules/telemetry/consent.py +115 -0
  715. app/modules/telemetry/scheduler.py +85 -0
  716. app/modules/telemetry/schemas.py +163 -0
  717. app/modules/telemetry/sender.py +221 -0
  718. app/modules/telemetry/snapshot.py +490 -0
  719. app/modules/usage/__init__.py +0 -0
  720. app/modules/usage/additional_quota_keys.py +379 -0
  721. app/modules/usage/api.py +45 -0
  722. app/modules/usage/background_repository.py +147 -0
  723. app/modules/usage/builders.py +559 -0
  724. app/modules/usage/depletion_service.py +277 -0
  725. app/modules/usage/live_ingest.py +356 -0
  726. app/modules/usage/mappers.py +29 -0
  727. app/modules/usage/plan_downgrade_observations.py +488 -0
  728. app/modules/usage/repository.py +1890 -0
  729. app/modules/usage/schemas.py +68 -0
  730. app/modules/usage/service.py +102 -0
  731. app/modules/usage/updater.py +1488 -0
  732. app/static/assets/Trans-Cj-J9mVA.js +1 -0
  733. app/static/assets/access-page-DHzZiXZi.js +1 -0
  734. app/static/assets/account-cost-donut-DGhfA4U8.js +1 -0
  735. app/static/assets/account-identifiers-CxbdLshZ.js +1 -0
  736. app/static/assets/account-status-DIsU5sUS.js +1 -0
  737. app/static/assets/account-trend-chart-BptlfwwQ.js +1 -0
  738. app/static/assets/accounts-page-Bbai8ift.js +3 -0
  739. app/static/assets/api-Dwa7XxPe.js +1 -0
  740. app/static/assets/api-client-dugX1Pti.js +55 -0
  741. app/static/assets/api-key-create-dialog-DnRm0oYN.js +1 -0
  742. app/static/assets/api-key-created-dialog-B6jkDG9f.js +1 -0
  743. app/static/assets/api-key-edit-dialog-Ck3Bnc3V.js +1 -0
  744. app/static/assets/api-trend-chart-BchCw7Hj.js +1 -0
  745. app/static/assets/apis-page-BvXz9c6i.js +2 -0
  746. app/static/assets/arrow-up-Dqh_KzJo.js +1 -0
  747. app/static/assets/arrow-up-down-LBRgVDGY.js +1 -0
  748. app/static/assets/automations-page-DQgNAJOR.js +3 -0
  749. app/static/assets/badge-DqMb9es0.js +1 -0
  750. app/static/assets/blur-email-HEvKagG2.js +1 -0
  751. app/static/assets/button-BjVmmiGW.js +1 -0
  752. app/static/assets/checkbox-apsl9vzk.js +1 -0
  753. app/static/assets/chevron-left-1d_pHVa5.js +1 -0
  754. app/static/assets/circle-alert-BS0xd_o1.js +1 -0
  755. app/static/assets/clsx-DB0hHKMi.js +1 -0
  756. app/static/assets/colors-BBy8GEtr.js +1 -0
  757. app/static/assets/copy-button-D7nAvOKR.js +1 -0
  758. app/static/assets/cost-per-day-chart-YdqlkZVM.js +1 -0
  759. app/static/assets/createLucideIcon-DBLzLNh2.js +1 -0
  760. app/static/assets/dashboard-page-CSdhBy0f.js +2 -0
  761. app/static/assets/distribution-metric-format-yZTy1ZEu.js +1 -0
  762. app/static/assets/donut-chart-Bxc69cra.js +1 -0
  763. app/static/assets/download-kQteIcsB.js +1 -0
  764. app/static/assets/ellipsis-B_5OgIre.js +1 -0
  765. app/static/assets/empty-state-JcA7tc8m.js +1 -0
  766. app/static/assets/es2015-bMH8YKZT.js +41 -0
  767. app/static/assets/es6-B264ni6W.js +69 -0
  768. app/static/assets/external-link-UVAOgMQ0.js +1 -0
  769. app/static/assets/i18n-BWqZ9Ua2.js +1 -0
  770. app/static/assets/inbox-B_RM2s1J.js +1 -0
  771. app/static/assets/index-DIpd264Z.js +23 -0
  772. app/static/assets/index-DMeLKRJx.css +2 -0
  773. app/static/assets/inherit-badge-QhTYVaU6.js +1 -0
  774. app/static/assets/invite-dialog-0CYh0GiQ.js +1 -0
  775. app/static/assets/iso-Bpe5Ihbb.js +1 -0
  776. app/static/assets/label-CGaLmLNf.js +1 -0
  777. app/static/assets/lazy-recharts-Pv8p9ArM.js +2 -0
  778. app/static/assets/loading-overlay-CzKQfHLI.js +1 -0
  779. app/static/assets/mini-quota-bar-cGT_t8Ae.js +1 -0
  780. app/static/assets/model-distribution-donut-BKjAfT48.js +1 -0
  781. app/static/assets/model-source-multi-select-D2d06Vc7.js +1 -0
  782. app/static/assets/multi-select-filter-KIA5qU6Z.js +1 -0
  783. app/static/assets/oauth-dialog-XeaGGDjS.js +1 -0
  784. app/static/assets/pagination-controls-D1Ypd0RU.js +1 -0
  785. app/static/assets/payload-CIYnGZz9.js +1 -0
  786. app/static/assets/queue-wait-chart-VoV7Yg9L.js +1 -0
  787. app/static/assets/report-chart-card-BXDliwcc.js +1 -0
  788. app/static/assets/reports-page-CGCJeMHG.js +3 -0
  789. app/static/assets/reset-credit-confirm-dialog-QOifTnVB.js +1 -0
  790. app/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  791. app/static/assets/search-HfhqwERt.js +1 -0
  792. app/static/assets/select-Cl33bz-g.js +1 -0
  793. app/static/assets/settings-page-mZj01HYl.js +2 -0
  794. app/static/assets/shim-CZwyM4Q7.js +1 -0
  795. app/static/assets/skeleton-C_EUc4Rp.js +1 -0
  796. app/static/assets/sparkline-chart-DdNNsddM.js +1 -0
  797. app/static/assets/status-badge-CHHA5X6c.js +1 -0
  798. app/static/assets/switch-CzxgokcM.js +1 -0
  799. app/static/assets/table-DXaNSQ0G.js +1 -0
  800. app/static/assets/time-to-first-token-chart--68L0u_v.js +1 -0
  801. app/static/assets/tokens-per-day-chart-Cs82BFuE.js +1 -0
  802. app/static/assets/tokens-per-second-chart-AD2dfOn6.js +1 -0
  803. app/static/assets/totp-settings-CRHtxQ7j.js +1 -0
  804. app/static/assets/trash-D3KyWr1h.js +1 -0
  805. app/static/assets/use-account-quota-display-BPmrTBTC.js +1 -0
  806. app/static/assets/use-accounts-PWlVGw3z.js +1 -0
  807. app/static/assets/use-auth-CuDlW3iC.js +1 -0
  808. app/static/assets/use-chart-colors-DIu6tClI.js +1 -0
  809. app/static/assets/use-model-sources-Bul24JBu.js +1 -0
  810. app/static/assets/use-models-DUCPUuZg.js +1 -0
  811. app/static/assets/use-reduced-motion-hAm5r6_d.js +1 -0
  812. app/static/assets/useTranslation--56nHhlH.js +1 -0
  813. app/static/assets/useragent-distribution-donut-Dup6sf_n.js +1 -0
  814. app/static/assets/users-uIC3TIlE.js +1 -0
  815. app/static/assets/vendor-query-_bHZuxEx.js +1 -0
  816. app/static/assets/vendor-react-BVL0YZPW.js +9 -0
  817. app/static/favicon.svg +10 -0
  818. app/static/fonts/GeistSans-Variable.woff2 +0 -0
  819. app/static/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
  820. app/static/fonts/jetbrains-mono-latin.woff2 +0 -0
  821. app/static/index.html +29 -0
  822. codexlb_up-1.25.0b10.dist-info/METADATA +124 -0
  823. codexlb_up-1.25.0b10.dist-info/RECORD +827 -0
  824. codexlb_up-1.25.0b10.dist-info/WHEEL +4 -0
  825. codexlb_up-1.25.0b10.dist-info/entry_points.txt +3 -0
  826. codexlb_up-1.25.0b10.dist-info/licenses/LICENSE +21 -0
  827. config/additional_quota_registry.json +11 -0
@@ -0,0 +1,769 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass, field
7
+ from typing import Any
8
+
9
+ from app.core.auth.dashboard_access import is_admin_level
10
+ from app.core.clients.thread_cache_identity import (
11
+ THREAD_CACHE_IDENTITY_MODE_DEFAULT,
12
+ normalize_thread_cache_identity_mode,
13
+ )
14
+ from app.core.config.background_jobs import BACKGROUND_JOB_SETTINGS
15
+ from app.core.config.dashboard_overrides import DASHBOARD_TIMEOUT_SETTINGS
16
+
17
+ # Re-exported: the resolver lives in ``app.core.config.inheritable`` so hot
18
+ # paths in ``app.core`` share it without importing this module.
19
+ from app.core.config.inheritable import InheritableValue as InheritableValue
20
+ from app.core.config.inheritable import SettingScalar as SettingScalar
21
+ from app.core.config.inheritable import SettingSource as SettingSource
22
+ from app.core.config.inheritable import resolve_inheritable as resolve_inheritable
23
+ from app.core.config.settings import Settings, get_settings
24
+ from app.core.config.spool_retention import OPERATION_SPOOL_RETENTION_SETTING
25
+ from app.core.conversation_archive import CONVERSATION_ARCHIVE_SETTING
26
+ from app.core.resilience.toggles import RESILIENCE_TOGGLE_SETTINGS
27
+ from app.db.models import DashboardSettings, LocalLoginPolicy
28
+ from app.modules.dashboard_roles.service import resolve_role_grants
29
+ from app.modules.dashboard_users.break_glass import BreakGlassRequiresTotpError
30
+ from app.modules.settings.repository import SettingsRepository
31
+ from app.modules.usage.additional_quota_keys import (
32
+ normalize_additional_quota_key,
33
+ )
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class DashboardSettingsData:
40
+ sticky_threads_enabled: bool
41
+ upstream_stream_transport: str
42
+ prohibit_fast_mode: bool
43
+ http_downstream_transport_policy: str
44
+ # Effective mode; ``provenance`` carries the source and the fallbacks.
45
+ thread_cache_identity_mode: str
46
+ thread_cache_identity_mode_override: str | None
47
+ proxy_account_response_create_limit: int
48
+ proxy_account_response_create_limit_override: int | None
49
+ proxy_account_stream_limit: int
50
+ proxy_account_stream_limit_override: int | None
51
+ proxy_account_stream_recovery_reserve: int
52
+ proxy_account_stream_recovery_reserve_override: int | None
53
+ proxy_api_key_fair_share_congestion_threshold_pct: int
54
+ proxy_api_key_fair_share_congestion_threshold_pct_override: int | None
55
+ # C2-2 routing/overload: effective values; the dashboard-stored value and
56
+ # the fallbacks are reported through ``provenance``.
57
+ proxy_overload_isolation_seconds: int
58
+ proxy_account_error_rate_weighting_enabled: bool
59
+ proxy_account_inflight_penalty_pct: float
60
+ proxy_account_lease_token_weight: float
61
+ proxy_account_lease_ttl_seconds: float
62
+ # end C2-2 routing/overload
63
+ upstream_proxy_routing_enabled: bool
64
+ upstream_proxy_default_pool_id: str | None
65
+ prefer_earlier_reset_accounts: bool
66
+ prefer_earlier_reset_window: str
67
+ show_reset_credit_badges: bool
68
+ auto_redeem_reset_credits_before_expiry: bool
69
+ show_reset_credit_expiry_badge: bool
70
+ routing_strategy: str
71
+ relative_availability_power: float
72
+ relative_availability_top_k: int
73
+ single_account_id: str | None
74
+ openai_cache_affinity_max_age_seconds: int
75
+ dashboard_session_ttl_seconds: int
76
+ http_responses_session_bridge_prompt_cache_idle_ttl_seconds: int
77
+ http_responses_session_bridge_gateway_safe_mode: bool
78
+ # M3 codex prewarm: effective value (dashboard column, else the deprecated
79
+ # env alias, else the code default); provenance carries the source.
80
+ http_responses_session_bridge_codex_prewarm_enabled: bool
81
+ # end M3 codex prewarm
82
+ sticky_reallocation_budget_threshold_pct: float
83
+ sticky_reallocation_primary_budget_threshold_pct: float
84
+ sticky_reallocation_secondary_budget_threshold_pct: float
85
+ additional_quota_routing_policies: dict[str, str]
86
+ warmup_model: str
87
+ import_without_overwrite: bool
88
+ totp_required_on_login: bool
89
+ totp_required_for_admin_role: bool
90
+ local_login_policy: str
91
+ users_without_totp_count: int
92
+ admins_without_totp_count: int
93
+ api_key_auth_enabled: bool
94
+ hide_upstream_quota_from_api_keys: bool
95
+ limit_warmup_enabled: bool
96
+ limit_warmup_windows: str
97
+ limit_warmup_model: str
98
+ limit_warmup_prompt: str
99
+ limit_warmup_cooldown_seconds: int
100
+ limit_warmup_exhausted_threshold_percent: float
101
+ limit_warmup_idle_threshold_percent: float
102
+ limit_warmup_min_available_percent: float
103
+ weekly_pace_working_days: str
104
+ weekly_pace_smoothing_minutes: int
105
+ guest_access_enabled: bool
106
+ guest_password_configured: bool
107
+ limit_warmup_staggered_idle_enabled: bool
108
+ request_log_retention_days: int
109
+ usage_history_retention_days: int
110
+ request_log_retention_override_days: int | None
111
+ usage_history_retention_override_days: int | None
112
+ # C2-3 resilience toggles: effective values (dashboard column, else the
113
+ # deprecated env alias, else the code default); provenance carries the source.
114
+ soft_drain_enabled: bool
115
+ deterministic_failover_enabled: bool
116
+ circuit_breaker_enabled: bool
117
+ # M2 background jobs: effective values (dashboard column, else the deprecated
118
+ # env alias, else the code default); provenance carries the source. The
119
+ # guardian additionally reports whether the static topology blocks it.
120
+ auth_guardian_enabled: bool
121
+ auth_guardian_blocked_by_topology: bool
122
+ automations_scheduler_enabled: bool
123
+ rate_limit_reset_credits_refresh_enabled: bool
124
+ # end M2 background jobs
125
+ # M5 conversation archive: effective toggle; provenance carries the source.
126
+ conversation_archive_enabled: bool
127
+ # end M5 conversation archive
128
+ # R2 spool retention: effective retention of the durable HTTP-bridge
129
+ # operation spool (dashboard column, else the deprecated env alias, else
130
+ # the code default); provenance carries the source.
131
+ http_responses_session_bridge_operation_spool_retention_seconds: float
132
+ # end R2 spool retention
133
+ version: int
134
+ # C2-1 timeouts: effective values (dashboard column, else environment,
135
+ # else code default); the column values are exposed through ``provenance``.
136
+ upstream_connect_timeout_seconds: float
137
+ proxy_request_budget_seconds: float
138
+ compact_request_budget_seconds: float
139
+ transcription_request_budget_seconds: float
140
+ stream_idle_timeout_seconds: float
141
+ proxy_downstream_websocket_idle_timeout_seconds: float
142
+ sse_keepalive_interval_seconds: float
143
+ # end C2-1 timeouts
144
+ # M1 stream/bridge budgets: effective values (dashboard column, else
145
+ # environment, else code default); provenance carries the source.
146
+ http_responses_stream_request_budget_seconds: float
147
+ http_responses_session_bridge_request_budget_seconds: float
148
+ # end M1 stream/bridge budgets
149
+ # Effective value, source and fallbacks of every inheritable setting, keyed
150
+ # by setting name; the settings API exposes it as ``provenance``.
151
+ provenance: Mapping[str, InheritableValue[Any]] = field(default_factory=dict)
152
+
153
+
154
+ @dataclass(frozen=True, slots=True)
155
+ class DashboardSettingsUpdateData:
156
+ sticky_threads_enabled: bool
157
+ upstream_stream_transport: str
158
+ prohibit_fast_mode: bool
159
+ http_downstream_transport_policy: str
160
+ thread_cache_identity_mode: str | None
161
+ clear_thread_cache_identity_mode: bool
162
+ proxy_account_response_create_limit: int | None
163
+ clear_proxy_account_response_create_limit: bool
164
+ proxy_account_stream_limit: int | None
165
+ clear_proxy_account_stream_limit: bool
166
+ proxy_account_stream_recovery_reserve: int | None
167
+ clear_proxy_account_stream_recovery_reserve: bool
168
+ proxy_api_key_fair_share_congestion_threshold_pct: int | None
169
+ clear_proxy_api_key_fair_share_congestion_threshold_pct: bool
170
+ # C2-2 routing/overload (tri-state: value = store, clear flag = inherit)
171
+ proxy_overload_isolation_seconds: int | None
172
+ clear_proxy_overload_isolation_seconds: bool
173
+ proxy_account_error_rate_weighting_enabled: bool | None
174
+ clear_proxy_account_error_rate_weighting_enabled: bool
175
+ proxy_account_inflight_penalty_pct: float | None
176
+ clear_proxy_account_inflight_penalty_pct: bool
177
+ proxy_account_lease_token_weight: float | None
178
+ clear_proxy_account_lease_token_weight: bool
179
+ proxy_account_lease_ttl_seconds: float | None
180
+ clear_proxy_account_lease_ttl_seconds: bool
181
+ # end C2-2 routing/overload
182
+ upstream_proxy_routing_enabled: bool
183
+ upstream_proxy_default_pool_id: str | None
184
+ prefer_earlier_reset_accounts: bool
185
+ prefer_earlier_reset_window: str
186
+ show_reset_credit_badges: bool
187
+ auto_redeem_reset_credits_before_expiry: bool
188
+ show_reset_credit_expiry_badge: bool
189
+ routing_strategy: str
190
+ relative_availability_power: float
191
+ relative_availability_top_k: int
192
+ single_account_id: str | None
193
+ openai_cache_affinity_max_age_seconds: int
194
+ dashboard_session_ttl_seconds: int
195
+ http_responses_session_bridge_prompt_cache_idle_ttl_seconds: int
196
+ http_responses_session_bridge_gateway_safe_mode: bool
197
+ sticky_reallocation_budget_threshold_pct: float
198
+ sticky_reallocation_primary_budget_threshold_pct: float
199
+ sticky_reallocation_secondary_budget_threshold_pct: float
200
+ additional_quota_routing_policies: dict[str, str]
201
+ warmup_model: str
202
+ import_without_overwrite: bool
203
+ totp_required_on_login: bool
204
+ totp_required_for_admin_role: bool
205
+ local_login_policy: str | None
206
+ api_key_auth_enabled: bool
207
+ hide_upstream_quota_from_api_keys: bool
208
+ limit_warmup_enabled: bool
209
+ limit_warmup_windows: str
210
+ limit_warmup_model: str
211
+ limit_warmup_prompt: str
212
+ limit_warmup_cooldown_seconds: int
213
+ limit_warmup_exhausted_threshold_percent: float
214
+ limit_warmup_idle_threshold_percent: float
215
+ limit_warmup_min_available_percent: float
216
+ weekly_pace_working_days: str
217
+ weekly_pace_smoothing_minutes: int
218
+ guest_access_enabled: bool
219
+ limit_warmup_staggered_idle_enabled: bool
220
+ # Tri-state retention overrides: value set = store, clear flag = reset to
221
+ # NULL (not configured = retention disabled), neither = leave untouched.
222
+ request_log_retention_override_days: int | None
223
+ usage_history_retention_override_days: int | None
224
+ clear_request_log_retention_override: bool
225
+ clear_usage_history_retention_override: bool
226
+ # C2-3 resilience toggles: tri-state (value = store, clear flag = back to
227
+ # NULL so the env alias / default applies again, neither = untouched).
228
+ soft_drain_enabled: bool | None = None
229
+ clear_soft_drain_enabled: bool = False
230
+ deterministic_failover_enabled: bool | None = None
231
+ clear_deterministic_failover_enabled: bool = False
232
+ circuit_breaker_enabled: bool | None = None
233
+ clear_circuit_breaker_enabled: bool = False
234
+ # M2 background jobs: tri-state like the resilience toggles.
235
+ auth_guardian_enabled: bool | None = None
236
+ clear_auth_guardian_enabled: bool = False
237
+ automations_scheduler_enabled: bool | None = None
238
+ clear_automations_scheduler_enabled: bool = False
239
+ rate_limit_reset_credits_refresh_enabled: bool | None = None
240
+ clear_rate_limit_reset_credits_refresh_enabled: bool = False
241
+ # end M2 background jobs
242
+ # M5 conversation archive: tri-state like the resilience toggles.
243
+ conversation_archive_enabled: bool | None = None
244
+ clear_conversation_archive_enabled: bool = False
245
+ # end M5 conversation archive
246
+ # R2 spool retention: tri-state like the C2-1 timeouts.
247
+ http_responses_session_bridge_operation_spool_retention_seconds: float | None = None
248
+ clear_http_responses_session_bridge_operation_spool_retention_seconds: bool = False
249
+ # end R2 spool retention
250
+ # C2-1 timeouts (tri-state like the caps: value = store, clear = NULL,
251
+ # neither = untouched).
252
+ upstream_connect_timeout_seconds: float | None = None
253
+ clear_upstream_connect_timeout_seconds: bool = False
254
+ proxy_request_budget_seconds: float | None = None
255
+ clear_proxy_request_budget_seconds: bool = False
256
+ compact_request_budget_seconds: float | None = None
257
+ clear_compact_request_budget_seconds: bool = False
258
+ transcription_request_budget_seconds: float | None = None
259
+ clear_transcription_request_budget_seconds: bool = False
260
+ stream_idle_timeout_seconds: float | None = None
261
+ clear_stream_idle_timeout_seconds: bool = False
262
+ proxy_downstream_websocket_idle_timeout_seconds: float | None = None
263
+ clear_proxy_downstream_websocket_idle_timeout_seconds: bool = False
264
+ sse_keepalive_interval_seconds: float | None = None
265
+ clear_sse_keepalive_interval_seconds: bool = False
266
+ # end C2-1 timeouts
267
+ # M3 codex prewarm (tri-state: value = store, clear flag = back to NULL so
268
+ # the env alias / default applies again, neither = untouched).
269
+ http_responses_session_bridge_codex_prewarm_enabled: bool | None = None
270
+ clear_http_responses_session_bridge_codex_prewarm_enabled: bool = False
271
+ # end M3 codex prewarm
272
+ # M1 stream/bridge budgets (tri-state like the C2-1 timeouts).
273
+ http_responses_stream_request_budget_seconds: float | None = None
274
+ clear_http_responses_stream_request_budget_seconds: bool = False
275
+ http_responses_session_bridge_request_budget_seconds: float | None = None
276
+ clear_http_responses_session_bridge_request_budget_seconds: bool = False
277
+ # end M1 stream/bridge budgets
278
+
279
+
280
+ @dataclass(frozen=True, slots=True)
281
+ class TotpEnrollmentSummary:
282
+ """Who still has to enrol, and whether the acting account already did."""
283
+
284
+ actor_configured: bool
285
+ users_without_totp: int
286
+ admins_without_totp: int
287
+
288
+
289
+ class SettingsService:
290
+ def __init__(self, repository: SettingsRepository) -> None:
291
+ self._repository = repository
292
+
293
+ async def totp_enrollment(self, actor_user_id: str | None) -> TotpEnrollmentSummary:
294
+ users = await self._repository.list_active_password_users()
295
+ without_totp = [user for user in users if user.totp_secret_encrypted is None]
296
+ return TotpEnrollmentSummary(
297
+ actor_configured=any(user.id == actor_user_id and user.totp_secret_encrypted is not None for user in users),
298
+ users_without_totp=len(without_totp),
299
+ admins_without_totp=sum(1 for user in without_totp if is_admin_level(resolve_role_grants(user.role))),
300
+ )
301
+
302
+ async def get_settings(self, *, actor_user_id: str | None = None) -> DashboardSettingsData:
303
+ row = await self._repository.get_or_create()
304
+ return _settings_data(row, await self.totp_enrollment(actor_user_id))
305
+
306
+ async def update_settings(
307
+ self,
308
+ payload: DashboardSettingsUpdateData,
309
+ *,
310
+ actor_user_id: str | None = None,
311
+ expected_version: int | None = None,
312
+ ) -> DashboardSettingsData:
313
+ # The accounts lock is taken before this service reads anything and is
314
+ # held until ``update`` commits, so the count below and the policy write
315
+ # are one step. Acquiring it first keeps both dialects taking the lock
316
+ # at the same moment instead of relying on the in-transaction fallback
317
+ # of ``acquire_write_intent``. It is only taken when the payload could
318
+ # be a tightening, so an ordinary settings save is never queued behind
319
+ # account mutations.
320
+ if payload.local_login_policy not in (None, LocalLoginPolicy.ENABLED.value):
321
+ await self._repository.acquire_account_write_intent()
322
+ current = await self._repository.get_or_create()
323
+ # Requiring TOTP of others starts with the acting account: whoever turns
324
+ # either requirement on must already hold a secret, or the next request
325
+ # would park them at the enrolment gate they just created.
326
+ enabling_global = payload.totp_required_on_login and not current.totp_required_on_login
327
+ enabling_admin_role = payload.totp_required_for_admin_role and not current.totp_required_for_admin_role
328
+ if enabling_global or enabling_admin_role:
329
+ enrollment = await self.totp_enrollment(actor_user_id)
330
+ if not enrollment.actor_configured:
331
+ raise ValueError("Set up your own TOTP before requiring it at sign-in")
332
+ # Closing the local password form is the most dangerous button in the
333
+ # product: it is also the setting that locks the install out when the
334
+ # identity provider is down. Only the tightening transition is gated
335
+ # (re-saving the stored value, and relaxing back to ``enabled``, never
336
+ # are), and the refusal names the account that would fix it. The count
337
+ # and the settings write are one atomic step: the accounts lock above
338
+ # is held until ``update`` commits, so no concurrent mutation can
339
+ # remove the last qualifying account in between.
340
+ tightening = (
341
+ payload.local_login_policy is not None
342
+ and payload.local_login_policy != current.local_login_policy
343
+ and payload.local_login_policy != LocalLoginPolicy.ENABLED.value
344
+ )
345
+ if tightening and await self._repository.count_qualifying_break_glass() == 0:
346
+ designated = await self._repository.list_break_glass_designations()
347
+ username = designated[0].username if designated else None
348
+ raise BreakGlassRequiresTotpError(
349
+ (
350
+ f"Turn on two-factor for '{username}' before restricting local sign-in"
351
+ if username is not None
352
+ else "Designate an admin account with two-factor as the emergency account "
353
+ "before restricting local sign-in"
354
+ ),
355
+ username=username,
356
+ )
357
+ row = await self._repository.update(
358
+ expected_version=expected_version,
359
+ sticky_threads_enabled=payload.sticky_threads_enabled,
360
+ upstream_stream_transport=payload.upstream_stream_transport,
361
+ prohibit_fast_mode=payload.prohibit_fast_mode,
362
+ http_downstream_transport_policy=payload.http_downstream_transport_policy,
363
+ thread_cache_identity_mode=payload.thread_cache_identity_mode,
364
+ clear_thread_cache_identity_mode=payload.clear_thread_cache_identity_mode,
365
+ proxy_account_response_create_limit=payload.proxy_account_response_create_limit,
366
+ clear_proxy_account_response_create_limit=payload.clear_proxy_account_response_create_limit,
367
+ proxy_account_stream_limit=payload.proxy_account_stream_limit,
368
+ clear_proxy_account_stream_limit=payload.clear_proxy_account_stream_limit,
369
+ proxy_account_stream_recovery_reserve=payload.proxy_account_stream_recovery_reserve,
370
+ clear_proxy_account_stream_recovery_reserve=payload.clear_proxy_account_stream_recovery_reserve,
371
+ proxy_api_key_fair_share_congestion_threshold_pct=(
372
+ payload.proxy_api_key_fair_share_congestion_threshold_pct
373
+ ),
374
+ clear_proxy_api_key_fair_share_congestion_threshold_pct=(
375
+ payload.clear_proxy_api_key_fair_share_congestion_threshold_pct
376
+ ),
377
+ # C2-2 routing/overload
378
+ proxy_overload_isolation_seconds=payload.proxy_overload_isolation_seconds,
379
+ clear_proxy_overload_isolation_seconds=payload.clear_proxy_overload_isolation_seconds,
380
+ proxy_account_error_rate_weighting_enabled=payload.proxy_account_error_rate_weighting_enabled,
381
+ clear_proxy_account_error_rate_weighting_enabled=payload.clear_proxy_account_error_rate_weighting_enabled,
382
+ proxy_account_inflight_penalty_pct=payload.proxy_account_inflight_penalty_pct,
383
+ clear_proxy_account_inflight_penalty_pct=payload.clear_proxy_account_inflight_penalty_pct,
384
+ proxy_account_lease_token_weight=payload.proxy_account_lease_token_weight,
385
+ clear_proxy_account_lease_token_weight=payload.clear_proxy_account_lease_token_weight,
386
+ proxy_account_lease_ttl_seconds=payload.proxy_account_lease_ttl_seconds,
387
+ clear_proxy_account_lease_ttl_seconds=payload.clear_proxy_account_lease_ttl_seconds,
388
+ # end C2-2 routing/overload
389
+ upstream_proxy_routing_enabled=payload.upstream_proxy_routing_enabled,
390
+ upstream_proxy_default_pool_id=payload.upstream_proxy_default_pool_id,
391
+ prefer_earlier_reset_accounts=payload.prefer_earlier_reset_accounts,
392
+ prefer_earlier_reset_window=payload.prefer_earlier_reset_window,
393
+ show_reset_credit_badges=payload.show_reset_credit_badges,
394
+ auto_redeem_reset_credits_before_expiry=payload.auto_redeem_reset_credits_before_expiry,
395
+ show_reset_credit_expiry_badge=payload.show_reset_credit_expiry_badge,
396
+ routing_strategy=payload.routing_strategy,
397
+ relative_availability_power=payload.relative_availability_power,
398
+ relative_availability_top_k=payload.relative_availability_top_k,
399
+ single_account_id=payload.single_account_id,
400
+ openai_cache_affinity_max_age_seconds=payload.openai_cache_affinity_max_age_seconds,
401
+ dashboard_session_ttl_seconds=payload.dashboard_session_ttl_seconds,
402
+ http_responses_session_bridge_prompt_cache_idle_ttl_seconds=(
403
+ payload.http_responses_session_bridge_prompt_cache_idle_ttl_seconds
404
+ ),
405
+ http_responses_session_bridge_gateway_safe_mode=payload.http_responses_session_bridge_gateway_safe_mode,
406
+ sticky_reallocation_budget_threshold_pct=payload.sticky_reallocation_budget_threshold_pct,
407
+ sticky_reallocation_primary_budget_threshold_pct=payload.sticky_reallocation_primary_budget_threshold_pct,
408
+ sticky_reallocation_secondary_budget_threshold_pct=payload.sticky_reallocation_secondary_budget_threshold_pct,
409
+ additional_quota_routing_policies_json=_dump_additional_quota_routing_policies(
410
+ payload.additional_quota_routing_policies
411
+ ),
412
+ warmup_model=payload.warmup_model,
413
+ import_without_overwrite=payload.import_without_overwrite,
414
+ totp_required_on_login=payload.totp_required_on_login,
415
+ totp_required_for_admin_role=payload.totp_required_for_admin_role,
416
+ local_login_policy=payload.local_login_policy,
417
+ api_key_auth_enabled=payload.api_key_auth_enabled,
418
+ hide_upstream_quota_from_api_keys=payload.hide_upstream_quota_from_api_keys,
419
+ limit_warmup_enabled=payload.limit_warmup_enabled,
420
+ limit_warmup_windows=payload.limit_warmup_windows,
421
+ limit_warmup_model=payload.limit_warmup_model,
422
+ limit_warmup_prompt=payload.limit_warmup_prompt,
423
+ limit_warmup_cooldown_seconds=payload.limit_warmup_cooldown_seconds,
424
+ limit_warmup_exhausted_threshold_percent=payload.limit_warmup_exhausted_threshold_percent,
425
+ limit_warmup_idle_threshold_percent=payload.limit_warmup_idle_threshold_percent,
426
+ limit_warmup_min_available_percent=payload.limit_warmup_min_available_percent,
427
+ weekly_pace_working_days=payload.weekly_pace_working_days,
428
+ weekly_pace_smoothing_minutes=payload.weekly_pace_smoothing_minutes,
429
+ guest_access_enabled=payload.guest_access_enabled,
430
+ limit_warmup_staggered_idle_enabled=payload.limit_warmup_staggered_idle_enabled,
431
+ request_log_retention_days=payload.request_log_retention_override_days,
432
+ usage_history_retention_days=payload.usage_history_retention_override_days,
433
+ clear_request_log_retention=payload.clear_request_log_retention_override,
434
+ clear_usage_history_retention=payload.clear_usage_history_retention_override,
435
+ # C2-3 resilience toggles
436
+ soft_drain_enabled=payload.soft_drain_enabled,
437
+ clear_soft_drain_enabled=payload.clear_soft_drain_enabled,
438
+ # M5 conversation archive
439
+ conversation_archive_enabled=payload.conversation_archive_enabled,
440
+ clear_conversation_archive_enabled=payload.clear_conversation_archive_enabled,
441
+ # end M5 conversation archive
442
+ # R2 spool retention
443
+ http_responses_session_bridge_operation_spool_retention_seconds=(
444
+ payload.http_responses_session_bridge_operation_spool_retention_seconds
445
+ ),
446
+ clear_http_responses_session_bridge_operation_spool_retention_seconds=(
447
+ payload.clear_http_responses_session_bridge_operation_spool_retention_seconds
448
+ ),
449
+ # end R2 spool retention
450
+ deterministic_failover_enabled=payload.deterministic_failover_enabled,
451
+ clear_deterministic_failover_enabled=payload.clear_deterministic_failover_enabled,
452
+ circuit_breaker_enabled=payload.circuit_breaker_enabled,
453
+ clear_circuit_breaker_enabled=payload.clear_circuit_breaker_enabled,
454
+ # M2 background jobs
455
+ auth_guardian_enabled=payload.auth_guardian_enabled,
456
+ clear_auth_guardian_enabled=payload.clear_auth_guardian_enabled,
457
+ automations_scheduler_enabled=payload.automations_scheduler_enabled,
458
+ clear_automations_scheduler_enabled=payload.clear_automations_scheduler_enabled,
459
+ rate_limit_reset_credits_refresh_enabled=payload.rate_limit_reset_credits_refresh_enabled,
460
+ clear_rate_limit_reset_credits_refresh_enabled=payload.clear_rate_limit_reset_credits_refresh_enabled,
461
+ # end M2 background jobs
462
+ # C2-1 timeouts
463
+ upstream_connect_timeout_seconds=payload.upstream_connect_timeout_seconds,
464
+ clear_upstream_connect_timeout_seconds=payload.clear_upstream_connect_timeout_seconds,
465
+ proxy_request_budget_seconds=payload.proxy_request_budget_seconds,
466
+ clear_proxy_request_budget_seconds=payload.clear_proxy_request_budget_seconds,
467
+ compact_request_budget_seconds=payload.compact_request_budget_seconds,
468
+ clear_compact_request_budget_seconds=payload.clear_compact_request_budget_seconds,
469
+ transcription_request_budget_seconds=payload.transcription_request_budget_seconds,
470
+ clear_transcription_request_budget_seconds=payload.clear_transcription_request_budget_seconds,
471
+ stream_idle_timeout_seconds=payload.stream_idle_timeout_seconds,
472
+ clear_stream_idle_timeout_seconds=payload.clear_stream_idle_timeout_seconds,
473
+ proxy_downstream_websocket_idle_timeout_seconds=payload.proxy_downstream_websocket_idle_timeout_seconds,
474
+ clear_proxy_downstream_websocket_idle_timeout_seconds=(
475
+ payload.clear_proxy_downstream_websocket_idle_timeout_seconds
476
+ ),
477
+ sse_keepalive_interval_seconds=payload.sse_keepalive_interval_seconds,
478
+ clear_sse_keepalive_interval_seconds=payload.clear_sse_keepalive_interval_seconds,
479
+ # end C2-1 timeouts
480
+ # M3 codex prewarm
481
+ http_responses_session_bridge_codex_prewarm_enabled=(
482
+ payload.http_responses_session_bridge_codex_prewarm_enabled
483
+ ),
484
+ clear_http_responses_session_bridge_codex_prewarm_enabled=(
485
+ payload.clear_http_responses_session_bridge_codex_prewarm_enabled
486
+ ),
487
+ # end M3 codex prewarm
488
+ # M1 stream/bridge budgets
489
+ http_responses_stream_request_budget_seconds=payload.http_responses_stream_request_budget_seconds,
490
+ clear_http_responses_stream_request_budget_seconds=(
491
+ payload.clear_http_responses_stream_request_budget_seconds
492
+ ),
493
+ http_responses_session_bridge_request_budget_seconds=(
494
+ payload.http_responses_session_bridge_request_budget_seconds
495
+ ),
496
+ clear_http_responses_session_bridge_request_budget_seconds=(
497
+ payload.clear_http_responses_session_bridge_request_budget_seconds
498
+ ),
499
+ # end M1 stream/bridge budgets
500
+ )
501
+ return _settings_data(row, await self.totp_enrollment(actor_user_id))
502
+ return _settings_data(row, await self.totp_enrollment(actor_user_id))
503
+
504
+
505
+ # Retention has no environment fallback: NULL = never set from the dashboard =
506
+ # disabled; 0 = explicitly disabled.
507
+ _RETENTION_DISABLED_DAYS = 0
508
+
509
+
510
+ _ROUTING_POLICIES = frozenset({"inherit", "normal", "burn_first", "preserve"})
511
+
512
+ # Inheritable settings with an environment fallback: the ``dashboard_settings``
513
+ # column, the ``Settings`` field and the provenance key share one name.
514
+ _ENVIRONMENT_INHERITABLE_SETTINGS = (
515
+ # Thread cache identity mode (str): NULL inherits the env value, then "shared".
516
+ "thread_cache_identity_mode",
517
+ "proxy_account_response_create_limit",
518
+ "proxy_account_stream_limit",
519
+ "proxy_account_stream_recovery_reserve",
520
+ "proxy_api_key_fair_share_congestion_threshold_pct",
521
+ *DASHBOARD_TIMEOUT_SETTINGS, # C2-1 timeouts
522
+ # C2-2 routing/overload
523
+ "proxy_overload_isolation_seconds",
524
+ "proxy_account_error_rate_weighting_enabled",
525
+ "proxy_account_inflight_penalty_pct",
526
+ "proxy_account_lease_token_weight",
527
+ "proxy_account_lease_ttl_seconds",
528
+ # end C2-2 routing/overload
529
+ # M3 codex prewarm: bool; a NULL column inherits the deprecated env alias.
530
+ "http_responses_session_bridge_codex_prewarm_enabled",
531
+ # end M3 codex prewarm
532
+ CONVERSATION_ARCHIVE_SETTING, # M5 conversation archive (bool, env alias)
533
+ # R2 spool retention: float; a NULL column inherits the deprecated env alias.
534
+ OPERATION_SPOOL_RETENTION_SETTING,
535
+ )
536
+ # Retention has no environment fallback: NULL = never set from the dashboard =
537
+ # disabled; 0 = explicitly disabled.
538
+ _RETENTION_DISABLED_DAYS = 0
539
+
540
+
541
+ def _resolve_environment_inheritable(row: DashboardSettings, name: str) -> InheritableValue[Any]:
542
+ default = Settings.model_fields[name].default
543
+ # ``Settings`` always carries every field; startup-settings fakes in tests
544
+ # may carry only the fields they exercise, so fall back to the code default.
545
+ return resolve_inheritable(getattr(row, name), getattr(get_settings(), name, default), default)
546
+
547
+
548
+ def _resolve_environment_toggle(row: DashboardSettings, name: str) -> InheritableValue[bool]:
549
+ # C2-3 resilience toggles: same precedence as the integer caps, typed bool.
550
+ default = bool(Settings.model_fields[name].default)
551
+ return resolve_inheritable(getattr(row, name), bool(getattr(get_settings(), name, default)), default)
552
+
553
+
554
+ def _auth_guardian_blocked_by_topology() -> bool:
555
+ # M2 background jobs: read the two topology fields defensively, the way
556
+ # ``_resolve_environment_toggle`` reads its field, so a stub startup-settings
557
+ # object without them reads as "not blocked" instead of raising.
558
+ startup = get_settings()
559
+ ring = getattr(startup, "http_responses_session_bridge_instance_ring", ())
560
+ leader_election_enabled = bool(getattr(startup, "leader_election_enabled", True))
561
+ return len(ring) > 1 and not leader_election_enabled
562
+
563
+
564
+ def warn_environment_shadowed_by_dashboard(row: DashboardSettings, settings: Settings | None = None) -> list[str]:
565
+ """Log one startup WARN naming env vars that are set but ignored because the dashboard owns the value.
566
+
567
+ Only settings whose environment variable is explicitly set (``model_fields_set``)
568
+ AND whose dashboard column is non-NULL are reported, so an operator who
569
+ edits the variable learns why nothing changed. Returns the names for tests.
570
+ """
571
+ environment = settings if settings is not None else get_settings()
572
+ shadowed = [
573
+ name
574
+ # M2 background jobs: their env aliases are shadowed the same way.
575
+ for name in (*_ENVIRONMENT_INHERITABLE_SETTINGS, *BACKGROUND_JOB_SETTINGS)
576
+ if name in environment.model_fields_set and getattr(row, name, None) is not None
577
+ ]
578
+ if shadowed:
579
+ logger.warning(
580
+ "environment value(s) ignored because the dashboard owns the setting: %s "
581
+ "(Settings -> Advanced; clear the dashboard value to inherit the environment again)",
582
+ ", ".join(f"CODEX_LB_{name.upper()}" for name in shadowed),
583
+ )
584
+ return shadowed
585
+
586
+
587
+ def _resolve_inheritable_settings(
588
+ row: DashboardSettings,
589
+ ) -> dict[str, InheritableValue[Any]]:
590
+ resolved: dict[str, InheritableValue[Any]] = {
591
+ name: _resolve_environment_inheritable(row, name) for name in _ENVIRONMENT_INHERITABLE_SETTINGS
592
+ }
593
+ # A stale or hand-edited column value is not a valid decision, so it is
594
+ # normalized away before it can reach the pattern-constrained settings
595
+ # response and fail ``GET /api/settings`` for every setting at once. A
596
+ # NULL-equivalent column simply falls back to the environment/default legs.
597
+ resolved["thread_cache_identity_mode"] = resolve_inheritable(
598
+ normalize_thread_cache_identity_mode(row.thread_cache_identity_mode),
599
+ normalize_thread_cache_identity_mode(getattr(get_settings(), "thread_cache_identity_mode", None))
600
+ or THREAD_CACHE_IDENTITY_MODE_DEFAULT,
601
+ THREAD_CACHE_IDENTITY_MODE_DEFAULT,
602
+ )
603
+ resolved["request_log_retention_days"] = resolve_inheritable(
604
+ row.request_log_retention_days, None, _RETENTION_DISABLED_DAYS
605
+ )
606
+ resolved["usage_history_retention_days"] = resolve_inheritable(
607
+ row.usage_history_retention_days, None, _RETENTION_DISABLED_DAYS
608
+ )
609
+ for name in RESILIENCE_TOGGLE_SETTINGS: # C2-3 resilience toggles
610
+ resolved[name] = _resolve_environment_toggle(row, name)
611
+ for name in BACKGROUND_JOB_SETTINGS: # M2 background jobs
612
+ resolved[name] = _resolve_environment_toggle(row, name)
613
+ return resolved
614
+
615
+
616
+ def _settings_data(row: DashboardSettings, totp: TotpEnrollmentSummary) -> DashboardSettingsData:
617
+ resolved = _resolve_inheritable_settings(row)
618
+ return DashboardSettingsData(
619
+ sticky_threads_enabled=row.sticky_threads_enabled,
620
+ upstream_stream_transport=row.upstream_stream_transport,
621
+ prohibit_fast_mode=row.prohibit_fast_mode,
622
+ http_downstream_transport_policy=row.http_downstream_transport_policy,
623
+ thread_cache_identity_mode=resolved["thread_cache_identity_mode"].value,
624
+ thread_cache_identity_mode_override=normalize_thread_cache_identity_mode(row.thread_cache_identity_mode),
625
+ proxy_account_response_create_limit=resolved["proxy_account_response_create_limit"].value,
626
+ proxy_account_response_create_limit_override=row.proxy_account_response_create_limit,
627
+ proxy_account_stream_limit=resolved["proxy_account_stream_limit"].value,
628
+ proxy_account_stream_limit_override=row.proxy_account_stream_limit,
629
+ proxy_account_stream_recovery_reserve=resolved["proxy_account_stream_recovery_reserve"].value,
630
+ proxy_account_stream_recovery_reserve_override=row.proxy_account_stream_recovery_reserve,
631
+ proxy_api_key_fair_share_congestion_threshold_pct=(
632
+ resolved["proxy_api_key_fair_share_congestion_threshold_pct"].value
633
+ ),
634
+ proxy_api_key_fair_share_congestion_threshold_pct_override=(
635
+ row.proxy_api_key_fair_share_congestion_threshold_pct
636
+ ),
637
+ # C2-2 routing/overload
638
+ proxy_overload_isolation_seconds=resolved["proxy_overload_isolation_seconds"].value,
639
+ proxy_account_error_rate_weighting_enabled=resolved["proxy_account_error_rate_weighting_enabled"].value,
640
+ proxy_account_inflight_penalty_pct=resolved["proxy_account_inflight_penalty_pct"].value,
641
+ proxy_account_lease_token_weight=resolved["proxy_account_lease_token_weight"].value,
642
+ proxy_account_lease_ttl_seconds=resolved["proxy_account_lease_ttl_seconds"].value,
643
+ # end C2-2 routing/overload
644
+ upstream_proxy_routing_enabled=row.upstream_proxy_routing_enabled,
645
+ upstream_proxy_default_pool_id=row.upstream_proxy_default_pool_id,
646
+ prefer_earlier_reset_accounts=row.prefer_earlier_reset_accounts,
647
+ prefer_earlier_reset_window=row.prefer_earlier_reset_window,
648
+ show_reset_credit_badges=row.show_reset_credit_badges,
649
+ auto_redeem_reset_credits_before_expiry=row.auto_redeem_reset_credits_before_expiry,
650
+ show_reset_credit_expiry_badge=row.show_reset_credit_expiry_badge,
651
+ routing_strategy=row.routing_strategy,
652
+ relative_availability_power=row.relative_availability_power,
653
+ relative_availability_top_k=row.relative_availability_top_k,
654
+ single_account_id=row.single_account_id,
655
+ openai_cache_affinity_max_age_seconds=row.openai_cache_affinity_max_age_seconds,
656
+ dashboard_session_ttl_seconds=row.dashboard_session_ttl_seconds,
657
+ http_responses_session_bridge_prompt_cache_idle_ttl_seconds=(
658
+ row.http_responses_session_bridge_prompt_cache_idle_ttl_seconds
659
+ ),
660
+ http_responses_session_bridge_gateway_safe_mode=row.http_responses_session_bridge_gateway_safe_mode,
661
+ # M3 codex prewarm
662
+ http_responses_session_bridge_codex_prewarm_enabled=bool(
663
+ resolved["http_responses_session_bridge_codex_prewarm_enabled"].value
664
+ ),
665
+ # end M3 codex prewarm
666
+ sticky_reallocation_budget_threshold_pct=row.sticky_reallocation_budget_threshold_pct,
667
+ sticky_reallocation_primary_budget_threshold_pct=row.sticky_reallocation_primary_budget_threshold_pct,
668
+ sticky_reallocation_secondary_budget_threshold_pct=row.sticky_reallocation_secondary_budget_threshold_pct,
669
+ additional_quota_routing_policies=_parse_additional_quota_routing_policies(
670
+ row.additional_quota_routing_policies_json
671
+ ),
672
+ warmup_model=row.warmup_model,
673
+ import_without_overwrite=row.import_without_overwrite,
674
+ totp_required_on_login=row.totp_required_on_login,
675
+ totp_required_for_admin_role=row.totp_required_for_admin_role,
676
+ local_login_policy=row.local_login_policy,
677
+ users_without_totp_count=totp.users_without_totp,
678
+ admins_without_totp_count=totp.admins_without_totp,
679
+ api_key_auth_enabled=row.api_key_auth_enabled,
680
+ hide_upstream_quota_from_api_keys=row.hide_upstream_quota_from_api_keys,
681
+ limit_warmup_enabled=row.limit_warmup_enabled,
682
+ limit_warmup_windows=row.limit_warmup_windows,
683
+ limit_warmup_model=row.limit_warmup_model,
684
+ limit_warmup_prompt=row.limit_warmup_prompt,
685
+ limit_warmup_cooldown_seconds=row.limit_warmup_cooldown_seconds,
686
+ limit_warmup_exhausted_threshold_percent=row.limit_warmup_exhausted_threshold_percent,
687
+ limit_warmup_idle_threshold_percent=row.limit_warmup_idle_threshold_percent,
688
+ limit_warmup_min_available_percent=row.limit_warmup_min_available_percent,
689
+ weekly_pace_working_days=row.weekly_pace_working_days,
690
+ weekly_pace_smoothing_minutes=row.weekly_pace_smoothing_minutes,
691
+ guest_access_enabled=row.guest_access_enabled,
692
+ guest_password_configured=row.guest_password_hash is not None,
693
+ limit_warmup_staggered_idle_enabled=row.limit_warmup_staggered_idle_enabled,
694
+ request_log_retention_days=resolved["request_log_retention_days"].value,
695
+ usage_history_retention_days=resolved["usage_history_retention_days"].value,
696
+ request_log_retention_override_days=row.request_log_retention_days,
697
+ usage_history_retention_override_days=row.usage_history_retention_days,
698
+ # C2-3 resilience toggles
699
+ soft_drain_enabled=bool(resolved["soft_drain_enabled"].value),
700
+ deterministic_failover_enabled=bool(resolved["deterministic_failover_enabled"].value),
701
+ circuit_breaker_enabled=bool(resolved["circuit_breaker_enabled"].value),
702
+ # M2 background jobs
703
+ auth_guardian_enabled=bool(resolved["auth_guardian_enabled"].value),
704
+ auth_guardian_blocked_by_topology=_auth_guardian_blocked_by_topology(),
705
+ automations_scheduler_enabled=bool(resolved["automations_scheduler_enabled"].value),
706
+ rate_limit_reset_credits_refresh_enabled=bool(resolved["rate_limit_reset_credits_refresh_enabled"].value),
707
+ # end M2 background jobs
708
+ # M5 conversation archive
709
+ conversation_archive_enabled=bool(resolved[CONVERSATION_ARCHIVE_SETTING].value),
710
+ # end M5 conversation archive
711
+ # R2 spool retention
712
+ http_responses_session_bridge_operation_spool_retention_seconds=float(
713
+ resolved[OPERATION_SPOOL_RETENTION_SETTING].value
714
+ ),
715
+ # end R2 spool retention
716
+ version=row.version,
717
+ # C2-1 timeouts
718
+ upstream_connect_timeout_seconds=float(resolved["upstream_connect_timeout_seconds"].value),
719
+ proxy_request_budget_seconds=float(resolved["proxy_request_budget_seconds"].value),
720
+ compact_request_budget_seconds=float(resolved["compact_request_budget_seconds"].value),
721
+ transcription_request_budget_seconds=float(resolved["transcription_request_budget_seconds"].value),
722
+ stream_idle_timeout_seconds=float(resolved["stream_idle_timeout_seconds"].value),
723
+ proxy_downstream_websocket_idle_timeout_seconds=float(
724
+ resolved["proxy_downstream_websocket_idle_timeout_seconds"].value
725
+ ),
726
+ sse_keepalive_interval_seconds=float(resolved["sse_keepalive_interval_seconds"].value),
727
+ # end C2-1 timeouts
728
+ # M1 stream/bridge budgets
729
+ http_responses_stream_request_budget_seconds=float(
730
+ resolved["http_responses_stream_request_budget_seconds"].value
731
+ ),
732
+ http_responses_session_bridge_request_budget_seconds=float(
733
+ resolved["http_responses_session_bridge_request_budget_seconds"].value
734
+ ),
735
+ # end M1 stream/bridge budgets
736
+ provenance=resolved,
737
+ )
738
+
739
+
740
+ def _parse_additional_quota_routing_policies(raw: str | None) -> dict[str, str]:
741
+ if not raw:
742
+ return {}
743
+ try:
744
+ parsed = json.loads(raw)
745
+ except json.JSONDecodeError:
746
+ return {}
747
+ if not isinstance(parsed, dict):
748
+ return {}
749
+ policies: dict[str, str] = {}
750
+ for quota_key, policy in parsed.items():
751
+ if not isinstance(quota_key, str) or not isinstance(policy, str):
752
+ continue
753
+ normalized_quota_key = normalize_additional_quota_key(quota_key)
754
+ policy = policy.strip().lower()
755
+ if normalized_quota_key and policy in _ROUTING_POLICIES:
756
+ policies[normalized_quota_key] = policy
757
+ return policies
758
+
759
+
760
+ def _dump_additional_quota_routing_policies(policies: dict[str, str]) -> str:
761
+ normalized = {}
762
+ for quota_key, policy in policies.items():
763
+ if not isinstance(quota_key, str) or not isinstance(policy, str):
764
+ continue
765
+ normalized_quota_key = normalize_additional_quota_key(quota_key)
766
+ normalized_policy = policy.strip().lower()
767
+ if normalized_quota_key is not None and normalized_policy in _ROUTING_POLICIES:
768
+ normalized[normalized_quota_key] = normalized_policy
769
+ return json.dumps(normalized, sort_keys=True, separators=(",", ":"))