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,1100 @@
1
+ # pyright: reportGeneralTypeIssues=false
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import sys
6
+ from typing import Any, AsyncIterator, Mapping, cast
7
+
8
+ import aiohttp
9
+
10
+ from app.core.balancer.types import UpstreamError
11
+ from app.core.clients.files import create_file as core_create_file # noqa: F401
12
+ from app.core.clients.files import finalize_file as core_finalize_file # noqa: F401
13
+ from app.core.clients.http import lease_http_session as lease_http_session # noqa: F401
14
+ from app.core.clients.proxy import CodexControlResponse as CodexControlResponse
15
+ from app.core.clients.proxy import ( # noqa: F401 # noqa: F401
16
+ ImageFetchSession,
17
+ ProxyResponseError,
18
+ UpstreamProxyRouteTrace,
19
+ _as_image_fetch_session,
20
+ _inline_content_images,
21
+ _inline_input_image_urls,
22
+ _is_native_codex_request,
23
+ _ws_transport_payload_budget_bytes,
24
+ filter_inbound_headers,
25
+ pop_compact_timeout_overrides,
26
+ pop_stream_timeout_overrides,
27
+ pop_transcribe_timeout_overrides,
28
+ push_compact_timeout_overrides,
29
+ push_stream_timeout_overrides,
30
+ push_transcribe_timeout_overrides,
31
+ )
32
+ from app.core.clients.proxy import codex_control_request as core_codex_control_request # noqa: F401
33
+ from app.core.clients.proxy import compact_responses as core_compact_responses # noqa: F401
34
+ from app.core.clients.proxy import transcribe_audio as core_transcribe_audio # noqa: F401
35
+ from app.core.clients.thread_cache_identity import ThreadCacheIdentity
36
+ from app.core.clock import clock_for, scheduler_for
37
+ from app.core.errors import (
38
+ PREVIOUS_RESPONSE_NOT_FOUND_CODE as PREVIOUS_RESPONSE_NOT_FOUND_CODE,
39
+ )
40
+ from app.core.errors import (
41
+ PREVIOUS_RESPONSE_NOT_FOUND_MESSAGE as PREVIOUS_RESPONSE_NOT_FOUND_MESSAGE,
42
+ )
43
+ from app.core.errors import synthetic_stream_failure_event as response_failed_event
44
+ from app.core.openai.parsing import _LIFECYCLE_EVENT_TYPES, classify_event_type, parse_sse_event_payload
45
+ from app.core.openai.requests import ResponsesRequest
46
+ from app.core.upstream_proxy import ResolvedUpstreamRoute, UpstreamProxyRouteError
47
+ from app.core.utils.sse import CODEX_KEEPALIVE_FRAME as CODEX_KEEPALIVE_FRAME # noqa: F401
48
+ from app.core.utils.sse import format_sse_event, parse_sse_data_json
49
+ from app.core.utils.time import utcnow as utcnow
50
+ from app.db.models import (
51
+ Account,
52
+ AccountStatus, # noqa: F401
53
+ )
54
+ from app.modules.api_keys.service import ApiKeyData, ApiKeyUsageReservationData
55
+ from app.modules.proxy._service.api_key_usage import (
56
+ _API_KEY_RESERVATION_HEARTBEAT_SECONDS as _API_KEY_RESERVATION_HEARTBEAT_SECONDS,
57
+ )
58
+ from app.modules.proxy._service.compact import (
59
+ _service_tier_from_compact_payload as _service_tier_from_compact_payload,
60
+ )
61
+ from app.modules.proxy._service.compact import (
62
+ _sticky_key_for_compact_request as _sticky_key_for_compact_request,
63
+ )
64
+ from app.modules.proxy._service.compact import (
65
+ _sticky_key_from_compact_payload as _sticky_key_from_compact_payload,
66
+ )
67
+ from app.modules.proxy._service.http_bridge.helpers import (
68
+ _active_http_bridge_instance_ring as _active_http_bridge_instance_ring,
69
+ )
70
+ from app.modules.proxy._service.http_bridge.helpers import (
71
+ _build_http_bridge_prewarm_text as _build_http_bridge_prewarm_text,
72
+ )
73
+ from app.modules.proxy._service.http_bridge.helpers import (
74
+ _durable_bridge_lookup_active_owner as _durable_bridge_lookup_active_owner,
75
+ )
76
+ from app.modules.proxy._service.http_bridge.helpers import (
77
+ _durable_bridge_lookup_allows_local_reuse as _durable_bridge_lookup_allows_local_reuse,
78
+ )
79
+ from app.modules.proxy._service.http_bridge.helpers import (
80
+ _effective_http_bridge_idle_ttl_seconds as _effective_http_bridge_idle_ttl_seconds,
81
+ )
82
+ from app.modules.proxy._service.http_bridge.helpers import (
83
+ _forwarded_http_bridge_session_key as _forwarded_http_bridge_session_key,
84
+ )
85
+ from app.modules.proxy._service.http_bridge.helpers import (
86
+ _has_http_bridge_response_output_marker as _has_http_bridge_response_output_marker,
87
+ )
88
+ from app.modules.proxy._service.http_bridge.helpers import (
89
+ _http_bridge_allow_durable_takeover as _http_bridge_allow_durable_takeover,
90
+ )
91
+ from app.modules.proxy._service.http_bridge.helpers import (
92
+ _http_bridge_can_local_recover_without_ring as _http_bridge_can_local_recover_without_ring,
93
+ )
94
+ from app.modules.proxy._service.http_bridge.helpers import (
95
+ _http_bridge_can_recover_during_drain as _http_bridge_can_recover_during_drain,
96
+ )
97
+ from app.modules.proxy._service.http_bridge.helpers import (
98
+ _http_bridge_continuity_lost_error_envelope as _http_bridge_continuity_lost_error_envelope,
99
+ )
100
+ from app.modules.proxy._service.http_bridge.helpers import (
101
+ _http_bridge_durable_lease_ttl_seconds as _http_bridge_durable_lease_ttl_seconds,
102
+ )
103
+ from app.modules.proxy._service.http_bridge.helpers import (
104
+ _http_bridge_endpoint_matches_current_instance as _http_bridge_endpoint_matches_current_instance,
105
+ )
106
+ from app.modules.proxy._service.http_bridge.helpers import (
107
+ _http_bridge_eviction_priority as _http_bridge_eviction_priority,
108
+ )
109
+ from app.modules.proxy._service.http_bridge.helpers import (
110
+ _http_bridge_has_durable_recovery_anchor as _http_bridge_has_durable_recovery_anchor,
111
+ )
112
+ from app.modules.proxy._service.http_bridge.helpers import (
113
+ _http_bridge_input_item_type as _http_bridge_input_item_type,
114
+ )
115
+ from app.modules.proxy._service.http_bridge.helpers import (
116
+ _http_bridge_is_context_overflow_error as _http_bridge_is_context_overflow_error,
117
+ )
118
+ from app.modules.proxy._service.http_bridge.helpers import (
119
+ _http_bridge_is_previous_response_owner_unavailable as _http_bridge_is_previous_response_owner_unavailable,
120
+ )
121
+ from app.modules.proxy._service.http_bridge.helpers import (
122
+ _http_bridge_key_strength as _http_bridge_key_strength,
123
+ )
124
+ from app.modules.proxy._service.http_bridge.helpers import (
125
+ _http_bridge_owner_check_required as _http_bridge_owner_check_required,
126
+ )
127
+ from app.modules.proxy._service.http_bridge.helpers import (
128
+ _http_bridge_owner_instance as _http_bridge_owner_instance,
129
+ )
130
+ from app.modules.proxy._service.http_bridge.helpers import (
131
+ _http_bridge_owner_lookup_unavailable_error_envelope as _http_bridge_owner_lookup_unavailable_error_envelope,
132
+ )
133
+ from app.modules.proxy._service.http_bridge.helpers import (
134
+ _http_bridge_payload_looks_like_full_resend as _http_bridge_payload_looks_like_full_resend,
135
+ )
136
+ from app.modules.proxy._service.http_bridge.helpers import (
137
+ _http_bridge_payload_without_previous_response_id as _http_bridge_payload_without_previous_response_id,
138
+ )
139
+ from app.modules.proxy._service.http_bridge.helpers import (
140
+ _http_bridge_precreated_retry_failure_error as _http_bridge_precreated_retry_failure_error,
141
+ )
142
+ from app.modules.proxy._service.http_bridge.helpers import (
143
+ _http_bridge_previous_response_alias_key as _http_bridge_previous_response_alias_key,
144
+ )
145
+ from app.modules.proxy._service.http_bridge.helpers import (
146
+ _http_bridge_previous_response_error_envelope as _http_bridge_previous_response_error_envelope,
147
+ )
148
+ from app.modules.proxy._service.http_bridge.helpers import (
149
+ _http_bridge_request_counts_against_queue as _http_bridge_request_counts_against_queue,
150
+ )
151
+ from app.modules.proxy._service.http_bridge.helpers import (
152
+ _http_bridge_request_stage as _http_bridge_request_stage,
153
+ )
154
+ from app.modules.proxy._service.http_bridge.helpers import (
155
+ _http_bridge_requires_cluster_registration as _http_bridge_requires_cluster_registration,
156
+ )
157
+ from app.modules.proxy._service.http_bridge.helpers import (
158
+ _http_bridge_runtime_config as _http_bridge_runtime_config,
159
+ )
160
+ from app.modules.proxy._service.http_bridge.helpers import (
161
+ _http_bridge_session_allows_api_key as _http_bridge_session_allows_api_key,
162
+ )
163
+ from app.modules.proxy._service.http_bridge.helpers import (
164
+ _http_bridge_session_has_visible_requests as _http_bridge_session_has_visible_requests,
165
+ )
166
+ from app.modules.proxy._service.http_bridge.helpers import (
167
+ _http_bridge_session_matches_preferred_account as _http_bridge_session_matches_preferred_account,
168
+ )
169
+ from app.modules.proxy._service.http_bridge.helpers import (
170
+ _http_bridge_session_retiring_with_visible_requests as _http_bridge_session_retiring_with_visible_requests,
171
+ )
172
+ from app.modules.proxy._service.http_bridge.helpers import (
173
+ _http_bridge_session_reusable_for_request as _http_bridge_session_reusable_for_request,
174
+ )
175
+ from app.modules.proxy._service.http_bridge.helpers import (
176
+ _http_bridge_should_attempt_local_bootstrap_rebind as _http_bridge_should_attempt_local_bootstrap_rebind,
177
+ )
178
+ from app.modules.proxy._service.http_bridge.helpers import (
179
+ _http_bridge_should_attempt_local_previous_response_recovery, # noqa: F401
180
+ )
181
+ from app.modules.proxy._service.http_bridge.helpers import (
182
+ _http_bridge_should_attempt_soft_affinity_reroute as _http_bridge_should_attempt_soft_affinity_reroute,
183
+ )
184
+ from app.modules.proxy._service.http_bridge.helpers import (
185
+ _http_bridge_should_rollover_after_context_overflow as _http_bridge_should_rollover_after_context_overflow,
186
+ )
187
+ from app.modules.proxy._service.http_bridge.helpers import (
188
+ _http_bridge_should_wait_for_registration as _http_bridge_should_wait_for_registration,
189
+ )
190
+ from app.modules.proxy._service.http_bridge.helpers import (
191
+ _http_bridge_startup_wait_timeout_error as _http_bridge_startup_wait_timeout_error,
192
+ )
193
+ from app.modules.proxy._service.http_bridge.helpers import (
194
+ _http_bridge_turn_state_alias_key as _http_bridge_turn_state_alias_key,
195
+ )
196
+ from app.modules.proxy._service.http_bridge.helpers import (
197
+ _is_http_bridge_previous_response_output_item as _is_http_bridge_previous_response_output_item,
198
+ )
199
+ from app.modules.proxy._service.http_bridge.helpers import (
200
+ _is_missing_durable_bridge_table_error as _is_missing_durable_bridge_table_error,
201
+ )
202
+ from app.modules.proxy._service.http_bridge.helpers import (
203
+ _log_http_bridge_event as _log_http_bridge_event,
204
+ )
205
+ from app.modules.proxy._service.http_bridge.helpers import (
206
+ _log_http_bridge_startup_wait_timeout as _log_http_bridge_startup_wait_timeout,
207
+ )
208
+ from app.modules.proxy._service.http_bridge.helpers import (
209
+ _make_http_bridge_session_key as _make_http_bridge_session_key,
210
+ )
211
+ from app.modules.proxy._service.http_bridge.helpers import (
212
+ _normalize_http_bridge_error_event as _normalize_http_bridge_error_event,
213
+ )
214
+ from app.modules.proxy._service.http_bridge.helpers import (
215
+ _normalized_http_bridge_instance_ring as _normalized_http_bridge_instance_ring,
216
+ )
217
+ from app.modules.proxy._service.http_bridge.helpers import (
218
+ _preferred_http_bridge_reconnect_turn_state as _preferred_http_bridge_reconnect_turn_state,
219
+ )
220
+ from app.modules.proxy._service.http_bridge.helpers import (
221
+ _record_bridge_drain_recovery_allowed as _record_bridge_drain_recovery_allowed,
222
+ )
223
+ from app.modules.proxy._service.http_bridge.helpers import (
224
+ _record_bridge_first_turn_timeout as _record_bridge_first_turn_timeout,
225
+ )
226
+ from app.modules.proxy._service.http_bridge.helpers import (
227
+ _record_bridge_reattach as _record_bridge_reattach,
228
+ )
229
+ from app.modules.proxy._service.http_bridge.helpers import (
230
+ _trim_http_bridge_previous_response_input_items as _trim_http_bridge_previous_response_input_items,
231
+ )
232
+ from app.modules.proxy._service.observability import (
233
+ _hash_identifier as _hash_identifier,
234
+ )
235
+ from app.modules.proxy._service.observability import (
236
+ _hash_identifier_or_none as _hash_identifier_or_none,
237
+ )
238
+ from app.modules.proxy._service.observability import (
239
+ _interesting_header_keys as _interesting_header_keys,
240
+ )
241
+ from app.modules.proxy._service.observability import (
242
+ _maybe_log_proxy_request_payload as _maybe_log_proxy_request_payload,
243
+ )
244
+ from app.modules.proxy._service.observability import (
245
+ _maybe_log_proxy_request_shape as _maybe_log_proxy_request_shape,
246
+ )
247
+ from app.modules.proxy._service.observability import (
248
+ _maybe_log_proxy_service_tier_trace as _maybe_log_proxy_service_tier_trace,
249
+ )
250
+ from app.modules.proxy._service.observability import (
251
+ _record_continuity_fail_closed as _record_continuity_fail_closed,
252
+ )
253
+ from app.modules.proxy._service.observability import (
254
+ _record_continuity_owner_resolution as _record_continuity_owner_resolution,
255
+ )
256
+ from app.modules.proxy._service.observability import (
257
+ _summarize_input as _summarize_input,
258
+ )
259
+ from app.modules.proxy._service.observability import (
260
+ _tools_hash as _tools_hash,
261
+ )
262
+ from app.modules.proxy._service.observability import (
263
+ _truncate_identifier as _truncate_identifier,
264
+ )
265
+ from app.modules.proxy._service.streaming.helpers import (
266
+ _classify_terminal_stream_error_frame,
267
+ _mark_downstream_stream_cancelled,
268
+ _mark_upstream_stream_incomplete,
269
+ _observe_terminal_stream_error_frame,
270
+ _openai_error_fields,
271
+ _publish_http_response_owner,
272
+ _rewrite_malformed_stream_error_event,
273
+ _stamp_terminal,
274
+ _stream_transport_failure_event_or_raise,
275
+ _upstream_terminal_latency_ms,
276
+ )
277
+ from app.modules.proxy._service.streaming.helpers import (
278
+ _handle_stream_error as _handle_stream_error_helper,
279
+ )
280
+ from app.modules.proxy._service.streaming.helpers import _raw_stream_error_fields as _raw_error_fields
281
+ from app.modules.proxy._service.streaming.helpers import (
282
+ _resolve_upstream_route_for_account as _resolve_upstream_route_for_account_helper,
283
+ )
284
+ from app.modules.proxy._service.streaming.helpers import (
285
+ _select_account_with_budget_for_stream as _select_account_with_budget_for_stream_helper,
286
+ )
287
+ from app.modules.proxy._service.streaming.helpers import _settle_background_ack as _settle_bg_ack
288
+ from app.modules.proxy._service.streaming.protocol import _StreamingServiceProtocol
289
+ from app.modules.proxy._service.streaming.retry import _StreamingRetryMixin
290
+ from app.modules.proxy._service.support import (
291
+ _HARD_HTTP_BRIDGE_AFFINITY_KINDS, # noqa: F401
292
+ _REQUEST_TRANSPORT_WEBSOCKET, # noqa: F401
293
+ _WEBSOCKET_FULL_REPLAY_WAIT_MIN_ITEMS, # noqa: F401
294
+ _WEBSOCKET_FULL_REPLAY_WAIT_POLL_SECONDS, # noqa: F401
295
+ _ApiKeyReservationTouchState,
296
+ _finalize_ttft_latency_ms,
297
+ _RequestLogFailureMetadata,
298
+ _RetryableStreamError,
299
+ _StreamSettlement,
300
+ _TerminalStreamError,
301
+ _ttft_event_latency_ms,
302
+ _verbatim_relay_event_type,
303
+ _WebSocketUpstreamControl,
304
+ )
305
+ from app.modules.proxy._service.support import (
306
+ _HTTPBridgeOwnerForward as _HTTPBridgeOwnerForward,
307
+ )
308
+ from app.modules.proxy._service.support import (
309
+ _record_websocket_route_metadata as _record_websocket_route_metadata,
310
+ )
311
+ from app.modules.proxy._service.support import (
312
+ _websocket_route_log_kwargs as _websocket_route_log_kwargs,
313
+ )
314
+ from app.modules.proxy._service.warmup import (
315
+ WarmupExecutionData as WarmupExecutionData,
316
+ )
317
+ from app.modules.proxy._service.warmup import (
318
+ WarmupFailedAccountData as WarmupFailedAccountData,
319
+ )
320
+ from app.modules.proxy._service.warmup import (
321
+ WarmupSkippedAccountData as WarmupSkippedAccountData,
322
+ )
323
+ from app.modules.proxy._service.warmup import (
324
+ WarmupSubmittedAccountData as WarmupSubmittedAccountData,
325
+ )
326
+ from app.modules.proxy._service.warmup import (
327
+ _is_warmup_usage_eligible as _is_warmup_usage_eligible,
328
+ )
329
+ from app.modules.proxy._service.warmup import (
330
+ _materialize_warmup_account as _materialize_warmup_account,
331
+ )
332
+ from app.modules.proxy._service.warmup import (
333
+ _snapshot_warmup_account as _snapshot_warmup_account,
334
+ )
335
+ from app.modules.proxy._service.warmup import (
336
+ _WarmupAccountSnapshot as _WarmupAccountSnapshot,
337
+ )
338
+ from app.modules.proxy._service.warmup import (
339
+ _WarmupSubmitResult as _WarmupSubmitResult,
340
+ )
341
+ from app.modules.proxy._service.warmup import (
342
+ _WarmupUsageSnapshot as _WarmupUsageSnapshot,
343
+ )
344
+ from app.modules.proxy._service.websocket.helpers import (
345
+ _app_error_to_websocket_event, # noqa: F401
346
+ _assign_websocket_response_id, # noqa: F401
347
+ _draining_websocket_request_states, # noqa: F401
348
+ _find_websocket_request_state_by_response_id, # noqa: F401
349
+ _is_websocket_previous_response_output_item, # noqa: F401
350
+ _is_websocket_response_create, # noqa: F401
351
+ _match_websocket_request_state_for_anonymous_event, # noqa: F401
352
+ _match_websocket_request_state_for_precreated_terminal_event, # noqa: F401
353
+ _match_websocket_request_state_for_previous_response_error, # noqa: F401
354
+ _matching_websocket_request_states_for_missing_tool_output_error, # noqa: F401
355
+ _matching_websocket_request_states_for_previous_response_error, # noqa: F401
356
+ _maybe_rewrite_websocket_previous_response_not_found_event, # noqa: F401
357
+ _parse_websocket_payload, # noqa: F401
358
+ _pop_matching_websocket_request_states, # noqa: F401
359
+ _pop_replayable_precreated_websocket_request_state, # noqa: F401
360
+ _pop_terminal_websocket_request_state, # noqa: F401
361
+ _prepare_websocket_request_state_for_auth_replay, # noqa: F401
362
+ _prepare_websocket_request_state_for_visible_output_replay, # noqa: F401
363
+ _record_websocket_continuity_completion, # noqa: F401
364
+ _refresh_websocket_request_input_fingerprint_from_text, # noqa: F401
365
+ _release_websocket_response_create_gate, # noqa: F401
366
+ _rewrite_websocket_continuity_corruption_event, # noqa: F401
367
+ _rewrite_websocket_downstream_response_id, # noqa: F401
368
+ _rewrite_websocket_previous_response_owner_unavailable_event, # noqa: F401
369
+ _rewrite_websocket_suppressed_duplicate_tool_call_completion_event, # noqa: F401
370
+ _sanitize_websocket_connect_failure, # noqa: F401
371
+ _sanitize_websocket_previous_response_error, # noqa: F401
372
+ _sanitize_websocket_terminal_error_fields, # noqa: F401
373
+ _serialize_websocket_error_event, # noqa: F401
374
+ _trim_websocket_previous_response_input_items, # noqa: F401
375
+ _upstream_websocket_disconnect_message, # noqa: F401
376
+ _websocket_auth_failure_permanent_code, # noqa: F401
377
+ _websocket_auth_failure_requires_reauth, # noqa: F401
378
+ _websocket_auth_request_can_switch_account, # noqa: F401
379
+ _websocket_client_previous_response_full_resend_is_retry_safe, # noqa: F401
380
+ _websocket_connect_deadline, # noqa: F401
381
+ _websocket_continuity_anchor_for_payload, # noqa: F401
382
+ _websocket_continuity_error_fields, # noqa: F401
383
+ _websocket_continuity_response_ids, # noqa: F401
384
+ _websocket_downstream_response_id, # noqa: F401
385
+ _websocket_full_resend_conflicts_with_visible_pending, # noqa: F401
386
+ _websocket_input_item_type, # noqa: F401
387
+ _websocket_owner_pinned_quota_error_code, # noqa: F401
388
+ _websocket_precreated_auth_error_code, # noqa: F401
389
+ _websocket_precreated_retry_error_code, # noqa: F401
390
+ _websocket_receive_timeout_for_pending_requests, # noqa: F401
391
+ _websocket_response_id, # noqa: F401
392
+ _websocket_top_level_error_payload, # noqa: F401
393
+ _wrapped_websocket_error_event, # noqa: F401
394
+ )
395
+ from app.modules.proxy.affinity import (
396
+ _owner_lookup_session_id_from_headers,
397
+ _sticky_key_from_session_header, # noqa: F401
398
+ )
399
+ from app.modules.proxy.affinity_observation import AffinityObservation
400
+ from app.modules.proxy.durable_bridge_coordinator import (
401
+ DurableBridgeLookup as DurableBridgeLookup,
402
+ )
403
+ from app.modules.proxy.helpers import (
404
+ _header_account_id,
405
+ _normalize_error_code,
406
+ _parse_openai_error,
407
+ _upstream_error_from_openai,
408
+ )
409
+ from app.modules.proxy.http_bridge_forwarding import (
410
+ HTTPBridgeForwardContext as HTTPBridgeForwardContext,
411
+ )
412
+ from app.modules.proxy.http_bridge_forwarding import (
413
+ OwnerForwardRelayFailure as OwnerForwardRelayFailure,
414
+ )
415
+ from app.modules.proxy.load_balancer import AccountConcurrencyCaps, AccountLease
416
+ from app.modules.proxy.tool_call_dedupe import mark_duplicate_tool_call_downstream_event
417
+ from app.modules.proxy.tool_call_dedupe import (
418
+ response_id_from_payload as tool_call_response_id_from_payload,
419
+ )
420
+ from app.modules.proxy.tool_call_dedupe import (
421
+ rewrite_parallel_tool_call_sse_line as _rewrite_tool_call_line,
422
+ )
423
+ from app.modules.proxy.work_admission import AdmissionLease
424
+
425
+
426
+ def _facade() -> Any:
427
+ return sys.modules["app.modules.proxy.service"]
428
+
429
+
430
+ _REQUEST_TRANSPORT_HTTP = "http"
431
+
432
+
433
+ class _StreamingMixin(_StreamingRetryMixin):
434
+ _handle_stream_error = _handle_stream_error_helper
435
+ _resolve_upstream_route_for_account = _resolve_upstream_route_for_account_helper
436
+ _select_account_with_budget_for_stream = _select_account_with_budget_for_stream_helper
437
+
438
+ def stream_responses(
439
+ self,
440
+ payload: ResponsesRequest,
441
+ headers: Mapping[str, str],
442
+ *,
443
+ codex_session_affinity: bool = False,
444
+ propagate_http_errors: bool = False,
445
+ openai_cache_affinity: bool = False,
446
+ api_key: ApiKeyData | None = None,
447
+ api_key_reservation: ApiKeyUsageReservationData | None = None,
448
+ suppress_text_done_events: bool = False,
449
+ request_transport: str = _REQUEST_TRANSPORT_HTTP,
450
+ client_ip: str | None = None,
451
+ enforce_openai_sdk_contract: bool = True,
452
+ ) -> AsyncIterator[str]:
453
+ proxy = cast(_StreamingServiceProtocol, self)
454
+ _maybe_log_proxy_request_payload("stream", payload, headers)
455
+ filtered = _facade().filter_inbound_headers(headers)
456
+ return proxy._stream_with_retry(
457
+ payload,
458
+ filtered,
459
+ codex_session_affinity=codex_session_affinity,
460
+ propagate_http_errors=propagate_http_errors,
461
+ openai_cache_affinity=openai_cache_affinity,
462
+ api_key=api_key,
463
+ api_key_reservation=api_key_reservation,
464
+ suppress_text_done_events=suppress_text_done_events,
465
+ request_transport=request_transport,
466
+ client_ip=client_ip,
467
+ enforce_openai_sdk_contract=enforce_openai_sdk_contract,
468
+ )
469
+
470
+ async def _stream_once(
471
+ self,
472
+ account: Account,
473
+ payload: ResponsesRequest,
474
+ headers: Mapping[str, str],
475
+ request_id: str,
476
+ allow_retry: bool,
477
+ *,
478
+ affinity_observation: AffinityObservation | None = None,
479
+ request_started_at: float,
480
+ allow_transient_retry: bool = False,
481
+ api_key: ApiKeyData | None,
482
+ api_key_reservation: ApiKeyUsageReservationData | None,
483
+ settlement: _StreamSettlement,
484
+ suppress_text_done_events: bool,
485
+ upstream_stream_transport: str | None,
486
+ request_transport: str,
487
+ concurrency_caps: AccountConcurrencyCaps | None = None,
488
+ useragent: str | None = None,
489
+ useragent_group: str | None = None,
490
+ conversation_id: str | None = None,
491
+ client_ip: str | None = None,
492
+ preferred_account_id: str | None = None,
493
+ tool_call_dedupe: _WebSocketUpstreamControl | None = None,
494
+ enforce_openai_sdk_contract: bool = True,
495
+ thread_cache_identity: ThreadCacheIdentity | None = None,
496
+ ) -> AsyncIterator[str]:
497
+ proxy = cast(_StreamingServiceProtocol, self)
498
+ clock = clock_for(proxy)
499
+ preserve_native_failure_lifecycle = not enforce_openai_sdk_contract and _is_native_codex_request(headers)
500
+ account_id_value = account.id
501
+ access_token = proxy._encryptor.decrypt(account.access_token_encrypted)
502
+ account_id = _header_account_id(account.chatgpt_account_id)
503
+ model = payload.model
504
+ requested_service_tier = payload.service_tier
505
+ service_tier = requested_service_tier
506
+ actual_service_tier: str | None = None
507
+ reasoning_effort = payload.reasoning.effort if payload.reasoning else None
508
+ session_id = _owner_lookup_session_id_from_headers(headers)
509
+ # Keep selection/failover waits out of latency and TTFT, record them as
510
+ # queue time, then re-anchor after this attempt's admission wait.
511
+ attempt_started_at = start = clock.monotonic()
512
+ latency_queue_ms = max(0, int((start - request_started_at) * 1000))
513
+ status, error_code, error_message = "success", None, None
514
+ failure_metadata = _RequestLogFailureMetadata()
515
+ response_id = request_id
516
+ usage = None
517
+ route: ResolvedUpstreamRoute | None = None
518
+ route_trace = UpstreamProxyRouteTrace()
519
+ route_fail_closed_reason: str | None = None
520
+ saw_text_delta = terminal_event_seen = suppressed_duplicate_tool_call = False
521
+ latency_first_token_ms: int | None = None
522
+ ttft_reasoning_deltas: dict[tuple[str | None, int | None, int | None], Any] = {}
523
+ if tool_call_dedupe is None:
524
+ tool_call_dedupe = _WebSocketUpstreamControl()
525
+ response_create_lease = AdmissionLease(None, stage="response_create", request_id=request_id)
526
+ account_response_create_lease: AccountLease | None = None
527
+ api_key_reservation_touch_state = _ApiKeyReservationTouchState(last_touch_at=start)
528
+
529
+ async def _touch_api_key_reservation() -> None:
530
+ api_key_reservation_touch_state.last_touch_at = await proxy._maybe_touch_api_key_reservation(
531
+ api_key=api_key,
532
+ reservation=api_key_reservation,
533
+ last_touch_at=api_key_reservation_touch_state.last_touch_at,
534
+ request_id=request_id,
535
+ surface="stream",
536
+ )
537
+
538
+ async def _record_or_defer_owner_recovery_health(error: UpstreamError, code: str | None) -> None:
539
+ if code is None:
540
+ return
541
+ if api_key_reservation is None:
542
+ await proxy._handle_stream_error(account, error, code)
543
+ return
544
+ settlement.error_code = code
545
+ settlement.account_health_error = True
546
+ settlement.settlement_order_required = True
547
+
548
+ api_key_reservation_heartbeat_stop = asyncio.Event()
549
+ api_key_reservation_heartbeat_task: asyncio.Task[None] | None = None
550
+ if api_key_reservation is not None:
551
+ api_key_reservation_heartbeat_task = scheduler_for(proxy).create_task(
552
+ proxy._run_api_key_reservation_heartbeat(
553
+ api_key=api_key,
554
+ reservation=api_key_reservation,
555
+ touch_state=api_key_reservation_touch_state,
556
+ request_id=request_id,
557
+ surface="stream",
558
+ stop_event=api_key_reservation_heartbeat_stop,
559
+ )
560
+ )
561
+ try:
562
+ route = await proxy._resolve_upstream_route_for_account(account, operation="responses")
563
+ account_response_create_lease = await proxy._acquire_account_response_create_lease_or_overload(
564
+ account_id=account.id,
565
+ request_id=request_id,
566
+ surface="stream",
567
+ concurrency_caps=concurrency_caps or _facade().effective_account_concurrency_caps(),
568
+ )
569
+ response_create_lease = await proxy._get_work_admission().acquire_response_create()
570
+ attempt_started_at = clock.monotonic()
571
+ latency_queue_ms = max(0, int((attempt_started_at - request_started_at) * 1000))
572
+ stream_optional_kwargs: dict[str, object] = {
573
+ "route": route,
574
+ "allow_direct_egress": route is None,
575
+ "route_trace": route_trace,
576
+ "codex_installation_id": account.codex_installation_id,
577
+ "enforce_openai_sdk_contract": enforce_openai_sdk_contract,
578
+ "codex_lb_account_id": account.id,
579
+ # Selected subscription Account, independent of optional legacy header.
580
+ "synthesize_routing_hint": True,
581
+ "thread_cache_identity": thread_cache_identity,
582
+ }
583
+ if upstream_stream_transport is not None:
584
+ stream_optional_kwargs["upstream_stream_transport_override"] = upstream_stream_transport
585
+ stream = _facade()._call_stream_with_supported_optional_kwargs(
586
+ _facade().core_stream_responses,
587
+ payload,
588
+ headers,
589
+ access_token,
590
+ account_id,
591
+ optional_kwargs=stream_optional_kwargs,
592
+ raise_for_status=True,
593
+ )
594
+ iterator = _facade()._stream_iterator_after_capacity_admission(stream)
595
+ try:
596
+ first = await iterator.__anext__()
597
+ except StopAsyncIteration:
598
+ response_create_lease.release()
599
+ await proxy._load_balancer.release_account_lease(account_response_create_lease)
600
+ account_response_create_lease = None
601
+ status = "error"
602
+ error_code = "stream_incomplete"
603
+ error_message = "Upstream websocket closed before response.completed"
604
+ settlement.record_success = False
605
+ terminal_event_seen = settlement.account_health_error = True
606
+ settlement.error = {"message": error_message}
607
+ yield _stream_transport_failure_event_or_raise(
608
+ error_code,
609
+ error_message,
610
+ response_id=request_id,
611
+ preserve_native_failure_lifecycle=preserve_native_failure_lifecycle,
612
+ )
613
+ return
614
+ except (aiohttp.ClientError, asyncio.TimeoutError) as exc:
615
+ response_create_lease.release()
616
+ await proxy._load_balancer.release_account_lease(account_response_create_lease)
617
+ account_response_create_lease = None
618
+ status = "error"
619
+ error_code = "upstream_unavailable"
620
+ error_message = str(exc) or "Request to upstream timed out"
621
+ settlement.record_success = False
622
+ terminal_event_seen = settlement.account_health_error = True
623
+ settlement.error = {"message": error_message}
624
+ yield _stream_transport_failure_event_or_raise(
625
+ error_code,
626
+ error_message,
627
+ response_id=request_id,
628
+ preserve_native_failure_lifecycle=preserve_native_failure_lifecycle,
629
+ )
630
+ return
631
+ response_create_lease.release()
632
+ await proxy._load_balancer.release_account_lease(account_response_create_lease)
633
+ account_response_create_lease = None
634
+ first_payload = parse_sse_data_json(first)
635
+ event_type = classify_event_type(first_payload)
636
+ event = parse_sse_event_payload(first_payload) if event_type in _LIFECYCLE_EVENT_TYPES else None
637
+ _publish_http_response_owner(proxy, event, first_payload, first, account_id_value, api_key, session_id)
638
+ preserve_raw_sse_line = not enforce_openai_sdk_contract and event_type == "error"
639
+ malformed_error_rewrite = _rewrite_malformed_stream_error_event(
640
+ enforce_openai_sdk_contract=enforce_openai_sdk_contract,
641
+ event=event,
642
+ event_type=event_type,
643
+ event_payload=first_payload,
644
+ response_id=response_id,
645
+ )
646
+ if malformed_error_rewrite is not None:
647
+ first, event, first_payload, event_type = malformed_error_rewrite
648
+ terminal_event_seen, response_id, usage = _settle_bg_ack(settlement, payload, first_payload, response_id)
649
+ if event_type not in {"response.completed", "response.failed", "response.incomplete", "error"}:
650
+ await _touch_api_key_reservation()
651
+ event_service_tier = _facade()._service_tier_from_event_payload(first_payload)
652
+ if event_service_tier is not None:
653
+ actual_service_tier = event_service_tier
654
+ service_tier = event_service_tier
655
+ if event and event.response and event.response.id:
656
+ response_id = event.response.id
657
+ settlement.response_id = response_id
658
+ terminal_stream_error: _TerminalStreamError | None = None
659
+ if event and event.type in ("response.failed", "error"):
660
+ if event.type == "response.failed":
661
+ response = event.response
662
+ error = response.error if response else None
663
+ else:
664
+ error = event.error
665
+ response_id = (
666
+ event.response.id
667
+ if event.type == "response.failed" and event.response and event.response.id
668
+ else request_id
669
+ )
670
+ if preserve_raw_sse_line and error is None:
671
+ raw_error_type, raw_error_message, raw_error_param, code = _raw_error_fields(
672
+ event_type,
673
+ first_payload,
674
+ )
675
+ settlement.error = cast(UpstreamError, {"message": raw_error_message or "Upstream error"})
676
+ else:
677
+ raw_error_type = error.type if error else None
678
+ raw_error_message = error.message if error else None
679
+ raw_error_param = error.param_state if error else None
680
+ code = _normalize_error_code(
681
+ error.code if error else None,
682
+ raw_error_type,
683
+ )
684
+ code = _classify_terminal_stream_error_frame(event_type, first_payload, code, raw_error_message)
685
+ rewritten_error = _facade()._rewrite_previous_response_stream_error(
686
+ previous_response_id=payload.previous_response_id,
687
+ preferred_account_id=preferred_account_id,
688
+ error_code=code,
689
+ error_type=raw_error_type,
690
+ error_message=raw_error_message,
691
+ error_param=raw_error_param,
692
+ )
693
+ status = "error"
694
+ if not (preserve_raw_sse_line and error is None):
695
+ settlement.error = _upstream_error_from_openai(error)
696
+ upstream_error: UpstreamError = settlement.error or cast(UpstreamError, {"message": "Upstream error"})
697
+ settlement.record_success = False
698
+ if rewritten_error is not None:
699
+ rewritten_code, rewritten_message, upstream_error_code = rewritten_error
700
+ first, event, first_payload, event_type = _facade()._build_rewritten_stream_response_failed_event(
701
+ response_id=response_id,
702
+ error_code=rewritten_code,
703
+ error_message=rewritten_message,
704
+ )
705
+ error_code = rewritten_code
706
+ error_message = rewritten_message
707
+ await _record_or_defer_owner_recovery_health(upstream_error, upstream_error_code)
708
+ upstream_error = cast(
709
+ UpstreamError, {"message": rewritten_message, "type": "upstream_error", "code": rewritten_code}
710
+ )
711
+ else:
712
+ error_code = code
713
+ error_message = raw_error_message
714
+ if error_code == "stream_incomplete":
715
+ failure_metadata = _RequestLogFailureMetadata(
716
+ failure_phase="upstream", failure_detail="upstream_eof_before_terminal_event"
717
+ )
718
+ settlement.account_health_error = _facade()._should_penalize_stream_error(code, error_message)
719
+ if allow_retry and code == "stream_idle_timeout":
720
+ raise _RetryableStreamError(code, upstream_error, exclude_account=True)
721
+ if allow_retry and _facade()._is_security_work_authorization_required_error(code, error_message):
722
+ error_code = _facade()._SECURITY_WORK_AUTHORIZATION_REQUIRED_CODE
723
+ raise _RetryableStreamError(
724
+ _facade()._SECURITY_WORK_AUTHORIZATION_REQUIRED_CODE,
725
+ upstream_error,
726
+ )
727
+ if allow_retry and _facade()._should_retry_stream_error(code, error_message):
728
+ raise _RetryableStreamError(code, upstream_error, exclude_account=True)
729
+ terminal_stream_error = _TerminalStreamError(
730
+ error_code or code,
731
+ upstream_error,
732
+ )
733
+ if allow_retry:
734
+ _facade().logger.info(
735
+ "Not retrying non-recoverable stream failure request_id=%s account_id=%s code=%s",
736
+ request_id,
737
+ account_id_value,
738
+ error_code or code,
739
+ )
740
+ elif preserve_raw_sse_line:
741
+ _, error_message, _, code = _raw_error_fields(event_type, first_payload)
742
+ status = "error"
743
+ error_code = code
744
+ settlement.error = {"message": error_message or "Upstream error"}
745
+ settlement.record_success = False
746
+ settlement.account_health_error = False
747
+ if event and event.type in ("response.completed", "response.incomplete"):
748
+ usage = event.response.usage if event.response else None
749
+ if event.response and event.response.id:
750
+ response_id = event.response.id
751
+ if event.type == "response.incomplete":
752
+ status = "error"
753
+
754
+ if event_type in _facade()._TEXT_DELTA_EVENT_TYPES:
755
+ saw_text_delta = True
756
+ if not _facade()._should_suppress_text_done_event(
757
+ event_type=event_type,
758
+ payload=first_payload,
759
+ suppress_text_done_events=suppress_text_done_events,
760
+ saw_text_delta=saw_text_delta,
761
+ ):
762
+ first, first_payload, event, event_type = _rewrite_tool_call_line(first, first_payload, event=event)
763
+ if mark_duplicate_tool_call_downstream_event(
764
+ first_payload,
765
+ seen_tool_call_keys=tool_call_dedupe.seen_tool_call_keys,
766
+ response_id=tool_call_response_id_from_payload(first_payload) or request_id,
767
+ scope_side_effects_by_response_id=False,
768
+ ):
769
+ suppressed_duplicate_tool_call = True
770
+ else:
771
+ if first_payload is not None and not preserve_raw_sse_line:
772
+ first = format_sse_event(first_payload)
773
+ terminal_event_seen = terminal_event_seen or _stamp_terminal(settlement, event_type, clock)
774
+ if latency_first_token_ms is None:
775
+ latency_first_token_ms = _ttft_event_latency_ms(
776
+ event_type, first_payload, ttft_reasoning_deltas, attempt_started_at, now=clock.monotonic()
777
+ )
778
+ settlement.downstream_visible = True
779
+ if event_type in _facade()._TEXT_DELTA_EVENT_TYPES:
780
+ settlement.downstream_text_visible = True
781
+ yield first
782
+ if terminal_stream_error is not None:
783
+ raise terminal_stream_error
784
+ async for line in iterator:
785
+ if verbatim_type := _verbatim_relay_event_type(line, latency_first_token_ms, ttft_reasoning_deltas):
786
+ await _touch_api_key_reservation()
787
+ if verbatim_type in _facade()._TEXT_DELTA_EVENT_TYPES:
788
+ saw_text_delta = settlement.downstream_text_visible = True
789
+ settlement.downstream_visible = True
790
+ yield line
791
+ continue
792
+ event_payload = parse_sse_data_json(line)
793
+ event_type = classify_event_type(event_payload)
794
+ event = parse_sse_event_payload(event_payload) if event_type in _LIFECYCLE_EVENT_TYPES else None
795
+ _publish_http_response_owner(proxy, event, event_payload, line, account_id_value, api_key, session_id)
796
+ preserve_raw_sse_line = not enforce_openai_sdk_contract and event_type == "error"
797
+ malformed_error_rewrite = _rewrite_malformed_stream_error_event(
798
+ enforce_openai_sdk_contract=enforce_openai_sdk_contract,
799
+ event=event,
800
+ event_type=event_type,
801
+ event_payload=event_payload,
802
+ response_id=response_id,
803
+ )
804
+ if malformed_error_rewrite is not None:
805
+ line, event, event_payload, event_type = malformed_error_rewrite
806
+ if event_type not in {"response.completed", "response.failed", "response.incomplete", "error"}:
807
+ await _touch_api_key_reservation()
808
+ event_service_tier = _facade()._service_tier_from_event_payload(event_payload)
809
+ if event_service_tier is not None:
810
+ actual_service_tier = event_service_tier
811
+ service_tier = event_service_tier
812
+ line, event_payload, event, event_type = _rewrite_tool_call_line(line, event_payload, event=event)
813
+ if event_type in _facade()._TEXT_DELTA_EVENT_TYPES:
814
+ saw_text_delta = True
815
+ if _facade()._should_suppress_text_done_event(
816
+ event_type=event_type,
817
+ payload=event_payload,
818
+ suppress_text_done_events=suppress_text_done_events,
819
+ saw_text_delta=saw_text_delta,
820
+ ):
821
+ continue
822
+ if event:
823
+ if event_type in ("response.failed", "error"):
824
+ status = "error"
825
+ if event_type == "response.failed":
826
+ response = event.response
827
+ error = response.error if response else None
828
+ if response and response.id:
829
+ response_id = response.id
830
+ settlement.response_id = response_id
831
+ else:
832
+ error = event.error
833
+ raw_error_type, raw_error_message, raw_error_param = _openai_error_fields(error)
834
+ if preserve_raw_sse_line and error is None:
835
+ raw_error_type, raw_error_message, raw_error_param, raw_error_code = _raw_error_fields(
836
+ event_type,
837
+ event_payload,
838
+ )
839
+ upstream_error = cast(UpstreamError, {"message": raw_error_message or "Upstream error"})
840
+ else:
841
+ raw_error_code = _normalize_error_code(error.code if error else None, raw_error_type)
842
+ upstream_error = _upstream_error_from_openai(error)
843
+ raw_error_code = _classify_terminal_stream_error_frame(
844
+ event_type, event_payload, raw_error_code, raw_error_message
845
+ )
846
+ rewritten_error = _facade()._rewrite_previous_response_stream_error(
847
+ previous_response_id=payload.previous_response_id,
848
+ preferred_account_id=preferred_account_id,
849
+ error_code=raw_error_code,
850
+ error_type=raw_error_type,
851
+ error_message=raw_error_message,
852
+ error_param=raw_error_param,
853
+ )
854
+ if rewritten_error is not None:
855
+ response_id = (
856
+ event.response.id
857
+ if event_type == "response.failed" and event.response and event.response.id
858
+ else request_id
859
+ )
860
+ rewritten_code, rewritten_message, upstream_error_code = rewritten_error
861
+ (
862
+ line,
863
+ event,
864
+ event_payload,
865
+ event_type,
866
+ ) = _facade()._build_rewritten_stream_response_failed_event(
867
+ response_id=response_id,
868
+ error_code=rewritten_code,
869
+ error_message=rewritten_message,
870
+ )
871
+ error_code = rewritten_code
872
+ error_message = rewritten_message
873
+ settlement.error = _upstream_error_from_openai(error)
874
+ settlement.record_success = False
875
+ await _record_or_defer_owner_recovery_health(
876
+ _upstream_error_from_openai(error), upstream_error_code
877
+ )
878
+ else:
879
+ error_code = raw_error_code
880
+ error_message = raw_error_message
881
+ settlement.error = upstream_error
882
+ settlement.record_success = False
883
+ if error_code == "stream_incomplete":
884
+ failure_metadata = _RequestLogFailureMetadata(
885
+ failure_phase="upstream",
886
+ failure_detail="upstream_eof_before_terminal_event",
887
+ )
888
+ if preserve_raw_sse_line and error is None:
889
+ settlement.account_health_error = not saw_text_delta
890
+ else:
891
+ settlement.account_health_error = (
892
+ _facade()._should_penalize_stream_error(error_code, error_message)
893
+ and not saw_text_delta
894
+ )
895
+ elif preserve_raw_sse_line:
896
+ _, raw_error_message, _, raw_error_code = _raw_error_fields(
897
+ event_type,
898
+ event_payload,
899
+ )
900
+ _observe_terminal_stream_error_frame(raw_error_code, raw_error_message)
901
+ status = "error"
902
+ error_code = raw_error_code
903
+ error_message = raw_error_message
904
+ settlement.error = {"message": raw_error_message or "Upstream error"}
905
+ settlement.record_success = False
906
+ settlement.account_health_error = not saw_text_delta
907
+ if event_type in ("response.completed", "response.incomplete"):
908
+ response = event.response if event is not None else None
909
+ usage = response.usage if response else None
910
+ if response and response.id:
911
+ response_id = response.id
912
+ settlement.response_id = response_id
913
+ if event_type == "response.incomplete":
914
+ status = "error"
915
+ if event_type == "response.completed" and suppressed_duplicate_tool_call:
916
+ (
917
+ line,
918
+ event,
919
+ event_payload,
920
+ event_type,
921
+ ) = _facade()._build_rewritten_stream_response_failed_event(
922
+ response_id=response_id,
923
+ error_code=_facade()._SUPPRESSED_DUPLICATE_TOOL_CALL_ERROR_CODE,
924
+ error_message=_facade()._SUPPRESSED_DUPLICATE_TOOL_CALL_MESSAGE,
925
+ )
926
+ status = "error"
927
+ error_code = _facade()._SUPPRESSED_DUPLICATE_TOOL_CALL_ERROR_CODE
928
+ error_message = _facade()._SUPPRESSED_DUPLICATE_TOOL_CALL_MESSAGE
929
+ settlement.record_success = False
930
+ settlement.account_health_error = False
931
+ if latency_first_token_ms is None:
932
+ latency_first_token_ms = _ttft_event_latency_ms(
933
+ event_type, event_payload, ttft_reasoning_deltas, attempt_started_at, now=clock.monotonic()
934
+ )
935
+ if mark_duplicate_tool_call_downstream_event(
936
+ event_payload,
937
+ seen_tool_call_keys=tool_call_dedupe.seen_tool_call_keys,
938
+ response_id=tool_call_response_id_from_payload(event_payload) or request_id,
939
+ scope_side_effects_by_response_id=False,
940
+ ):
941
+ suppressed_duplicate_tool_call = True
942
+ continue
943
+ if event_payload is not None and not preserve_raw_sse_line:
944
+ line = format_sse_event(event_payload)
945
+ settlement.downstream_visible = True
946
+ if event_type in _facade()._TEXT_DELTA_EVENT_TYPES:
947
+ settlement.downstream_text_visible = True
948
+ terminal_event_seen = terminal_event_seen or _stamp_terminal(settlement, event_type, clock)
949
+ yield line
950
+ if not terminal_event_seen:
951
+ status, error_code, error_message, failure_metadata = _mark_upstream_stream_incomplete(settlement)
952
+ except ProxyResponseError as exc:
953
+ response_create_lease.release()
954
+ failure_metadata = _facade()._request_log_failure_metadata(exc)
955
+ error = _parse_openai_error(exc.payload)
956
+ rewritten_error = _facade()._rewrite_previous_response_stream_error(
957
+ previous_response_id=payload.previous_response_id,
958
+ preferred_account_id=preferred_account_id,
959
+ error_code=_normalize_error_code(
960
+ error.code if error else None,
961
+ error.type if error else None,
962
+ ),
963
+ error_type=error.type if error else None,
964
+ error_message=error.message if error else None,
965
+ error_param=error.param_state if error else None,
966
+ )
967
+ if rewritten_error is not None:
968
+ rewritten_code, rewritten_message, upstream_error_code = rewritten_error
969
+ status = "error"
970
+ error_code = rewritten_code
971
+ error_message = rewritten_message
972
+ settlement.record_success = False
973
+ settlement.error = _upstream_error_from_openai(error)
974
+ await _record_or_defer_owner_recovery_health(_upstream_error_from_openai(error), upstream_error_code)
975
+ yield _facade()._build_rewritten_stream_response_failed_event(
976
+ response_id=request_id,
977
+ error_code=rewritten_code,
978
+ error_message=rewritten_message,
979
+ )[0]
980
+ return
981
+ status = "error"
982
+ error_code = _normalize_error_code(
983
+ error.code if error else None,
984
+ error.type if error else None,
985
+ )
986
+ error_message = error.message if error else None
987
+ settlement.record_success = False
988
+ settlement.account_health_error = _facade()._should_penalize_stream_error(error_code, error_message)
989
+ raise
990
+ except UpstreamProxyRouteError as exc:
991
+ route_fail_closed_reason = exc.reason
992
+ status = "error"
993
+ error_code = "upstream_proxy_unavailable"
994
+ error_message = f"Upstream proxy route unavailable: {exc.reason}"
995
+ settlement.record_success = False
996
+ settlement.account_health_error = False
997
+ settlement.error = {"message": error_message}
998
+ yield format_sse_event(
999
+ response_failed_event(
1000
+ "upstream_proxy_unavailable",
1001
+ error_message,
1002
+ response_id=request_id,
1003
+ )
1004
+ )
1005
+ return
1006
+ except _TerminalStreamError:
1007
+ raise
1008
+ except (asyncio.CancelledError, GeneratorExit):
1009
+ if not terminal_event_seen:
1010
+ status, error_code, error_message, failure_metadata = _mark_downstream_stream_cancelled(settlement)
1011
+ raise
1012
+ except Exception:
1013
+ if settlement.downstream_visible:
1014
+ status, error_code, error_message, failure_metadata = _mark_upstream_stream_incomplete(settlement)
1015
+ yield _stream_transport_failure_event_or_raise(
1016
+ error_code,
1017
+ error_message,
1018
+ response_id=request_id,
1019
+ preserve_native_failure_lifecycle=preserve_native_failure_lifecycle,
1020
+ )
1021
+ return
1022
+ raise
1023
+ finally:
1024
+ api_key_reservation_heartbeat_stop.set()
1025
+ if api_key_reservation_heartbeat_task is not None:
1026
+ proxy._cancel_api_key_reservation_heartbeat_task(api_key_reservation_heartbeat_task)
1027
+ response_create_lease.release()
1028
+ await proxy._load_balancer.release_account_lease(account_response_create_lease)
1029
+ input_tokens = usage.input_tokens if usage else None
1030
+ output_tokens = usage.output_tokens if usage else None
1031
+ cached_input_tokens = (
1032
+ usage.input_tokens_details.cached_tokens if usage and usage.input_tokens_details else None
1033
+ )
1034
+ reasoning_tokens = (
1035
+ usage.output_tokens_details.reasoning_tokens if usage and usage.output_tokens_details else None
1036
+ )
1037
+ if latency_first_token_ms is None:
1038
+ latency_first_token_ms = _finalize_ttft_latency_ms(
1039
+ ttft_reasoning_deltas, attempt_started_at, now=clock.monotonic()
1040
+ )
1041
+ settlement.status = status
1042
+ settlement.model = model
1043
+ settlement.service_tier = service_tier
1044
+ settlement.input_tokens = input_tokens
1045
+ settlement.output_tokens = output_tokens
1046
+ settlement.cached_input_tokens = cached_input_tokens
1047
+ if settlement.error_code is None:
1048
+ settlement.error_code = error_code
1049
+ settlement.error_message = error_message
1050
+ await proxy._write_request_log(
1051
+ affinity_observation=affinity_observation,
1052
+ account_id=account_id_value,
1053
+ api_key=api_key,
1054
+ request_id=response_id,
1055
+ archive_request_id=request_id,
1056
+ model=model,
1057
+ latency_ms=int((clock.monotonic() - attempt_started_at) * 1000),
1058
+ status=status,
1059
+ error_code=error_code,
1060
+ error_message=error_message,
1061
+ input_tokens=input_tokens,
1062
+ output_tokens=output_tokens,
1063
+ cached_input_tokens=cached_input_tokens,
1064
+ reasoning_tokens=reasoning_tokens,
1065
+ reasoning_effort=reasoning_effort,
1066
+ transport=request_transport,
1067
+ upstream_transport=upstream_stream_transport,
1068
+ service_tier=service_tier,
1069
+ requested_service_tier=requested_service_tier,
1070
+ actual_service_tier=actual_service_tier,
1071
+ latency_first_token_ms=latency_first_token_ms,
1072
+ latency_queue_ms=latency_queue_ms,
1073
+ latency_upstream_send_ms=0, # attempt_started_at is re-anchored right before the upstream send
1074
+ latency_upstream_terminal_ms=_upstream_terminal_latency_ms(settlement, attempt_started_at),
1075
+ session_id=session_id,
1076
+ failure_phase=failure_metadata.failure_phase,
1077
+ failure_detail=failure_metadata.failure_detail,
1078
+ failure_exception_type=failure_metadata.failure_exception_type,
1079
+ upstream_status_code=failure_metadata.upstream_status_code,
1080
+ upstream_error_code=failure_metadata.upstream_error_code,
1081
+ bridge_stage=failure_metadata.bridge_stage,
1082
+ upstream_proxy_route_mode=route_trace.mode or (route.mode if route is not None else None),
1083
+ upstream_proxy_pool_id=route_trace.pool_id or (route.pool_id if route is not None else None),
1084
+ upstream_proxy_endpoint_id=route_trace.endpoint_id or (route.endpoint_id if route else None),
1085
+ upstream_proxy_fallback_used=(
1086
+ route_trace.fallback_used
1087
+ if route_trace.endpoint_id is not None
1088
+ else (False if route is not None else None)
1089
+ ),
1090
+ upstream_proxy_fail_closed_reason=route_fail_closed_reason,
1091
+ useragent=useragent,
1092
+ useragent_group=useragent_group,
1093
+ conversation_id=conversation_id,
1094
+ client_ip=client_ip,
1095
+ )
1096
+ _maybe_log_proxy_service_tier_trace(
1097
+ "stream",
1098
+ requested_service_tier=requested_service_tier,
1099
+ actual_service_tier=actual_service_tier,
1100
+ )