Skip to main content

codex_helper_core/proxy/
mod.rs

1use 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_evidence;
43mod provider_execution;
44mod provider_orchestration;
45mod providers_api;
46mod reasoning_guard;
47mod request_body;
48mod request_context;
49mod request_continuity;
50mod request_encoding;
51mod request_failures;
52mod request_observer;
53mod request_preparation;
54mod request_routing;
55mod response_finalization;
56mod response_fixer;
57mod response_semantics;
58mod responses_websocket;
59mod retry;
60mod route_affinity;
61mod route_attempts;
62mod route_executor_runtime;
63mod route_executor_shadow;
64mod route_metadata;
65mod route_provenance;
66mod route_target_selection;
67mod route_unavailability;
68mod router_setup;
69mod routing_plan;
70mod runtime_admin_api;
71mod runtime_config;
72mod selected_upstream_request;
73mod service_core;
74mod session_overrides;
75mod stations_api;
76mod stream;
77mod target_builder;
78#[cfg(test)]
79mod tests;
80
81use crate::filter::RequestFilter;
82use crate::host_local::HostLocalSessionHistoryMode;
83use crate::lb::LbState;
84use crate::state::{ProviderBalanceSnapshot, ProxyState};
85use crate::usage_providers::UsageProviderRefreshSummary;
86
87pub use self::admin::{
88    admin_base_url_from_proxy_base_url, admin_loopback_addr_for_proxy_port,
89    admin_port_for_proxy_port, local_admin_base_url_for_proxy_port, local_proxy_base_url,
90};
91pub use self::api_responses::{ProfilesResponse, ReloadResult, RuntimeStatusResponse};
92pub use self::codex_relay_capabilities::{
93    CodexRelayCapabilitiesObserved, CodexRelayCapabilitiesRequest, CodexRelayCapabilitiesResponse,
94    CodexRelayCapabilityMismatch, CodexRelayContinuityDiagnostics,
95    CodexRelayContinuityDomainSummary,
96};
97pub use self::codex_relay_evidence::{
98    CodexRelayEvidenceEntry, CodexRelayEvidenceFilters, CodexRelayEvidenceKind,
99    codex_relay_evidence_path, read_recent_codex_relay_evidence,
100};
101pub use self::codex_relay_live_smoke::{
102    CODEX_RELAY_LIVE_SMOKE_ACK, CodexRelayLiveSmokeCase, CodexRelayLiveSmokeConfidence,
103    CodexRelayLiveSmokeOutcome, CodexRelayLiveSmokeRequest, CodexRelayLiveSmokeResponse,
104    CodexRelayLiveSmokeResult, CodexRelayLiveSmokeSideEffect,
105};
106pub use self::codex_relay_probe::{
107    CodexRelayProbeClient, CodexRelayProbeConfidence, CodexRelayProbeKind, CodexRelayProbeResult,
108    CodexRelayProbeSideEffect, CodexRelayProbeSpec, CodexRelayProbeSupport,
109    classify_codex_relay_probe_response,
110};
111use self::concurrency_limits::ConcurrencyLimiter;
112pub use self::entrypoint::handle_proxy;
113pub use self::persisted_registry_api::PersistedRoutingUpsertRequest;
114pub use self::router_setup::{
115    admin_listener_router, proxy_only_router, proxy_only_router_with_admin_base_url, router,
116};
117use self::runtime_config::RuntimeConfig;
118
119pub const ADMIN_TOKEN_ENV_VAR: &str = "CODEX_HELPER_ADMIN_TOKEN";
120pub const ADMIN_TOKEN_HEADER: &str = "x-codex-helper-admin-token";
121pub const CLIENT_NAME_HEADER: &str = "x-codex-helper-client-name";
122pub const ADMIN_PORT_OFFSET: u16 = 1000;
123
124#[cfg(test)]
125const AUTH_FILE_CACHE_MIN_CHECK_INTERVAL: Duration = Duration::from_millis(20);
126#[cfg(not(test))]
127const AUTH_FILE_CACHE_MIN_CHECK_INTERVAL: Duration = Duration::from_millis(800);
128
129#[cfg(test)]
130fn codex_auth_json_value(key: &str) -> Option<String> {
131    auth_resolution::codex_auth_json_value(key)
132}
133
134#[cfg(test)]
135fn claude_settings_env_value(key: &str) -> Option<String> {
136    auth_resolution::claude_settings_env_value(key)
137}
138
139/// Generic proxy service; currently used by both Codex and Claude.
140#[derive(Clone)]
141pub struct ProxyService {
142    pub client: Client,
143    config: Arc<RuntimeConfig>,
144    pub service_name: &'static str,
145    lb_states: Arc<Mutex<HashMap<String, LbState>>>,
146    concurrency_limiter: Arc<ConcurrencyLimiter>,
147    filter: RequestFilter,
148    state: Arc<ProxyState>,
149    shutdown_tx: Option<watch::Sender<bool>>,
150    host_local_session_history_mode: HostLocalSessionHistoryMode,
151}
152
153#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
154pub struct ProviderBalanceRefreshResponse {
155    pub service_name: String,
156    pub refresh: UsageProviderRefreshSummary,
157    pub provider_balances: HashMap<String, Vec<ProviderBalanceSnapshot>>,
158}
159
160#[derive(Debug, Clone)]
161pub struct ProxyControlError {
162    status: axum::http::StatusCode,
163    message: String,
164}
165
166impl ProxyControlError {
167    pub fn new(status: axum::http::StatusCode, message: impl Into<String>) -> Self {
168        Self {
169            status,
170            message: message.into(),
171        }
172    }
173
174    pub fn status(&self) -> axum::http::StatusCode {
175        self.status
176    }
177
178    pub fn message(&self) -> &str {
179        self.message.as_str()
180    }
181
182    pub fn into_http_error(self) -> (axum::http::StatusCode, String) {
183        (self.status, self.message)
184    }
185}
186
187impl std::fmt::Display for ProxyControlError {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        write!(f, "status={}, {}", self.status, self.message)
190    }
191}
192
193impl std::error::Error for ProxyControlError {}
194
195impl From<(axum::http::StatusCode, String)> for ProxyControlError {
196    fn from((status, message): (axum::http::StatusCode, String)) -> Self {
197        Self::new(status, message)
198    }
199}