Files
codex-proxy-rs/src/proxy/dispatch/responses.rs
T
2026-06-28 23:13:17 +08:00

3272 lines
130 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Responses 创建编排与调度服务。
//!
//! 包含了将 OpenAI 请求调度到 Codex 上游账号的完整逻辑,包括:
//! - 响应创建(非流式 / 流式 / compact)
//! - 会话亲和性与隐式续接
//! - reasoning replay
//! - 账号回退与错误恢复
//! - 配额验证
use std::{
pin::Pin,
sync::Arc,
task::{Context, Poll},
time::Instant,
};
use chrono::{DateTime, Duration, Utc};
use futures::{stream::Stream, StreamExt};
use serde_json::{json, Value};
use thiserror::Error;
use tokio::sync::{mpsc, oneshot, Mutex};
use tracing;
use crate::{
admin::monitoring::{
usage_record::{ResponseUsageRecord, UsageRecord, UsageRecordLevel},
usage_record_store::AdminUsageRecordService,
},
proxy::dispatch::{
cloudflare::{
cloudflare_challenge_error_message, cloudflare_path_block_error_message,
is_cloudflare_challenge_upstream_error, is_cloudflare_path_block_upstream_error,
CloudflareRecovery,
},
errors::{
auth_failure_account_status, backend_transport_name, is_auth_upstream_error,
is_history_recovery_signal, is_history_recovery_upstream_error,
is_invalid_encrypted_content_signal, is_model_unsupported_signal,
is_model_unsupported_upstream_error, is_quota_exhausted_upstream_error,
is_rate_limit_upstream_error, rate_limit_cooldown_until, upstream_error_body,
upstream_error_http_status, upstream_error_set_cookie_headers,
},
reasoning_replay::ReasoningReplayCache,
session_affinity::{
compute_variant_hash, ensure_prompt_cache_key, hash_instructions,
prepare_variant_identity, RuntimeSessionAffinityService,
},
upstream::{
create_compact_response_with_account_retrying_5xx,
create_response_stream_with_account_retrying_5xx,
create_response_with_account_retrying_5xx, verify_acquired_quota_if_required,
QuotaVerificationContext, QuotaVerificationDecision,
QUOTA_VERIFY_LIMIT_REACHED_MESSAGE,
},
},
upstream::accounts::{
model::{Account, AccountStatus},
pool::{AccountAcquireRequest, RuntimeAccountPoolService},
},
upstream::{
protocol::{
events::{extract_sse_usage, extract_usage, TokenUsage},
responses::{
apply_response_model_options, completed_response_metadata,
reconvert_responses_sse_event_tuple_values, response_from_codex_sse,
CodexCompactRequest, CodexResponsesRequest, CollectedResponse, ResponsesSseFailure,
},
sse::{
encode_sse_event, parse_sse_events, sse_body_has_done, SseError, DONE_SSE_FRAME,
},
},
transport::{
backend_transport_for_response_request, is_banned_auth_signal,
is_banned_upstream_error, CodexBackendClient, CodexBackendResponse,
CodexBackendSseStream, CodexBackendTransport, CodexClientError,
CodexRateLimitHeaderUpdates, CodexTurnStateUpdate,
},
},
};
use super::implicit_resume::{
continuation_input_start, implicit_resume_allowed, ImplicitResumeSnapshot,
};
use crate::proxy::openai::responses::response_failed_sse_event_with_id;
#[derive(Clone, Copy)]
enum ExhaustedAccountClass {
QuotaExhausted,
RateLimited,
Expired,
Disabled,
Banned,
CloudflareChallenge,
CloudflarePathBlocked,
ModelUnsupported,
}
/// OpenAI Responses 调度服务。
#[derive(Clone)]
pub struct ResponseDispatchService {
account_pool: Arc<RuntimeAccountPoolService>,
models: Arc<crate::upstream::models::ModelService>,
codex: Arc<CodexBackendClient>,
session_affinity: Arc<RuntimeSessionAffinityService>,
reasoning_replay: Arc<Mutex<ReasoningReplayCache>>,
usage_records: Arc<AdminUsageRecordService>,
installation_id: Option<String>,
cloudflare: CloudflareRecovery,
}
/// 默认 reasoning replay TTL 秒数。
const DEFAULT_REASONING_REPLAY_TTL_SECS: i64 = 55 * 60;
const IMPLICIT_RESUME_MAX_AGE_SECS: i64 = DEFAULT_REASONING_REPLAY_TTL_SECS;
/// Responses live SSE 响应体流。
pub type ResponseBodyStream =
Pin<Box<dyn Stream<Item = Result<Bytes, ResponseDispatchStreamError>> + Send + 'static>>;
/// Responses live SSE 调度结果。
pub struct ResponseDispatchStream {
pub body: ResponseBodyStream,
}
struct MpscResponseBodyStream {
receiver: mpsc::Receiver<Result<Bytes, ResponseDispatchStreamError>>,
cancel: Option<oneshot::Sender<()>>,
}
impl Drop for MpscResponseBodyStream {
fn drop(&mut self) {
if let Some(cancel) = self.cancel.take() {
let _ = cancel.send(());
}
}
}
impl Stream for MpscResponseBodyStream {
type Item = Result<Bytes, ResponseDispatchStreamError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.receiver.poll_recv(cx)
}
}
use axum::body::Bytes;
impl ResponseDispatchService {
pub(crate) fn new(
account_pool: Arc<RuntimeAccountPoolService>,
models: Arc<crate::upstream::models::ModelService>,
codex: Arc<CodexBackendClient>,
session_affinity: Arc<RuntimeSessionAffinityService>,
usage_records: Arc<AdminUsageRecordService>,
installation_id: Option<String>,
cloudflare: CloudflareRecovery,
) -> Self {
Self {
account_pool,
models,
codex,
session_affinity,
reasoning_replay: Arc::new(Mutex::new(ReasoningReplayCache::new(Duration::seconds(
DEFAULT_REASONING_REPLAY_TTL_SECS,
)))),
usage_records,
installation_id,
cloudflare,
}
}
async fn prepare_response_session(
&self,
request: &mut CodexResponsesRequest,
) -> Option<ImplicitResumeSnapshot> {
prepare_variant_identity(request);
if let Some(previous_response_id) = request.previous_response_id.clone() {
if request.prompt_cache_key.is_none() {
request.prompt_cache_key = self
.session_affinity
.lookup_conversation_id(&previous_response_id, Utc::now())
.await;
}
if request.turn_state.as_deref().is_none_or(str::is_empty) {
request.turn_state = self
.session_affinity
.lookup_turn_state(&previous_response_id, Utc::now())
.await;
}
ensure_prompt_cache_key(request);
return None;
}
ensure_prompt_cache_key(request);
self.apply_implicit_resume(request).await
}
async fn apply_implicit_resume(
&self,
request: &mut CodexResponsesRequest,
) -> Option<ImplicitResumeSnapshot> {
let continuation_start = continuation_input_start(&request.input);
if continuation_start == 0 || continuation_start >= request.input.len() {
return None;
}
let conversation_id = request
.prompt_cache_key
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())?
.to_string();
let snapshot = ImplicitResumeSnapshot::capture(request);
let variant_hash = compute_variant_hash(request);
let now = Utc::now();
let previous_response_id = self
.session_affinity
.lookup_latest_response_by_conversation(
&conversation_id,
Some(Duration::seconds(IMPLICIT_RESUME_MAX_AGE_SECS)),
Some(&variant_hash),
now,
)
.await?;
let current_instructions_hash = hash_instructions(Some(&request.instructions));
if self
.session_affinity
.lookup_instructions_hash(&previous_response_id, now)
.await
.as_deref()
!= Some(current_instructions_hash.as_str())
{
return None;
}
let stored_function_call_ids = self
.session_affinity
.lookup_function_call_ids(&previous_response_id, now)
.await;
if !implicit_resume_allowed(
&request.input[continuation_start..],
&request.input,
&stored_function_call_ids,
) {
return None;
}
let account_id = self
.session_affinity
.lookup_account(&previous_response_id, now)
.await?;
let replay_items = self.reasoning_replay.lock().await.lookup(
&previous_response_id,
&account_id,
&conversation_id,
&variant_hash,
now,
);
let continuation = request.input[continuation_start..].to_vec();
let mut input = replay_items;
input.extend(continuation);
request.previous_response_id = Some(previous_response_id.clone());
request.use_websocket = true;
request.force_http_sse = false;
request.input = input;
if let Some(turn_state) = self
.session_affinity
.lookup_turn_state(&previous_response_id, now)
.await
{
request.turn_state = Some(turn_state);
}
Some(snapshot)
}
async fn preferred_account_id_for_request(
&self,
request: &CodexResponsesRequest,
now: DateTime<Utc>,
) -> Option<String> {
let previous_response_id = request.previous_response_id.as_deref()?;
self.session_affinity
.lookup_account(previous_response_id, now)
.await
}
async fn recover_request_history(
&self,
request: &mut CodexResponsesRequest,
implicit_resume: &mut Option<ImplicitResumeSnapshot>,
) {
if let Some(previous_response_id) = request.previous_response_id.as_deref() {
self.session_affinity.forget(previous_response_id).await;
}
if let Some(snapshot) = implicit_resume.take() {
snapshot.restore(request);
request.previous_response_id = None;
request.turn_state = None;
} else {
strip_request_history(request);
}
}
async fn apply_cascading_ban_defense(
&self,
request: &mut CodexResponsesRequest,
implicit_resume: &mut Option<ImplicitResumeSnapshot>,
preferred_account_id: Option<&str>,
acquired_account_id: &str,
explicit_previous_response_id: Option<&str>,
) -> bool {
let Some(preferred_account_id) =
preferred_account_id.filter(|account_id| *account_id != acquired_account_id)
else {
return false;
};
let has_history = request.previous_response_id.is_some()
|| request
.turn_state
.as_deref()
.is_some_and(|value| !value.is_empty());
if !has_history {
return false;
}
let Some(preferred_account) = self
.account_pool
.account_snapshot(preferred_account_id)
.await
else {
return false;
};
if !matches!(
preferred_account.status,
AccountStatus::Banned | AccountStatus::Disabled
) {
return false;
}
let response_id_to_forget = explicit_previous_response_id
.or(request.previous_response_id.as_deref())
.map(str::to_string);
if let Some(snapshot) = implicit_resume.take() {
snapshot.restore(request);
request.previous_response_id = None;
request.turn_state = None;
} else {
strip_request_history(request);
}
if let Some(response_id) = response_id_to_forget {
self.session_affinity.forget(&response_id).await;
}
true
}
async fn evict_reasoning_replay(&self, request: &CodexResponsesRequest, account_id: &str) {
evict_reasoning_replay(&self.reasoning_replay, request, account_id).await;
}
async fn record_response_affinity(
&self,
request: &CodexResponsesRequest,
account_id: &str,
body: &str,
turn_state: Option<String>,
usage: Option<TokenUsage>,
) {
record_response_affinity(
&self.session_affinity,
&self.reasoning_replay,
request,
account_id,
body,
turn_state,
usage,
)
.await;
}
/// 调度非流式 Responses 请求到 Codex Responses 上游。
pub async fn complete(
&self,
request_id: &str,
route: &str,
mut request: CodexResponsesRequest,
requested_model: &str,
) -> Result<Value, ResponseDispatchError> {
let started_at = Instant::now();
let catalog = self.models.catalog().await;
let parsed_model = catalog.parse_model_name(requested_model);
apply_response_model_options(&mut request, &parsed_model);
let tuple_schema = request.tuple_schema.clone();
let image_generation_requested = request.expects_image_generation();
let now = Utc::now();
let explicit_previous_response_id = request.previous_response_id.clone();
let mut implicit_resume = self.prepare_response_session(&mut request).await;
let preferred_account_id = self.preferred_account_id_for_request(&request, now).await;
let mut acquire_request = AccountAcquireRequest::new(&request.model, now);
if let Some(preferred_account_id) = preferred_account_id.as_deref() {
acquire_request = acquire_request.with_preferred_account_id(preferred_account_id);
}
let mut excluded_account_ids = Vec::new();
let mut rate_limited_count = 0usize;
let mut last_rate_limit_error = None;
let mut quota_exhausted_count = 0usize;
let mut last_quota_error = None;
let mut expired_count = 0usize;
let mut last_auth_error = None;
let mut disabled_count = 0usize;
let mut last_disabled_auth_error = None;
let mut banned_count = 0usize;
let mut last_banned_auth_error = None;
let mut last_banned_status_code: Option<u16> = None;
let mut cloudflare_challenge_count = 0usize;
let mut last_cloudflare_challenge_error = None;
let mut cloudflare_path_block_count = 0usize;
let mut last_cloudflare_path_block_error = None;
let mut model_unsupported_count = 0usize;
let mut last_model_unsupported_error = None;
let mut model_unsupported_retry_used = false;
let mut history_recovery_used = false;
let mut last_exhausted_account_class = None;
let mut empty_response_retries = 0u8;
let mut quota_verify_attempts = 0usize;
let mut last_attempted_account_id = None;
const MAX_EMPTY_RESPONSE_RETRIES: u8 = 2;
let (account, response, collected_response): (
Account,
CodexBackendResponse,
CollectedResponse,
) = loop {
let mut attempt_acquire_request = acquire_request
.clone()
.with_exclude_account_ids(excluded_account_ids.iter().cloned());
attempt_acquire_request.now = Utc::now();
let Some(acquired) = self
.account_pool
.acquire_with(&attempt_acquire_request)
.await
else {
let error = match last_exhausted_account_class {
Some(ExhaustedAccountClass::QuotaExhausted) => {
ResponseDispatchError::QuotaExhausted {
count: quota_exhausted_count,
upstream_error: last_quota_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::RateLimited) => {
ResponseDispatchError::RateLimited {
count: rate_limited_count,
upstream_error: last_rate_limit_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::Expired) => ResponseDispatchError::Expired {
count: expired_count,
upstream_error: last_auth_error.unwrap_or_default(),
},
Some(ExhaustedAccountClass::Disabled) => ResponseDispatchError::Disabled {
count: disabled_count,
upstream_error: last_disabled_auth_error.unwrap_or_default(),
},
Some(ExhaustedAccountClass::Banned) => ResponseDispatchError::Banned {
count: banned_count,
upstream_error: last_banned_auth_error.unwrap_or_default(),
status_code: last_banned_status_code.unwrap_or(403),
},
Some(ExhaustedAccountClass::CloudflareChallenge) => {
ResponseDispatchError::CloudflareChallenge {
count: cloudflare_challenge_count,
upstream_error: last_cloudflare_challenge_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::CloudflarePathBlocked) => {
ResponseDispatchError::CloudflarePathBlocked {
count: cloudflare_path_block_count,
upstream_error: last_cloudflare_path_block_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::ModelUnsupported) => {
ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count,
upstream_error: last_model_unsupported_error.unwrap_or_default(),
}
}
None => ResponseDispatchError::NoActiveAccount,
};
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: last_attempted_account_id.as_deref(),
stream: false,
compact: false,
transport: Some(backend_transport_name(
backend_transport_for_response_request(&request),
)),
},
&error,
)
.await;
return Err(error);
};
let acquired_account_id = acquired.account.id.clone();
// 配额验证
let acquired = match verify_acquired_quota_if_required(
QuotaVerificationContext {
account_pool: self.account_pool.as_ref(),
codex: self.codex.as_ref(),
cloudflare: &self.cloudflare,
installation_id: self.installation_id.as_deref(),
request_id,
excluded_account_ids: &mut excluded_account_ids,
verify_attempts: &mut quota_verify_attempts,
},
acquired,
)
.await
{
QuotaVerificationDecision::Ready(acquired) => *acquired,
QuotaVerificationDecision::RetryWithAnotherAccount => {
rate_limited_count += 1;
last_rate_limit_error = Some(QUOTA_VERIFY_LIMIT_REACHED_MESSAGE.to_string());
last_exhausted_account_class = Some(ExhaustedAccountClass::RateLimited);
continue;
}
QuotaVerificationDecision::MaxAttemptsReached => {
let error = ResponseDispatchError::RateLimited {
count: rate_limited_count + 1,
upstream_error: QUOTA_VERIFY_LIMIT_REACHED_MESSAGE.to_string(),
};
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: Some(&acquired_account_id),
stream: false,
compact: false,
transport: Some(backend_transport_name(
backend_transport_for_response_request(&request),
)),
},
&error,
)
.await;
return Err(error);
}
};
self.apply_cascading_ban_defense(
&mut request,
&mut implicit_resume,
preferred_account_id.as_deref(),
&acquired.account.id,
explicit_previous_response_id.as_deref(),
)
.await;
self.account_pool.wait_for_request_interval(&acquired).await;
let account = acquired.account;
let release_account_id = account.id.clone();
last_attempted_account_id = Some(release_account_id.clone());
let response_result = create_response_with_account_retrying_5xx(
&self.codex,
self.installation_id.as_deref(),
&self.cloudflare,
&request,
request_id,
&account,
)
.await;
self.account_pool.release(&release_account_id).await;
if let Err(error) = &response_result {
self.cloudflare
.capture_set_cookie_headers(
&release_account_id,
upstream_error_set_cookie_headers(error),
)
.await;
}
match response_result {
Ok(response) => {
self.cloudflare
.capture_set_cookie_headers(
&release_account_id,
&response.set_cookie_headers,
)
.await;
let collected_response =
match response_from_codex_sse(&response.body, tuple_schema.as_ref()) {
Ok(collected_response) => collected_response,
Err(error) => {
let error = ResponseDispatchError::InvalidSse(error);
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: Some(&release_account_id),
stream: false,
compact: false,
transport: Some(backend_transport_name(response.transport)),
},
&error,
)
.await;
return Err(error);
}
};
if matches!(collected_response, CollectedResponse::Empty) {
self.account_pool
.record_empty_response_attempt(
&release_account_id,
&request.model,
image_generation_requested,
)
.await;
empty_response_retries += 1;
if empty_response_retries <= MAX_EMPTY_RESPONSE_RETRIES {
continue;
}
}
if let CollectedResponse::Failed(failure) = &collected_response {
if is_history_recovery_sse_failure(failure) && !history_recovery_used {
if sse_failure_invalid_reasoning_replay(failure) {
self.evict_reasoning_replay(&request, &release_account_id)
.await;
}
self.recover_request_history(&mut request, &mut implicit_resume)
.await;
history_recovery_used = true;
continue;
}
if is_model_unsupported_sse_failure(failure) {
let upstream_error = sse_failure_error_body(failure);
if model_unsupported_retry_used {
let error = ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count + 1,
upstream_error,
};
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: Some(&release_account_id),
stream: false,
compact: false,
transport: Some(backend_transport_name(response.transport)),
},
&error,
)
.await;
return Err(error);
}
model_unsupported_count += 1;
last_model_unsupported_error = Some(upstream_error);
last_exhausted_account_class =
Some(ExhaustedAccountClass::ModelUnsupported);
model_unsupported_retry_used = true;
excluded_account_ids.push(release_account_id);
continue;
}
if is_quota_exhausted_sse_failure(failure) {
quota_exhausted_count += 1;
last_quota_error = Some(failure.message.clone());
last_exhausted_account_class =
Some(ExhaustedAccountClass::QuotaExhausted);
self.account_pool
.set_status(&release_account_id, AccountStatus::QuotaExhausted)
.await;
excluded_account_ids.push(release_account_id);
continue;
}
if is_auth_sse_failure(failure) {
let upstream_error = sse_failure_error_body(failure);
let account_status = auth_sse_failure_account_status(failure);
match account_status {
AccountStatus::Disabled => {
disabled_count += 1;
last_disabled_auth_error = Some(upstream_error);
last_exhausted_account_class =
Some(ExhaustedAccountClass::Disabled);
}
AccountStatus::Banned => {
banned_count += 1;
last_banned_status_code =
Some(stream_failure_http_status(failure));
last_banned_auth_error = Some(upstream_error);
last_exhausted_account_class =
Some(ExhaustedAccountClass::Banned);
}
_ => {
expired_count += 1;
last_auth_error = Some(upstream_error);
last_exhausted_account_class =
Some(ExhaustedAccountClass::Expired);
}
}
self.account_pool
.set_status(&release_account_id, account_status)
.await;
excluded_account_ids.push(release_account_id);
continue;
}
}
break (account, response, collected_response);
}
Err(error) if is_rate_limit_upstream_error(&error) => {
rate_limited_count += 1;
last_rate_limit_error = Some(upstream_error_body(&error));
last_exhausted_account_class = Some(ExhaustedAccountClass::RateLimited);
let cooldown_until = rate_limit_cooldown_until(&error, Utc::now());
self.account_pool
.mark_quota_limited_until(&release_account_id, cooldown_until)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_quota_exhausted_upstream_error(&error) => {
quota_exhausted_count += 1;
last_quota_error = Some(upstream_error_body(&error));
last_exhausted_account_class = Some(ExhaustedAccountClass::QuotaExhausted);
self.account_pool
.set_status(&release_account_id, AccountStatus::QuotaExhausted)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error)
if is_history_recovery_upstream_error(&error) && !history_recovery_used =>
{
if client_error_invalid_reasoning_replay(&error) {
self.evict_reasoning_replay(&request, &release_account_id)
.await;
}
self.recover_request_history(&mut request, &mut implicit_resume)
.await;
history_recovery_used = true;
}
Err(error) if is_auth_upstream_error(&error) => {
let upstream_error = upstream_error_body(&error);
let account_status = auth_failure_account_status(&error);
match account_status {
AccountStatus::Disabled => {
disabled_count += 1;
last_disabled_auth_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::Disabled);
}
AccountStatus::Banned => {
banned_count += 1;
last_banned_status_code = Some(upstream_error_http_status(&error));
last_banned_auth_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::Banned);
}
_ => {
expired_count += 1;
last_auth_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::Expired);
}
}
self.account_pool
.set_status(&release_account_id, account_status)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_cloudflare_challenge_upstream_error(&error) => {
cloudflare_challenge_count += 1;
last_cloudflare_challenge_error =
Some(cloudflare_challenge_error_message().to_string());
last_exhausted_account_class = Some(ExhaustedAccountClass::CloudflareChallenge);
self.cloudflare
.apply_challenge(self.account_pool.as_ref(), &release_account_id)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_cloudflare_path_block_upstream_error(&error) => {
cloudflare_path_block_count += 1;
last_cloudflare_path_block_error =
Some(cloudflare_path_block_error_message().to_string());
last_exhausted_account_class =
Some(ExhaustedAccountClass::CloudflarePathBlocked);
self.cloudflare
.apply_path_block(self.account_pool.as_ref(), &release_account_id)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_model_unsupported_upstream_error(&error) => {
let upstream_error = upstream_error_body(&error);
if model_unsupported_retry_used {
let error = ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count + 1,
upstream_error,
};
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: Some(&release_account_id),
stream: false,
compact: false,
transport: Some(backend_transport_name(
backend_transport_for_response_request(&request),
)),
},
&error,
)
.await;
return Err(error);
}
model_unsupported_count += 1;
last_model_unsupported_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::ModelUnsupported);
model_unsupported_retry_used = true;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_banned_upstream_error(&error) => {
banned_count += 1;
last_banned_status_code = Some(upstream_error_http_status(&error));
last_banned_auth_error = Some(upstream_error_body(&error));
last_exhausted_account_class = Some(ExhaustedAccountClass::Banned);
self.account_pool
.set_status(&release_account_id, AccountStatus::Banned)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) => {
record_response_upstream_error_event(ResponseUpstreamErrorEventRecord {
usage_records: &self.usage_records,
request_id,
account_id: &release_account_id,
route,
model: requested_model,
started_at,
stream: false,
transport: backend_transport_for_response_request(&request),
error: &error,
})
.await;
return Err(ResponseDispatchError::Upstream(error));
}
}
};
match collected_response {
CollectedResponse::Completed(body) => {
let response_id = body.get("id").and_then(Value::as_str);
self.cloudflare.reset_account_recovery(&account.id).await;
self.account_pool
.sync_passive_rate_limit_headers(&account, &response.rate_limit_headers)
.await;
if let Some(usage) = response.usage {
self.account_pool
.record_response_usage(
&account.id,
&request.model,
usage,
image_generation_requested,
)
.await;
}
self.record_response_affinity(
&request,
&account.id,
&response.body,
response.turn_state.clone(),
response.usage,
)
.await;
record_response_event(ResponseUsageRecord {
usage_records: &self.usage_records,
request_id,
account_id: &account.id,
route,
model: &request.model,
requested_model: Some(requested_model),
client_ip: request.client_ip.as_deref(),
client_user_agent: request.client_user_agent.as_deref(),
reasoning_effort: reasoning_effort_from_request(&request),
started_at,
status_code: 200,
level: UsageRecordLevel::Info,
message: "v1 responses completed",
metadata: json!({
"responseId": response_id,
"stream": false,
"transport": backend_transport_name(response.transport),
"usage": response.usage,
}),
rate_limit_headers: &response.rate_limit_headers,
})
.await;
Ok(body)
}
CollectedResponse::Failed(failure) => {
let error = ResponseDispatchError::Failed(failure);
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: Some(&account.id),
stream: false,
compact: false,
transport: Some(backend_transport_name(response.transport)),
},
&error,
)
.await;
Err(error)
}
CollectedResponse::MissingCompleted => {
let error = ResponseDispatchError::MissingCompleted;
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: Some(&account.id),
stream: false,
compact: false,
transport: Some(backend_transport_name(response.transport)),
},
&error,
)
.await;
Err(error)
}
CollectedResponse::Empty => {
let error = ResponseDispatchError::EmptyUpstreamResponse;
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: Some(&account.id),
stream: false,
compact: false,
transport: Some(backend_transport_name(response.transport)),
},
&error,
)
.await;
Err(error)
}
}
}
async fn record_response_dispatch_error(
&self,
request_id: &str,
route: &str,
requested_model: &str,
started_at: Instant,
details: ResponseDispatchErrorDetails<'_>,
error: &ResponseDispatchError,
) {
record_response_dispatch_error_event(ResponseDispatchErrorEventRecord {
usage_records: &self.usage_records,
request_id,
account_id: details.account_id,
route,
model: requested_model,
started_at,
stream: details.stream,
compact: details.compact,
transport: details.transport,
error,
})
.await;
}
/// 调度流式 Responses 请求到 Codex Responses 上游。
pub async fn stream(
&self,
request_id: &str,
route: &str,
mut request: CodexResponsesRequest,
requested_model: &str,
) -> Result<ResponseDispatchStream, ResponseDispatchError> {
let started_at = Instant::now();
let catalog = self.models.catalog().await;
let parsed_model = catalog.parse_model_name(requested_model);
apply_response_model_options(&mut request, &parsed_model);
request.stream = true;
let tuple_schema = request.tuple_schema.clone();
let now = Utc::now();
let explicit_previous_response_id = request.previous_response_id.clone();
let mut implicit_resume = self.prepare_response_session(&mut request).await;
let preferred_account_id = self.preferred_account_id_for_request(&request, now).await;
let mut acquire_request = AccountAcquireRequest::new(&request.model, now);
if let Some(preferred_account_id) = preferred_account_id.as_deref() {
acquire_request = acquire_request.with_preferred_account_id(preferred_account_id);
}
let mut excluded_account_ids = Vec::new();
let mut rate_limited_count = 0usize;
let mut last_rate_limit_error = None;
let mut quota_exhausted_count = 0usize;
let mut last_quota_error = None;
let mut expired_count = 0usize;
let mut last_auth_error = None;
let mut disabled_count = 0usize;
let mut last_disabled_auth_error = None;
let mut banned_count = 0usize;
let mut last_banned_auth_error = None;
let mut last_banned_status_code: Option<u16> = None;
let mut cloudflare_challenge_count = 0usize;
let mut last_cloudflare_challenge_error = None;
let mut cloudflare_path_block_count = 0usize;
let mut last_cloudflare_path_block_error = None;
let mut model_unsupported_count = 0usize;
let mut last_model_unsupported_error = None;
let mut model_unsupported_retry_used = false;
let mut history_recovery_used = false;
let mut last_exhausted_account_class = None;
let mut quota_verify_attempts = 0usize;
let mut last_attempted_account_id = None::<String>;
macro_rules! return_stream_dispatch_error {
($error:expr) => {{
let error = $error;
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: last_attempted_account_id.as_deref(),
stream: true,
compact: false,
transport: Some(backend_transport_name(
backend_transport_for_response_request(&request),
)),
},
&error,
)
.await;
return Err(error);
}};
($error:expr, account_id: $account_id:expr, transport: $transport:expr) => {{
let error = $error;
self.record_response_dispatch_error(
request_id,
route,
requested_model,
started_at,
ResponseDispatchErrorDetails {
account_id: $account_id,
stream: true,
compact: false,
transport: $transport,
},
&error,
)
.await;
return Err(error);
}};
}
loop {
let mut attempt_acquire_request = acquire_request
.clone()
.with_exclude_account_ids(excluded_account_ids.iter().cloned());
attempt_acquire_request.now = Utc::now();
let Some(acquired) = self
.account_pool
.acquire_with(&attempt_acquire_request)
.await
else {
let error = match last_exhausted_account_class {
Some(ExhaustedAccountClass::QuotaExhausted) => {
ResponseDispatchError::QuotaExhausted {
count: quota_exhausted_count,
upstream_error: last_quota_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::RateLimited) => {
ResponseDispatchError::RateLimited {
count: rate_limited_count,
upstream_error: last_rate_limit_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::Expired) => ResponseDispatchError::Expired {
count: expired_count,
upstream_error: last_auth_error.unwrap_or_default(),
},
Some(ExhaustedAccountClass::Disabled) => ResponseDispatchError::Disabled {
count: disabled_count,
upstream_error: last_disabled_auth_error.unwrap_or_default(),
},
Some(ExhaustedAccountClass::Banned) => ResponseDispatchError::Banned {
count: banned_count,
upstream_error: last_banned_auth_error.unwrap_or_default(),
status_code: last_banned_status_code.unwrap_or(403),
},
Some(ExhaustedAccountClass::CloudflareChallenge) => {
ResponseDispatchError::CloudflareChallenge {
count: cloudflare_challenge_count,
upstream_error: last_cloudflare_challenge_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::CloudflarePathBlocked) => {
ResponseDispatchError::CloudflarePathBlocked {
count: cloudflare_path_block_count,
upstream_error: last_cloudflare_path_block_error.unwrap_or_default(),
}
}
Some(ExhaustedAccountClass::ModelUnsupported) => {
ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count,
upstream_error: last_model_unsupported_error.unwrap_or_default(),
}
}
None => ResponseDispatchError::NoActiveAccount,
};
return_stream_dispatch_error!(error);
};
let acquired_account_id = acquired.account.id.clone();
let acquired = match verify_acquired_quota_if_required(
QuotaVerificationContext {
account_pool: self.account_pool.as_ref(),
codex: self.codex.as_ref(),
cloudflare: &self.cloudflare,
installation_id: self.installation_id.as_deref(),
request_id,
excluded_account_ids: &mut excluded_account_ids,
verify_attempts: &mut quota_verify_attempts,
},
acquired,
)
.await
{
QuotaVerificationDecision::Ready(acquired) => *acquired,
QuotaVerificationDecision::RetryWithAnotherAccount => {
rate_limited_count += 1;
last_rate_limit_error = Some(QUOTA_VERIFY_LIMIT_REACHED_MESSAGE.to_string());
last_exhausted_account_class = Some(ExhaustedAccountClass::RateLimited);
continue;
}
QuotaVerificationDecision::MaxAttemptsReached => {
return_stream_dispatch_error!(
ResponseDispatchError::RateLimited {
count: rate_limited_count + 1,
upstream_error: QUOTA_VERIFY_LIMIT_REACHED_MESSAGE.to_string(),
},
account_id: Some(&acquired_account_id),
transport: Some(backend_transport_name(backend_transport_for_response_request(
&request
)))
);
}
};
self.apply_cascading_ban_defense(
&mut request,
&mut implicit_resume,
preferred_account_id.as_deref(),
&acquired.account.id,
explicit_previous_response_id.as_deref(),
)
.await;
self.account_pool.wait_for_request_interval(&acquired).await;
let account = acquired.account;
let release_account_id = account.id.clone();
last_attempted_account_id = Some(release_account_id.clone());
let response_result = create_response_stream_with_account_retrying_5xx(
&self.codex,
self.installation_id.as_deref(),
&self.cloudflare,
&request,
request_id,
&account,
)
.await;
if let Err(error) = &response_result {
self.cloudflare
.capture_set_cookie_headers(
&release_account_id,
upstream_error_set_cookie_headers(error),
)
.await;
}
match response_result {
Ok(response) => {
let transport = response.transport;
let set_cookie_headers = response.set_cookie_headers;
let rate_limit_headers = response.rate_limit_headers;
let rate_limit_header_updates = response.rate_limit_header_updates;
let turn_state_update = response.turn_state_update;
let turn_state = response.turn_state;
self.cloudflare
.capture_set_cookie_headers(&release_account_id, &set_cookie_headers)
.await;
let (prefetched, body) = match prefetch_first_sse_chunk(response.body).await {
Ok(prefetched) => prefetched,
Err(ResponseDispatchError::Upstream(error))
if is_history_recovery_upstream_error(&error)
&& !history_recovery_used =>
{
self.account_pool.release(&release_account_id).await;
if client_error_invalid_reasoning_replay(&error) {
self.evict_reasoning_replay(&request, &release_account_id)
.await;
}
self.recover_request_history(&mut request, &mut implicit_resume)
.await;
history_recovery_used = true;
continue;
}
Err(error) => {
self.account_pool.release(&release_account_id).await;
if let ResponseDispatchError::Upstream(upstream_error) = &error {
record_response_upstream_error_event(
ResponseUpstreamErrorEventRecord {
usage_records: &self.usage_records,
request_id,
account_id: &release_account_id,
route,
model: requested_model,
started_at,
stream: true,
transport,
error: upstream_error,
},
)
.await;
return Err(error);
}
return_stream_dispatch_error!(
error,
account_id: Some(&release_account_id),
transport: Some(backend_transport_name(transport))
);
}
};
let first_failure = match first_sse_failure(&prefetched) {
Ok(failure) => failure,
Err(error) => {
self.account_pool.release(&release_account_id).await;
return_stream_dispatch_error!(
ResponseDispatchError::InvalidSse(error),
account_id: Some(&release_account_id),
transport: Some(backend_transport_name(transport))
);
}
};
if let Some(failure) = first_failure {
if is_history_recovery_sse_failure(&failure) && !history_recovery_used {
self.account_pool.release(&release_account_id).await;
if sse_failure_invalid_reasoning_replay(&failure) {
self.evict_reasoning_replay(&request, &release_account_id)
.await;
}
self.recover_request_history(&mut request, &mut implicit_resume)
.await;
history_recovery_used = true;
continue;
}
if is_model_unsupported_sse_failure(&failure) {
let upstream_error = sse_failure_error_body(&failure);
if model_unsupported_retry_used {
self.account_pool.release(&release_account_id).await;
return_stream_dispatch_error!(
ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count + 1,
upstream_error,
},
account_id: Some(&release_account_id),
transport: Some(backend_transport_name(transport))
);
}
model_unsupported_count += 1;
last_model_unsupported_error = Some(upstream_error);
model_unsupported_retry_used = true;
excluded_account_ids.push(release_account_id);
self.account_pool.release(&account.id).await;
continue;
}
if is_quota_exhausted_sse_failure(&failure) {
quota_exhausted_count += 1;
last_quota_error = Some(failure.message.clone());
last_exhausted_account_class =
Some(ExhaustedAccountClass::QuotaExhausted);
self.account_pool
.set_status(&release_account_id, AccountStatus::QuotaExhausted)
.await;
excluded_account_ids.push(release_account_id);
self.account_pool.release(&account.id).await;
continue;
}
if is_auth_sse_failure(&failure) {
let upstream_error = sse_failure_error_body(&failure);
let account_status = auth_sse_failure_account_status(&failure);
match account_status {
AccountStatus::Disabled => {
disabled_count += 1;
last_disabled_auth_error = Some(upstream_error);
last_exhausted_account_class =
Some(ExhaustedAccountClass::Disabled);
}
AccountStatus::Banned => {
banned_count += 1;
last_banned_status_code =
Some(stream_failure_http_status(&failure));
last_banned_auth_error = Some(upstream_error);
last_exhausted_account_class =
Some(ExhaustedAccountClass::Banned);
}
_ => {
expired_count += 1;
last_auth_error = Some(upstream_error);
last_exhausted_account_class =
Some(ExhaustedAccountClass::Expired);
}
}
self.account_pool
.set_status(&release_account_id, account_status)
.await;
excluded_account_ids.push(release_account_id);
self.account_pool.release(&account.id).await;
continue;
}
self.account_pool.release(&release_account_id).await;
record_prefetched_response_stream_failure_event(
ResponseStreamFailureEventRecord {
usage_records: &self.usage_records,
request_id,
account_id: &release_account_id,
route,
model: &request.model,
requested_model,
started_at,
transport,
request: &request,
failure: &failure,
rate_limit_headers: &rate_limit_headers,
prefetched: &prefetched,
},
)
.await;
return Err(ResponseDispatchError::Failed(failure.clone()));
}
let context = LiveResponseStreamContext {
account_pool: Arc::clone(&self.account_pool),
session_affinity: Arc::clone(&self.session_affinity),
reasoning_replay: Arc::clone(&self.reasoning_replay),
usage_records: Arc::clone(&self.usage_records),
cloudflare: self.cloudflare.clone(),
account_id: account.id,
request_id: request_id.to_string(),
route: route.to_string(),
model: request.model.clone(),
requested_model: requested_model.to_string(),
client_ip: request.client_ip.clone(),
request,
tuple_schema,
transport,
rate_limit_headers,
rate_limit_header_updates,
turn_state_update,
turn_state,
started_at,
};
return Ok(spawn_live_response_stream(context, prefetched, body));
}
Err(error) if is_rate_limit_upstream_error(&error) => {
self.account_pool.release(&release_account_id).await;
rate_limited_count += 1;
last_rate_limit_error = Some(upstream_error_body(&error));
last_exhausted_account_class = Some(ExhaustedAccountClass::RateLimited);
let cooldown_until = rate_limit_cooldown_until(&error, Utc::now());
self.account_pool
.mark_quota_limited_until(&release_account_id, cooldown_until)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_quota_exhausted_upstream_error(&error) => {
self.account_pool.release(&release_account_id).await;
quota_exhausted_count += 1;
last_quota_error = Some(upstream_error_body(&error));
last_exhausted_account_class = Some(ExhaustedAccountClass::QuotaExhausted);
self.account_pool
.set_status(&release_account_id, AccountStatus::QuotaExhausted)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error)
if is_history_recovery_upstream_error(&error) && !history_recovery_used =>
{
self.account_pool.release(&release_account_id).await;
if client_error_invalid_reasoning_replay(&error) {
self.evict_reasoning_replay(&request, &release_account_id)
.await;
}
self.recover_request_history(&mut request, &mut implicit_resume)
.await;
history_recovery_used = true;
}
Err(error) if is_auth_upstream_error(&error) => {
self.account_pool.release(&release_account_id).await;
let upstream_error = upstream_error_body(&error);
let account_status = auth_failure_account_status(&error);
match account_status {
AccountStatus::Disabled => {
disabled_count += 1;
last_disabled_auth_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::Disabled);
}
AccountStatus::Banned => {
banned_count += 1;
last_banned_status_code = Some(upstream_error_http_status(&error));
last_banned_auth_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::Banned);
}
_ => {
expired_count += 1;
last_auth_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::Expired);
}
}
self.account_pool
.set_status(&release_account_id, account_status)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_cloudflare_challenge_upstream_error(&error) => {
self.account_pool.release(&release_account_id).await;
cloudflare_challenge_count += 1;
last_cloudflare_challenge_error =
Some(cloudflare_challenge_error_message().to_string());
last_exhausted_account_class = Some(ExhaustedAccountClass::CloudflareChallenge);
self.cloudflare
.apply_challenge(self.account_pool.as_ref(), &release_account_id)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_cloudflare_path_block_upstream_error(&error) => {
self.account_pool.release(&release_account_id).await;
cloudflare_path_block_count += 1;
last_cloudflare_path_block_error =
Some(cloudflare_path_block_error_message().to_string());
last_exhausted_account_class =
Some(ExhaustedAccountClass::CloudflarePathBlocked);
self.cloudflare
.apply_path_block(self.account_pool.as_ref(), &release_account_id)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_model_unsupported_upstream_error(&error) => {
self.account_pool.release(&release_account_id).await;
let upstream_error = upstream_error_body(&error);
if model_unsupported_retry_used {
return_stream_dispatch_error!(
ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count + 1,
upstream_error,
},
account_id: Some(&release_account_id),
transport: Some(backend_transport_name(backend_transport_for_response_request(
&request
)))
);
}
model_unsupported_count += 1;
last_model_unsupported_error = Some(upstream_error);
last_exhausted_account_class = Some(ExhaustedAccountClass::ModelUnsupported);
model_unsupported_retry_used = true;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_banned_upstream_error(&error) => {
self.account_pool.release(&release_account_id).await;
banned_count += 1;
last_banned_status_code = Some(upstream_error_http_status(&error));
last_banned_auth_error = Some(upstream_error_body(&error));
last_exhausted_account_class = Some(ExhaustedAccountClass::Banned);
self.account_pool
.set_status(&release_account_id, AccountStatus::Banned)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) => {
self.account_pool.release(&release_account_id).await;
record_response_upstream_error_event(ResponseUpstreamErrorEventRecord {
usage_records: &self.usage_records,
request_id,
account_id: &release_account_id,
route,
model: requested_model,
started_at,
stream: true,
transport: backend_transport_for_response_request(&request),
error: &error,
})
.await;
return Err(ResponseDispatchError::Upstream(error));
}
}
}
}
/// 调度 Responses compact 请求到 Codex compact 上游。
pub async fn compact(
&self,
request_id: &str,
mut request: CodexCompactRequest,
requested_model: &str,
) -> Result<Value, ResponseDispatchError> {
let started_at = Instant::now();
let catalog = self.models.catalog().await;
let parsed_model = catalog.parse_model_name(requested_model);
request.model = parsed_model.model_id;
let mut excluded_account_ids = Vec::new();
let mut rate_limited_count = 0usize;
let mut last_rate_limit_error = None;
let mut quota_exhausted_count = 0usize;
let mut last_quota_error = None;
let mut expired_count = 0usize;
let mut last_auth_error = None;
let mut disabled_count = 0usize;
let mut last_disabled_auth_error = None;
let mut banned_count = 0usize;
let mut last_banned_auth_error = None;
let mut last_banned_status_code: Option<u16> = None;
let mut cloudflare_challenge_count = 0usize;
let mut last_cloudflare_challenge_error = None;
let mut cloudflare_path_block_count = 0usize;
let mut last_cloudflare_path_block_error = None;
let mut model_unsupported_count = 0usize;
let mut last_model_unsupported_error = None;
let mut model_unsupported_retry_used = false;
let mut quota_verify_attempts = 0usize;
let mut last_attempted_account_id = None::<String>;
loop {
let acquire_request = AccountAcquireRequest::new(&request.model, Utc::now())
.with_exclude_account_ids(excluded_account_ids.iter().cloned());
let acquired = match self.account_pool.acquire_with(&acquire_request).await {
Some(acquired) => acquired,
None if quota_exhausted_count > 0 => {
let error = ResponseDispatchError::QuotaExhausted {
count: quota_exhausted_count,
upstream_error: last_quota_error.unwrap_or_default(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None if rate_limited_count > 0 => {
let error = ResponseDispatchError::RateLimited {
count: rate_limited_count,
upstream_error: last_rate_limit_error.unwrap_or_default(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None if expired_count > 0 => {
let error = ResponseDispatchError::Expired {
count: expired_count,
upstream_error: last_auth_error.unwrap_or_default(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None if disabled_count > 0 => {
let error = ResponseDispatchError::Disabled {
count: disabled_count,
upstream_error: last_disabled_auth_error.unwrap_or_default(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None if banned_count > 0 => {
let error = ResponseDispatchError::Banned {
count: banned_count,
upstream_error: last_banned_auth_error.unwrap_or_default(),
status_code: last_banned_status_code.unwrap_or(403),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None if cloudflare_challenge_count > 0 => {
let error = ResponseDispatchError::CloudflareChallenge {
count: cloudflare_challenge_count,
upstream_error: last_cloudflare_challenge_error.unwrap_or_default(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None if cloudflare_path_block_count > 0 => {
let error = ResponseDispatchError::CloudflarePathBlocked {
count: cloudflare_path_block_count,
upstream_error: last_cloudflare_path_block_error.unwrap_or_default(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None if model_unsupported_count > 0 => {
let error = ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count,
upstream_error: last_model_unsupported_error.unwrap_or_default(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
None => {
let error = ResponseDispatchError::NoActiveAccount;
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
};
last_attempted_account_id = Some(acquired.account.id.clone());
let acquired = match verify_acquired_quota_if_required(
QuotaVerificationContext {
account_pool: self.account_pool.as_ref(),
codex: self.codex.as_ref(),
cloudflare: &self.cloudflare,
installation_id: self.installation_id.as_deref(),
request_id,
excluded_account_ids: &mut excluded_account_ids,
verify_attempts: &mut quota_verify_attempts,
},
acquired,
)
.await
{
QuotaVerificationDecision::Ready(acquired) => *acquired,
QuotaVerificationDecision::RetryWithAnotherAccount => {
rate_limited_count += 1;
last_rate_limit_error = Some(QUOTA_VERIFY_LIMIT_REACHED_MESSAGE.to_string());
continue;
}
QuotaVerificationDecision::MaxAttemptsReached => {
let error = ResponseDispatchError::RateLimited {
count: rate_limited_count + 1,
upstream_error: QUOTA_VERIFY_LIMIT_REACHED_MESSAGE.to_string(),
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
last_attempted_account_id.as_deref(),
&error,
)
.await;
return Err(error);
}
};
let account = acquired.account;
let release_account_id = account.id.clone();
let response_result = create_compact_response_with_account_retrying_5xx(
&self.codex,
self.installation_id.as_deref(),
&self.cloudflare,
&request,
request_id,
&account,
)
.await;
self.account_pool.release(&release_account_id).await;
if let Err(error) = &response_result {
self.cloudflare
.capture_set_cookie_headers(
&release_account_id,
upstream_error_set_cookie_headers(error),
)
.await;
}
match response_result {
Ok(response) => {
self.cloudflare
.capture_set_cookie_headers(
&release_account_id,
&response.set_cookie_headers,
)
.await;
self.cloudflare.reset_account_recovery(&account.id).await;
let usage = extract_usage(&response.body);
if let Some(usage) = usage {
self.account_pool
.record_token_usage(&account.id, &request.model, &usage)
.await;
}
record_response_event(ResponseUsageRecord {
usage_records: &self.usage_records,
request_id,
account_id: &account.id,
route: "/v1/responses/compact",
model: &request.model,
requested_model: Some(requested_model),
client_ip: request.client_ip.as_deref(),
client_user_agent: request.client_user_agent.as_deref(),
reasoning_effort: reasoning_effort_from_compact_request(&request),
started_at,
status_code: 200,
level: UsageRecordLevel::Info,
message: "v1 responses compact completed",
metadata: json!({
"stream": false,
"compact": true,
"usage": usage,
}),
rate_limit_headers: &response.rate_limit_headers,
})
.await;
return Ok(response.body);
}
Err(error) if is_rate_limit_upstream_error(&error) => {
rate_limited_count += 1;
last_rate_limit_error = Some(upstream_error_body(&error));
let cooldown_until = rate_limit_cooldown_until(&error, Utc::now());
self.account_pool
.mark_quota_limited_until(&release_account_id, cooldown_until)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_quota_exhausted_upstream_error(&error) => {
quota_exhausted_count += 1;
last_quota_error = Some(upstream_error_body(&error));
self.account_pool
.set_status(&release_account_id, AccountStatus::QuotaExhausted)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_auth_upstream_error(&error) => {
let upstream_error = upstream_error_body(&error);
let account_status = auth_failure_account_status(&error);
match account_status {
AccountStatus::Disabled => {
disabled_count += 1;
last_disabled_auth_error = Some(upstream_error);
}
AccountStatus::Banned => {
banned_count += 1;
last_banned_status_code = Some(upstream_error_http_status(&error));
last_banned_auth_error = Some(upstream_error);
}
_ => {
expired_count += 1;
last_auth_error = Some(upstream_error);
}
}
self.account_pool
.set_status(&release_account_id, account_status)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_cloudflare_challenge_upstream_error(&error) => {
cloudflare_challenge_count += 1;
last_cloudflare_challenge_error =
Some(cloudflare_challenge_error_message().to_string());
self.cloudflare
.apply_challenge(self.account_pool.as_ref(), &release_account_id)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_cloudflare_path_block_upstream_error(&error) => {
cloudflare_path_block_count += 1;
last_cloudflare_path_block_error =
Some(cloudflare_path_block_error_message().to_string());
self.cloudflare
.apply_path_block(self.account_pool.as_ref(), &release_account_id)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_model_unsupported_upstream_error(&error) => {
let upstream_error = upstream_error_body(&error);
if model_unsupported_retry_used {
let error = ResponseDispatchError::ModelUnsupported {
count: model_unsupported_count + 1,
upstream_error,
};
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
Some(&release_account_id),
&error,
)
.await;
return Err(error);
}
model_unsupported_count += 1;
last_model_unsupported_error = Some(upstream_error);
model_unsupported_retry_used = true;
excluded_account_ids.push(release_account_id);
}
Err(error) if is_banned_upstream_error(&error) => {
banned_count += 1;
last_banned_status_code = Some(upstream_error_http_status(&error));
last_banned_auth_error = Some(upstream_error_body(&error));
self.account_pool
.set_status(&release_account_id, AccountStatus::Banned)
.await;
excluded_account_ids.push(release_account_id);
}
Err(error) => {
let error = ResponseDispatchError::Upstream(error);
self.record_compact_dispatch_error(
request_id,
requested_model,
started_at,
Some(&release_account_id),
&error,
)
.await;
return Err(error);
}
}
}
}
async fn record_compact_dispatch_error(
&self,
request_id: &str,
requested_model: &str,
started_at: Instant,
account_id: Option<&str>,
error: &ResponseDispatchError,
) {
record_response_dispatch_error_event(ResponseDispatchErrorEventRecord {
usage_records: &self.usage_records,
request_id,
account_id,
route: "/v1/responses/compact",
model: requested_model,
started_at,
stream: false,
compact: true,
transport: Some("http"),
error,
})
.await;
}
}
fn sse_failure_error_body(failure: &ResponsesSseFailure) -> String {
match failure.upstream_code.as_deref() {
Some(code) => serde_json::json!({
"error": {
"code": code,
"message": failure.message.as_str(),
}
})
.to_string(),
None => failure.message.clone(),
}
}
fn is_quota_exhausted_sse_failure(failure: &ResponsesSseFailure) -> bool {
failure
.upstream_code
.as_deref()
.is_some_and(|code| matches!(code, "quota_exceeded" | "insufficient_quota"))
|| failure.message.to_ascii_lowercase().contains("quota")
}
fn is_auth_sse_failure(failure: &ResponsesSseFailure) -> bool {
failure.upstream_code.as_deref().is_some_and(|code| {
let code = code.to_ascii_lowercase();
matches!(
code.as_str(),
"token_invalid"
| "token_expired"
| "token_revoked"
| "account_deactivated"
| "unauthorized"
| "invalid_api_key"
)
}) || {
let message = failure.message.to_ascii_lowercase();
message.contains("token revoked")
|| message.contains("token invalid")
|| message.contains("token expired")
}
}
fn is_model_unsupported_sse_failure(failure: &ResponsesSseFailure) -> bool {
failure
.upstream_code
.as_deref()
.is_some_and(is_model_unsupported_signal)
|| is_model_unsupported_signal(&failure.message)
}
fn is_history_recovery_sse_failure(failure: &ResponsesSseFailure) -> bool {
failure
.upstream_code
.as_deref()
.is_some_and(is_history_recovery_signal)
|| is_history_recovery_signal(&failure.message)
}
fn sse_failure_invalid_reasoning_replay(failure: &ResponsesSseFailure) -> bool {
failure
.upstream_code
.as_deref()
.is_some_and(is_invalid_encrypted_content_signal)
|| is_invalid_encrypted_content_signal(&failure.message)
}
fn client_error_invalid_reasoning_replay(error: &CodexClientError) -> bool {
matches!(
error,
CodexClientError::Upstream { body, .. } if is_invalid_encrypted_content_signal(body)
)
}
fn auth_sse_failure_account_status(failure: &ResponsesSseFailure) -> AccountStatus {
if failure
.upstream_code
.as_deref()
.is_some_and(is_banned_auth_signal)
|| is_banned_auth_signal(&failure.message)
{
AccountStatus::Banned
} else {
AccountStatus::Expired
}
}
fn strip_request_history(request: &mut CodexResponsesRequest) {
request.previous_response_id = None;
request.turn_state = None;
}
const MAX_STREAM_PREFETCH_BYTES: usize = 64 * 1024;
async fn prefetch_first_sse_chunk(
mut body: CodexBackendSseStream,
) -> Result<(Bytes, CodexBackendSseStream), ResponseDispatchError> {
let mut prefetched = Vec::new();
while !contains_sse_event_separator(&prefetched) {
let Some(next) = body.next().await else {
if prefetched.is_empty() {
return Err(ResponseDispatchError::EmptyUpstreamResponse);
}
return Err(ResponseDispatchError::MissingCompleted);
};
let chunk = next.map_err(ResponseDispatchError::Upstream)?;
prefetched.extend_from_slice(&chunk);
if prefetched.len() > MAX_STREAM_PREFETCH_BYTES {
return Err(ResponseDispatchError::InvalidSse(
SseError::BufferExceeded {
max_bytes: MAX_STREAM_PREFETCH_BYTES,
},
));
}
}
Ok((Bytes::from(prefetched), body))
}
fn contains_sse_event_separator(bytes: &[u8]) -> bool {
bytes.windows(2).any(|window| window == b"\n\n")
|| bytes.windows(4).any(|window| window == b"\r\n\r\n")
}
fn first_sse_failure(prefetched: &[u8]) -> Result<Option<ResponsesSseFailure>, SseError> {
let body = String::from_utf8_lossy(prefetched);
match response_from_codex_sse(&body, None)? {
CollectedResponse::Failed(failure) => Ok(Some(failure)),
CollectedResponse::Completed(_)
| CollectedResponse::MissingCompleted
| CollectedResponse::Empty => Ok(None),
}
}
struct LiveResponseStreamContext {
account_pool: Arc<RuntimeAccountPoolService>,
session_affinity: Arc<RuntimeSessionAffinityService>,
reasoning_replay: Arc<Mutex<ReasoningReplayCache>>,
usage_records: Arc<AdminUsageRecordService>,
cloudflare: CloudflareRecovery,
account_id: String,
request_id: String,
route: String,
model: String,
requested_model: String,
client_ip: Option<String>,
request: CodexResponsesRequest,
tuple_schema: Option<Value>,
transport: CodexBackendTransport,
rate_limit_headers: Vec<(String, String)>,
rate_limit_header_updates: Option<CodexRateLimitHeaderUpdates>,
turn_state_update: Option<CodexTurnStateUpdate>,
turn_state: Option<String>,
started_at: Instant,
}
fn spawn_live_response_stream(
context: LiveResponseStreamContext,
prefetched: Bytes,
mut body: CodexBackendSseStream,
) -> ResponseDispatchStream {
let (sender, receiver) = mpsc::channel(8);
let (cancel_sender, mut cancel_receiver) = oneshot::channel();
tokio::spawn(async move {
let mut tuple_transformer = context
.tuple_schema
.clone()
.map(TupleSseEventTransformer::new);
let mut body_bytes = Vec::new();
let mut first_token_ms = None;
if !send_live_response_stream_chunk(
&sender,
&mut body_bytes,
tuple_transformer.as_mut(),
prefetched,
)
.await
{
context.account_pool.release(&context.account_id).await;
return;
}
update_first_token_ms(&context, &body_bytes, &mut first_token_ms);
loop {
let next = tokio::select! {
_ = &mut cancel_receiver => {
context.account_pool.release(&context.account_id).await;
return;
}
next = body.next() => next,
};
let Some(next) = next else {
break;
};
match next {
Ok(chunk) => {
if !send_live_response_stream_chunk(
&sender,
&mut body_bytes,
tuple_transformer.as_mut(),
chunk,
)
.await
{
context.account_pool.release(&context.account_id).await;
return;
}
update_first_token_ms(&context, &body_bytes, &mut first_token_ms);
}
Err(error) => {
if !flush_live_response_stream_transformer(
&sender,
&mut body_bytes,
tuple_transformer.as_mut(),
)
.await
{
context.account_pool.release(&context.account_id).await;
return;
}
let detail = error.to_string();
let Some(body_text) =
send_live_response_stream_tail(&sender, &mut body_bytes, Some(&detail))
.await
else {
context.account_pool.release(&context.account_id).await;
return;
};
finalize_live_response_stream(context, body_text, first_token_ms).await;
return;
}
}
}
if !flush_live_response_stream_transformer(
&sender,
&mut body_bytes,
tuple_transformer.as_mut(),
)
.await
{
context.account_pool.release(&context.account_id).await;
return;
}
let Some(body_text) = send_live_response_stream_tail(&sender, &mut body_bytes, None).await
else {
context.account_pool.release(&context.account_id).await;
return;
};
finalize_live_response_stream(context, body_text, first_token_ms).await;
});
ResponseDispatchStream {
body: Box::pin(MpscResponseBodyStream {
receiver,
cancel: Some(cancel_sender),
}),
}
}
fn update_first_token_ms(
context: &LiveResponseStreamContext,
body_bytes: &[u8],
first_token_ms: &mut Option<i64>,
) {
if first_token_ms.is_none() && response_body_has_first_token(body_bytes) {
*first_token_ms = Some(elapsed_millis_i64(context.started_at));
}
}
fn response_body_has_first_token(body_bytes: &[u8]) -> bool {
let body = String::from_utf8_lossy(body_bytes);
let Some(event_end) = body.rfind("\n\n") else {
return false;
};
parse_sse_events(&body[..event_end + 2]).is_ok_and(|events| {
events.iter().any(|event| {
serde_json::from_str::<Value>(&event.data).is_ok_and(|value| {
let event_type = event
.event
.as_deref()
.or_else(|| value.get("type").and_then(Value::as_str));
matches!(
event_type,
Some("response.output_text.delta" | "response.reasoning_summary_text.delta")
) && value
.get("delta")
.and_then(Value::as_str)
.is_some_and(|delta| !delta.is_empty())
})
})
})
}
async fn send_live_response_stream_chunk(
sender: &mpsc::Sender<Result<Bytes, ResponseDispatchStreamError>>,
body_bytes: &mut Vec<u8>,
transformer: Option<&mut TupleSseEventTransformer>,
chunk: Bytes,
) -> bool {
let chunks = match transformer {
Some(transformer) => transformer.push(&chunk),
None => vec![chunk],
};
send_live_response_stream_chunks(sender, body_bytes, chunks).await
}
async fn flush_live_response_stream_transformer(
sender: &mpsc::Sender<Result<Bytes, ResponseDispatchStreamError>>,
body_bytes: &mut Vec<u8>,
transformer: Option<&mut TupleSseEventTransformer>,
) -> bool {
let Some(transformer) = transformer else {
return true;
};
send_live_response_stream_chunks(sender, body_bytes, transformer.finish()).await
}
async fn send_live_response_stream_chunks(
sender: &mpsc::Sender<Result<Bytes, ResponseDispatchStreamError>>,
body_bytes: &mut Vec<u8>,
chunks: Vec<Bytes>,
) -> bool {
for chunk in chunks {
body_bytes.extend_from_slice(&chunk);
if sender.send(Ok(chunk)).await.is_err() {
return false;
}
}
true
}
struct TupleSseEventTransformer {
tuple_schema: Value,
pending: Vec<u8>,
}
impl TupleSseEventTransformer {
fn new(tuple_schema: Value) -> Self {
Self {
tuple_schema,
pending: Vec::new(),
}
}
fn push(&mut self, chunk: &[u8]) -> Vec<Bytes> {
self.pending.extend_from_slice(chunk);
let mut chunks = Vec::new();
while let Some(frame_end) = next_sse_frame_end(&self.pending) {
let frame = self.pending.drain(..frame_end).collect::<Vec<_>>();
chunks.push(self.transform_frame(&frame));
}
chunks
}
fn finish(&mut self) -> Vec<Bytes> {
if self.pending.is_empty() {
return Vec::new();
}
let frame = std::mem::take(&mut self.pending);
vec![self.transform_frame(&frame)]
}
fn transform_frame(&self, frame: &[u8]) -> Bytes {
let frame_text = String::from_utf8_lossy(frame);
let Ok(events) = parse_sse_events(&frame_text) else {
return Bytes::copy_from_slice(frame);
};
let [event] = events.as_slice() else {
return Bytes::copy_from_slice(frame);
};
let Ok(data) = serde_json::from_str::<Value>(&event.data) else {
return Bytes::copy_from_slice(frame);
};
let transformed = reconvert_responses_sse_event_tuple_values(
event.event.as_deref(),
data,
&self.tuple_schema,
);
Bytes::from(encode_sse_event(
event.event.as_deref().unwrap_or_default(),
&transformed.to_string(),
))
}
}
fn next_sse_frame_end(bytes: &[u8]) -> Option<usize> {
let lf_lf = bytes
.windows(2)
.position(|window| window == b"\n\n")
.map(|position| position + 2);
let crlf_crlf = bytes
.windows(4)
.position(|window| window == b"\r\n\r\n")
.map(|position| position + 4);
match (lf_lf, crlf_crlf) {
(Some(left), Some(right)) => Some(left.min(right)),
(Some(end), None) | (None, Some(end)) => Some(end),
(None, None) => None,
}
}
async fn send_live_response_stream_tail(
sender: &mpsc::Sender<Result<Bytes, ResponseDispatchStreamError>>,
body_bytes: &mut Vec<u8>,
failure_detail: Option<&str>,
) -> Option<String> {
let mut body_text = String::from_utf8_lossy(body_bytes).to_string();
if !sse_body_has_terminal_event(&body_text) {
if let Some(separator) = missing_sse_event_separator(&body_text) {
body_text.push_str(separator);
body_bytes.extend_from_slice(separator.as_bytes());
if sender
.send(Ok(Bytes::copy_from_slice(separator.as_bytes())))
.await
.is_err()
{
return None;
}
}
let failure =
premature_close_failed_event(latest_response_id(&body_text).as_deref(), failure_detail);
body_text.push_str(&failure);
body_bytes.extend_from_slice(failure.as_bytes());
if sender.send(Ok(Bytes::from(failure))).await.is_err() {
return None;
}
}
if !sse_body_has_done(&body_text) {
body_text.push_str(DONE_SSE_FRAME);
body_bytes.extend_from_slice(DONE_SSE_FRAME.as_bytes());
if sender
.send(Ok(Bytes::from_static(DONE_SSE_FRAME.as_bytes())))
.await
.is_err()
{
return None;
}
}
Some(body_text)
}
fn sse_body_has_terminal_event(body: &str) -> bool {
parse_sse_events(body).is_ok_and(|events| {
events.iter().any(|event| {
matches!(
event.event.as_deref(),
Some("response.completed" | "response.failed" | "error")
)
})
})
}
fn missing_sse_event_separator(body: &str) -> Option<&'static str> {
if body.is_empty()
|| body.ends_with("\n\n")
|| body.ends_with("\r\n\r\n")
|| body.ends_with("\r\r")
{
None
} else if body.ends_with('\n') || body.ends_with('\r') {
Some("\n")
} else {
Some("\n\n")
}
}
fn latest_response_id(body: &str) -> Option<String> {
parse_sse_events(body).ok().and_then(|events| {
events.iter().rev().find_map(|event| {
serde_json::from_str::<Value>(&event.data)
.ok()
.and_then(|data| {
data.pointer("/response/id")
.and_then(Value::as_str)
.filter(|id| !id.trim().is_empty())
.map(ToString::to_string)
})
})
})
}
fn premature_close_failed_event(response_id: Option<&str>, detail: Option<&str>) -> String {
let message = match detail.filter(|value| !value.trim().is_empty()) {
Some(detail) => format!("Upstream stream closed before response.completed: {detail}"),
None => "Upstream stream closed before response.completed".to_string(),
};
response_failed_sse_event_with_id(response_id, "server_error", "stream_disconnected", &message)
}
async fn finalize_live_response_stream(
context: LiveResponseStreamContext,
body: String,
first_token_ms: Option<i64>,
) {
let rate_limit_headers = live_response_rate_limit_headers(&context).await;
let turn_state = live_response_turn_state(&context).await;
let usage = match extract_sse_usage(&body) {
Ok(Some(usage)) => {
context
.account_pool
.record_token_usage(&context.account_id, &context.model, &usage)
.await;
Some(usage)
}
Ok(None) => None,
Err(error) => {
tracing::warn!(account_id = %context.account_id, error = %error, "failed to extract streaming token usage");
None
}
};
match response_from_codex_sse(&body, context.tuple_schema.as_ref()) {
Ok(CollectedResponse::Completed(completed)) => {
context
.cloudflare
.reset_account_recovery(&context.account_id)
.await;
let response_id = completed.get("id").and_then(Value::as_str);
record_response_affinity(
&context.session_affinity,
&context.reasoning_replay,
&context.request,
&context.account_id,
&body,
turn_state,
usage,
)
.await;
record_live_response_stream_event(
&context,
200,
UsageRecordLevel::Info,
"v1 responses stream completed",
serde_json::json!({
"stream": true,
"completed": true,
"responseId": response_id,
"firstTokenMs": first_token_ms,
"usage": usage,
}),
&rate_limit_headers,
&body,
)
.await;
}
Ok(CollectedResponse::Failed(failure)) => {
if sse_failure_invalid_reasoning_replay(&failure) {
evict_reasoning_replay(
&context.reasoning_replay,
&context.request,
&context.account_id,
)
.await;
}
tracing::warn!(
account_id = %context.account_id,
event = %failure.event,
code = ?failure.upstream_code.as_deref(),
"live upstream stream ended with response.failed"
);
let mut metadata = stream_failure_metadata(&failure, usage);
insert_first_token_ms(&mut metadata, first_token_ms);
record_live_response_stream_event(
&context,
status_code_for_stream_failure(&failure),
UsageRecordLevel::Error,
"v1 responses stream failed",
metadata,
&rate_limit_headers,
&body,
)
.await;
}
Ok(CollectedResponse::MissingCompleted | CollectedResponse::Empty) => {
tracing::warn!(
account_id = %context.account_id,
"live upstream stream ended without response.completed"
);
let mut metadata = serde_json::json!({
"stream": true,
"failed": true,
"upstreamCode": "missing_completed",
"usage": usage,
});
insert_first_token_ms(&mut metadata, first_token_ms);
record_live_response_stream_event(
&context,
502,
UsageRecordLevel::Error,
"v1 responses stream ended without response.completed",
metadata,
&rate_limit_headers,
&body,
)
.await;
}
Err(error) => {
tracing::warn!(account_id = %context.account_id, error = %error, "failed to parse completed live stream");
let mut metadata = serde_json::json!({
"stream": true,
"sseParseError": error.to_string(),
"usage": usage,
});
insert_first_token_ms(&mut metadata, first_token_ms);
record_live_response_stream_event(
&context,
502,
UsageRecordLevel::Warn,
"v1 responses stream SSE response invalid",
metadata,
&rate_limit_headers,
&body,
)
.await;
}
}
context.account_pool.release(&context.account_id).await;
}
// ====================================================================
// Event recording helpers
// ====================================================================
struct ResponseUpstreamErrorEventRecord<'a> {
usage_records: &'a AdminUsageRecordService,
request_id: &'a str,
account_id: &'a str,
route: &'a str,
model: &'a str,
started_at: Instant,
stream: bool,
transport: CodexBackendTransport,
error: &'a CodexClientError,
}
struct ResponseStreamFailureEventRecord<'a> {
usage_records: &'a AdminUsageRecordService,
request_id: &'a str,
account_id: &'a str,
route: &'a str,
model: &'a str,
requested_model: &'a str,
started_at: Instant,
transport: CodexBackendTransport,
request: &'a CodexResponsesRequest,
failure: &'a ResponsesSseFailure,
rate_limit_headers: &'a [(String, String)],
prefetched: &'a [u8],
}
struct ResponseDispatchErrorEventRecord<'a> {
usage_records: &'a AdminUsageRecordService,
request_id: &'a str,
account_id: Option<&'a str>,
route: &'a str,
model: &'a str,
started_at: Instant,
stream: bool,
compact: bool,
transport: Option<&'a str>,
error: &'a ResponseDispatchError,
}
struct ResponseDispatchErrorDetails<'a> {
account_id: Option<&'a str>,
stream: bool,
compact: bool,
transport: Option<&'a str>,
}
async fn record_response_dispatch_error_event(record: ResponseDispatchErrorEventRecord<'_>) {
let mut metadata = dispatch_error_metadata(
record.error,
record.stream,
record.compact,
record.transport,
);
enrich_response_dispatch_error_metadata(&mut metadata, record.error);
enrich_event_route_metadata(&mut metadata, record.route);
let mut event = UsageRecord::new(
response_event_kind(record.route),
UsageRecordLevel::Error,
"v1 responses dispatch failed",
);
event.request_id = Some(record.request_id.to_string());
event.account_id = record.account_id.map(ToString::to_string);
event.route = Some(record.route.to_string());
event.model = Some(record.model.to_string());
event.status_code = Some(i64::from(record.error.http_status_code()));
event.latency_ms = Some(elapsed_millis_i64(record.started_at));
event.metadata = metadata;
if let Err(error) = record.usage_records.record(event).await {
tracing::warn!(
account_id = record.account_id.unwrap_or(""),
error = %error,
"failed to record response dispatch error event"
);
}
}
async fn record_response_upstream_error_event(record: ResponseUpstreamErrorEventRecord<'_>) {
let mut metadata = dispatch_error_metadata(
record.error,
record.stream,
false,
Some(backend_transport_name(record.transport)),
);
enrich_event_route_metadata(&mut metadata, record.route);
let mut event = UsageRecord::new(
"v1.response",
UsageRecordLevel::Error,
"v1 responses upstream request failed",
);
event.request_id = Some(record.request_id.to_string());
event.account_id = Some(record.account_id.to_string());
event.route = Some(record.route.to_string());
event.model = Some(record.model.to_string());
event.status_code = Some(i64::from(upstream_error_http_status(record.error)));
event.latency_ms = Some(elapsed_millis_i64(record.started_at));
event.metadata = metadata;
if let Err(error) = record.usage_records.record(event).await {
tracing::warn!(account_id = %record.account_id, error = %error, "failed to record upstream error event");
}
}
async fn record_prefetched_response_stream_failure_event(
record: ResponseStreamFailureEventRecord<'_>,
) {
let mut metadata = stream_failure_metadata(record.failure, None);
if let Some(object) = metadata.as_object_mut() {
if record.transport == CodexBackendTransport::WebSocket {
object.insert(
"transport".to_string(),
Value::String("websocket".to_string()),
);
}
object.insert("requestBody".to_string(), json!(record.request));
object.insert(
"responseBody".to_string(),
Value::String(String::from_utf8_lossy(record.prefetched).to_string()),
);
}
record_response_event(ResponseUsageRecord {
usage_records: record.usage_records,
request_id: record.request_id,
account_id: record.account_id,
route: record.route,
model: record.model,
requested_model: Some(record.requested_model),
client_ip: record.request.client_ip.as_deref(),
client_user_agent: record.request.client_user_agent.as_deref(),
reasoning_effort: reasoning_effort_from_request(record.request),
started_at: record.started_at,
status_code: status_code_for_stream_failure(record.failure),
level: UsageRecordLevel::Error,
message: "v1 responses stream failed",
metadata,
rate_limit_headers: record.rate_limit_headers,
})
.await;
}
async fn record_response_event(record: ResponseUsageRecord<'_>) {
let mut metadata = record.metadata;
enrich_event_route_metadata(&mut metadata, record.route);
let mut event = UsageRecord::new(
response_event_kind(record.route),
record.level,
record.message,
);
event.request_id = Some(record.request_id.to_string());
event.account_id = Some(record.account_id.to_string());
event.route = Some(record.route.to_string());
event.model = Some(record.model.to_string());
event.status_code = Some(record.status_code);
event.latency_ms = Some(elapsed_millis_i64(record.started_at));
enrich_usage_record_identity(
&mut metadata,
record.requested_model,
record.model,
record.client_ip,
record.client_user_agent,
record.reasoning_effort,
);
event.metadata = metadata;
let rate_limit_headers = record.rate_limit_headers;
if !rate_limit_headers.is_empty() {
if let Some(object) = event.metadata.as_object_mut() {
object.insert(
"rateLimitHeaders".to_string(),
serde_json::json!(rate_limit_headers),
);
}
}
if let Err(error) = record.usage_records.record(event).await {
tracing::warn!(account_id = %record.account_id, error = %error, "failed to record response event");
}
}
fn response_event_kind(route: &str) -> &'static str {
if route == "/v1/chat/completions" {
"v1.chat"
} else {
"v1.response"
}
}
fn response_api_kind(route: &str) -> &'static str {
if route == "/v1/chat/completions" {
"chat"
} else {
"responses"
}
}
fn enrich_event_route_metadata(metadata: &mut Value, route: &str) {
let Some(object) = metadata.as_object_mut() else {
return;
};
object
.entry("route".to_string())
.or_insert_with(|| Value::String(route.to_string()));
object
.entry("apiKind".to_string())
.or_insert_with(|| Value::String(response_api_kind(route).to_string()));
}
fn enrich_usage_record_identity(
metadata: &mut Value,
requested_model: Option<&str>,
upstream_model: &str,
client_ip: Option<&str>,
client_user_agent: Option<&str>,
reasoning_effort: Option<&str>,
) {
let Some(object) = metadata.as_object_mut() else {
return;
};
let upstream_model = upstream_model.trim();
let requested_model = requested_model
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or(upstream_model);
object.insert(
"requestedModel".to_string(),
Value::String(requested_model.to_string()),
);
object.insert(
"upstreamModel".to_string(),
Value::String(upstream_model.to_string()),
);
if let Some(client_ip) = client_ip.map(str::trim).filter(|value| !value.is_empty()) {
object.insert("clientIp".to_string(), Value::String(client_ip.to_string()));
}
if let Some(user_agent) = client_user_agent
.map(str::trim)
.filter(|value| !value.is_empty())
{
object.insert(
"userAgent".to_string(),
Value::String(user_agent.to_string()),
);
}
if let Some(reasoning_effort) = reasoning_effort
.map(str::trim)
.filter(|value| !value.is_empty())
{
object.insert(
"reasoningEffort".to_string(),
Value::String(reasoning_effort.to_string()),
);
}
}
fn reasoning_effort_from_request(request: &CodexResponsesRequest) -> Option<&str> {
reasoning_effort_from_value(request.reasoning.as_ref())
}
fn reasoning_effort_from_compact_request(request: &CodexCompactRequest) -> Option<&str> {
reasoning_effort_from_value(request.reasoning.as_ref())
}
fn reasoning_effort_from_value(reasoning: Option<&Value>) -> Option<&str> {
reasoning?
.get("effort")
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
}
fn ensure_stream_metadata_flag(metadata: &mut Value) {
let Some(object) = metadata.as_object_mut() else {
*metadata = serde_json::json!({ "stream": true });
return;
};
object
.entry("stream".to_string())
.or_insert(Value::Bool(true));
}
fn enrich_live_response_stream_metadata(
context: &LiveResponseStreamContext,
rate_limit_headers: &[(String, String)],
metadata: &mut Value,
body: &str,
) {
let Some(object) = metadata.as_object_mut() else {
return;
};
object
.entry("transport".to_string())
.or_insert_with(|| Value::String(backend_transport_name(context.transport).to_string()));
if !rate_limit_headers.is_empty() {
object
.entry("rateLimitHeaders".to_string())
.or_insert_with(|| serde_json::json!(rate_limit_headers));
}
object
.entry("requestBody".to_string())
.or_insert_with(|| serde_json::json!(context.request));
object
.entry("responseBody".to_string())
.or_insert_with(|| Value::String(body.to_string()));
}
async fn record_live_response_stream_event(
context: &LiveResponseStreamContext,
status_code: i64,
level: UsageRecordLevel,
message: &str,
mut metadata: Value,
rate_limit_headers: &[(String, String)],
body: &str,
) {
ensure_stream_metadata_flag(&mut metadata);
enrich_event_route_metadata(&mut metadata, &context.route);
enrich_live_response_stream_metadata(context, rate_limit_headers, &mut metadata, body);
let mut event = UsageRecord::new(response_event_kind(&context.route), level, message);
event.request_id = Some(context.request_id.clone());
event.account_id = Some(context.account_id.clone());
event.route = Some(context.route.clone());
event.model = Some(context.model.clone());
event.status_code = Some(status_code);
event.latency_ms = Some(elapsed_millis_i64(context.started_at));
enrich_usage_record_identity(
&mut metadata,
Some(&context.requested_model),
&context.model,
context.client_ip.as_deref(),
context.request.client_user_agent.as_deref(),
reasoning_effort_from_request(&context.request),
);
event.metadata = metadata;
if let Err(error) = context.usage_records.record(event).await {
tracing::warn!(account_id = %context.account_id, error = %error, "failed to record live response stream event");
}
}
async fn live_response_rate_limit_headers(
context: &LiveResponseStreamContext,
) -> Vec<(String, String)> {
let mut headers = context.rate_limit_headers.clone();
if let Some(updates) = &context.rate_limit_header_updates {
headers.extend(updates.lock().await.iter().cloned());
}
headers
}
async fn live_response_turn_state(context: &LiveResponseStreamContext) -> Option<String> {
if let Some(update) = &context.turn_state_update {
return update.lock().await.clone();
}
context.turn_state.clone()
}
fn stream_failure_metadata(failure: &ResponsesSseFailure, usage: Option<TokenUsage>) -> Value {
serde_json::json!({
"stream": true,
"failed": true,
"failureEvent": failure.event,
"failureMessage": failure.message,
"upstreamCode": failure.upstream_code,
"usage": usage,
})
}
fn insert_first_token_ms(metadata: &mut Value, first_token_ms: Option<i64>) {
let Some(first_token_ms) = first_token_ms else {
return;
};
if let Some(object) = metadata.as_object_mut() {
object.insert(
"firstTokenMs".to_string(),
Value::Number(first_token_ms.into()),
);
}
}
fn status_code_for_stream_failure(failure: &ResponsesSseFailure) -> i64 {
let code = failure
.upstream_code
.as_deref()
.unwrap_or("error")
.to_ascii_lowercase();
if code.contains("model") && (code.contains("not_supported") || code.contains("not_available"))
{
return 400;
}
if code.contains("invalid_request") || code.contains("not_found") {
return 400;
}
if code.contains("context_window")
|| code.contains("invalid_prompt")
|| code.contains("cyber_policy")
|| code.contains("bad_request")
{
return 400;
}
if code.contains("rate_limit") || code.contains("usage_limit") {
return 429;
}
if code.contains("unauthorized")
|| code.contains("invalid_api_key")
|| code == "token_invalid"
|| code == "token_expired"
|| code == "account_deactivated"
{
return 401;
}
if code.contains("forbidden") || code.contains("banned") {
return 403;
}
if code.contains("payment") || code.contains("quota") {
return 402;
}
if code.contains("server_overloaded") {
return 503;
}
502
}
fn stream_failure_http_status(failure: &ResponsesSseFailure) -> u16 {
u16::try_from(status_code_for_stream_failure(failure)).unwrap_or(502)
}
// ====================================================================
// Affinity + replay recording
// ====================================================================
async fn record_response_affinity(
session_affinity: &Arc<RuntimeSessionAffinityService>,
reasoning_replay: &Arc<Mutex<ReasoningReplayCache>>,
request: &CodexResponsesRequest,
account_id: &str,
body: &str,
turn_state: Option<String>,
usage: Option<TokenUsage>,
) {
let Some(conversation_id) = request
.prompt_cache_key
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
else {
return;
};
let metadata = match completed_response_metadata(body) {
Ok(Some(metadata)) => metadata,
Ok(None) => return,
Err(error) => {
tracing::warn!(
error = %error,
"failed to parse completed response metadata for session affinity"
);
return;
}
};
let variant_hash = compute_variant_hash(request);
let entry = crate::proxy::dispatch::session_affinity::SessionAffinityEntry {
account_id: account_id.to_string(),
conversation_id: conversation_id.to_string(),
turn_state: turn_state
.filter(|value| !value.trim().is_empty())
.or_else(|| request.turn_state.clone()),
instructions_hash: Some(hash_instructions(Some(&request.instructions))),
input_tokens: usage.map(|usage| usage.input_tokens),
function_call_ids: metadata.function_call_ids,
variant_hash: Some(variant_hash.clone()),
created_at: Utc::now(),
};
if let Err(error) = session_affinity
.record(metadata.response_id.clone(), entry)
.await
{
tracing::warn!(
error = %error,
response_id = %metadata.response_id,
account_id = %account_id,
"failed to record session affinity"
);
}
reasoning_replay.lock().await.record(
metadata.response_id,
account_id,
conversation_id,
&variant_hash,
&metadata.replay_items,
Utc::now(),
);
}
async fn evict_reasoning_replay(
reasoning_replay: &Arc<Mutex<ReasoningReplayCache>>,
request: &CodexResponsesRequest,
account_id: &str,
) {
let variant_hash = compute_variant_hash(request);
let conversation_id = request
.prompt_cache_key
.as_deref()
.unwrap_or("")
.to_string();
reasoning_replay.lock().await.evict_by_identity(
account_id,
&conversation_id,
&variant_hash,
Utc::now(),
);
}
// ====================================================================
// ResponseDispatchError and ResponseDispatchStreamError
// ====================================================================
/// Responses 调度错误。
#[derive(Debug, Error)]
pub enum ResponseDispatchError {
#[error("failed to list runtime accounts")]
AccountStore,
#[error("no active account is available")]
NoActiveAccount,
#[error("all accounts exhausted by quota")]
QuotaExhausted {
count: usize,
upstream_error: String,
},
#[error("all accounts exhausted by rate limit")]
RateLimited {
count: usize,
upstream_error: String,
},
#[error("all accounts exhausted by expired auth")]
Expired {
count: usize,
upstream_error: String,
},
#[error("all accounts exhausted by disabled auth")]
Disabled {
count: usize,
upstream_error: String,
},
#[error("all accounts exhausted by banned auth")]
Banned {
count: usize,
upstream_error: String,
status_code: u16,
},
#[error("all accounts exhausted by Cloudflare challenge")]
CloudflareChallenge {
count: usize,
upstream_error: String,
},
#[error("all accounts exhausted by Cloudflare path-block")]
CloudflarePathBlocked {
count: usize,
upstream_error: String,
},
#[error("all accounts exhausted by unsupported model")]
ModelUnsupported {
count: usize,
upstream_error: String,
},
#[error("upstream request failed: {0}")]
Upstream(#[from] CodexClientError),
#[error("invalid upstream SSE response: {0}")]
InvalidSse(#[from] SseError),
#[error("upstream response did not include response.completed")]
MissingCompleted,
#[error("upstream response did not include visible output")]
EmptyUpstreamResponse,
#[error("upstream response failed: {0:?}")]
Failed(ResponsesSseFailure),
}
impl ResponseDispatchError {
pub fn http_status_code(&self) -> u16 {
match self {
Self::NoActiveAccount | Self::AccountStore => 503,
Self::QuotaExhausted { .. } => 402,
Self::RateLimited { .. } => 429,
Self::Expired { .. } | Self::Disabled { .. } => 401,
Self::Banned { status_code, .. } => *status_code,
Self::CloudflareChallenge { .. }
| Self::CloudflarePathBlocked { .. }
| Self::InvalidSse(_)
| Self::MissingCompleted
| Self::EmptyUpstreamResponse => 502,
Self::Failed(failure) => stream_failure_http_status(failure),
Self::ModelUnsupported { .. } => 400,
Self::Upstream(error) => upstream_error_http_status(error),
}
}
}
/// Responses live SSE body stream error.
#[derive(Debug, Error)]
pub enum ResponseDispatchStreamError {
#[error("upstream stream failed: {0}")]
Upstream(#[from] CodexClientError),
}
fn dispatch_error_metadata(
error: impl std::fmt::Display,
stream: bool,
compact: bool,
transport: Option<&str>,
) -> Value {
let mut metadata = serde_json::json!({
"stream": stream,
"failed": true,
"errorKind": "dispatch",
"error": error.to_string(),
});
let Some(object) = metadata.as_object_mut() else {
return metadata;
};
if compact {
object.insert("compact".to_string(), Value::Bool(true));
}
if let Some(transport) = transport {
object.insert(
"transport".to_string(),
Value::String(transport.to_string()),
);
}
metadata
}
fn enrich_response_dispatch_error_metadata(metadata: &mut Value, error: &ResponseDispatchError) {
let Some(object) = metadata.as_object_mut() else {
return;
};
let (failure_class, exhausted_count, upstream_error, upstream_status) = match error {
ResponseDispatchError::AccountStore => ("account_store", None, None, None),
ResponseDispatchError::NoActiveAccount => ("no_available_accounts", None, None, None),
ResponseDispatchError::QuotaExhausted {
count,
upstream_error,
} => (
"quota_exhausted",
Some(*count),
Some(upstream_error.clone()),
None,
),
ResponseDispatchError::RateLimited {
count,
upstream_error,
} => (
"rate_limited",
Some(*count),
Some(upstream_error.clone()),
None,
),
ResponseDispatchError::Expired {
count,
upstream_error,
} => ("expired", Some(*count), Some(upstream_error.clone()), None),
ResponseDispatchError::Disabled {
count,
upstream_error,
} => ("disabled", Some(*count), Some(upstream_error.clone()), None),
ResponseDispatchError::Banned {
count,
upstream_error,
..
} => ("banned", Some(*count), Some(upstream_error.clone()), None),
ResponseDispatchError::CloudflareChallenge {
count,
upstream_error,
} => (
"cloudflare_challenge",
Some(*count),
Some(upstream_error.clone()),
None,
),
ResponseDispatchError::CloudflarePathBlocked {
count,
upstream_error,
} => (
"cloudflare_path_blocked",
Some(*count),
Some(upstream_error.clone()),
None,
),
ResponseDispatchError::ModelUnsupported {
count,
upstream_error,
} => (
"model_unsupported",
Some(*count),
Some(upstream_error.clone()),
None,
),
ResponseDispatchError::Upstream(error) => {
let upstream_status = match error {
CodexClientError::Upstream { status, .. } => Some(status.as_u16()),
_ => None,
};
(
"upstream",
None,
Some(upstream_error_body(error)),
upstream_status,
)
}
ResponseDispatchError::InvalidSse(_) => ("invalid_sse", None, None, None),
ResponseDispatchError::MissingCompleted => ("missing_completed", None, None, None),
ResponseDispatchError::EmptyUpstreamResponse => {
("empty_upstream_response", None, None, None)
}
ResponseDispatchError::Failed(failure) => (
"response_failed",
None,
Some(sse_failure_error_body(failure)),
None,
),
};
object.insert(
"failureClass".to_string(),
Value::String(failure_class.to_string()),
);
if let Some(count) = exhausted_count {
object.insert("exhaustedCount".to_string(), json!(count));
}
if let Some(error) = upstream_error {
object.insert("upstreamError".to_string(), Value::String(error));
}
if let Some(status) = upstream_status {
object.insert("upstreamStatus".to_string(), json!(status));
}
}
fn elapsed_millis_i64(started_at: Instant) -> i64 {
started_at.elapsed().as_millis().min(i64::MAX as u128) as i64
}