mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 16:06:08 +00:00
style: describe behavior in comments instead of narrating changes
Rewrite the refresh, token-classification, passthrough, and stream-tolerance comments to state the behavior and constraints they guard, dropping change narration and reviewer-facing baseline references. Also apply rustfmt reflow in the openai test files. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ use super::types::{CodexCredentialPrincipal, CodexOAuthSecret, RotateCodexCreden
|
||||
|
||||
const PROVIDER_NAME: &str = "openai";
|
||||
const MAX_REFRESH_BATCH: u32 = 1_000;
|
||||
/// 连续失败计数窗口;每次瞬态失败刷新该 TTL,静默满窗后计数自动归零。
|
||||
/// 连续失败计数窗口;每次瞬态失败刷新该 TTL,静默满窗后计数过期归零。
|
||||
const REFRESH_BACKOFF_WINDOW: Duration = Duration::from_secs(30 * 60);
|
||||
|
||||
pub struct DueCodexCredential {
|
||||
@@ -207,8 +207,8 @@ impl CodexCredentialRefreshService {
|
||||
}
|
||||
}
|
||||
Err(RefreshFailure::Transport) => {
|
||||
// 上游瞬态(429/5xx/超时/畸形响应等,可能已发出)不再永久失效账号:
|
||||
// 保留现有凭据、推进退避重试,仅 invalid_grant/banned 才终态。
|
||||
// 上游瞬态(429/5xx/超时/畸形响应等,发送状态未知)保留现有凭据、
|
||||
// 推进退避重试;仅 invalid_grant/banned 才终态失效账号。
|
||||
if self
|
||||
.defer_refresh(&due.account, "transport-ambiguous")
|
||||
.await?
|
||||
@@ -330,8 +330,8 @@ impl CodexCredentialRefreshService {
|
||||
.await;
|
||||
}
|
||||
Err(CodexIdentityVerificationError::Unavailable) => {
|
||||
// JWKS/签名边界短暂不可用是瞬态:保留现有凭据、推进退避重试,
|
||||
// 不再永久失效账号(defer_refresh 正是为“签名边界不可用”而设计)。
|
||||
// JWKS/签名边界短暂不可用属瞬态:保留现有凭据、推进退避重试,
|
||||
// 不终态失效账号。
|
||||
return if self
|
||||
.defer_refresh(&due.account, "identity-unavailable")
|
||||
.await?
|
||||
@@ -402,7 +402,7 @@ impl CodexCredentialRefreshService {
|
||||
.await;
|
||||
match result {
|
||||
Ok(revision) => {
|
||||
// 凭据完整成功轮换:清零连续失败计数,退避窗口重新从 base 起步。
|
||||
// 凭据完整成功轮换后清零连续失败计数,退避窗口重新从 base 起步。
|
||||
let _ = self
|
||||
.credential_state
|
||||
.clear_refresh_backoff(due.account.id())
|
||||
|
||||
@@ -308,12 +308,12 @@ fn parse_token_pair(body: &[u8]) -> Result<TokenPair, ()> {
|
||||
}
|
||||
|
||||
fn classify_refresh_failure(status: StatusCode, body: &[u8]) -> RefreshFailure {
|
||||
// 服务器错误与限流按状态码判为瞬态:5xx/CDN/网关页正文不是权威 OAuth 错误响应,
|
||||
// 不能据其把账号永久终态(对齐官方 codex-rs:只 401/明确 expired/reused 才 Permanent)。
|
||||
// 5xx 与 429 按状态码判为瞬态:其正文(CDN/网关页等)不是权威 OAuth 错误响应,
|
||||
// 不据此终态失效账号。
|
||||
if status.is_server_error() || status == StatusCode::TOO_MANY_REQUESTS {
|
||||
return RefreshFailure::Transport;
|
||||
}
|
||||
// 4xx:正文才是权威 OAuth 错误响应,据其区分真过期/封禁与其余瞬态。
|
||||
// 4xx 正文才是权威 OAuth 错误响应,据其区分真过期/封禁与其余瞬态。
|
||||
let lower = String::from_utf8_lossy(body).to_ascii_lowercase();
|
||||
if lower.contains("account has been deactivated") {
|
||||
return RefreshFailure::Banned;
|
||||
|
||||
@@ -818,10 +818,10 @@ fn cold_response_stream(response: ColdResponse) -> EventStream {
|
||||
let failure_rate_limit_headers = response.rate_limit_headers.clone();
|
||||
let rate_limit_updates = response.rate_limit_header_updates;
|
||||
let turn_state_updates = response.turn_state_update;
|
||||
// HTTP SSE 与 WebSocket 两条上游都启用 raw 透传:OpenAI 线路保持透明代理。
|
||||
// WS 的 body 由 reducer 以 encode_sse_event(&event, raw) 逐字节内嵌上游原文
|
||||
// (transport/protocol/websocket.rs),push_frames 抽出的 data 即上游原始 JSON,
|
||||
// 下游按字节转发,避免 serde 往返改写数值/精度(大整数→f64、logprobs 等)。
|
||||
// OpenAI 线路为透明代理:HTTP SSE 与 WebSocket 两条上游均启用 raw 透传,
|
||||
// 下游按字节转发上游原文,避免 serde 往返改写数值/精度(大整数→f64、logprobs 等)。
|
||||
// WS 帧由 reducer 以 encode_sse_event(&event, raw) 逐字节内嵌上游原始 JSON
|
||||
// (transport/protocol/websocket.rs),push_frames 抽出的 data 即上游原文。
|
||||
let mut decoder =
|
||||
CodexCanonicalDecoder::new(upstream_model.as_str()).with_raw_sse_passthrough();
|
||||
loop {
|
||||
|
||||
@@ -725,7 +725,9 @@ async fn refresh_backoff_grows_exponentially_across_attempts() {
|
||||
first.as_slice(),
|
||||
[CodexCredentialRefreshOutcome::Transient { .. }]
|
||||
));
|
||||
let first_account = store.account("acct_refresh").expect("account after first defer");
|
||||
let first_account = store
|
||||
.account("acct_refresh")
|
||||
.expect("account after first defer");
|
||||
let first_delay = first_account
|
||||
.next_refresh_at()
|
||||
.expect("first retry scheduled")
|
||||
|
||||
@@ -69,10 +69,9 @@ fn websocket_raw_passthrough_should_preserve_upstream_number_bytes() {
|
||||
// OpenAI 线路透明代理:WebSocket 上游帧经 reducer→SSE→decoder(raw 透传) 后,
|
||||
// data 段必须与上游原文逐字节一致,不得经 serde 往返改写数值/精度。
|
||||
// `1e3` 若被重序列化会变成 `1000.0`——用它作为字节改写的探针。
|
||||
let upstream =
|
||||
r#"{"type":"response.created","response":{"id":"resp_ws_raw","model":"gpt-test","x_precision":1e3}}"#;
|
||||
let frame =
|
||||
websocket_event_to_sse_frame(upstream).expect("client-visible WS event yields an SSE frame");
|
||||
let upstream = r#"{"type":"response.created","response":{"id":"resp_ws_raw","model":"gpt-test","x_precision":1e3}}"#;
|
||||
let frame = websocket_event_to_sse_frame(upstream)
|
||||
.expect("client-visible WS event yields an SSE frame");
|
||||
|
||||
let events = CodexCanonicalDecoder::new("fallback")
|
||||
.with_raw_sse_passthrough()
|
||||
@@ -83,10 +82,16 @@ fn websocket_raw_passthrough_should_preserve_upstream_number_bytes() {
|
||||
.filter_map(ProviderEvent::wire_event)
|
||||
.find(|wire| wire.has_json_data())
|
||||
.expect("response.created wire");
|
||||
let raw = wire.raw_sse_frame().map(AsRef::as_ref).expect("raw frame bytes");
|
||||
let raw = wire
|
||||
.raw_sse_frame()
|
||||
.map(AsRef::as_ref)
|
||||
.expect("raw frame bytes");
|
||||
let raw = std::str::from_utf8(raw).expect("utf8 raw frame");
|
||||
|
||||
assert!(raw.contains("1e3"), "raw frame must keep upstream `1e3` verbatim: {raw}");
|
||||
assert!(
|
||||
raw.contains("1e3"),
|
||||
"raw frame must keep upstream `1e3` verbatim: {raw}"
|
||||
);
|
||||
assert!(
|
||||
!raw.contains("1000.0"),
|
||||
"raw frame must not re-serialize the number via serde: {raw}"
|
||||
@@ -101,10 +106,7 @@ fn wire_for_response_created(events: &[ProviderEvent]) -> &[GatewayEvent] {
|
||||
events
|
||||
.iter()
|
||||
.find(|event| {
|
||||
event
|
||||
.wire_event()
|
||||
.and_then(|wire| wire.event_type())
|
||||
== Some("response.created")
|
||||
event.wire_event().and_then(|wire| wire.event_type()) == Some("response.created")
|
||||
})
|
||||
.map_or(&[], ProviderEvent::canonical_facts)
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ impl GrokCanonicalDecoder {
|
||||
continue;
|
||||
};
|
||||
let event_type = event_type.to_owned();
|
||||
// 无法转换的上游帧跳过而非断流(转换失败多为畸形帧),避免打断客户端流。
|
||||
// 无法转换的上游帧(多为畸形帧)直接丢弃,保持已开始的客户端流不中断。
|
||||
let Ok(transformed) = self
|
||||
.response_transform
|
||||
.rewrite_stream_event(&event_type, value)
|
||||
@@ -225,10 +225,10 @@ impl GrokCanonicalDecoder {
|
||||
}
|
||||
let value = transformed.into_value();
|
||||
let mut canonical = Vec::new();
|
||||
// 终态事件(completed/incomplete)保留 fail-closed:用量/计费校验失败仍断流。
|
||||
// 终态事件(completed/incomplete)fail-closed:用量/计费校验失败即断流。
|
||||
// 其余内容事件容忍字段校验失败——正常上游变体(空 delta、重复 index、
|
||||
// 缺字段等)不应打断已开始的客户端流:跳过 canonical 提取、wire 原样转发。
|
||||
// 真·上游错误(response.failed/error)为非 Protocol 类别,仍按终态传播。
|
||||
// 缺字段等)不打断已开始的客户端流:跳过 canonical 提取、wire 原样转发。
|
||||
// 真·上游错误(response.failed/error)为非 Protocol 类别,按终态传播。
|
||||
let terminal_event = matches!(
|
||||
transformed_type.as_str(),
|
||||
"response.completed" | "response.incomplete"
|
||||
|
||||
Reference in New Issue
Block a user