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.
- app/__init__.py +10 -0
- app/admin_cli.py +494 -0
- app/cli.py +339 -0
- app/codex_sessions_retag.py +487 -0
- app/core/__init__.py +0 -0
- app/core/audit/__init__.py +0 -0
- app/core/audit/service.py +216 -0
- app/core/audit/types.py +94 -0
- app/core/auth/__init__.py +236 -0
- app/core/auth/api_key_cache.py +65 -0
- app/core/auth/dashboard_access.py +429 -0
- app/core/auth/dashboard_mode.py +171 -0
- app/core/auth/dashboard_session_ttl.py +74 -0
- app/core/auth/dashboard_users_cache.py +120 -0
- app/core/auth/dependencies.py +710 -0
- app/core/auth/external_identity.py +78 -0
- app/core/auth/guardian.py +374 -0
- app/core/auth/models.py +49 -0
- app/core/auth/providers/__init__.py +129 -0
- app/core/auth/providers/oidc.py +838 -0
- app/core/auth/providers/registry.py +118 -0
- app/core/auth/refresh.py +394 -0
- app/core/auth/step_up.py +117 -0
- app/core/auth/totp.py +81 -0
- app/core/balancer/__init__.py +71 -0
- app/core/balancer/logic.py +1612 -0
- app/core/balancer/rendezvous_hash.py +21 -0
- app/core/balancer/types.py +21 -0
- app/core/bootstrap.py +172 -0
- app/core/cache/invalidation.py +426 -0
- app/core/clients/__init__.py +0 -0
- app/core/clients/codex.py +996 -0
- app/core/clients/codex_version.py +187 -0
- app/core/clients/codex_version_snapshot.py +2 -0
- app/core/clients/files.py +534 -0
- app/core/clients/headers.py +29 -0
- app/core/clients/http.py +511 -0
- app/core/clients/model_fetcher.py +276 -0
- app/core/clients/native_egress.py +1304 -0
- app/core/clients/oauth.py +477 -0
- app/core/clients/proxy.py +6089 -0
- app/core/clients/proxy_websocket.py +1411 -0
- app/core/clients/rate_limit_reset_credits.py +412 -0
- app/core/clients/stream_errors.py +12 -0
- app/core/clients/thread_cache_identity.py +347 -0
- app/core/clients/usage.py +467 -0
- app/core/clock.py +170 -0
- app/core/config/__init__.py +0 -0
- app/core/config/background_jobs.py +73 -0
- app/core/config/context_window_overrides.py +166 -0
- app/core/config/dashboard_overrides.py +145 -0
- app/core/config/inheritable.py +47 -0
- app/core/config/key_fingerprint.py +115 -0
- app/core/config/settings.py +872 -0
- app/core/config/settings_cache.py +100 -0
- app/core/config/spool_retention.py +217 -0
- app/core/config/tiers.py +174 -0
- app/core/conversation_archive.py +568 -0
- app/core/crypto.py +37 -0
- app/core/draining.py +11 -0
- app/core/errors.py +365 -0
- app/core/exceptions.py +155 -0
- app/core/handlers/__init__.py +3 -0
- app/core/handlers/exceptions.py +478 -0
- app/core/http_protocol.py +176 -0
- app/core/http_protocol_httptools.py +204 -0
- app/core/ingress_limits.py +17 -0
- app/core/metadata_store.py +18 -0
- app/core/metrics/__init__.py +0 -0
- app/core/metrics/middleware.py +74 -0
- app/core/metrics/prometheus.py +604 -0
- app/core/middleware/__init__.py +25 -0
- app/core/middleware/api_firewall.py +111 -0
- app/core/middleware/app_version.py +37 -0
- app/core/middleware/dashboard_auth_proxy.py +55 -0
- app/core/middleware/dashboard_csrf.py +177 -0
- app/core/middleware/dashboard_gzip.py +41 -0
- app/core/middleware/dashboard_overrides.py +59 -0
- app/core/middleware/firewall_cache.py +91 -0
- app/core/middleware/inflight.py +126 -0
- app/core/middleware/multipart_content_encoding.py +129 -0
- app/core/middleware/path_rewrite.py +148 -0
- app/core/middleware/request_body_limit.py +196 -0
- app/core/middleware/request_decompression.py +209 -0
- app/core/middleware/request_id.py +72 -0
- app/core/middleware/required_capability_http.py +116 -0
- app/core/middleware/trusted_proxy_headers.py +36 -0
- app/core/multipart.py +237 -0
- app/core/multipart_fields.py +91 -0
- app/core/openai/__init__.py +0 -0
- app/core/openai/chat_requests.py +493 -0
- app/core/openai/chat_responses.py +931 -0
- app/core/openai/contracts.py +46 -0
- app/core/openai/exceptions.py +24 -0
- app/core/openai/host_models.py +14 -0
- app/core/openai/images.py +413 -0
- app/core/openai/message_coercion.py +376 -0
- app/core/openai/model_refresh_scheduler.py +423 -0
- app/core/openai/model_registry.py +997 -0
- app/core/openai/model_registry_store.py +548 -0
- app/core/openai/models.py +172 -0
- app/core/openai/parsing.py +97 -0
- app/core/openai/requests.py +1980 -0
- app/core/openai/strict_schema.py +199 -0
- app/core/openai/tool_call_safety.py +99 -0
- app/core/openai/v1_requests.py +187 -0
- app/core/plan_types.py +90 -0
- app/core/prestop.py +202 -0
- app/core/rate_limiter/__init__.py +0 -0
- app/core/rate_limiter/db_rate_limiter.py +215 -0
- app/core/request_locality.py +473 -0
- app/core/resilience/__init__.py +0 -0
- app/core/resilience/backpressure.py +57 -0
- app/core/resilience/bulkhead.py +202 -0
- app/core/resilience/circuit_breaker.py +150 -0
- app/core/resilience/degradation.py +55 -0
- app/core/resilience/loop_lag_monitor.py +60 -0
- app/core/resilience/memory_monitor.py +137 -0
- app/core/resilience/network_recovery.py +256 -0
- app/core/resilience/overload.py +98 -0
- app/core/resilience/toggles.py +114 -0
- app/core/retention/__init__.py +0 -0
- app/core/retention/job.py +240 -0
- app/core/retention/scheduler.py +69 -0
- app/core/runtime_logging.py +601 -0
- app/core/scheduling/__init__.py +0 -0
- app/core/scheduling/leader_election.py +993 -0
- app/core/scheduling/leader_election_handle.py +25 -0
- app/core/server.py +140 -0
- app/core/shutdown.py +326 -0
- app/core/socket_peer.py +22 -0
- app/core/startup.py +41 -0
- app/core/timeout_invariants.py +347 -0
- app/core/tracing/__init__.py +0 -0
- app/core/tracing/otel.py +207 -0
- app/core/types.py +6 -0
- app/core/upstream_proxy/__init__.py +13 -0
- app/core/upstream_proxy/cache.py +130 -0
- app/core/upstream_proxy/resolver.py +189 -0
- app/core/upstream_proxy/types.py +104 -0
- app/core/usage/__init__.py +407 -0
- app/core/usage/depletion.py +109 -0
- app/core/usage/live_hub.py +41 -0
- app/core/usage/live_snapshots.py +174 -0
- app/core/usage/logs.py +212 -0
- app/core/usage/metadata_scheduler.py +118 -0
- app/core/usage/models.py +54 -0
- app/core/usage/pricing.py +576 -0
- app/core/usage/pricing_catalog.py +241 -0
- app/core/usage/pricing_snapshot.json +821 -0
- app/core/usage/quota.py +133 -0
- app/core/usage/refresh_policy.py +21 -0
- app/core/usage/refresh_scheduler.py +713 -0
- app/core/usage/reset_credits_refresh_scheduler.py +435 -0
- app/core/usage/types.py +168 -0
- app/core/utils/__init__.py +0 -0
- app/core/utils/json_guards.py +18 -0
- app/core/utils/locks.py +33 -0
- app/core/utils/masking.py +20 -0
- app/core/utils/proxy_env.py +104 -0
- app/core/utils/request_id.py +65 -0
- app/core/utils/retry.py +77 -0
- app/core/utils/shared_future.py +172 -0
- app/core/utils/sse.py +286 -0
- app/core/utils/time.py +24 -0
- app/db/__init__.py +0 -0
- app/db/account_identity_lock.py +56 -0
- app/db/alembic/__init__.py +1 -0
- app/db/alembic/env.py +70 -0
- app/db/alembic/revision_ids.py +43 -0
- app/db/alembic/versions/20260213_000000_base_schema.py +248 -0
- app/db/alembic/versions/20260213_000100_normalize_account_plan_types.py +45 -0
- app/db/alembic/versions/20260213_000200_add_request_logs_reasoning_effort.py +45 -0
- app/db/alembic/versions/20260213_000300_add_accounts_reset_at.py +45 -0
- app/db/alembic/versions/20260213_000400_add_accounts_chatgpt_account_id.py +45 -0
- app/db/alembic/versions/20260213_000500_add_dashboard_settings.py +70 -0
- app/db/alembic/versions/20260213_000600_add_dashboard_settings_totp.py +64 -0
- app/db/alembic/versions/20260213_000700_add_dashboard_settings_password.py +59 -0
- app/db/alembic/versions/20260213_000800_add_api_keys.py +87 -0
- app/db/alembic/versions/20260214_000000_add_api_key_limits.py +143 -0
- app/db/alembic/versions/20260215_000000_add_idx_logs_requested_at.py +38 -0
- app/db/alembic/versions/20260218_000000_add_api_key_usage_reservations.py +117 -0
- app/db/alembic/versions/20260218_000100_add_import_without_overwrite_and_drop_accounts_email_unique.py +198 -0
- app/db/alembic/versions/20260225_000000_add_dashboard_settings_routing_strategy.py +53 -0
- app/db/alembic/versions/20260228_020000_align_api_key_limit_enum_types.py +104 -0
- app/db/alembic/versions/20260228_030000_add_api_firewall_allowlist.py +42 -0
- app/db/alembic/versions/20260306_000000_add_request_logs_service_tier.py +45 -0
- app/db/alembic/versions/20260307_000000_add_api_key_enforcement_fields.py +56 -0
- app/db/alembic/versions/20260308_000000_add_sqlite_performance_indexes.py +41 -0
- app/db/alembic/versions/20260309_000000_add_additional_usage_history.py +71 -0
- app/db/alembic/versions/20260309_000000_request_logs_nullable_account_id.py +28 -0
- app/db/alembic/versions/20260310_000000_add_request_logs_transport.py +45 -0
- app/db/alembic/versions/20260310_000000_fix_postgresql_enum_value_casing.py +79 -0
- app/db/alembic/versions/20260310_120000_add_sticky_session_kinds_and_affinity_ttl.py +110 -0
- app/db/alembic/versions/20260312_000000_add_additional_usage_quota_key.py +193 -0
- app/db/alembic/versions/20260312_000000_split_sticky_sessions_primary_key_by_kind.py +96 -0
- app/db/alembic/versions/20260312_010000_merge_additional_usage_and_sticky_session_heads.py +25 -0
- app/db/alembic/versions/20260312_020000_merge_request_logs_transport_and_feature_heads.py +25 -0
- app/db/alembic/versions/20260312_020000_merge_request_logs_transport_and_sticky_heads.py +25 -0
- app/db/alembic/versions/20260312_120000_add_dashboard_upstream_stream_transport.py +62 -0
- app/db/alembic/versions/20260313_024500_merge_request_log_transport_heads.py +27 -0
- app/db/alembic/versions/20260319_100937_increase_prompt_cache_ttl_to_1800s.py +73 -0
- app/db/alembic/versions/20260319_111500_merge_prompt_cache_ttl_and_upstream_transport_heads.py +25 -0
- app/db/alembic/versions/20260319_183000_normalize_sqlite_account_status_casing.py +48 -0
- app/db/alembic/versions/20260320_000000_add_request_log_requested_actual_tiers.py +51 -0
- app/db/alembic/versions/20260321_190000_tighten_dashboard_db_indexes.py +64 -0
- app/db/alembic/versions/20260321_210000_merge_request_log_tiers_and_dashboard_index_heads.py +25 -0
- app/db/alembic/versions/20260325_000000_add_request_log_cost.py +193 -0
- app/db/alembic/versions/20260328_000000_add_rate_limit_attempts.py +48 -0
- app/db/alembic/versions/20260328_120000_add_request_log_ttft.py +37 -0
- app/db/alembic/versions/20260328_130000_add_audit_logs_table.py +45 -0
- app/db/alembic/versions/20260328_140000_add_scheduler_leader_table.py +39 -0
- app/db/alembic/versions/20260330_000000_add_cache_locality_settings.py +69 -0
- app/db/alembic/versions/20260330_010000_merge_scheduler_leader_and_cache_locality_heads.py +25 -0
- app/db/alembic/versions/20260330_020000_add_bridge_ring_members.py +46 -0
- app/db/alembic/versions/20260401_000000_add_api_key_enforced_service_tier.py +52 -0
- app/db/alembic/versions/20260401_000000_add_cache_invalidation.py +41 -0
- app/db/alembic/versions/20260401_020000_merge_cache_invalidation_and_api_key_service_tier_heads.py +22 -0
- app/db/alembic/versions/20260402_000000_switch_dashboard_routing_default_to_capacity_weighted.py +66 -0
- app/db/alembic/versions/20260403_000000_add_credit_api_key_limit_values.py +44 -0
- app/db/alembic/versions/20260406_000000_add_api_key_account_assignments.py +57 -0
- app/db/alembic/versions/20260406_010000_add_api_key_assignment_scope_flag.py +75 -0
- app/db/alembic/versions/20260407_000000_add_bridge_gateway_safe_mode.py +51 -0
- app/db/alembic/versions/20260407_010000_merge_api_key_assignment_and_bridge_gateway_heads.py +25 -0
- app/db/alembic/versions/20260408_000000_switch_import_without_overwrite_default_to_true.py +73 -0
- app/db/alembic/versions/20260408_010000_merge_import_without_overwrite_and_assignment_heads.py +25 -0
- app/db/alembic/versions/20260409_000000_add_http_bridge_sessions.py +130 -0
- app/db/alembic/versions/20260409_000000_switch_sticky_threads_and_prefer_earlier_reset_defaults_to_true.py +89 -0
- app/db/alembic/versions/20260409_010000_add_dashboard_settings_bootstrap_token.py +57 -0
- app/db/alembic/versions/20260409_010000_merge_http_bridge_and_import_without_overwrite_heads.py +25 -0
- app/db/alembic/versions/20260409_020000_fix_http_bridge_last_seen_index.py +46 -0
- app/db/alembic/versions/20260410_000000_backfill_pristine_dashboard_settings_defaults.py +77 -0
- app/db/alembic/versions/20260410_030000_restore_import_without_overwrite_default_true.py +73 -0
- app/db/alembic/versions/20260410_040000_merge_dashboard_defaults_and_import_default_heads.py +25 -0
- app/db/alembic/versions/20260413_000000_add_accounts_blocked_at.py +44 -0
- app/db/alembic/versions/20260415_160000_add_automations_tables.py +189 -0
- app/db/alembic/versions/20260415_160000_add_request_logs_response_lookup_index.py +67 -0
- app/db/alembic/versions/20260417_000000_add_request_log_plan_type.py +44 -0
- app/db/alembic/versions/20260418_190000_add_automation_runs_cycle_key.py +87 -0
- app/db/alembic/versions/20260419_000000_add_automation_run_cycle_metadata.py +56 -0
- app/db/alembic/versions/20260419_020000_add_automation_run_cycles_snapshot_tables.py +342 -0
- app/db/alembic/versions/20260421_000000_add_dashboard_session_ttl_setting.py +51 -0
- app/db/alembic/versions/20260421_120000_merge_request_log_lookup_and_plan_type_heads.py +25 -0
- app/db/alembic/versions/20260421_130000_merge_automation_and_request_log_heads.py +25 -0
- app/db/alembic/versions/20260422_103000_normalize_legacy_automation_run_cycle_keys.py +648 -0
- app/db/alembic/versions/20260423_120000_add_api_key_limit_reset_at_index.py +43 -0
- app/db/alembic/versions/20260424_000000_merge_dashboard_session_ttl_and_request_log_heads.py +25 -0
- app/db/alembic/versions/20260426_000000_add_dashboard_relative_availability_settings.py +68 -0
- app/db/alembic/versions/20260427_130000_add_warmup_model_and_request_kind.py +75 -0
- app/db/alembic/versions/20260506_000000_add_account_routing_policy.py +37 -0
- app/db/alembic/versions/20260509_000000_add_api_key_traffic_class.py +37 -0
- app/db/alembic/versions/20260509_010000_add_additional_quota_routing_policies.py +37 -0
- app/db/alembic/versions/20260513_000000_add_accounts_alias.py +44 -0
- app/db/alembic/versions/20260513_000000_add_api_key_apply_to_codex_model.py +59 -0
- app/db/alembic/versions/20260514_000000_add_request_logs_api_key_time_index.py +43 -0
- app/db/alembic/versions/20260515_000000_add_prefer_earlier_reset_window.py +47 -0
- app/db/alembic/versions/20260515_000000_soft_delete_request_logs_on_account_delete.py +83 -0
- app/db/alembic/versions/20260515_020000_add_split_sticky_budget_thresholds.py +62 -0
- app/db/alembic/versions/20260516_000000_add_sqlite_hot_path_indexes.py +75 -0
- app/db/alembic/versions/20260517_000000_merge_sqlite_hot_path_and_request_log_delete_heads.py +24 -0
- app/db/alembic/versions/20260518_000000_add_http_bridge_durable_input_prefix.py +44 -0
- app/db/alembic/versions/20260518_010000_merge_http_bridge_and_sqlite_recovery_heads.py +25 -0
- app/db/alembic/versions/20260518_120000_add_dashboard_guest_access.py +57 -0
- app/db/alembic/versions/20260520_000000_merge_api_key_and_http_bridge_heads.py +26 -0
- app/db/alembic/versions/20260520_010000_add_request_logs_api_key_account_index.py +45 -0
- app/db/alembic/versions/20260520_010000_merge_automation_and_api_key_heads.py +26 -0
- app/db/alembic/versions/20260520_030000_add_quota_planner.py +194 -0
- app/db/alembic/versions/20260520_040000_merge_quota_planner_and_request_log_index_heads.py +24 -0
- app/db/alembic/versions/20260521_000000_add_account_security_work_authorized.py +46 -0
- app/db/alembic/versions/20260521_000000_merge_dashboard_guest_and_api_key_heads.py +26 -0
- app/db/alembic/versions/20260521_000000_merge_prefer_reset_window_head.py +26 -0
- app/db/alembic/versions/20260521_000000_merge_warmup_and_request_log_index_heads.py +25 -0
- app/db/alembic/versions/20260522_000000_add_limit_warmup_trigger.py +195 -0
- app/db/alembic/versions/20260524_000000_merge_accounts_alias_and_reset_window_heads.py +24 -0
- app/db/alembic/versions/20260525_000000_add_usage_raw_window_latest_index.py +31 -0
- app/db/alembic/versions/20260526_000000_add_request_log_failure_metadata.py +100 -0
- app/db/alembic/versions/20260531_000000_add_account_workspace_identity.py +39 -0
- app/db/alembic/versions/20260531_000000_add_single_account_routing.py +42 -0
- app/db/alembic/versions/20260601_000000_merge_relative_availability_and_usage_raw_heads.py +24 -0
- app/db/alembic/versions/20260601_010000_add_api_key_usage_sections.py +58 -0
- app/db/alembic/versions/20260601_010000_add_routing_policy_persistence.py +67 -0
- app/db/alembic/versions/20260601_010000_merge_security_work_and_relative_availability_heads.py +25 -0
- app/db/alembic/versions/20260601_010000_merge_split_sticky_and_relative_usage_heads.py +25 -0
- app/db/alembic/versions/20260601_010000_merge_warmup_and_relative_availability_heads.py +25 -0
- app/db/alembic/versions/20260601_020000_merge_additional_quota_routing_and_relative_availability_heads.py +25 -0
- app/db/alembic/versions/20260601_020000_merge_quota_planner_and_relative_availability_heads.py +25 -0
- app/db/alembic/versions/20260601_020000_merge_reset_window_and_relative_availability_heads.py +25 -0
- app/db/alembic/versions/20260601_020000_merge_routing_policy_and_split_threshold_heads.py +24 -0
- app/db/alembic/versions/20260601_020000_merge_warmup_and_request_log_failure_heads.py +25 -0
- app/db/alembic/versions/20260601_030000_merge_single_account_and_routing_quota_heads.py +29 -0
- app/db/alembic/versions/20260601_040000_merge_security_work_and_routing_consolidation_heads.py +25 -0
- app/db/alembic/versions/20260601_050000_merge_quota_planner_and_routing_consolidation_heads.py +25 -0
- app/db/alembic/versions/20260602_050000_add_upstream_proxy_routing.py +164 -0
- app/db/alembic/versions/20260602_060000_merge_account_workspace_and_failure_heads.py +25 -0
- app/db/alembic/versions/20260602_070000_add_request_log_useragent_fields.py +86 -0
- app/db/alembic/versions/20260602_070000_merge_quota_planner_and_account_workspace_heads.py +25 -0
- app/db/alembic/versions/20260602_080000_merge_upstream_proxy_and_quota_planner_heads.py +25 -0
- app/db/alembic/versions/20260602_080000_merge_useragent_and_upstream_proxy_heads.py +24 -0
- app/db/alembic/versions/20260603_000000_add_weekly_pace_working_days.py +52 -0
- app/db/alembic/versions/20260603_000000_free_account_monthly_window.py +60 -0
- app/db/alembic/versions/20260604_000000_add_reauth_required_account_status.py +159 -0
- app/db/alembic/versions/20260604_010000_merge_automations_and_reauth_status_heads.py +25 -0
- app/db/alembic/versions/20260604_010000_merge_dashboard_guest_and_reauth_status_heads.py +25 -0
- app/db/alembic/versions/20260604_010000_merge_useragent_and_reauth_heads.py +24 -0
- app/db/alembic/versions/20260607_000000_merge_weekly_monthly_useragent_heads.py +28 -0
- app/db/alembic/versions/20260608_000000_add_hide_upstream_quota_from_api_keys.py +50 -0
- app/db/alembic/versions/20260611_000000_add_request_log_archive_request_id.py +47 -0
- app/db/alembic/versions/20260611_000000_merge_automations_and_weekly_monthly_useragent_heads.py +26 -0
- app/db/alembic/versions/20260611_000000_merge_dashboard_guest_and_weekly_useragent_heads.py +26 -0
- app/db/alembic/versions/20260611_010000_add_request_log_client_ip.py +57 -0
- app/db/alembic/versions/20260611_020000_merge_request_log_archive_and_client_ip_heads.py +26 -0
- app/db/alembic/versions/20260611_030000_merge_dashboard_guest_and_request_log_client_ip_heads.py +26 -0
- app/db/alembic/versions/20260613_000000_add_accounts_codex_installation_id.py +57 -0
- app/db/alembic/versions/20260613_010000_merge_installation_id_and_dashboard_weekly_heads.py +26 -0
- app/db/alembic/versions/20260615_000000_merge_automations_and_dashboard_guest_heads.py +26 -0
- app/db/alembic/versions/20260623_000000_add_limit_warmup_exhausted_threshold.py +51 -0
- app/db/alembic/versions/20260626_000000_add_http_downstream_transport_policy.py +46 -0
- app/db/alembic/versions/20260626_010000_add_request_logs_upstream_transport.py +31 -0
- app/db/alembic/versions/20260629_000000_add_dashboard_query_hot_path_indexes.py +53 -0
- app/db/alembic/versions/20260630_000000_add_limit_warmup_staggered_idle.py +47 -0
- app/db/alembic/versions/20260630_000000_merge_request_log_client_ip_and_dashboard_hot_path_heads.py +26 -0
- app/db/alembic/versions/20260630_010000_merge_warmup_and_request_log_dashboard_heads.py +26 -0
- app/db/alembic/versions/20260630_020000_merge_automations_and_main_heads.py +26 -0
- app/db/alembic/versions/20260630_020000_merge_warmup_threshold_and_main_heads.py +26 -0
- app/db/alembic/versions/20260630_030000_add_automation_run_model_snapshot.py +59 -0
- app/db/alembic/versions/20260630_040000_merge_automation_snapshot_and_warmup_threshold_heads.py +26 -0
- app/db/alembic/versions/20260630_050000_add_automation_run_prompt_snapshot.py +52 -0
- app/db/alembic/versions/20260701_000000_add_weekly_pace_smoothing_minutes.py +51 -0
- app/db/alembic/versions/20260702_000000_add_model_sources.py +176 -0
- app/db/alembic/versions/20260705_000000_add_model_source_audio_transcriptions.py +50 -0
- app/db/alembic/versions/20260705_000000_harden_dashboard_session_ttl.py +62 -0
- app/db/alembic/versions/20260706_000000_add_model_source_audio_per_minute.py +43 -0
- app/db/alembic/versions/20260707_000000_merge_model_sources_and_session_ttl.py +24 -0
- app/db/alembic/versions/20260707_010000_merge_automations_and_model_sources_heads.py +24 -0
- app/db/alembic/versions/20260707_020000_add_automation_job_account_scope.py +125 -0
- app/db/alembic/versions/20260709_000000_add_ttft_phase_observability.py +66 -0
- app/db/alembic/versions/20260711_000000_add_dashboard_prohibit_fast_mode.py +42 -0
- app/db/alembic/versions/20260711_010000_add_account_chatgpt_user_id.py +40 -0
- app/db/alembic/versions/20260711_020000_add_dashboard_account_concurrency_caps.py +71 -0
- app/db/alembic/versions/20260711_030000_add_limit_warmup_idle_threshold.py +51 -0
- app/db/alembic/versions/20260712_010000_add_account_usage_rollups.py +56 -0
- app/db/alembic/versions/20260712_020000_add_api_key_usage_rollups.py +53 -0
- app/db/alembic/versions/20260713_020000_add_model_registry_snapshot.py +42 -0
- app/db/alembic/versions/20260713_040000_add_account_refresh_claims.py +42 -0
- app/db/alembic/versions/20260713_040000_add_replica_guardrails.py +64 -0
- app/db/alembic/versions/20260713_070000_add_reset_credit_redeem_tables.py +58 -0
- app/db/alembic/versions/20260714_000000_add_oauth_flow_states.py +63 -0
- app/db/alembic/versions/20260715_000000_add_request_log_queue_latency.py +44 -0
- app/db/alembic/versions/20260716_000000_add_oauth_device_flow_slots.py +41 -0
- app/db/alembic/versions/20260716_010000_add_dashboard_retention_settings.py +48 -0
- app/db/alembic/versions/20260716_010000_add_reset_credit_display_settings.py +73 -0
- app/db/alembic/versions/20260717_000000_add_http_bridge_retry_circuits.py +47 -0
- app/db/alembic/versions/20260717_000000_merge_retention_and_reset_credit_display_heads.py +26 -0
- app/db/alembic/versions/20260717_000000_optimize_dashboard_hot_path_indexes.py +162 -0
- app/db/alembic/versions/20260717_000001_merge_retry_circuits_and_dashboard_indexes.py +22 -0
- app/db/alembic/versions/20260720_000000_add_request_log_conversation_id.py +57 -0
- app/db/alembic/versions/20260722_000000_backfill_request_log_useragent_families.py +40 -0
- app/db/alembic/versions/20260724_000000_add_request_usage_time_rollups.py +176 -0
- app/db/alembic/versions/20260724_000000_merge_request_log_schema_heads.py +33 -0
- app/db/alembic/versions/20260725_000000_add_http_bridge_pending_tool_calls.py +43 -0
- app/db/alembic/versions/20260726_000000_add_account_plan_downgrade_observations.py +44 -0
- app/db/alembic/versions/20260726_000000_repair_request_usage_rollups_after_merge.py +37 -0
- app/db/alembic/versions/20260727_000000_add_sticky_session_continuity_abandoned_at.py +45 -0
- app/db/alembic/versions/20260728_000000_merge_pending_tool_calls_and_rollup_repair_heads.py +31 -0
- app/db/alembic/versions/20260730_000000_add_api_key_fair_share_threshold.py +44 -0
- app/db/alembic/versions/20260730_000000_add_http_bridge_recovery_attempts.py +60 -0
- app/db/alembic/versions/20260731_000000_add_capability_lineage_markers.py +38 -0
- app/db/alembic/versions/20260803_000000_merge_http_bridge_recovery_and_capability_lineage_heads.py +30 -0
- app/db/alembic/versions/20260804_000000_add_http_bridge_operations.py +63 -0
- app/db/alembic/versions/20260804_000001_add_global_http_bridge_operation_fingerprint.py +53 -0
- app/db/alembic/versions/20260804_230000_add_request_log_connection_request_kind.py +43 -0
- app/db/alembic/versions/20260805_000000_add_http_bridge_operation_spool.py +75 -0
- app/db/alembic/versions/20260805_000001_finalize_http_bridge_operation_spool.py +65 -0
- app/db/alembic/versions/20260806_000000_add_additional_usage_alias_probe_indexes.py +84 -0
- app/db/alembic/versions/20260806_000000_add_anonymous_telemetry.py +49 -0
- app/db/alembic/versions/20260806_010000_add_conversation_presence_rollup.py +72 -0
- app/db/alembic/versions/20260806_020000_add_usage_history_bulk_covering_indexes.py +99 -0
- app/db/alembic/versions/20260806_030000_add_api_key_allowed_reasoning_efforts.py +65 -0
- app/db/alembic/versions/20260806_120000_add_http_bridge_owner_process_epoch.py +57 -0
- app/db/alembic/versions/20260807_000000_merge_http_bridge_operation_spool_and_latest_main.py +24 -0
- app/db/alembic/versions/20260808_000000_tune_usage_history_autovacuum.py +54 -0
- app/db/alembic/versions/20260810_000000_add_http_bridge_recovery_dispatch_count.py +44 -0
- app/db/alembic/versions/20260811_000000_add_hourly_rollup_cancelled_count.py +92 -0
- app/db/alembic/versions/20260812_000000_merge_recovery_dispatch_and_hourly_cancelled_heads.py +25 -0
- app/db/alembic/versions/20260812_120000_add_sticky_abandonment_scope.py +43 -0
- app/db/alembic/versions/20260813_000000_add_file_account_pins.py +40 -0
- app/db/alembic/versions/20260816_000000_add_account_pending_deletion.py +81 -0
- app/db/alembic/versions/20260816_000000_add_model_source_embeddings.py +50 -0
- app/db/alembic/versions/20260821_000000_add_retry_circuit_admission_generation.py +49 -0
- app/db/alembic/versions/20260826_000000_add_http_bridge_event_chunks.py +107 -0
- app/db/alembic/versions/20260828_000000_add_accounts_chatgpt_identity_index.py +55 -0
- app/db/alembic/versions/20260830_000000_add_quota_warmup_claim_expiry.py +83 -0
- app/db/alembic/versions/20260908_000000_add_guest_session_generation.py +37 -0
- app/db/alembic/versions/20260908_000000_add_subscription_overflow.py +140 -0
- app/db/alembic/versions/20260908_000000_replace_upstream_stream_transport_default_sentinel.py +50 -0
- app/db/alembic/versions/20260908_020000_merge_overflow_transport_heads.py +27 -0
- app/db/alembic/versions/20260909_000000_add_dashboard_roles.py +72 -0
- app/db/alembic/versions/20260909_010000_add_dashboard_users.py +183 -0
- app/db/alembic/versions/20260909_020000_reproject_compat_admin_credentials.py +105 -0
- app/db/alembic/versions/20260909_030000_add_audit_actor_columns.py +103 -0
- app/db/alembic/versions/20260909_030000_dashboard_resilience_toggle_settings.py +63 -0
- app/db/alembic/versions/20260909_040000_add_dashboard_user_invites.py +54 -0
- app/db/alembic/versions/20260909_040000_dashboard_timeout_settings.py +68 -0
- app/db/alembic/versions/20260909_050000_dashboard_routing_overload_settings.py +65 -0
- app/db/alembic/versions/20260909_060000_add_report_rollup.py +74 -0
- app/db/alembic/versions/20260909_070000_automation_run_claim_budget.py +50 -0
- app/db/alembic/versions/20260909_080000_dashboard_stream_bridge_budgets.py +62 -0
- app/db/alembic/versions/20260909_090000_dashboard_background_job_toggles.py +63 -0
- app/db/alembic/versions/20260909_100000_dashboard_codex_prewarm.py +49 -0
- app/db/alembic/versions/20260909_110000_model_context_window_overrides.py +45 -0
- app/db/alembic/versions/20260909_120000_dashboard_conversation_archive.py +48 -0
- app/db/alembic/versions/20260909_130000_add_request_logs_live_facet_indexes.py +96 -0
- app/db/alembic/versions/20260910_000000_add_totp_required_for_admin_role.py +44 -0
- app/db/alembic/versions/20260910_000000_request_logs_missing_cost_index.py +52 -0
- app/db/alembic/versions/20260910_010000_add_dashboard_auth_providers.py +134 -0
- app/db/alembic/versions/20260910_010000_dashboard_spool_retention.py +52 -0
- app/db/alembic/versions/20260910_020000_add_dashboard_role_mappings.py +74 -0
- app/db/alembic/versions/20260910_143000_request_log_affinity.py +28 -0
- app/db/alembic/versions/20260910_180000_merge_affinity_guest_heads.py +17 -0
- app/db/alembic/versions/20260910_200000_merge_affinity_identity_heads.py +17 -0
- app/db/alembic/versions/20260910_220000_merge_affinity_invite_heads.py +17 -0
- app/db/alembic/versions/20260911_000000_model_source_pins_kind_expires_index.py +43 -0
- app/db/alembic/versions/20260911_010000_merge_pin_index_and_affinity_heads.py +17 -0
- app/db/alembic/versions/20260911_020000_add_http_bridge_terminal_append_phase.py +54 -0
- app/db/alembic/versions/20260911_030000_add_local_login_policy.py +48 -0
- app/db/alembic/versions/20260911_040000_add_thread_cache_identity_mode.py +48 -0
- app/db/alembic/versions/20260911_060000_add_bridge_session_continuity_abandonment.py +40 -0
- app/db/alembic/versions/20260912_000000_merge_thread_cache_and_bridge_retirement_heads.py +22 -0
- app/db/alembic/versions/20260912_010000_drop_legacy_dashboard_credentials.py +140 -0
- app/db/alembic/versions/20260913_000000_add_oidc_provider_flow.py +134 -0
- app/db/alembic/versions/20260914_000000_add_scim_tokens.py +78 -0
- app/db/alembic/versions/20260914_000000_drop_subscription_overflow_schema.py +132 -0
- app/db/alembic/versions/20260918_000000_merge_scim_and_overflow_heads.py +17 -0
- app/db/alembic/versions/__init__.py +1 -0
- app/db/backup.py +64 -0
- app/db/migrate.py +1249 -0
- app/db/migration_lock.py +160 -0
- app/db/migration_url.py +18 -0
- app/db/models.py +3100 -0
- app/db/recover.py +244 -0
- app/db/session.py +1295 -0
- app/db/snapshot.py +46 -0
- app/db/sqlite_lock_retry.py +190 -0
- app/db/sqlite_utils.py +549 -0
- app/dependencies.py +462 -0
- app/main.py +1240 -0
- app/modules/__init__.py +0 -0
- app/modules/accounts/__init__.py +0 -0
- app/modules/accounts/api.py +426 -0
- app/modules/accounts/auth_manager.py +1439 -0
- app/modules/accounts/background_repository.py +160 -0
- app/modules/accounts/deletion.py +540 -0
- app/modules/accounts/mappers.py +654 -0
- app/modules/accounts/refresh_claims.py +352 -0
- app/modules/accounts/repository.py +1679 -0
- app/modules/accounts/schemas.py +308 -0
- app/modules/accounts/service.py +809 -0
- app/modules/accounts/usage_rollup.py +486 -0
- app/modules/accounts/usage_rollup_scheduler.py +75 -0
- app/modules/accounts/usage_time_rollup.py +1037 -0
- app/modules/accounts/usage_time_rollup_read.py +522 -0
- app/modules/api_keys/__init__.py +1 -0
- app/modules/api_keys/api.py +344 -0
- app/modules/api_keys/last_used_coalescer.py +216 -0
- app/modules/api_keys/limit_windows.py +41 -0
- app/modules/api_keys/repository.py +1200 -0
- app/modules/api_keys/reset_scheduler.py +85 -0
- app/modules/api_keys/schemas.py +135 -0
- app/modules/api_keys/service.py +2123 -0
- app/modules/audit/__init__.py +0 -0
- app/modules/audit/api.py +88 -0
- app/modules/audit/repository.py +74 -0
- app/modules/audit/schemas.py +33 -0
- app/modules/audit/service.py +71 -0
- app/modules/auth_providers/__init__.py +0 -0
- app/modules/auth_providers/api.py +127 -0
- app/modules/auth_providers/config.py +343 -0
- app/modules/auth_providers/repository.py +101 -0
- app/modules/auth_providers/schemas.py +88 -0
- app/modules/auth_providers/seed.py +105 -0
- app/modules/auth_providers/service.py +289 -0
- app/modules/automations/__init__.py +1 -0
- app/modules/automations/api.py +369 -0
- app/modules/automations/repository.py +1896 -0
- app/modules/automations/scheduler.py +107 -0
- app/modules/automations/schemas.py +158 -0
- app/modules/automations/service.py +2548 -0
- app/modules/cache_isolation_probe/__init__.py +0 -0
- app/modules/cache_isolation_probe/api.py +188 -0
- app/modules/cache_isolation_probe/prefix.py +144 -0
- app/modules/cache_isolation_probe/schemas.py +94 -0
- app/modules/cache_isolation_probe/sender.py +229 -0
- app/modules/cache_isolation_probe/service.py +525 -0
- app/modules/conversation_archive/__init__.py +1 -0
- app/modules/conversation_archive/api.py +101 -0
- app/modules/conversation_archive/schemas.py +28 -0
- app/modules/conversation_archive/service.py +165 -0
- app/modules/dashboard/__init__.py +1 -0
- app/modules/dashboard/api.py +106 -0
- app/modules/dashboard/builders.py +107 -0
- app/modules/dashboard/repository.py +304 -0
- app/modules/dashboard/schemas.py +148 -0
- app/modules/dashboard/service.py +534 -0
- app/modules/dashboard/timeframes.py +19 -0
- app/modules/dashboard/weekly_pace.py +617 -0
- app/modules/dashboard_auth/__init__.py +1 -0
- app/modules/dashboard_auth/api.py +1344 -0
- app/modules/dashboard_auth/oidc_api.py +753 -0
- app/modules/dashboard_auth/oidc_flows.py +330 -0
- app/modules/dashboard_auth/repository.py +452 -0
- app/modules/dashboard_auth/schemas.py +232 -0
- app/modules/dashboard_auth/service.py +1384 -0
- app/modules/dashboard_roles/__init__.py +0 -0
- app/modules/dashboard_roles/api.py +64 -0
- app/modules/dashboard_roles/repository.py +54 -0
- app/modules/dashboard_roles/schemas.py +29 -0
- app/modules/dashboard_roles/seed.py +80 -0
- app/modules/dashboard_roles/service.py +142 -0
- app/modules/dashboard_users/__init__.py +0 -0
- app/modules/dashboard_users/api.py +278 -0
- app/modules/dashboard_users/break_glass.py +169 -0
- app/modules/dashboard_users/credentials.py +18 -0
- app/modules/dashboard_users/identity_resolver.py +665 -0
- app/modules/dashboard_users/repository.py +725 -0
- app/modules/dashboard_users/schemas.py +117 -0
- app/modules/dashboard_users/service.py +1054 -0
- app/modules/firewall/__init__.py +1 -0
- app/modules/firewall/api.py +78 -0
- app/modules/firewall/repository.py +51 -0
- app/modules/firewall/schemas.py +28 -0
- app/modules/firewall/service.py +93 -0
- app/modules/fleet/__init__.py +1 -0
- app/modules/fleet/api.py +177 -0
- app/modules/fleet/mappers.py +56 -0
- app/modules/fleet/observability.py +372 -0
- app/modules/fleet/schemas.py +119 -0
- app/modules/health/__init__.py +0 -0
- app/modules/health/api.py +246 -0
- app/modules/health/schemas.py +27 -0
- app/modules/limit_warmup/__init__.py +1 -0
- app/modules/limit_warmup/repository.py +159 -0
- app/modules/limit_warmup/service.py +1001 -0
- app/modules/model_sources/__init__.py +1 -0
- app/modules/model_sources/api.py +99 -0
- app/modules/model_sources/catalog.py +307 -0
- app/modules/model_sources/forwarding.py +1435 -0
- app/modules/model_sources/projection.py +55 -0
- app/modules/model_sources/repository.py +201 -0
- app/modules/model_sources/schemas.py +79 -0
- app/modules/model_sources/selection.py +166 -0
- app/modules/model_sources/service.py +236 -0
- app/modules/oauth/__init__.py +0 -0
- app/modules/oauth/api.py +101 -0
- app/modules/oauth/repository.py +238 -0
- app/modules/oauth/schemas.py +49 -0
- app/modules/oauth/service.py +1196 -0
- app/modules/oauth/templates/oauth_success.html +122 -0
- app/modules/proxy/__init__.py +0 -0
- app/modules/proxy/_load_balancer/__init__.py +1 -0
- app/modules/proxy/_load_balancer/error_rate.py +116 -0
- app/modules/proxy/_load_balancer/latency_cohort.py +154 -0
- app/modules/proxy/_load_balancer/model_eligibility.py +250 -0
- app/modules/proxy/_load_balancer/opportunistic_admission.py +350 -0
- app/modules/proxy/_load_balancer/overload_backoff.py +394 -0
- app/modules/proxy/_load_balancer/sticky_selection.py +2417 -0
- app/modules/proxy/_load_balancer/throughput_cohort.py +233 -0
- app/modules/proxy/_load_balancer/ttft_cohort.py +214 -0
- app/modules/proxy/_load_balancer/tunables.py +97 -0
- app/modules/proxy/_load_balancer/types.py +96 -0
- app/modules/proxy/_load_balancer/unbound_selection.py +534 -0
- app/modules/proxy/_service/__init__.py +6 -0
- app/modules/proxy/_service/api_key_usage.py +745 -0
- app/modules/proxy/_service/clock_budget.py +38 -0
- app/modules/proxy/_service/codex_control.py +611 -0
- app/modules/proxy/_service/compact.py +2211 -0
- app/modules/proxy/_service/file_ops.py +749 -0
- app/modules/proxy/_service/http_bridge/__init__.py +3 -0
- app/modules/proxy/_service/http_bridge/accepted_replay.py +331 -0
- app/modules/proxy/_service/http_bridge/account_sessions.py +57 -0
- app/modules/proxy/_service/http_bridge/activity.py +178 -0
- app/modules/proxy/_service/http_bridge/helpers.py +3959 -0
- app/modules/proxy/_service/http_bridge/mixin.py +2435 -0
- app/modules/proxy/_service/http_bridge/owner_forwarding.py +609 -0
- app/modules/proxy/_service/http_bridge/protocol.py +167 -0
- app/modules/proxy/_service/http_bridge/proxy_failover.py +58 -0
- app/modules/proxy/_service/http_bridge/quarantine.py +443 -0
- app/modules/proxy/_service/http_bridge/request_submit.py +4582 -0
- app/modules/proxy/_service/http_bridge/retry_circuit.py +2442 -0
- app/modules/proxy/_service/http_bridge/service_stubs.py +492 -0
- app/modules/proxy/_service/http_bridge/session_registry.py +778 -0
- app/modules/proxy/_service/http_bridge/streaming.py +5328 -0
- app/modules/proxy/_service/http_bridge/upstream_events.py +4538 -0
- app/modules/proxy/_service/observability.py +410 -0
- app/modules/proxy/_service/rate_limit.py +361 -0
- app/modules/proxy/_service/realtime_live.py +655 -0
- app/modules/proxy/_service/refresh.py +154 -0
- app/modules/proxy/_service/request_log.py +585 -0
- app/modules/proxy/_service/response_create.py +930 -0
- app/modules/proxy/_service/streaming/__init__.py +3 -0
- app/modules/proxy/_service/streaming/helpers.py +1317 -0
- app/modules/proxy/_service/streaming/mixin.py +1100 -0
- app/modules/proxy/_service/streaming/protocol.py +32 -0
- app/modules/proxy/_service/streaming/retry.py +3762 -0
- app/modules/proxy/_service/support.py +2122 -0
- app/modules/proxy/_service/transcribe.py +434 -0
- app/modules/proxy/_service/warmup.py +483 -0
- app/modules/proxy/_service/websocket/__init__.py +3 -0
- app/modules/proxy/_service/websocket/helpers.py +2360 -0
- app/modules/proxy/_service/websocket/mixin.py +7285 -0
- app/modules/proxy/_service/websocket/protocol.py +74 -0
- app/modules/proxy/account_cache.py +229 -0
- app/modules/proxy/account_eligibility.py +87 -0
- app/modules/proxy/additional_model_limits.py +40 -0
- app/modules/proxy/affinity.py +830 -0
- app/modules/proxy/affinity_observation.py +101 -0
- app/modules/proxy/api.py +10254 -0
- app/modules/proxy/api_key_usage.py +113 -0
- app/modules/proxy/cap_partitioning.py +280 -0
- app/modules/proxy/capability_lineage.py +65 -0
- app/modules/proxy/capability_lineage_repository.py +82 -0
- app/modules/proxy/capability_routing.py +253 -0
- app/modules/proxy/continuity.py +112 -0
- app/modules/proxy/downstream_delivery.py +277 -0
- app/modules/proxy/durable_bridge_coordinator.py +1217 -0
- app/modules/proxy/durable_bridge_repository.py +4516 -0
- app/modules/proxy/durable_bridge_runtime.py +9 -0
- app/modules/proxy/durable_bridge_transcript_codec.py +122 -0
- app/modules/proxy/fair_share.py +131 -0
- app/modules/proxy/file_pin_repository.py +210 -0
- app/modules/proxy/helpers.py +439 -0
- app/modules/proxy/http_bridge_event_batcher.py +613 -0
- app/modules/proxy/http_bridge_forwarding.py +797 -0
- app/modules/proxy/http_continuation.py +76 -0
- app/modules/proxy/images_observability.py +62 -0
- app/modules/proxy/images_service.py +1054 -0
- app/modules/proxy/load_balancer.py +3016 -0
- app/modules/proxy/rate_limit_cache.py +46 -0
- app/modules/proxy/replay_safety.py +1063 -0
- app/modules/proxy/repo_bundle.py +31 -0
- app/modules/proxy/request_policy.py +1054 -0
- app/modules/proxy/ring_membership.py +251 -0
- app/modules/proxy/schemas.py +345 -0
- app/modules/proxy/selection_errors.py +41 -0
- app/modules/proxy/service.py +2600 -0
- app/modules/proxy/source_admission.py +125 -0
- app/modules/proxy/source_dispatch.py +999 -0
- app/modules/proxy/sticky_repository.py +831 -0
- app/modules/proxy/thread_anchors.py +491 -0
- app/modules/proxy/tool_call_dedupe.py +802 -0
- app/modules/proxy/types.py +54 -0
- app/modules/proxy/work_admission.py +121 -0
- app/modules/quota_planner/__init__.py +1 -0
- app/modules/quota_planner/api.py +276 -0
- app/modules/quota_planner/logic.py +743 -0
- app/modules/quota_planner/repository.py +722 -0
- app/modules/quota_planner/scheduler.py +212 -0
- app/modules/quota_planner/schemas.py +102 -0
- app/modules/quota_planner/warmup.py +693 -0
- app/modules/rate_limit_reset_credits/__init__.py +0 -0
- app/modules/rate_limit_reset_credits/api.py +601 -0
- app/modules/rate_limit_reset_credits/redeem_coordination.py +274 -0
- app/modules/rate_limit_reset_credits/store.py +114 -0
- app/modules/reports/__init__.py +1 -0
- app/modules/reports/api.py +134 -0
- app/modules/reports/cache.py +74 -0
- app/modules/reports/filters.py +27 -0
- app/modules/reports/repository.py +525 -0
- app/modules/reports/rollup.py +150 -0
- app/modules/reports/rollup_read.py +148 -0
- app/modules/reports/schemas.py +123 -0
- app/modules/reports/service.py +287 -0
- app/modules/reports/thread_identity.py +256 -0
- app/modules/request_logs/__init__.py +0 -0
- app/modules/request_logs/api.py +224 -0
- app/modules/request_logs/cost_backfill.py +154 -0
- app/modules/request_logs/mappers.py +97 -0
- app/modules/request_logs/repository.py +1765 -0
- app/modules/request_logs/schemas.py +145 -0
- app/modules/request_logs/service.py +348 -0
- app/modules/role_mappings/__init__.py +0 -0
- app/modules/role_mappings/api.py +206 -0
- app/modules/role_mappings/matching.py +55 -0
- app/modules/role_mappings/repository.py +106 -0
- app/modules/role_mappings/schemas.py +67 -0
- app/modules/role_mappings/service.py +334 -0
- app/modules/runtime/__init__.py +1 -0
- app/modules/runtime/api.py +18 -0
- app/modules/runtime/schemas.py +14 -0
- app/modules/runtime/service.py +218 -0
- app/modules/scim/__init__.py +1 -0
- app/modules/scim/api.py +194 -0
- app/modules/scim/dependencies.py +112 -0
- app/modules/scim/errors.py +77 -0
- app/modules/scim/management_api.py +156 -0
- app/modules/scim/repository.py +212 -0
- app/modules/scim/schemas.py +145 -0
- app/modules/scim/service.py +786 -0
- app/modules/scim/tokens.py +43 -0
- app/modules/settings/__init__.py +1 -0
- app/modules/settings/api.py +1677 -0
- app/modules/settings/repository.py +606 -0
- app/modules/settings/schemas.py +496 -0
- app/modules/settings/service.py +769 -0
- app/modules/shared/__init__.py +0 -0
- app/modules/shared/schemas.py +22 -0
- app/modules/sticky_sessions/__init__.py +1 -0
- app/modules/sticky_sessions/api.py +118 -0
- app/modules/sticky_sessions/cleanup_scheduler.py +487 -0
- app/modules/sticky_sessions/schemas.py +78 -0
- app/modules/sticky_sessions/service.py +196 -0
- app/modules/telemetry/__init__.py +5 -0
- app/modules/telemetry/api.py +115 -0
- app/modules/telemetry/clients.py +62 -0
- app/modules/telemetry/consent.py +115 -0
- app/modules/telemetry/scheduler.py +85 -0
- app/modules/telemetry/schemas.py +163 -0
- app/modules/telemetry/sender.py +221 -0
- app/modules/telemetry/snapshot.py +490 -0
- app/modules/usage/__init__.py +0 -0
- app/modules/usage/additional_quota_keys.py +379 -0
- app/modules/usage/api.py +45 -0
- app/modules/usage/background_repository.py +147 -0
- app/modules/usage/builders.py +559 -0
- app/modules/usage/depletion_service.py +277 -0
- app/modules/usage/live_ingest.py +356 -0
- app/modules/usage/mappers.py +29 -0
- app/modules/usage/plan_downgrade_observations.py +488 -0
- app/modules/usage/repository.py +1890 -0
- app/modules/usage/schemas.py +68 -0
- app/modules/usage/service.py +102 -0
- app/modules/usage/updater.py +1488 -0
- app/static/assets/Trans-Cj-J9mVA.js +1 -0
- app/static/assets/access-page-DHzZiXZi.js +1 -0
- app/static/assets/account-cost-donut-DGhfA4U8.js +1 -0
- app/static/assets/account-identifiers-CxbdLshZ.js +1 -0
- app/static/assets/account-status-DIsU5sUS.js +1 -0
- app/static/assets/account-trend-chart-BptlfwwQ.js +1 -0
- app/static/assets/accounts-page-Bbai8ift.js +3 -0
- app/static/assets/api-Dwa7XxPe.js +1 -0
- app/static/assets/api-client-dugX1Pti.js +55 -0
- app/static/assets/api-key-create-dialog-DnRm0oYN.js +1 -0
- app/static/assets/api-key-created-dialog-B6jkDG9f.js +1 -0
- app/static/assets/api-key-edit-dialog-Ck3Bnc3V.js +1 -0
- app/static/assets/api-trend-chart-BchCw7Hj.js +1 -0
- app/static/assets/apis-page-BvXz9c6i.js +2 -0
- app/static/assets/arrow-up-Dqh_KzJo.js +1 -0
- app/static/assets/arrow-up-down-LBRgVDGY.js +1 -0
- app/static/assets/automations-page-DQgNAJOR.js +3 -0
- app/static/assets/badge-DqMb9es0.js +1 -0
- app/static/assets/blur-email-HEvKagG2.js +1 -0
- app/static/assets/button-BjVmmiGW.js +1 -0
- app/static/assets/checkbox-apsl9vzk.js +1 -0
- app/static/assets/chevron-left-1d_pHVa5.js +1 -0
- app/static/assets/circle-alert-BS0xd_o1.js +1 -0
- app/static/assets/clsx-DB0hHKMi.js +1 -0
- app/static/assets/colors-BBy8GEtr.js +1 -0
- app/static/assets/copy-button-D7nAvOKR.js +1 -0
- app/static/assets/cost-per-day-chart-YdqlkZVM.js +1 -0
- app/static/assets/createLucideIcon-DBLzLNh2.js +1 -0
- app/static/assets/dashboard-page-CSdhBy0f.js +2 -0
- app/static/assets/distribution-metric-format-yZTy1ZEu.js +1 -0
- app/static/assets/donut-chart-Bxc69cra.js +1 -0
- app/static/assets/download-kQteIcsB.js +1 -0
- app/static/assets/ellipsis-B_5OgIre.js +1 -0
- app/static/assets/empty-state-JcA7tc8m.js +1 -0
- app/static/assets/es2015-bMH8YKZT.js +41 -0
- app/static/assets/es6-B264ni6W.js +69 -0
- app/static/assets/external-link-UVAOgMQ0.js +1 -0
- app/static/assets/i18n-BWqZ9Ua2.js +1 -0
- app/static/assets/inbox-B_RM2s1J.js +1 -0
- app/static/assets/index-DIpd264Z.js +23 -0
- app/static/assets/index-DMeLKRJx.css +2 -0
- app/static/assets/inherit-badge-QhTYVaU6.js +1 -0
- app/static/assets/invite-dialog-0CYh0GiQ.js +1 -0
- app/static/assets/iso-Bpe5Ihbb.js +1 -0
- app/static/assets/label-CGaLmLNf.js +1 -0
- app/static/assets/lazy-recharts-Pv8p9ArM.js +2 -0
- app/static/assets/loading-overlay-CzKQfHLI.js +1 -0
- app/static/assets/mini-quota-bar-cGT_t8Ae.js +1 -0
- app/static/assets/model-distribution-donut-BKjAfT48.js +1 -0
- app/static/assets/model-source-multi-select-D2d06Vc7.js +1 -0
- app/static/assets/multi-select-filter-KIA5qU6Z.js +1 -0
- app/static/assets/oauth-dialog-XeaGGDjS.js +1 -0
- app/static/assets/pagination-controls-D1Ypd0RU.js +1 -0
- app/static/assets/payload-CIYnGZz9.js +1 -0
- app/static/assets/queue-wait-chart-VoV7Yg9L.js +1 -0
- app/static/assets/report-chart-card-BXDliwcc.js +1 -0
- app/static/assets/reports-page-CGCJeMHG.js +3 -0
- app/static/assets/reset-credit-confirm-dialog-QOifTnVB.js +1 -0
- app/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- app/static/assets/search-HfhqwERt.js +1 -0
- app/static/assets/select-Cl33bz-g.js +1 -0
- app/static/assets/settings-page-mZj01HYl.js +2 -0
- app/static/assets/shim-CZwyM4Q7.js +1 -0
- app/static/assets/skeleton-C_EUc4Rp.js +1 -0
- app/static/assets/sparkline-chart-DdNNsddM.js +1 -0
- app/static/assets/status-badge-CHHA5X6c.js +1 -0
- app/static/assets/switch-CzxgokcM.js +1 -0
- app/static/assets/table-DXaNSQ0G.js +1 -0
- app/static/assets/time-to-first-token-chart--68L0u_v.js +1 -0
- app/static/assets/tokens-per-day-chart-Cs82BFuE.js +1 -0
- app/static/assets/tokens-per-second-chart-AD2dfOn6.js +1 -0
- app/static/assets/totp-settings-CRHtxQ7j.js +1 -0
- app/static/assets/trash-D3KyWr1h.js +1 -0
- app/static/assets/use-account-quota-display-BPmrTBTC.js +1 -0
- app/static/assets/use-accounts-PWlVGw3z.js +1 -0
- app/static/assets/use-auth-CuDlW3iC.js +1 -0
- app/static/assets/use-chart-colors-DIu6tClI.js +1 -0
- app/static/assets/use-model-sources-Bul24JBu.js +1 -0
- app/static/assets/use-models-DUCPUuZg.js +1 -0
- app/static/assets/use-reduced-motion-hAm5r6_d.js +1 -0
- app/static/assets/useTranslation--56nHhlH.js +1 -0
- app/static/assets/useragent-distribution-donut-Dup6sf_n.js +1 -0
- app/static/assets/users-uIC3TIlE.js +1 -0
- app/static/assets/vendor-query-_bHZuxEx.js +1 -0
- app/static/assets/vendor-react-BVL0YZPW.js +9 -0
- app/static/favicon.svg +10 -0
- app/static/fonts/GeistSans-Variable.woff2 +0 -0
- app/static/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
- app/static/fonts/jetbrains-mono-latin.woff2 +0 -0
- app/static/index.html +29 -0
- codexlb_up-1.25.0b10.dist-info/METADATA +124 -0
- codexlb_up-1.25.0b10.dist-info/RECORD +827 -0
- codexlb_up-1.25.0b10.dist-info/WHEEL +4 -0
- codexlb_up-1.25.0b10.dist-info/entry_points.txt +3 -0
- codexlb_up-1.25.0b10.dist-info/licenses/LICENSE +21 -0
- config/additional_quota_registry.json +11 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Lazy handle onto the process-wide scheduler leader election.
|
|
2
|
+
|
|
3
|
+
Background schedulers resolve the election at call time instead of importing
|
|
4
|
+
:mod:`app.core.scheduling.leader_election` at module load, so importing a
|
|
5
|
+
scheduler never drags in the election's database session dependencies. Every
|
|
6
|
+
scheduler binds :func:`get_leader_election` to a module global named
|
|
7
|
+
``_get_leader_election`` so tests can swap in a fake leader per module.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import importlib
|
|
13
|
+
from collections.abc import Awaitable, Callable
|
|
14
|
+
from typing import Protocol, TypeVar, cast
|
|
15
|
+
|
|
16
|
+
_T = TypeVar("_T")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LeaderElectionLike(Protocol):
|
|
20
|
+
async def run_if_leader(self, fn: Callable[[], Awaitable[_T]]) -> _T | None: ...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_leader_election() -> LeaderElectionLike:
|
|
24
|
+
module = importlib.import_module("app.core.scheduling.leader_election")
|
|
25
|
+
return cast(LeaderElectionLike, module.get_leader_election())
|
app/core/server.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import signal
|
|
7
|
+
import socket
|
|
8
|
+
from collections.abc import Iterator
|
|
9
|
+
from contextlib import contextmanager
|
|
10
|
+
from types import FrameType
|
|
11
|
+
from typing import NoReturn
|
|
12
|
+
|
|
13
|
+
import uvicorn
|
|
14
|
+
|
|
15
|
+
from app.core import shutdown as shutdown_state
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("uvicorn.error")
|
|
18
|
+
|
|
19
|
+
POST_DRAIN_CLEANUP_TIMEOUT_SECONDS = 25.0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _force_process_exit(signum: int) -> NoReturn:
|
|
23
|
+
try:
|
|
24
|
+
signal.signal(signum, signal.SIG_DFL)
|
|
25
|
+
signal.raise_signal(signum)
|
|
26
|
+
finally:
|
|
27
|
+
os._exit(128 + signum)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _finish_abandoned_uvicorn_shutdown(
|
|
31
|
+
task: asyncio.Task[None],
|
|
32
|
+
*,
|
|
33
|
+
config: uvicorn.Config,
|
|
34
|
+
original_timeout: float | None,
|
|
35
|
+
) -> None:
|
|
36
|
+
setattr(config, "timeout_graceful_shutdown", original_timeout)
|
|
37
|
+
if task.cancelled():
|
|
38
|
+
return
|
|
39
|
+
exc = task.exception()
|
|
40
|
+
if exc is not None:
|
|
41
|
+
logger.warning("Abandoned Uvicorn shutdown finished with error", exc_info=exc)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SignalNeutralServer(uvicorn.Server):
|
|
45
|
+
"""Embedded server that leaves process signal ownership to its parent."""
|
|
46
|
+
|
|
47
|
+
@contextmanager
|
|
48
|
+
def capture_signals(self) -> Iterator[None]:
|
|
49
|
+
yield
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class GracefulDrainServer(uvicorn.Server):
|
|
53
|
+
"""Uvicorn server that drains application work before closing connections."""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
config: uvicorn.Config,
|
|
58
|
+
*,
|
|
59
|
+
drain_timeout_seconds: float,
|
|
60
|
+
post_drain_cleanup_timeout_seconds: float = POST_DRAIN_CLEANUP_TIMEOUT_SECONDS,
|
|
61
|
+
) -> None:
|
|
62
|
+
super().__init__(config)
|
|
63
|
+
self._drain_timeout_seconds = max(float(drain_timeout_seconds), 0.0)
|
|
64
|
+
self._post_drain_cleanup_timeout_seconds = max(float(post_drain_cleanup_timeout_seconds), 0.0)
|
|
65
|
+
|
|
66
|
+
async def serve(self, sockets: list[socket.socket] | None = None) -> None:
|
|
67
|
+
# Reset process-global state before Uvicorn installs its signal
|
|
68
|
+
# handlers. Lifespan startup is too late: SIGTERM may already have
|
|
69
|
+
# committed the one-way barrier by then.
|
|
70
|
+
shutdown_state.prepare_server_start()
|
|
71
|
+
await super().serve(sockets=sockets)
|
|
72
|
+
|
|
73
|
+
def handle_exit(self, sig: int, frame: FrameType | None) -> None:
|
|
74
|
+
shutdown_state.commit_shutdown(timeout_seconds=self._drain_timeout_seconds)
|
|
75
|
+
super().handle_exit(sig, frame)
|
|
76
|
+
|
|
77
|
+
async def shutdown(self, sockets: list[socket.socket] | None = None) -> None:
|
|
78
|
+
shutdown_state.set_post_drain_cleanup_timeout_seconds(self._post_drain_cleanup_timeout_seconds)
|
|
79
|
+
shutdown_state.commit_shutdown(timeout_seconds=self._drain_timeout_seconds)
|
|
80
|
+
remaining = shutdown_state.remaining_drain_timeout_seconds() or 0.0
|
|
81
|
+
drained = await shutdown_state.wait_for_in_flight_drain(
|
|
82
|
+
timeout_seconds=remaining,
|
|
83
|
+
should_abort=lambda: self.force_exit,
|
|
84
|
+
)
|
|
85
|
+
if not drained and not self.force_exit:
|
|
86
|
+
logger.warning("Drain timeout reached before connection shutdown")
|
|
87
|
+
|
|
88
|
+
original_timeout = self.config.timeout_graceful_shutdown
|
|
89
|
+
remaining = shutdown_state.remaining_drain_timeout_seconds() or 0.0
|
|
90
|
+
bounded_timeout = remaining
|
|
91
|
+
if original_timeout is not None:
|
|
92
|
+
bounded_timeout = min(float(original_timeout), bounded_timeout)
|
|
93
|
+
# Uvicorn annotates this setting as int but passes it directly to
|
|
94
|
+
# asyncio.timeout(); preserving the float avoids discarding the last
|
|
95
|
+
# subsecond of the shared process deadline.
|
|
96
|
+
setattr(self.config, "timeout_graceful_shutdown", bounded_timeout)
|
|
97
|
+
shutdown_wait_timeout = remaining + self._post_drain_cleanup_timeout_seconds
|
|
98
|
+
shutdown_task = asyncio.create_task(
|
|
99
|
+
super().shutdown(sockets=sockets),
|
|
100
|
+
name="uvicorn-post-drain-shutdown",
|
|
101
|
+
)
|
|
102
|
+
restore_timeout_on_return = True
|
|
103
|
+
try:
|
|
104
|
+
done, _ = await asyncio.wait(
|
|
105
|
+
{shutdown_task},
|
|
106
|
+
timeout=shutdown_wait_timeout,
|
|
107
|
+
)
|
|
108
|
+
if shutdown_task in done:
|
|
109
|
+
await shutdown_task
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
restore_timeout_on_return = False
|
|
113
|
+
shutdown_task.cancel()
|
|
114
|
+
shutdown_task.add_done_callback(
|
|
115
|
+
lambda task: _finish_abandoned_uvicorn_shutdown(
|
|
116
|
+
task,
|
|
117
|
+
config=self.config,
|
|
118
|
+
original_timeout=original_timeout,
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
logger.warning(
|
|
122
|
+
"Shared drain deadline plus %.1fs Uvicorn cleanup reserve exhausted; forcing process exit",
|
|
123
|
+
self._post_drain_cleanup_timeout_seconds,
|
|
124
|
+
)
|
|
125
|
+
exit_signal = self._captured_signals[-1] if self._captured_signals else signal.SIGTERM
|
|
126
|
+
_force_process_exit(exit_signal)
|
|
127
|
+
except asyncio.CancelledError:
|
|
128
|
+
restore_timeout_on_return = False
|
|
129
|
+
shutdown_task.cancel()
|
|
130
|
+
shutdown_task.add_done_callback(
|
|
131
|
+
lambda task: _finish_abandoned_uvicorn_shutdown(
|
|
132
|
+
task,
|
|
133
|
+
config=self.config,
|
|
134
|
+
original_timeout=original_timeout,
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
raise
|
|
138
|
+
finally:
|
|
139
|
+
if restore_timeout_on_return:
|
|
140
|
+
setattr(self.config, "timeout_graceful_shutdown", original_timeout)
|
app/core/shutdown.py
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import math
|
|
5
|
+
import time
|
|
6
|
+
from collections.abc import Callable, Collection
|
|
7
|
+
from typing import TypeVar
|
|
8
|
+
|
|
9
|
+
_TaskResultT = TypeVar("_TaskResultT")
|
|
10
|
+
|
|
11
|
+
DRAIN_DEADLINE_HEADER = "x-codex-lb-drain-deadline-monotonic"
|
|
12
|
+
|
|
13
|
+
_draining: bool = False
|
|
14
|
+
_drain_deadline: float | None = None
|
|
15
|
+
_drain_started: bool = False
|
|
16
|
+
_shutdown_committed: bool = False
|
|
17
|
+
_shutdown_deadline_candidates: list[float] = []
|
|
18
|
+
_server_start_pending: bool = False
|
|
19
|
+
_lifespan_completed: bool = False
|
|
20
|
+
_bridge_drain_active: bool = False
|
|
21
|
+
_in_flight: int = 0
|
|
22
|
+
_control_plane_task_admission_open: bool = True
|
|
23
|
+
_post_drain_cleanup_timeout_seconds: float | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _effective_drain_deadline() -> float | None:
|
|
27
|
+
"""Return the earliest deadline observed by reversible or committed drain."""
|
|
28
|
+
|
|
29
|
+
candidates = _shutdown_deadline_candidates
|
|
30
|
+
if _drain_deadline is None:
|
|
31
|
+
return min(candidates) if candidates else None
|
|
32
|
+
if not candidates:
|
|
33
|
+
return _drain_deadline
|
|
34
|
+
return min(_drain_deadline, *candidates)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def reset() -> None:
|
|
38
|
+
global _draining, _drain_deadline, _drain_started, _shutdown_committed
|
|
39
|
+
global _shutdown_deadline_candidates
|
|
40
|
+
global _server_start_pending, _lifespan_completed
|
|
41
|
+
global _bridge_drain_active, _in_flight, _control_plane_task_admission_open
|
|
42
|
+
global _post_drain_cleanup_timeout_seconds
|
|
43
|
+
_draining = False
|
|
44
|
+
_drain_deadline = None
|
|
45
|
+
_drain_started = False
|
|
46
|
+
_shutdown_committed = False
|
|
47
|
+
_shutdown_deadline_candidates = []
|
|
48
|
+
_server_start_pending = False
|
|
49
|
+
_lifespan_completed = False
|
|
50
|
+
_bridge_drain_active = False
|
|
51
|
+
_in_flight = 0
|
|
52
|
+
_control_plane_task_admission_open = True
|
|
53
|
+
_post_drain_cleanup_timeout_seconds = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def prepare_server_start() -> None:
|
|
57
|
+
"""Reset before signal capture and mark the matching lifespan as prepared."""
|
|
58
|
+
|
|
59
|
+
global _server_start_pending
|
|
60
|
+
reset()
|
|
61
|
+
_server_start_pending = True
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def prepare_lifespan_start() -> None:
|
|
65
|
+
"""Reset only a completed embedded lifecycle, never a server signal latch."""
|
|
66
|
+
|
|
67
|
+
global _server_start_pending
|
|
68
|
+
if _server_start_pending:
|
|
69
|
+
_server_start_pending = False
|
|
70
|
+
return
|
|
71
|
+
if _lifespan_completed:
|
|
72
|
+
reset()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def mark_lifespan_completed() -> None:
|
|
76
|
+
"""Allow a later embedded lifespan in this process to start cleanly."""
|
|
77
|
+
|
|
78
|
+
global _server_start_pending, _lifespan_completed
|
|
79
|
+
_server_start_pending = False
|
|
80
|
+
_lifespan_completed = True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def close_control_plane_task_admission() -> None:
|
|
84
|
+
global _control_plane_task_admission_open
|
|
85
|
+
_control_plane_task_admission_open = False
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def is_control_plane_task_admission_open() -> bool:
|
|
89
|
+
return _control_plane_task_admission_open
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def set_draining(val: bool = True) -> None:
|
|
93
|
+
global _draining
|
|
94
|
+
if val:
|
|
95
|
+
_draining = True
|
|
96
|
+
return
|
|
97
|
+
stop_drain()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _new_drain_deadline_candidate(
|
|
101
|
+
timeout_seconds: float,
|
|
102
|
+
*,
|
|
103
|
+
deadline_monotonic: float | None,
|
|
104
|
+
) -> float:
|
|
105
|
+
requested_deadline: float | None = None
|
|
106
|
+
if deadline_monotonic is not None:
|
|
107
|
+
requested_deadline = float(deadline_monotonic)
|
|
108
|
+
if not math.isfinite(requested_deadline):
|
|
109
|
+
raise ValueError("drain deadline must be finite")
|
|
110
|
+
|
|
111
|
+
deadline = time.monotonic() + max(float(timeout_seconds), 0.0)
|
|
112
|
+
if requested_deadline is not None:
|
|
113
|
+
deadline = min(deadline, requested_deadline)
|
|
114
|
+
return deadline
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def begin_drain(
|
|
118
|
+
timeout_seconds: float,
|
|
119
|
+
*,
|
|
120
|
+
deadline_monotonic: float | None = None,
|
|
121
|
+
) -> float:
|
|
122
|
+
"""Start one bounded process drain without extending an existing deadline."""
|
|
123
|
+
|
|
124
|
+
global _draining, _drain_deadline, _drain_started, _bridge_drain_active
|
|
125
|
+
global _shutdown_deadline_candidates
|
|
126
|
+
# This single monotonic snapshot is the function's linearization point.
|
|
127
|
+
# If a signal commits after observing False, this invocation still
|
|
128
|
+
# publishes its own candidate and the effective deadline takes the minimum.
|
|
129
|
+
if _drain_started and deadline_monotonic is None:
|
|
130
|
+
_draining = True
|
|
131
|
+
_bridge_drain_active = True
|
|
132
|
+
deadline = _effective_drain_deadline()
|
|
133
|
+
assert deadline is not None
|
|
134
|
+
return deadline
|
|
135
|
+
deadline = _new_drain_deadline_candidate(
|
|
136
|
+
timeout_seconds,
|
|
137
|
+
deadline_monotonic=deadline_monotonic,
|
|
138
|
+
)
|
|
139
|
+
if _drain_deadline is None or deadline < _drain_deadline:
|
|
140
|
+
_drain_deadline = deadline
|
|
141
|
+
_draining = True
|
|
142
|
+
_bridge_drain_active = True
|
|
143
|
+
_drain_started = True
|
|
144
|
+
# A synchronous signal handler can commit shutdown between any Python
|
|
145
|
+
# bytecodes above. The handler's committed deadline and this call's
|
|
146
|
+
# reversible deadline are therefore independent monotonic candidates; the
|
|
147
|
+
# effective deadline is always their minimum.
|
|
148
|
+
deadline = _effective_drain_deadline()
|
|
149
|
+
assert deadline is not None
|
|
150
|
+
if _shutdown_committed:
|
|
151
|
+
# A signal may have committed before this invocation published its
|
|
152
|
+
# reversible candidate. Promote that candidate through one atomic list
|
|
153
|
+
# append so a later stop continuation cannot erase it.
|
|
154
|
+
_shutdown_deadline_candidates.append(deadline)
|
|
155
|
+
return deadline
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def commit_shutdown(
|
|
159
|
+
timeout_seconds: float,
|
|
160
|
+
*,
|
|
161
|
+
deadline_monotonic: float | None = None,
|
|
162
|
+
) -> float:
|
|
163
|
+
"""Permanently close admission and publish a committed deadline candidate."""
|
|
164
|
+
|
|
165
|
+
global _draining, _drain_started, _shutdown_committed
|
|
166
|
+
global _shutdown_deadline_candidates
|
|
167
|
+
global _bridge_drain_active
|
|
168
|
+
if _shutdown_committed and deadline_monotonic is None:
|
|
169
|
+
deadline = _effective_drain_deadline()
|
|
170
|
+
assert deadline is not None
|
|
171
|
+
return deadline
|
|
172
|
+
|
|
173
|
+
# A deadline-bearing preStop request may arrive after SIGTERM even though
|
|
174
|
+
# its deadline was anchored before that signal. Let that explicit earlier
|
|
175
|
+
# candidate tighten committed shutdown; headerless later signals only reuse
|
|
176
|
+
# the first committed value through the fast path above.
|
|
177
|
+
deadline = _new_drain_deadline_candidate(
|
|
178
|
+
timeout_seconds,
|
|
179
|
+
deadline_monotonic=deadline_monotonic,
|
|
180
|
+
)
|
|
181
|
+
# Each invocation that crossed the fast path publishes through one atomic
|
|
182
|
+
# append. A signal can re-enter between any surrounding bytecodes, but an
|
|
183
|
+
# append-only minimum cannot overwrite an earlier candidate with a later
|
|
184
|
+
# deadline.
|
|
185
|
+
_shutdown_deadline_candidates.append(deadline)
|
|
186
|
+
# Promote every already-published reversible deadline into committed
|
|
187
|
+
# storage. Otherwise a signal interleaving with stop_drain() could return
|
|
188
|
+
# the earlier effective deadline here, let stop_drain() erase its reversible
|
|
189
|
+
# owner, and then expose this later committed candidate.
|
|
190
|
+
if _drain_deadline is not None:
|
|
191
|
+
_shutdown_deadline_candidates.append(_drain_deadline)
|
|
192
|
+
_shutdown_committed = True
|
|
193
|
+
_draining = True
|
|
194
|
+
_bridge_drain_active = True
|
|
195
|
+
_drain_started = True
|
|
196
|
+
effective_deadline = _effective_drain_deadline()
|
|
197
|
+
assert effective_deadline is not None
|
|
198
|
+
return effective_deadline
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def is_shutdown_committed() -> bool:
|
|
202
|
+
return _shutdown_committed
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def stop_drain() -> bool:
|
|
206
|
+
"""Stop a reversible operator drain unless process shutdown has committed."""
|
|
207
|
+
|
|
208
|
+
global _draining, _drain_deadline, _drain_started, _bridge_drain_active
|
|
209
|
+
if _shutdown_committed:
|
|
210
|
+
return False
|
|
211
|
+
# Clear the admission token first so a synchronous signal never observes
|
|
212
|
+
# "started" with the deadline already erased.
|
|
213
|
+
_drain_started = False
|
|
214
|
+
_draining = False
|
|
215
|
+
_drain_deadline = None
|
|
216
|
+
_bridge_drain_active = False
|
|
217
|
+
# A synchronous signal may have committed shutdown between the first
|
|
218
|
+
# check and the reversible state clears above. Committed state is stored
|
|
219
|
+
# separately; restore the token/deadline before returning to preserve the
|
|
220
|
+
# begin_drain entry invariant.
|
|
221
|
+
if _shutdown_committed:
|
|
222
|
+
assert _shutdown_deadline_candidates
|
|
223
|
+
_drain_started = True
|
|
224
|
+
return not _shutdown_committed
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def remaining_drain_timeout_seconds() -> float | None:
|
|
228
|
+
deadline = _effective_drain_deadline()
|
|
229
|
+
if deadline is None:
|
|
230
|
+
return None
|
|
231
|
+
return max(deadline - time.monotonic(), 0.0)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def set_post_drain_cleanup_timeout_seconds(timeout_seconds: float) -> None:
|
|
235
|
+
"""Publish the fixed cleanup reserve belonging to the server shutdown."""
|
|
236
|
+
|
|
237
|
+
global _post_drain_cleanup_timeout_seconds
|
|
238
|
+
_post_drain_cleanup_timeout_seconds = max(float(timeout_seconds), 0.0)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def remaining_post_drain_cleanup_timeout_seconds() -> float | None:
|
|
242
|
+
"""Return the unused portion of the shared drain-plus-reserve deadline."""
|
|
243
|
+
|
|
244
|
+
deadline = _effective_drain_deadline()
|
|
245
|
+
reserve = _post_drain_cleanup_timeout_seconds
|
|
246
|
+
if deadline is None or reserve is None:
|
|
247
|
+
return None
|
|
248
|
+
return max(deadline + reserve - time.monotonic(), 0.0)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def is_draining() -> bool:
|
|
252
|
+
return _draining or _shutdown_committed
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def set_bridge_drain_active(val: bool = True) -> None:
|
|
256
|
+
global _bridge_drain_active
|
|
257
|
+
_bridge_drain_active = val
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def is_bridge_drain_active() -> bool:
|
|
261
|
+
return _bridge_drain_active or _shutdown_committed
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def increment_in_flight() -> None:
|
|
265
|
+
global _in_flight
|
|
266
|
+
_in_flight += 1
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def decrement_in_flight() -> None:
|
|
270
|
+
global _in_flight
|
|
271
|
+
_in_flight = max(0, _in_flight - 1)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_in_flight() -> int:
|
|
275
|
+
return _in_flight
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
async def wait_for_in_flight_drain(
|
|
279
|
+
timeout_seconds: float,
|
|
280
|
+
poll_interval_seconds: float = 0.1,
|
|
281
|
+
should_abort: Callable[[], bool] | None = None,
|
|
282
|
+
) -> bool:
|
|
283
|
+
deadline = time.monotonic() + max(float(timeout_seconds), 0.0)
|
|
284
|
+
shared_deadline = _effective_drain_deadline()
|
|
285
|
+
if shared_deadline is not None:
|
|
286
|
+
deadline = min(deadline, shared_deadline)
|
|
287
|
+
while get_in_flight() > 0:
|
|
288
|
+
if should_abort is not None and should_abort():
|
|
289
|
+
return False
|
|
290
|
+
shared_deadline = _effective_drain_deadline()
|
|
291
|
+
if shared_deadline is not None:
|
|
292
|
+
# A deadline-bearing preStop request can overtake SIGTERM after
|
|
293
|
+
# this wait has begun. Re-read the shared candidate so that late
|
|
294
|
+
# publication can only tighten, never extend, the active wait.
|
|
295
|
+
deadline = min(deadline, shared_deadline)
|
|
296
|
+
remaining = deadline - time.monotonic()
|
|
297
|
+
if remaining <= 0:
|
|
298
|
+
break
|
|
299
|
+
await asyncio.sleep(min(poll_interval_seconds, remaining))
|
|
300
|
+
return get_in_flight() == 0
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
async def wait_for_tasks_to_drain(
|
|
304
|
+
tasks: Collection[asyncio.Task[_TaskResultT]],
|
|
305
|
+
timeout_seconds: float,
|
|
306
|
+
) -> set[asyncio.Task[_TaskResultT]]:
|
|
307
|
+
"""Wait until a live task collection is stable or its deadline expires."""
|
|
308
|
+
|
|
309
|
+
loop = asyncio.get_running_loop()
|
|
310
|
+
deadline = loop.time() + timeout_seconds
|
|
311
|
+
while True:
|
|
312
|
+
pending = {task for task in tasks if not task.done()}
|
|
313
|
+
if not pending:
|
|
314
|
+
# Let done callbacks run before declaring a live registry empty.
|
|
315
|
+
await asyncio.sleep(0)
|
|
316
|
+
pending = {task for task in tasks if not task.done()}
|
|
317
|
+
if not pending:
|
|
318
|
+
return set()
|
|
319
|
+
|
|
320
|
+
remaining = deadline - loop.time()
|
|
321
|
+
if remaining <= 0:
|
|
322
|
+
return pending
|
|
323
|
+
|
|
324
|
+
_, still_pending = await asyncio.wait(pending, timeout=remaining)
|
|
325
|
+
if still_pending:
|
|
326
|
+
return {task for task in tasks if not task.done()}
|
app/core/socket_peer.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Final, TypeAlias, cast
|
|
4
|
+
|
|
5
|
+
from starlette.requests import HTTPConnection
|
|
6
|
+
from starlette.types import Scope
|
|
7
|
+
|
|
8
|
+
_RawSocketPeer: TypeAlias = tuple[str, int] | None
|
|
9
|
+
_RAW_SOCKET_PEER_SCOPE_KEY: Final = "_codex_lb_raw_socket_peer"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _capture_raw_socket_peer(scope: Scope) -> None:
|
|
13
|
+
"""Store the server-observed peer before proxy headers mutate the scope."""
|
|
14
|
+
scope[_RAW_SOCKET_PEER_SCOPE_KEY] = cast(_RawSocketPeer, scope.get("client"))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def raw_socket_peer_host(connection: HTTPConnection) -> str | None:
|
|
18
|
+
"""Return the server-observed socket peer, failing closed if uncaptured."""
|
|
19
|
+
if _RAW_SOCKET_PEER_SCOPE_KEY not in connection.scope:
|
|
20
|
+
return None
|
|
21
|
+
peer = cast(_RawSocketPeer, connection.scope[_RAW_SOCKET_PEER_SCOPE_KEY])
|
|
22
|
+
return peer[0] if peer is not None else None
|
app/core/startup.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Startup state management."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
_startup_complete: bool = False
|
|
8
|
+
_bridge_registration_complete: bool = False
|
|
9
|
+
_bridge_durable_schema_ready: bool = False
|
|
10
|
+
_bridge_registration_event: asyncio.Event | None = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def reset_bridge_registration() -> None:
|
|
14
|
+
global _bridge_registration_complete, _bridge_durable_schema_ready, _bridge_registration_event
|
|
15
|
+
_bridge_registration_complete = False
|
|
16
|
+
_bridge_durable_schema_ready = False
|
|
17
|
+
_bridge_registration_event = asyncio.Event()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def mark_bridge_durable_schema_ready() -> None:
|
|
21
|
+
global _bridge_durable_schema_ready
|
|
22
|
+
_bridge_durable_schema_ready = True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def mark_bridge_registration_complete() -> None:
|
|
26
|
+
global _bridge_registration_complete
|
|
27
|
+
_bridge_registration_complete = True
|
|
28
|
+
if _bridge_registration_event is not None:
|
|
29
|
+
_bridge_registration_event.set()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def wait_for_bridge_registration(timeout_seconds: float) -> bool:
|
|
33
|
+
if _bridge_registration_complete:
|
|
34
|
+
return True
|
|
35
|
+
if _bridge_registration_event is None:
|
|
36
|
+
return False
|
|
37
|
+
try:
|
|
38
|
+
await asyncio.wait_for(_bridge_registration_event.wait(), timeout=timeout_seconds)
|
|
39
|
+
except asyncio.TimeoutError:
|
|
40
|
+
return False
|
|
41
|
+
return True
|