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,802 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ from collections.abc import Mapping
6
+ from typing import cast
7
+
8
+ from app.core.openai import tool_call_safety
9
+ from app.core.openai.models import OpenAIEvent
10
+ from app.core.openai.parsing import classify_event_type, parse_sse_event_payload
11
+ from app.core.types import JsonValue
12
+ from app.core.utils.sse import format_sse_event
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ _TOOL_CALL_DEDUPE_CACHE_LIMIT = 1024
17
+ _SIDE_EFFECT_TOOL_CALL_ITEM_TYPES = frozenset({"apply_patch_call"})
18
+ _SIDE_EFFECT_VOLATILE_ARG_KEYS = frozenset({"max_output_tokens", "timeout_ms", "yield_time_ms"})
19
+
20
+ ToolCallDedupeKey = tuple[str, str, str | None, str | None, str | None, str]
21
+ ReplayedSideEffectToolCallKey = tuple[str, str | None, str | None, str | None, str]
22
+
23
+
24
+ def is_downstream_side_effect_tool_call(item: Mapping[str, JsonValue]) -> bool:
25
+ """Return whether a tool-call history item represents a local side effect.
26
+
27
+ Compact history retention and downstream replay deduplication must agree on
28
+ this classification. In particular, code-mode's ``exec`` and
29
+ ``collaboration`` wrappers are side effects even though they do not use the
30
+ lower-level ``functions.exec_command`` spelling.
31
+ """
32
+
33
+ return tool_call_safety.is_downstream_side_effect_tool_call_item(item)
34
+
35
+
36
+ def event_type_from_payload(event: OpenAIEvent | None, payload: dict[str, JsonValue] | None) -> str | None:
37
+ if event is not None:
38
+ return event.type
39
+ return classify_event_type(payload)
40
+
41
+
42
+ def response_id_from_payload(payload: dict[str, JsonValue] | None) -> str | None:
43
+ if not isinstance(payload, dict):
44
+ return None
45
+ top_level_response_id = payload.get("response_id")
46
+ if isinstance(top_level_response_id, str):
47
+ stripped = top_level_response_id.strip()
48
+ if stripped:
49
+ return stripped
50
+ response = payload.get("response")
51
+ if not isinstance(response, dict):
52
+ return None
53
+ response_id = response.get("id")
54
+ if not isinstance(response_id, str):
55
+ return None
56
+ stripped = response_id.strip()
57
+ return stripped or None
58
+
59
+
60
+ def mark_duplicate_tool_call_downstream_event(
61
+ payload: dict[str, JsonValue] | None,
62
+ *,
63
+ seen_tool_call_keys: dict[ToolCallDedupeKey, None],
64
+ response_id: str | None,
65
+ scope_side_effects_by_response_id: bool = True,
66
+ ) -> bool:
67
+ if not isinstance(payload, dict) or payload.get("type") != "response.output_item.done":
68
+ return False
69
+ item = payload.get("item")
70
+ if not isinstance(item, dict):
71
+ return False
72
+ item_type = item.get("type")
73
+ if item_type == "function_call":
74
+ argument_value = item.get("arguments")
75
+ elif item_type == "custom_tool_call":
76
+ argument_value = item.get("input")
77
+ elif item_type == "apply_patch_call":
78
+ operation_value = item.get("operation")
79
+ if isinstance(operation_value, dict):
80
+ argument_value = json.dumps(operation_value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
81
+ else:
82
+ argument_value = operation_value
83
+ else:
84
+ _clear_legacy_downstream_tool_call_keys(seen_tool_call_keys)
85
+ return False
86
+ if not isinstance(argument_value, str):
87
+ return False
88
+ item_name = item.get("name")
89
+ if item_name is not None and not isinstance(item_name, str):
90
+ item_name = None
91
+ item_namespace = item.get("namespace")
92
+ if item_namespace is not None and not isinstance(item_namespace, str):
93
+ item_namespace = None
94
+ call_id = item.get("call_id")
95
+ if call_id is not None and not isinstance(call_id, str):
96
+ call_id = None
97
+ is_side_effect_tool_call = is_downstream_side_effect_tool_call(item)
98
+ if not is_side_effect_tool_call:
99
+ _clear_legacy_downstream_tool_call_keys(seen_tool_call_keys)
100
+ return False
101
+ if item_name == tool_call_safety.PARALLEL_TOOL_CALL_NAME and is_side_effect_tool_call:
102
+ return _mark_duplicate_parallel_tool_call_downstream_event(
103
+ item,
104
+ argument_value,
105
+ seen_tool_call_keys=seen_tool_call_keys,
106
+ response_id=response_id,
107
+ scope_side_effects_by_response_id=scope_side_effects_by_response_id,
108
+ )
109
+ if is_side_effect_tool_call:
110
+ item_name = normalize_tool_call_name(item_name)
111
+ argument_key = canonical_downstream_side_effect_argument_key(item_name, argument_value)
112
+ else:
113
+ argument_key = argument_value
114
+ dedupe_response_id = response_id if response_id is not None else ""
115
+ key = (dedupe_response_id, str(item_type), item_namespace, item_name, call_id, argument_key)
116
+ if key in seen_tool_call_keys:
117
+ logger.warning(
118
+ "Suppressed duplicate downstream tool call response_id=%s item_type=%s name=%s",
119
+ response_id,
120
+ item_type,
121
+ item_name,
122
+ )
123
+ return True
124
+ if is_side_effect_tool_call:
125
+ same_response_argument_key = (
126
+ dedupe_response_id,
127
+ str(item_type),
128
+ item_namespace,
129
+ item_name,
130
+ None,
131
+ argument_key,
132
+ )
133
+ code_mode_call = item_name in tool_call_safety.CODE_MODE_DOWNSTREAM_SIDE_EFFECT_TOOL_CALL_NAMES
134
+ identity_scoped_call = code_mode_call or item_namespace is not None
135
+ cross_response_call_id = call_id if identity_scoped_call else None
136
+ cross_response_argument_key = (
137
+ "",
138
+ str(item_type),
139
+ item_namespace,
140
+ item_name,
141
+ cross_response_call_id,
142
+ argument_key,
143
+ )
144
+ has_cross_response_identity = not identity_scoped_call or cross_response_call_id is not None
145
+ if (
146
+ not scope_side_effects_by_response_id
147
+ and has_cross_response_identity
148
+ and cross_response_argument_key in seen_tool_call_keys
149
+ and (identity_scoped_call or same_response_argument_key not in seen_tool_call_keys)
150
+ ):
151
+ logger.warning(
152
+ "Suppressed duplicate downstream side-effect replay response_id=%s item_type=%s name=%s",
153
+ response_id,
154
+ item_type,
155
+ item_name,
156
+ )
157
+ return True
158
+ seen_tool_call_keys[key] = None
159
+ if is_side_effect_tool_call:
160
+ seen_tool_call_keys[same_response_argument_key] = None
161
+ if not scope_side_effects_by_response_id and has_cross_response_identity:
162
+ seen_tool_call_keys[cross_response_argument_key] = None
163
+ while len(seen_tool_call_keys) > _TOOL_CALL_DEDUPE_CACHE_LIMIT:
164
+ seen_tool_call_keys.pop(next(iter(seen_tool_call_keys)))
165
+ return False
166
+
167
+
168
+ def _clear_legacy_downstream_tool_call_keys(seen_tool_call_keys: dict[ToolCallDedupeKey, None]) -> None:
169
+ for key in tuple(seen_tool_call_keys):
170
+ _, item_type, namespace, _, call_id, _ = key
171
+ if namespace is not None and (call_id is not None or item_type == "parallel_tool_use"):
172
+ continue
173
+ seen_tool_call_keys.pop(key, None)
174
+
175
+
176
+ def _mark_duplicate_parallel_tool_call_downstream_event(
177
+ item: dict[str, JsonValue],
178
+ argument_value: str,
179
+ *,
180
+ seen_tool_call_keys: dict[ToolCallDedupeKey, None],
181
+ response_id: str | None,
182
+ scope_side_effects_by_response_id: bool,
183
+ ) -> bool:
184
+ argument = json_object_from_argument(argument_value)
185
+ if argument is None:
186
+ return False
187
+ tool_uses = argument.get("tool_uses")
188
+ if not isinstance(tool_uses, list):
189
+ return False
190
+
191
+ candidate_keys: list[ToolCallDedupeKey] = []
192
+ outer_call_id = item.get("call_id")
193
+ for tool_use in tool_uses:
194
+ if not isinstance(tool_use, dict):
195
+ continue
196
+ recipient_name = tool_use.get("recipient_name")
197
+ if (
198
+ not isinstance(recipient_name, str)
199
+ or recipient_name not in tool_call_safety.PARALLEL_TOOL_USE_SIDE_EFFECT_RECIPIENT_NAMES
200
+ ):
201
+ continue
202
+ dedupe_response_id = response_id if scope_side_effects_by_response_id else None
203
+ identity_call_id = (
204
+ outer_call_id
205
+ if recipient_name.removeprefix("functions.")
206
+ in tool_call_safety.CODE_MODE_DOWNSTREAM_SIDE_EFFECT_TOOL_CALL_NAMES
207
+ and isinstance(outer_call_id, str)
208
+ else None
209
+ )
210
+ candidate_keys.append(
211
+ (
212
+ dedupe_response_id or "",
213
+ "parallel_tool_use",
214
+ identity_call_id,
215
+ recipient_name,
216
+ None,
217
+ canonical_parallel_tool_use_key(tool_use),
218
+ )
219
+ )
220
+ kept_tool_uses: list[JsonValue] = []
221
+ removed_count = 0
222
+ for tool_use in tool_uses:
223
+ if not isinstance(tool_use, dict):
224
+ kept_tool_uses.append(cast(JsonValue, tool_use))
225
+ continue
226
+ recipient_name = tool_use.get("recipient_name")
227
+ if (
228
+ not isinstance(recipient_name, str)
229
+ or recipient_name not in tool_call_safety.PARALLEL_TOOL_USE_SIDE_EFFECT_RECIPIENT_NAMES
230
+ ):
231
+ kept_tool_uses.append(cast(JsonValue, tool_use))
232
+ continue
233
+ dedupe_response_id = response_id if scope_side_effects_by_response_id else None
234
+ identity_call_id = (
235
+ outer_call_id
236
+ if recipient_name.removeprefix("functions.")
237
+ in tool_call_safety.CODE_MODE_DOWNSTREAM_SIDE_EFFECT_TOOL_CALL_NAMES
238
+ and isinstance(outer_call_id, str)
239
+ else None
240
+ )
241
+ key = (
242
+ dedupe_response_id or "",
243
+ "parallel_tool_use",
244
+ identity_call_id,
245
+ recipient_name,
246
+ None,
247
+ canonical_parallel_tool_use_key(tool_use),
248
+ )
249
+ if key in seen_tool_call_keys:
250
+ removed_count += 1
251
+ continue
252
+ seen_tool_call_keys[key] = None
253
+ kept_tool_uses.append(cast(JsonValue, tool_use))
254
+
255
+ while len(seen_tool_call_keys) > _TOOL_CALL_DEDUPE_CACHE_LIMIT:
256
+ seen_tool_call_keys.pop(next(iter(seen_tool_call_keys)))
257
+
258
+ if removed_count == 0:
259
+ return False
260
+ logger.warning(
261
+ "Suppressed duplicate downstream parallel tool uses response_id=%s removed=%s",
262
+ response_id,
263
+ removed_count,
264
+ )
265
+ if not kept_tool_uses:
266
+ return True
267
+
268
+ rewritten_argument = dict(argument)
269
+ rewritten_argument["tool_uses"] = kept_tool_uses
270
+ item["arguments"] = json.dumps(rewritten_argument, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
271
+ return False
272
+
273
+
274
+ def json_object_from_argument(argument_value: str) -> dict[str, JsonValue] | None:
275
+ try:
276
+ decoded_argument = json.loads(argument_value)
277
+ except json.JSONDecodeError:
278
+ return None
279
+ if not isinstance(decoded_argument, dict):
280
+ return None
281
+ return cast(dict[str, JsonValue], decoded_argument)
282
+
283
+
284
+ def parallel_argument_has_only_code_mode_side_effects(argument_value: str) -> bool:
285
+ argument = json_object_from_argument(argument_value)
286
+ if argument is None:
287
+ return False
288
+ tool_uses = argument.get("tool_uses")
289
+ if not isinstance(tool_uses, list):
290
+ return False
291
+ side_effect_names = tool_call_safety.PARALLEL_TOOL_USE_SIDE_EFFECT_RECIPIENT_NAMES
292
+ side_effects = [
293
+ tool_use.get("recipient_name")
294
+ for tool_use in tool_uses
295
+ if isinstance(tool_use, dict) and tool_use.get("recipient_name") in side_effect_names
296
+ ]
297
+ return bool(side_effects) and all(
298
+ isinstance(name, str)
299
+ and name.removeprefix("functions.") in tool_call_safety.CODE_MODE_DOWNSTREAM_SIDE_EFFECT_TOOL_CALL_NAMES
300
+ for name in side_effects
301
+ )
302
+
303
+
304
+ def canonical_json_key(value: JsonValue) -> str:
305
+ return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
306
+
307
+
308
+ def canonical_parameters_key(recipient_name: str, parameters: dict[str, JsonValue]) -> str:
309
+ canonical_parameters = dict(parameters)
310
+ for key in _SIDE_EFFECT_VOLATILE_ARG_KEYS:
311
+ canonical_parameters.pop(key, None)
312
+ return canonical_json_key({"recipient_name": recipient_name, "parameters": canonical_parameters})
313
+
314
+
315
+ def canonical_wait_agent_targets(targets: JsonValue | None) -> JsonValue | None:
316
+ if not isinstance(targets, list):
317
+ return targets
318
+ return cast(
319
+ JsonValue,
320
+ sorted(
321
+ targets,
322
+ key=lambda target: (
323
+ target.__class__.__name__,
324
+ canonical_json_key(cast(JsonValue, target)),
325
+ ),
326
+ ),
327
+ )
328
+
329
+
330
+ def canonical_side_effect_argument_key(item_name: str | None, argument_value: str) -> str:
331
+ argument = json_object_from_argument(argument_value)
332
+ if argument is None:
333
+ return argument_value
334
+ normalized_item_name = normalize_tool_call_name(item_name)
335
+ if normalized_item_name == "write_stdin":
336
+ return canonical_json_key(
337
+ {
338
+ "name": normalized_item_name,
339
+ "session_id": argument.get("session_id"),
340
+ "chars": argument.get("chars"),
341
+ "yield_time_ms": argument.get("yield_time_ms"),
342
+ }
343
+ )
344
+ if normalized_item_name == "wait_agent":
345
+ return canonical_json_key(
346
+ {
347
+ "name": normalized_item_name,
348
+ "targets": canonical_wait_agent_targets(argument.get("targets")),
349
+ }
350
+ )
351
+ if normalized_item_name == "exec_command":
352
+ return canonical_parameters_key(normalized_item_name, argument)
353
+ if item_name != tool_call_safety.PARALLEL_TOOL_CALL_NAME:
354
+ return canonical_json_key({"name": normalized_item_name or item_name, "parameters": cast(JsonValue, argument)})
355
+
356
+ tool_uses = argument.get("tool_uses")
357
+ if not isinstance(tool_uses, list):
358
+ return canonical_json_key(cast(JsonValue, argument))
359
+
360
+ canonical_tool_uses: list[JsonValue] = []
361
+ for tool_use in tool_uses:
362
+ if isinstance(tool_use, dict):
363
+ canonical_tool_use = json_object_from_argument(canonical_parallel_tool_use_key(tool_use))
364
+ canonical_tool_uses.append(canonical_tool_use or tool_use)
365
+ else:
366
+ canonical_tool_uses.append(cast(JsonValue, tool_use))
367
+ canonical_argument = dict(argument)
368
+ canonical_argument["tool_uses"] = canonical_tool_uses
369
+ return canonical_json_key(cast(JsonValue, canonical_argument))
370
+
371
+
372
+ def canonical_downstream_side_effect_argument_key(item_name: str | None, argument_value: str) -> str:
373
+ argument = json_object_from_argument(argument_value)
374
+ if argument is None:
375
+ return argument_value
376
+ normalized_item_name = normalize_tool_call_name(item_name)
377
+ if normalized_item_name == "write_stdin":
378
+ return canonical_parameters_key(
379
+ normalized_item_name,
380
+ {
381
+ "session_id": argument.get("session_id"),
382
+ "chars": argument.get("chars"),
383
+ "yield_time_ms": argument.get("yield_time_ms"),
384
+ "max_output_tokens": argument.get("max_output_tokens"),
385
+ },
386
+ )
387
+ if normalized_item_name == "exec_command":
388
+ return canonical_parameters_key(normalized_item_name, argument)
389
+ return canonical_side_effect_argument_key(item_name, argument_value)
390
+
391
+
392
+ def dedupe_replayed_side_effect_input_items(
393
+ input_items: list[JsonValue],
394
+ *,
395
+ sanitize_missing_outputs: bool = False,
396
+ ) -> tuple[list[JsonValue], int]:
397
+ call_keys: dict[int, ReplayedSideEffectToolCallKey] = {}
398
+ call_ids: dict[int, str] = {}
399
+ output_indices_by_call_id: dict[str, list[int]] = {}
400
+ for index, item in enumerate(input_items):
401
+ if not isinstance(item, dict):
402
+ continue
403
+ tool_call_key = replayed_side_effect_tool_call_key(item)
404
+ if tool_call_key is not None:
405
+ call_keys[index] = tool_call_key
406
+ call_id = item.get("call_id")
407
+ if isinstance(call_id, str) and call_id:
408
+ call_ids[index] = call_id
409
+ continue
410
+ output_call_id = replayed_tool_output_call_id(item)
411
+ if output_call_id is not None:
412
+ output_indices_by_call_id.setdefault(output_call_id, []).append(index)
413
+
414
+ if not call_keys:
415
+ return input_items, 0
416
+
417
+ kept = [True] * len(input_items)
418
+ rewritten: dict[int, JsonValue] = {}
419
+ first_call_id_by_key: dict[ReplayedSideEffectToolCallKey, str | None] = {}
420
+ first_call_index_by_key: dict[ReplayedSideEffectToolCallKey, int] = {}
421
+ output_index_by_call_index: dict[int, int | None] = {}
422
+ next_output_cursor_by_call_id: dict[str, int] = {}
423
+ last_side_effect_key: ReplayedSideEffectToolCallKey | None = None
424
+ removed_count = 0
425
+ for index, item in enumerate(input_items):
426
+ if isinstance(item, dict) and replayed_input_segment_boundary(item):
427
+ first_call_id_by_key.clear()
428
+ first_call_index_by_key.clear()
429
+ last_side_effect_key = None
430
+ key = call_keys.get(index)
431
+ if key is None:
432
+ output_call_id = replayed_tool_output_call_id(item) if isinstance(item, dict) else None
433
+ if (
434
+ last_side_effect_key is not None
435
+ and output_call_id is not None
436
+ and first_call_id_by_key.get(last_side_effect_key) == output_call_id
437
+ ):
438
+ continue
439
+ if isinstance(item, dict) and replayed_tool_call_segment_boundary(item):
440
+ _clear_legacy_replayed_side_effect_keys(first_call_id_by_key, first_call_index_by_key)
441
+ continue
442
+ if output_call_id is not None:
443
+ _clear_legacy_replayed_side_effect_keys(first_call_id_by_key, first_call_index_by_key)
444
+ continue
445
+ if last_side_effect_key is not None and key != last_side_effect_key:
446
+ _clear_legacy_replayed_side_effect_keys(first_call_id_by_key, first_call_index_by_key)
447
+ last_side_effect_key = key
448
+ call_id = call_ids.get(index)
449
+ output_index = (
450
+ replayed_tool_output_index_for_call(
451
+ output_indices_by_call_id,
452
+ next_output_cursor_by_call_id,
453
+ call_index=index,
454
+ call_id=call_id,
455
+ )
456
+ if call_id is not None
457
+ else None
458
+ )
459
+ output_index_by_call_index[index] = output_index
460
+ if key not in first_call_id_by_key:
461
+ first_call_id_by_key[key] = call_id
462
+ first_call_index_by_key[key] = index
463
+ continue
464
+
465
+ first_call_index = first_call_index_by_key.get(key)
466
+ if first_call_index is not None and output_index_by_call_index.get(first_call_index) is None:
467
+ rewritten[first_call_index] = replayed_tool_call_without_output_as_assistant_message(
468
+ cast(Mapping[str, JsonValue], input_items[first_call_index])
469
+ )
470
+ removed_count += 1
471
+ kept[index] = False
472
+ if output_index is not None:
473
+ output_item = input_items[output_index]
474
+ if isinstance(output_item, dict):
475
+ rewritten[output_index] = replayed_tool_output_as_assistant_message(output_item)
476
+
477
+ missing_output_rewrites = 0
478
+ if sanitize_missing_outputs:
479
+ for index, key in call_keys.items():
480
+ del key
481
+ if not kept[index] or index in rewritten:
482
+ continue
483
+ if output_index_by_call_index.get(index) is not None:
484
+ continue
485
+ item = input_items[index]
486
+ if not isinstance(item, dict):
487
+ continue
488
+ rewritten[index] = replayed_tool_call_without_output_as_assistant_message(item)
489
+ missing_output_rewrites += 1
490
+
491
+ if removed_count == 0:
492
+ if missing_output_rewrites == 0:
493
+ return input_items, 0
494
+
495
+ deduped_items: list[JsonValue] = []
496
+ for index, item in enumerate(input_items):
497
+ if kept[index]:
498
+ deduped_items.append(rewritten.get(index, item))
499
+ return deduped_items, removed_count + missing_output_rewrites
500
+
501
+
502
+ def replayed_side_effect_tool_call_key(item: Mapping[str, JsonValue]) -> ReplayedSideEffectToolCallKey | None:
503
+ item_type_value = item.get("type")
504
+ item_type = item_type_value if isinstance(item_type_value, str) else None
505
+ if item_type == "function_call":
506
+ item_name_value = item.get("name")
507
+ item_name = item_name_value if isinstance(item_name_value, str) else None
508
+ argument_value = item.get("arguments")
509
+ if not isinstance(argument_value, str):
510
+ return None
511
+ is_side_effect_tool_call = (
512
+ item_name in tool_call_safety.HISTORY_SIDE_EFFECT_TOOL_CALL_NAMES
513
+ and tool_call_safety.is_downstream_side_effect_tool_call(item_name, argument_value)
514
+ )
515
+ if not is_side_effect_tool_call:
516
+ return None
517
+ argument_key = canonical_side_effect_argument_key(item_name, argument_value)
518
+ elif item_type == "custom_tool_call":
519
+ item_name_value = item.get("name")
520
+ item_name = item_name_value if isinstance(item_name_value, str) else None
521
+ if item_name not in tool_call_safety.HISTORY_SIDE_EFFECT_TOOL_CALL_NAMES:
522
+ return None
523
+ argument_value = item.get("input")
524
+ if not isinstance(argument_value, str):
525
+ return None
526
+ argument_key = canonical_side_effect_argument_key(item_name, argument_value)
527
+ elif item_type in _SIDE_EFFECT_TOOL_CALL_ITEM_TYPES:
528
+ item_name = item_type
529
+ operation_value = item.get("operation")
530
+ argument_key = canonical_json_key(operation_value)
531
+ else:
532
+ return None
533
+ namespace_value = item.get("namespace")
534
+ namespace = namespace_value if isinstance(namespace_value, str) else None
535
+ call_id_value = item.get("call_id")
536
+ identity_scoped = namespace is not None
537
+ if item_name == tool_call_safety.PARALLEL_TOOL_CALL_NAME:
538
+ # A mixed parallel wrapper remains argument-scoped for ordinary
539
+ # side effects; only an all-code-mode wrapper gets outer-call
540
+ # identity because those nested calls are independently meaningful.
541
+ identity_scoped = parallel_argument_has_only_code_mode_side_effects(argument_value)
542
+ call_id = call_id_value if identity_scoped and isinstance(call_id_value, str) and call_id_value else None
543
+ return (item_type, namespace, item_name, call_id, argument_key)
544
+
545
+
546
+ def _replayed_side_effect_key_has_stable_identity(key: ReplayedSideEffectToolCallKey) -> bool:
547
+ _, namespace, item_name, call_id, _ = key
548
+ return call_id is not None and (namespace is not None or item_name == tool_call_safety.PARALLEL_TOOL_CALL_NAME)
549
+
550
+
551
+ def _clear_legacy_replayed_side_effect_keys(
552
+ first_call_id_by_key: dict[ReplayedSideEffectToolCallKey, str | None],
553
+ first_call_index_by_key: dict[ReplayedSideEffectToolCallKey, int],
554
+ ) -> None:
555
+ for key in tuple(first_call_id_by_key):
556
+ if _replayed_side_effect_key_has_stable_identity(key):
557
+ continue
558
+ first_call_id_by_key.pop(key, None)
559
+ first_call_index_by_key.pop(key, None)
560
+
561
+
562
+ def replayed_input_segment_boundary(item: Mapping[str, JsonValue]) -> bool:
563
+ role = item.get("role")
564
+ if role not in {"developer", "system", "user"}:
565
+ return False
566
+ item_type = item.get("type")
567
+ return item_type in {None, "message"}
568
+
569
+
570
+ def replayed_tool_call_segment_boundary(item: Mapping[str, JsonValue]) -> bool:
571
+ item_type = item.get("type")
572
+ return item_type in {"function_call", "custom_tool_call"} | _SIDE_EFFECT_TOOL_CALL_ITEM_TYPES
573
+
574
+
575
+ def replayed_tool_output_call_id(item: Mapping[str, JsonValue]) -> str | None:
576
+ if item.get("type") not in {
577
+ "function_call_output",
578
+ "custom_tool_call_output",
579
+ "apply_patch_call_output",
580
+ }:
581
+ return None
582
+ call_id = item.get("call_id")
583
+ if isinstance(call_id, str) and call_id:
584
+ return call_id
585
+ return None
586
+
587
+
588
+ def replayed_tool_output_as_assistant_message(item: Mapping[str, JsonValue]) -> JsonValue:
589
+ output_value = item.get("output")
590
+ if isinstance(output_value, str):
591
+ output_text = output_value
592
+ else:
593
+ output_text = canonical_json_key(cast(JsonValue, output_value))
594
+ return {
595
+ "type": "message",
596
+ "role": "assistant",
597
+ "content": [
598
+ {
599
+ "type": "output_text",
600
+ "text": output_text,
601
+ }
602
+ ],
603
+ }
604
+
605
+
606
+ def replayed_tool_call_without_output_as_assistant_message(item: Mapping[str, JsonValue]) -> JsonValue:
607
+ name_value = item.get("name")
608
+ name = name_value if isinstance(name_value, str) and name_value else "tool"
609
+ return {
610
+ "type": "message",
611
+ "role": "assistant",
612
+ "content": [
613
+ {
614
+ "type": "output_text",
615
+ "text": f"Omitted replayed side-effect tool call without matching output: {name}.",
616
+ }
617
+ ],
618
+ }
619
+
620
+
621
+ def replayed_tool_output_index_for_call(
622
+ output_indices_by_call_id: dict[str, list[int]],
623
+ next_output_cursor_by_call_id: dict[str, int],
624
+ *,
625
+ call_index: int,
626
+ call_id: str,
627
+ ) -> int | None:
628
+ output_indices = output_indices_by_call_id.get(call_id)
629
+ if not output_indices:
630
+ return None
631
+ cursor = next_output_cursor_by_call_id.get(call_id, 0)
632
+ while cursor < len(output_indices) and output_indices[cursor] < call_index:
633
+ cursor += 1
634
+ if cursor >= len(output_indices):
635
+ next_output_cursor_by_call_id[call_id] = cursor
636
+ return None
637
+ output_index = output_indices[cursor]
638
+ next_output_cursor_by_call_id[call_id] = cursor + 1
639
+ return output_index
640
+
641
+
642
+ def canonical_parallel_tool_use_key(tool_use: Mapping[str, JsonValue]) -> str:
643
+ recipient_name = tool_use.get("recipient_name")
644
+ parameters = tool_use.get("parameters")
645
+ if not isinstance(recipient_name, str):
646
+ return json.dumps(tool_use, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
647
+ normalized_recipient_name = normalize_tool_call_name(recipient_name)
648
+ if normalized_recipient_name == "write_stdin" and isinstance(parameters, dict):
649
+ return canonical_parameters_key(
650
+ normalized_recipient_name,
651
+ {
652
+ "session_id": parameters.get("session_id"),
653
+ "chars": parameters.get("chars"),
654
+ "yield_time_ms": parameters.get("yield_time_ms"),
655
+ "max_output_tokens": parameters.get("max_output_tokens"),
656
+ },
657
+ )
658
+ if normalized_recipient_name == "wait_agent" and isinstance(parameters, dict):
659
+ targets = parameters.get("targets")
660
+ return json.dumps(
661
+ {
662
+ "recipient_name": normalized_recipient_name,
663
+ "targets": canonical_wait_agent_targets(targets),
664
+ },
665
+ sort_keys=True,
666
+ separators=(",", ":"),
667
+ ensure_ascii=False,
668
+ )
669
+ if normalized_recipient_name == "exec_command" and isinstance(parameters, dict):
670
+ return canonical_parameters_key(normalized_recipient_name, parameters)
671
+ return json.dumps(tool_use, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
672
+
673
+
674
+ def normalize_tool_call_name(name: str | None) -> str | None:
675
+ if name is None:
676
+ return None
677
+ if name.startswith("functions."):
678
+ return name.removeprefix("functions.")
679
+ return name
680
+
681
+
682
+ def dedupe_parallel_tool_uses_argument(argument_value: str) -> tuple[str, bool, int]:
683
+ try:
684
+ decoded_arguments = json.loads(argument_value)
685
+ except json.JSONDecodeError:
686
+ return argument_value, False, 0
687
+ if not isinstance(decoded_arguments, dict):
688
+ return argument_value, False, 0
689
+ tool_uses = decoded_arguments.get("tool_uses")
690
+ if not isinstance(tool_uses, list):
691
+ return argument_value, False, 0
692
+
693
+ seen_tool_uses: set[str] = set()
694
+ deduped_tool_uses: list[JsonValue] = []
695
+ removed_count = 0
696
+ for tool_use in tool_uses:
697
+ if not isinstance(tool_use, dict):
698
+ deduped_tool_uses.append(cast(JsonValue, tool_use))
699
+ continue
700
+ recipient_name = tool_use.get("recipient_name")
701
+ if (
702
+ not isinstance(recipient_name, str)
703
+ or recipient_name not in tool_call_safety.PARALLEL_TOOL_USE_DEDUPE_RECIPIENT_NAMES
704
+ ):
705
+ deduped_tool_uses.append(cast(JsonValue, tool_use))
706
+ continue
707
+ tool_use_key = canonical_parallel_tool_use_key(cast(dict[str, JsonValue], tool_use))
708
+ if tool_use_key in seen_tool_uses:
709
+ removed_count += 1
710
+ continue
711
+ seen_tool_uses.add(tool_use_key)
712
+ deduped_tool_uses.append(cast(JsonValue, tool_use))
713
+
714
+ if removed_count == 0:
715
+ return argument_value, False, 0
716
+
717
+ rewritten_arguments: dict[str, JsonValue] = dict(cast(dict[str, JsonValue], decoded_arguments))
718
+ rewritten_arguments["tool_uses"] = deduped_tool_uses
719
+ return (
720
+ json.dumps(rewritten_arguments, sort_keys=True, separators=(",", ":"), ensure_ascii=False),
721
+ True,
722
+ removed_count,
723
+ )
724
+
725
+
726
+ def rewrite_parallel_tool_call_payload(
727
+ payload: dict[str, JsonValue] | None,
728
+ ) -> tuple[dict[str, JsonValue] | None, bool, int]:
729
+ if not isinstance(payload, dict) or payload.get("type") != "response.output_item.done":
730
+ return payload, False, 0
731
+ item = payload.get("item")
732
+ if not isinstance(item, dict):
733
+ return payload, False, 0
734
+ if item.get("type") != "function_call" or item.get("name") != tool_call_safety.PARALLEL_TOOL_CALL_NAME:
735
+ return payload, False, 0
736
+ argument_value = item.get("arguments")
737
+ if not isinstance(argument_value, str):
738
+ return payload, False, 0
739
+
740
+ rewritten_arguments, changed, removed_count = dedupe_parallel_tool_uses_argument(argument_value)
741
+ if not changed:
742
+ return payload, False, 0
743
+
744
+ rewritten_item: dict[str, JsonValue] = dict(item)
745
+ rewritten_item["arguments"] = rewritten_arguments
746
+ rewritten_payload: dict[str, JsonValue] = dict(payload)
747
+ rewritten_payload["item"] = rewritten_item
748
+ logger.warning(
749
+ "Suppressed duplicate nested parallel tool uses response_id=%s removed=%s",
750
+ response_id_from_payload(rewritten_payload),
751
+ removed_count,
752
+ )
753
+ return rewritten_payload, True, removed_count
754
+
755
+
756
+ def rewrite_parallel_tool_call_text(
757
+ text: str,
758
+ payload: dict[str, JsonValue] | None,
759
+ *,
760
+ event_block: str,
761
+ event: OpenAIEvent | None = None,
762
+ ) -> tuple[str, dict[str, JsonValue] | None, OpenAIEvent | None, str | None, str]:
763
+ rewritten_payload, changed, _removed_count = rewrite_parallel_tool_call_payload(payload)
764
+ if not changed:
765
+ # Reuse the caller's parsed event as-is. Hot streaming paths validate
766
+ # only lifecycle frames, so ``event`` is intentionally None for the
767
+ # delta bulk; the event type is classified from the payload dict
768
+ # instead of re-validating per frame.
769
+ return text, payload, event, event_type_from_payload(event, payload), event_block
770
+ assert rewritten_payload is not None
771
+ rewritten_text = json.dumps(rewritten_payload, ensure_ascii=True, separators=(",", ":"))
772
+ rewritten_event_block = format_sse_event(rewritten_payload)
773
+ rewritten_event = parse_sse_event_payload(rewritten_payload)
774
+ return (
775
+ rewritten_text,
776
+ rewritten_payload,
777
+ rewritten_event,
778
+ event_type_from_payload(rewritten_event, rewritten_payload),
779
+ rewritten_event_block,
780
+ )
781
+
782
+
783
+ def rewrite_parallel_tool_call_sse_line(
784
+ line: str,
785
+ payload: dict[str, JsonValue] | None,
786
+ *,
787
+ event: OpenAIEvent | None = None,
788
+ ) -> tuple[str, dict[str, JsonValue] | None, OpenAIEvent | None, str | None]:
789
+ rewritten_payload, changed, _removed_count = rewrite_parallel_tool_call_payload(payload)
790
+ if not changed:
791
+ # See rewrite_parallel_tool_call_text: no per-frame re-validation on
792
+ # the unchanged path; callers own lifecycle-gated validation.
793
+ return line, payload, event, event_type_from_payload(event, payload)
794
+ assert rewritten_payload is not None
795
+ rewritten_line = format_sse_event(rewritten_payload)
796
+ rewritten_event = parse_sse_event_payload(rewritten_payload)
797
+ return (
798
+ rewritten_line,
799
+ rewritten_payload,
800
+ rewritten_event,
801
+ event_type_from_payload(rewritten_event, rewritten_payload),
802
+ )