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