codex_helper_core/proxy/
mod.rs1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3use std::time::Duration;
4
5use reqwest::Client;
6use tokio::sync::watch;
7
8mod admin;
9mod api_responses;
10mod attempt_execution;
11mod attempt_failures;
12mod attempt_health;
13mod attempt_request;
14mod attempt_response;
15mod attempt_selection;
16mod attempt_target;
17mod attempt_transport;
18mod auth_resolution;
19mod classify;
20mod client_identity;
21mod codex_failure;
22mod codex_relay_capabilities;
23mod codex_relay_evidence;
24mod codex_relay_live_smoke;
25mod codex_relay_probe;
26mod codex_relay_target;
27mod concurrency_limits;
28mod control_plane;
29mod control_plane_manifest;
30mod control_plane_routes;
31mod control_plane_service;
32mod entrypoint;
33mod failure_summary;
34mod headers;
35mod healthcheck_api;
36mod http_debug;
37mod models_compat;
38mod openai_images;
39mod passive_health;
40mod persisted_registry_api;
41mod profile_defaults;
42mod provider_execution;
43mod provider_orchestration;
44mod providers_api;
45mod request_body;
46mod request_context;
47mod request_continuity;
48mod request_encoding;
49mod request_failures;
50mod request_preparation;
51mod request_routing;
52mod response_finalization;
53mod response_fixer;
54mod responses_websocket;
55mod retry;
56mod route_affinity;
57mod route_attempts;
58mod route_executor_runtime;
59mod route_executor_shadow;
60mod route_metadata;
61mod route_provenance;
62mod route_target_selection;
63mod route_unavailability;
64mod router_setup;
65mod routing_plan;
66mod runtime_admin_api;
67mod runtime_config;
68mod selected_upstream_request;
69mod service_core;
70mod session_overrides;
71mod stations_api;
72mod stream;
73mod target_builder;
74#[cfg(test)]
75mod tests;
76
77use crate::filter::RequestFilter;
78use crate::host_local::HostLocalSessionHistoryMode;
79use crate::lb::LbState;
80use crate::state::{ProviderBalanceSnapshot, ProxyState};
81use crate::usage_providers::UsageProviderRefreshSummary;
82
83pub use self::admin::{
84 admin_base_url_from_proxy_base_url, admin_loopback_addr_for_proxy_port,
85 admin_port_for_proxy_port, local_admin_base_url_for_proxy_port, local_proxy_base_url,
86};
87pub use self::api_responses::{ProfilesResponse, ReloadResult, RuntimeStatusResponse};
88pub use self::codex_relay_capabilities::{
89 CodexRelayCapabilitiesObserved, CodexRelayCapabilitiesRequest, CodexRelayCapabilitiesResponse,
90 CodexRelayCapabilityMismatch, CodexRelayContinuityDiagnostics,
91 CodexRelayContinuityDomainSummary,
92};
93pub use self::codex_relay_evidence::{
94 CodexRelayEvidenceEntry, CodexRelayEvidenceFilters, CodexRelayEvidenceKind,
95 codex_relay_evidence_path, read_recent_codex_relay_evidence,
96};
97pub use self::codex_relay_live_smoke::{
98 CODEX_RELAY_LIVE_SMOKE_ACK, CodexRelayLiveSmokeCase, CodexRelayLiveSmokeConfidence,
99 CodexRelayLiveSmokeOutcome, CodexRelayLiveSmokeRequest, CodexRelayLiveSmokeResponse,
100 CodexRelayLiveSmokeResult, CodexRelayLiveSmokeSideEffect,
101};
102pub use self::codex_relay_probe::{
103 CodexRelayProbeClient, CodexRelayProbeConfidence, CodexRelayProbeKind, CodexRelayProbeResult,
104 CodexRelayProbeSideEffect, CodexRelayProbeSpec, CodexRelayProbeSupport,
105 classify_codex_relay_probe_response,
106};
107use self::concurrency_limits::ConcurrencyLimiter;
108pub use self::entrypoint::handle_proxy;
109pub use self::persisted_registry_api::PersistedRoutingUpsertRequest;
110pub use self::router_setup::{
111 admin_listener_router, proxy_only_router, proxy_only_router_with_admin_base_url, router,
112};
113use self::runtime_config::RuntimeConfig;
114
115pub const ADMIN_TOKEN_ENV_VAR: &str = "CODEX_HELPER_ADMIN_TOKEN";
116pub const ADMIN_TOKEN_HEADER: &str = "x-codex-helper-admin-token";
117pub const CLIENT_NAME_HEADER: &str = "x-codex-helper-client-name";
118pub const ADMIN_PORT_OFFSET: u16 = 1000;
119
120#[cfg(test)]
121const AUTH_FILE_CACHE_MIN_CHECK_INTERVAL: Duration = Duration::from_millis(20);
122#[cfg(not(test))]
123const AUTH_FILE_CACHE_MIN_CHECK_INTERVAL: Duration = Duration::from_millis(800);
124
125#[cfg(test)]
126fn codex_auth_json_value(key: &str) -> Option<String> {
127 auth_resolution::codex_auth_json_value(key)
128}
129
130#[cfg(test)]
131fn claude_settings_env_value(key: &str) -> Option<String> {
132 auth_resolution::claude_settings_env_value(key)
133}
134
135#[derive(Clone)]
137pub struct ProxyService {
138 pub client: Client,
139 config: Arc<RuntimeConfig>,
140 pub service_name: &'static str,
141 lb_states: Arc<Mutex<HashMap<String, LbState>>>,
142 concurrency_limiter: Arc<ConcurrencyLimiter>,
143 filter: RequestFilter,
144 state: Arc<ProxyState>,
145 shutdown_tx: Option<watch::Sender<bool>>,
146 host_local_session_history_mode: HostLocalSessionHistoryMode,
147}
148
149#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
150pub struct ProviderBalanceRefreshResponse {
151 pub service_name: String,
152 pub refresh: UsageProviderRefreshSummary,
153 pub provider_balances: HashMap<String, Vec<ProviderBalanceSnapshot>>,
154}
155
156#[derive(Debug, Clone)]
157pub struct ProxyControlError {
158 status: axum::http::StatusCode,
159 message: String,
160}
161
162impl ProxyControlError {
163 pub fn new(status: axum::http::StatusCode, message: impl Into<String>) -> Self {
164 Self {
165 status,
166 message: message.into(),
167 }
168 }
169
170 pub fn status(&self) -> axum::http::StatusCode {
171 self.status
172 }
173
174 pub fn message(&self) -> &str {
175 self.message.as_str()
176 }
177
178 pub fn into_http_error(self) -> (axum::http::StatusCode, String) {
179 (self.status, self.message)
180 }
181}
182
183impl std::fmt::Display for ProxyControlError {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 write!(f, "status={}, {}", self.status, self.message)
186 }
187}
188
189impl std::error::Error for ProxyControlError {}
190
191impl From<(axum::http::StatusCode, String)> for ProxyControlError {
192 fn from((status, message): (axum::http::StatusCode, String)) -> Self {
193 Self::new(status, message)
194 }
195}