diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 000000000..5a9134515
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+warnings = "deny"
diff --git a/.github/workflows/_quality.yml b/.github/workflows/_quality.yml
index 25d45dde4..8d383ffde 100644
--- a/.github/workflows/_quality.yml
+++ b/.github/workflows/_quality.yml
@@ -24,7 +24,6 @@ jobs:
CARGO_TERM_COLOR: always
CPR_TEST_DATABASE_URL: postgres://codex_proxy:codex_proxy@127.0.0.1:5432/codex_proxy
CPR_TEST_REDIS_URL: redis://127.0.0.1:6379
- RUSTFLAGS: -D warnings
services:
postgres:
image: postgres:18-bookworm@sha256:d9c83446333daec3f0588cc709adb80c26090b7f9f0f7ec8d43c243385d79818
@@ -56,7 +55,7 @@ jobs:
ref: ${{ inputs.ref }}
- name: Setup Rust
- uses: dtolnay/rust-toolchain@eac0f66a48bc4b70a10b9acd4c4e930f835d95ff # 1.95
+ uses: dtolnay/rust-toolchain@1a3a6d54512beeaffd394f8d516ca16f2c506a20 # 1.97
with:
components: rustfmt,clippy
@@ -71,7 +70,7 @@ jobs:
- name: Rust clippy
working-directory: backend
- run: cargo clippy --all-targets --all-features --locked -- -D warnings
+ run: cargo clippy --all-targets --all-features --locked
- name: Rust tests
working-directory: backend
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9b99d034b..8fe3dffe4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -205,7 +205,7 @@ jobs:
-v "$HOME/.cargo:/cargo" \
-v "$PWD/..:/workspace" \
-w /workspace/backend \
- rust:1.95-bookworm@sha256:6258907abe69656e41cd992e0b705cdcfabcbbe3db374f92ed2d47121282d4a1 \
+ rust:1.97-bookworm@sha256:7d0723df719e7f213b69dc7c8c595985c3f4b060cfbee4f7bc0e347a86fe3b6a \
cargo build --release --locked --target "${{ matrix.target }}" --bin codex-proxy-rs
cp "target/${{ matrix.target }}/release/${{ matrix.binary }}" "../dist/binaries/${{ matrix.os }}-${{ matrix.arch }}/${{ matrix.binary }}"
working-directory: backend
@@ -239,7 +239,7 @@ jobs:
ref: ${{ needs.release-info.outputs.git_sha }}
- name: Setup Rust
- uses: dtolnay/rust-toolchain@eac0f66a48bc4b70a10b9acd4c4e930f835d95ff # 1.95
+ uses: dtolnay/rust-toolchain@1a3a6d54512beeaffd394f8d516ca16f2c506a20 # 1.97
with:
targets: ${{ matrix.target }}
diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml
index 7629bc213..01a071f3e 100644
--- a/.github/workflows/security-scan.yml
+++ b/.github/workflows/security-scan.yml
@@ -26,7 +26,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Rust
- uses: dtolnay/rust-toolchain@eac0f66a48bc4b70a10b9acd4c4e930f835d95ff # 1.95
+ uses: dtolnay/rust-toolchain@1a3a6d54512beeaffd394f8d516ca16f2c506a20 # 1.97
- name: Cache Rust
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
diff --git a/.gitignore b/.gitignore
index c619267e2..3b25d6ca9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,43 +1,54 @@
-# Rust build output.
+# Rust
/backend/target/
-/target/
-/dist/
-/package/
-/checksums.txt
-/codex-proxy-rs_*.tar.gz
-/codex-proxy-rs_*.zip
+*.profraw
+*.profdata
-# Frontend dependencies and build output.
+# Frontend
/frontend/node_modules/
/frontend/dist/
-.pnpm-store
+/frontend/.vite/
+/.pnpm-store/
+/frontend/.pnpm-store/
+*.tsbuildinfo
-# Application runtime state.
-*.runtime
+# Release artifacts
+/dist/
+
+# Runtime data
+/.runtime/
*.sqlite
*.sqlite3
+*.sqlite-*
*.db
*.db-shm
*.db-wal
*.log
-# Local configuration.
-/local.yaml
-/local.yml
-/.env
+# Local configuration and secrets
+.env
+.env.*
+!.env.example
+!**/.env.example
+/.envrc
/config.yaml
/backend/config.yaml
-/deploy/.env
-# Local workspaces and tooling state.
+# Editors and operating systems
+/.idea/
+/.vscode/
+.DS_Store
+Thumbs.db
+*.swp
+*.swo
+*~
+
+# Local tools and worktrees
+/.codegraph/
+/.codex-ws-audit/
+/.agents/
+/.reasonix/
/.worktrees/
/worktrees/
-/.codex-ws-audit/
-/.codegraph/
+/.cargo/*
+!/.cargo/config.toml
*.tmp
-*.swp
-*~
-*.codegraph
-/.agents/
-/.cargo/
-/.reasonix/
diff --git a/README.md b/README.md
index 14bec442a..f1d24f6dc 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Codex Proxy RS
OpenAI Responses 兼容的 Codex 多账号网关
-
+
diff --git a/backend/Cargo.toml b/backend/Cargo.toml
index 5c7406a18..4a5473e72 100644
--- a/backend/Cargo.toml
+++ b/backend/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "codex-proxy-rs"
-edition = "2021"
+edition = "2024"
license = "MIT"
publish = false
-rust-version = "1.95"
+rust-version = "1.97"
[dependencies]
argon2 = "0.5.3"
diff --git a/backend/rust-toolchain.toml b/backend/rust-toolchain.toml
index ac3be6b9d..e8fcdf698 100644
--- a/backend/rust-toolchain.toml
+++ b/backend/rust-toolchain.toml
@@ -1,4 +1,4 @@
[toolchain]
-channel = "1.95"
+channel = "1.97"
+components = ["clippy", "rustfmt"]
profile = "minimal"
-components = ["rustfmt", "clippy"]
diff --git a/backend/src/api/admin/accounts_routes/lifecycle_routes.rs b/backend/src/api/admin/accounts_routes/lifecycle_routes.rs
index 423eac433..f4e57cc30 100644
--- a/backend/src/api/admin/accounts_routes/lifecycle_routes.rs
+++ b/backend/src/api/admin/accounts_routes/lifecycle_routes.rs
@@ -33,12 +33,12 @@ pub async fn health_check_accounts(
"stagger_ms must be between 500 and 30000",
));
}
- if let Some(concurrency) = payload.concurrency {
- if !(1..=10).contains(&concurrency) {
- return Err(AdminError::bad_request(
- "concurrency must be between 1 and 10",
- ));
- }
+ if let Some(concurrency) = payload.concurrency
+ && !(1..=10).contains(&concurrency)
+ {
+ return Err(AdminError::bad_request(
+ "concurrency must be between 1 and 10",
+ ));
}
match state
.services
diff --git a/backend/src/api/admin/accounts_routes/mod.rs b/backend/src/api/admin/accounts_routes/mod.rs
index 3b7f846f0..fb1330a8f 100644
--- a/backend/src/api/admin/accounts_routes/mod.rs
+++ b/backend/src/api/admin/accounts_routes/mod.rs
@@ -1,14 +1,14 @@
//! 账号管理 HTTP 处理器。
use axum::{
+ Json,
body::Body,
extract::{Query, State},
http::{
- header::{CACHE_CONTROL, CONNECTION, CONTENT_TYPE},
StatusCode,
+ header::{CACHE_CONTROL, CONNECTION, CONTENT_TYPE},
},
response::{IntoResponse, Response},
- Json,
};
use std::collections::{HashMap, HashSet};
@@ -17,22 +17,22 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
+ api::AppState,
api::admin::{
response::{
- parse_editable_update, AdminEnvelope, AdminError, AdminResponse, BatchDeleteData,
- EditableUpdateMessages, PageMeta, ADMIN_OK_CODE, ADMIN_OK_MESSAGE,
+ ADMIN_OK_CODE, ADMIN_OK_MESSAGE, AdminEnvelope, AdminError, AdminResponse,
+ BatchDeleteData, EditableUpdateMessages, PageMeta, parse_editable_update,
},
session::AdminAuth,
},
- api::AppState,
fleet::{
account::AccountStatus,
manage::{
- quota_view::{
- quota_data, AccountQuotaData, AccountQuotaUsageWindow, AccountQuotaWindowLocalUsage,
- },
AccountHealthCheck, AccountManageError, AccountRefreshOutcome, AccountRefreshResult,
AccountUpdate, ManagedAccount, OAuthExchangeInput,
+ quota_view::{
+ AccountQuotaData, AccountQuotaUsageWindow, AccountQuotaWindowLocalUsage, quota_data,
+ },
},
refresh::token_refresh_status_eligible,
},
diff --git a/backend/src/api/admin/auth_routes.rs b/backend/src/api/admin/auth_routes.rs
index 4c6c9b5f5..3ccf41605 100644
--- a/backend/src/api/admin/auth_routes.rs
+++ b/backend/src/api/admin/auth_routes.rs
@@ -1,21 +1,21 @@
//! 管理端认证路由。
use axum::{
- extract::State,
- http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode},
- response::{IntoResponse, Response},
Extension, Json,
+ extract::State,
+ http::{HeaderMap, HeaderValue, StatusCode, header::SET_COOKIE},
+ response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
use crate::{
api::{
+ AppState,
admin::{
response::{AdminEnvelope, AdminError, AdminResponse},
- session::{admin_session_cookie, ADMIN_SESSION_COOKIE},
+ session::{ADMIN_SESSION_COOKIE, admin_session_cookie},
},
middleware::request_id::ClientIp,
- AppState,
},
auth::types::SessionError,
infra::time::china_rfc3339,
@@ -107,15 +107,14 @@ pub(crate) async fn logout(
State(state): State,
headers: HeaderMap,
) -> Result {
- if let Some(session_id) = admin_session_cookie(&headers) {
- if let Err(error) = state
+ if let Some(session_id) = admin_session_cookie(&headers)
+ && let Err(error) = state
.services
.admin_sessions
.delete_session(&session_id)
.await
- {
- tracing::warn!(error = %error, "failed to revoke admin session during logout");
- }
+ {
+ tracing::warn!(error = %error, "failed to revoke admin session during logout");
}
let mut response = AdminResponse::new(
diff --git a/backend/src/api/admin/dashboard_routes.rs b/backend/src/api/admin/dashboard_routes.rs
index acb4c5297..ee1f5b176 100644
--- a/backend/src/api/admin/dashboard_routes.rs
+++ b/backend/src/api/admin/dashboard_routes.rs
@@ -12,15 +12,15 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
+ api::AppState,
api::admin::{
response::{AdminEnvelope, AdminError, AdminResponse},
session::AdminAuth,
usage_routes::{
- usage_cost_details, usage_record_models, usage_token_details,
- UsageRecordCostDetailsData, UsageRecordTokenDetailsData,
+ UsageRecordCostDetailsData, UsageRecordTokenDetailsData, usage_cost_details,
+ usage_record_models, usage_token_details,
},
},
- api::AppState,
fleet::{
account::{Account, AccountStatus},
pool::AccountCapacitySummary,
@@ -36,13 +36,13 @@ use crate::{
telemetry::{
account_usage::query::AccountUsageTimeBucket,
dashboard::{
- dashboard_cards, dashboard_health_timeline_data, dashboard_trend_data,
DashboardAccountCounts, DashboardCardsData, DashboardHealthTimelineData,
- DashboardTrendData, DashboardTrendKind,
+ DashboardTrendData, DashboardTrendKind, dashboard_cards,
+ dashboard_health_timeline_data, dashboard_trend_data,
},
usage::{
query::{UsageQueryFilter, UsageRecordAccountUsage},
- types::{metadata_string, UsageRecord},
+ types::{UsageRecord, metadata_string},
},
},
upstream::openai::fingerprint::Fingerprint,
diff --git a/backend/src/api/admin/keys_routes.rs b/backend/src/api/admin/keys_routes.rs
index b15dddd55..28b7029f0 100644
--- a/backend/src/api/admin/keys_routes.rs
+++ b/backend/src/api/admin/keys_routes.rs
@@ -1,23 +1,23 @@
//! 管理端 v1 接口访问 Key 处理器(列表、创建、生命周期)。
use axum::{
+ Json,
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
- Json,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
+ api::AppState,
api::admin::response::{
- parse_editable_update, AdminEnvelope, AdminError, AdminPageEnvelope, AdminResponse,
- BatchDeleteData, EditableUpdateMessages,
+ AdminEnvelope, AdminError, AdminPageEnvelope, AdminResponse, BatchDeleteData,
+ EditableUpdateMessages, parse_editable_update,
},
api::admin::session::AdminAuth,
- api::AppState,
infra::{
- json::{clamp_limit, clamp_page, NumberedPage},
+ json::{NumberedPage, clamp_limit, clamp_page},
time::{china_relative_time_str, china_rfc3339_str},
},
keys::types::{KeyManageError, ManagedClientApiKey},
diff --git a/backend/src/api/admin/ops_routes.rs b/backend/src/api/admin/ops_routes.rs
index 80df4cbc7..8d7a2bf28 100644
--- a/backend/src/api/admin/ops_routes.rs
+++ b/backend/src/api/admin/ops_routes.rs
@@ -8,13 +8,13 @@ use axum::{
use serde::{Deserialize, Serialize};
use crate::{
+ api::AppState,
api::admin::{
response::{AdminError, AdminPageEnvelope, AdminResponse},
session::AdminAuth,
},
- api::AppState,
infra::{
- json::{clamp_limit, clamp_page, NumberedPage},
+ json::{NumberedPage, clamp_limit, clamp_page},
time::china_datetime,
},
telemetry::{ops::query::OpsQueryError, ops::store::OpsErrorFilter, ops::types::OpsErrorLog},
diff --git a/backend/src/api/admin/response.rs b/backend/src/api/admin/response.rs
index 42deef828..1f852a8f5 100644
--- a/backend/src/api/admin/response.rs
+++ b/backend/src/api/admin/response.rs
@@ -1,10 +1,10 @@
//! 管理端响应封装与 From 转换。
-use crate::infra::json::{total_pages, NumberedPage};
+use crate::infra::json::{NumberedPage, total_pages};
use axum::{
+ Json,
http::StatusCode,
response::{IntoResponse, Response},
- Json,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
diff --git a/backend/src/api/admin/router.rs b/backend/src/api/admin/router.rs
index 397b2e43e..8764e5aee 100644
--- a/backend/src/api/admin/router.rs
+++ b/backend/src/api/admin/router.rs
@@ -1,11 +1,11 @@
//! 管理端路由。
use axum::{
- http::{header, HeaderValue},
+ Router,
+ http::{HeaderValue, header},
middleware,
response::Response,
routing::{get, post},
- Router,
};
use crate::api::AppState;
diff --git a/backend/src/api/admin/session.rs b/backend/src/api/admin/session.rs
index 17d87c29a..9a36f3de0 100644
--- a/backend/src/api/admin/session.rs
+++ b/backend/src/api/admin/session.rs
@@ -2,10 +2,10 @@
use axum::{
extract::FromRequestParts,
- http::{request::Parts, HeaderMap},
+ http::{HeaderMap, request::Parts},
};
-use crate::api::{admin::response::AdminError, AppState};
+use crate::api::{AppState, admin::response::AdminError};
pub(super) const ADMIN_SESSION_COOKIE: &str = "cpr_admin_session";
diff --git a/backend/src/api/admin/settings_routes.rs b/backend/src/api/admin/settings_routes.rs
index ffff58b07..c313419ca 100644
--- a/backend/src/api/admin/settings_routes.rs
+++ b/backend/src/api/admin/settings_routes.rs
@@ -6,11 +6,11 @@ use serde_json::Value;
use std::collections::BTreeMap;
use crate::{
+ api::AppState,
api::admin::{
response::{AdminEnvelope, AdminError, AdminResponse},
session::AdminAuth,
},
- api::AppState,
settings::{SettingsError, SettingsPatch, SettingsSnapshot, SettingsValidationError},
};
diff --git a/backend/src/api/admin/system_routes.rs b/backend/src/api/admin/system_routes.rs
index bf062c0e2..df9d58caa 100644
--- a/backend/src/api/admin/system_routes.rs
+++ b/backend/src/api/admin/system_routes.rs
@@ -6,24 +6,20 @@ use axum::{
extract::{Json, Query, State},
http::StatusCode,
response::{
- sse::{Event, KeepAlive, Sse},
IntoResponse,
+ sse::{Event, KeepAlive, Sse},
},
};
use futures::Stream;
use serde::Deserialize;
use crate::{
+ api::AppState,
api::admin::{
response::{AdminEnvelope, AdminError, AdminResponse},
session::AdminAuth,
},
- api::AppState,
- update::{
- service::{self, RestartAction},
- state::operation_id,
- types::UpdateError,
- },
+ update::{service::RestartAction, state::operation_id, types::UpdateError},
};
#[derive(Debug, Deserialize)]
@@ -38,20 +34,30 @@ pub(crate) struct UpdateRequest {
target_version: String,
}
-pub(crate) async fn version(_auth: AdminAuth) -> Result {
+pub(crate) async fn version(
+ State(state): State,
+ _auth: AdminAuth,
+) -> Result {
Ok(AdminResponse::new(
StatusCode::OK,
- AdminEnvelope::ok(service::version_data().await),
+ AdminEnvelope::ok(state.services.system_update.version_data().await),
))
}
pub(crate) async fn update_detail(
+ State(state): State,
_auth: AdminAuth,
Query(query): Query,
) -> Result {
Ok(AdminResponse::new(
StatusCode::OK,
- AdminEnvelope::ok(service::update_detail(query.refresh.unwrap_or(false)).await),
+ AdminEnvelope::ok(
+ state
+ .services
+ .system_update
+ .update_detail(query.refresh.unwrap_or(false))
+ .await,
+ ),
))
}
@@ -59,7 +65,7 @@ pub(crate) async fn update_event_stream(
State(state): State,
_auth: AdminAuth,
) -> Result>>, AdminError> {
- let receiver = service::subscribe_update_events();
+ let receiver = state.services.system_update.subscribe_update_events();
let shutdown = state.services.process_control.subscribe_shutdown();
let stream = futures::stream::unfold(
(receiver, shutdown, false),
@@ -90,11 +96,15 @@ pub(crate) async fn update_event_stream(
}
pub(crate) async fn perform_update(
+ State(state): State,
_auth: AdminAuth,
payload: Option>,
) -> Result {
let target = payload.map(|Json(payload)| payload.target_version);
- let result = service::perform_update(target)
+ let result = state
+ .services
+ .system_update
+ .perform_update(target)
.await
.map_err(update_error)?;
Ok(AdminResponse::new(
@@ -103,16 +113,31 @@ pub(crate) async fn perform_update(
))
}
-pub(crate) async fn update_status(_auth: AdminAuth) -> Result {
- let status = service::update_status().map_err(update_error)?;
+pub(crate) async fn update_status(
+ State(state): State,
+ _auth: AdminAuth,
+) -> Result {
+ let status = state
+ .services
+ .system_update
+ .update_status()
+ .map_err(update_error)?;
Ok(AdminResponse::new(
StatusCode::OK,
AdminEnvelope::ok(status),
))
}
-pub(crate) async fn rollback(_auth: AdminAuth) -> Result {
- let operation_id = service::rollback().await.map_err(update_error)?;
+pub(crate) async fn rollback(
+ State(state): State,
+ _auth: AdminAuth,
+) -> Result {
+ let operation_id = state
+ .services
+ .system_update
+ .rollback()
+ .await
+ .map_err(update_error)?;
Ok(AdminResponse::new(
StatusCode::OK,
AdminEnvelope::ok(serde_json::json!({
@@ -127,7 +152,11 @@ pub(crate) async fn restart(
State(state): State,
_auth: AdminAuth,
) -> Result {
- let plan = service::restart_plan().map_err(update_error)?;
+ let plan = state
+ .services
+ .system_update
+ .restart_plan()
+ .map_err(update_error)?;
let message = plan.message;
let process_control = state.services.process_control.clone();
tokio::spawn(async move {
diff --git a/backend/src/api/admin/usage_routes.rs b/backend/src/api/admin/usage_routes.rs
index bd7ef2605..c19b832f9 100644
--- a/backend/src/api/admin/usage_routes.rs
+++ b/backend/src/api/admin/usage_routes.rs
@@ -10,26 +10,26 @@ use axum::{
use serde::{Deserialize, Serialize};
use crate::{
+ api::AppState,
api::admin::{
response::{AdminEnvelope, AdminError, AdminPageEnvelope, AdminResponse},
session::AdminAuth,
},
- api::AppState,
infra::{
format::{
format_compact_number, format_cost, format_duration_ms, format_duration_ms_f64,
format_multiplier, format_number, format_percent, format_token_price, format_tokens,
},
- json::{clamp_limit, clamp_page, NumberedPage},
+ json::{NumberedPage, clamp_limit, clamp_page},
time::china_datetime,
},
telemetry::{
usage::query::{
- usage_record_cost_details, UsageQueryError, UsageQueryFilter, UsageRecordBreakdown,
- UsageRecordEndpointSource, UsageRecordModelSource, UsageRecordSummary,
- UsageRecordTrendPoint,
+ UsageQueryError, UsageQueryFilter, UsageRecordBreakdown, UsageRecordEndpointSource,
+ UsageRecordModelSource, UsageRecordSummary, UsageRecordTrendPoint,
+ usage_record_cost_details,
},
- usage::types::{metadata_string, UsageRecord},
+ usage::types::{UsageRecord, metadata_string},
},
};
diff --git a/backend/src/api/assets.rs b/backend/src/api/assets.rs
index 4e8ce7f4e..1449c4121 100644
--- a/backend/src/api/assets.rs
+++ b/backend/src/api/assets.rs
@@ -4,10 +4,10 @@ use std::{
};
use axum::{
+ Json, Router,
body::Body,
http::{Request, StatusCode},
response::{IntoResponse, Response},
- Json, Router,
};
use serde_json::json;
use tower::service_fn;
diff --git a/backend/src/api/client/auth.rs b/backend/src/api/client/auth.rs
index 7ccaccb88..89c07aa69 100644
--- a/backend/src/api/client/auth.rs
+++ b/backend/src/api/client/auth.rs
@@ -2,7 +2,7 @@
//!
//! 提供 Bearer token 提取与客户端 API key 校验。
-use axum::http::{header::AUTHORIZATION, HeaderMap};
+use axum::http::{HeaderMap, header::AUTHORIZATION};
use crate::api::AppState;
diff --git a/backend/src/api/client/errors.rs b/backend/src/api/client/errors.rs
index 298014914..687045e83 100644
--- a/backend/src/api/client/errors.rs
+++ b/backend/src/api/client/errors.rs
@@ -1,11 +1,11 @@
//! OpenAI 路由共享错误响应。
use axum::{
+ Json,
http::StatusCode,
response::{IntoResponse, Response},
- Json,
};
-use serde_json::{json, Value};
+use serde_json::{Value, json};
use crate::{
dispatch::errors::{DispatchFailureClass, ResponseDispatchError},
@@ -117,12 +117,10 @@ pub fn responses_dispatch_error_response(error: ResponseDispatchError) -> Respon
pub fn responses_dispatch_error_response_ref(error: &ResponseDispatchError) -> Response {
if let ResponseDispatchError::Upstream(CodexClientError::Upstream { status, body, .. }) = error
+ && status.is_client_error()
+ && let Ok(body) = serde_json::from_str::(body)
{
- if status.is_client_error() {
- if let Ok(body) = serde_json::from_str::(body) {
- return (*status, Json(body)).into_response();
- }
- }
+ return (*status, Json(body)).into_response();
}
match error {
diff --git a/backend/src/api/client/models.rs b/backend/src/api/client/models.rs
index 951ed2d3e..8d1a569c5 100644
--- a/backend/src/api/client/models.rs
+++ b/backend/src/api/client/models.rs
@@ -1,15 +1,15 @@
//! OpenAI 模型列表以及模型处理器。
use axum::{
+ Json,
extract::{Path, State},
http::{HeaderMap, StatusCode},
response::IntoResponse,
- Json,
};
-use serde_json::{json, Value};
+use serde_json::{Value, json};
use crate::{
- api::client::auth::authorize_client_api_key, api::AppState, models::catalog::ModelCatalog,
+ api::AppState, api::client::auth::authorize_client_api_key, models::catalog::ModelCatalog,
};
use super::errors::{missing_client_api_key_response, model_not_found_response};
diff --git a/backend/src/api/client/responses.rs b/backend/src/api/client/responses.rs
index 2358230d3..d7e069caf 100644
--- a/backend/src/api/client/responses.rs
+++ b/backend/src/api/client/responses.rs
@@ -2,17 +2,17 @@
//! 做最小 patch 后交给调度层,请求语义原样透传上游。
use axum::{
+ Extension, Json,
body::{Body, Bytes},
extract::State,
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
- Extension, Json,
};
use serde_json::{Map, Value};
use crate::{
- api::middleware::request_id::{ClientIp, RequestId},
api::AppState,
+ api::middleware::request_id::{ClientIp, RequestId},
dispatch::{errors::ResponseDispatchError, service::ResponseDispatchStream},
upstream::openai::protocol::{
responses::{CodexCompactRequest, CodexResponsesRequest},
@@ -449,15 +449,11 @@ pub(crate) fn user_agent_from_headers(headers: &HeaderMap) -> Option {
}
fn openai_subagent_from_headers(headers: &HeaderMap) -> Option {
- header_string(headers, OPENAI_SUBAGENT_HEADER).and_then(|value| {
- if matches!(
+ header_string(headers, OPENAI_SUBAGENT_HEADER).filter(|value| {
+ matches!(
value.as_str(),
"review" | "compact" | "memory_consolidation" | "collab_spawn"
- ) {
- Some(value)
- } else {
- None
- }
+ )
})
}
diff --git a/backend/src/api/client/router.rs b/backend/src/api/client/router.rs
index 58d742507..a74ca88e7 100644
--- a/backend/src/api/client/router.rs
+++ b/backend/src/api/client/router.rs
@@ -1,9 +1,9 @@
//! OpenAI 兼容 API 路由。
use axum::{
+ Router,
extract::DefaultBodyLimit,
routing::{get, post},
- Router,
};
use crate::api::AppState;
diff --git a/backend/src/api/client/sse.rs b/backend/src/api/client/sse.rs
index d1cdd6d59..f64cae753 100644
--- a/backend/src/api/client/sse.rs
+++ b/backend/src/api/client/sse.rs
@@ -5,8 +5,8 @@
use axum::{
body::Body,
http::{
- header::{CACHE_CONTROL, CONTENT_TYPE},
StatusCode,
+ header::{CACHE_CONTROL, CONTENT_TYPE},
},
response::{IntoResponse, Response},
};
diff --git a/backend/src/api/middleware/request_id.rs b/backend/src/api/middleware/request_id.rs
index 82f21edde..4090c2736 100644
--- a/backend/src/api/middleware/request_id.rs
+++ b/backend/src/api/middleware/request_id.rs
@@ -4,7 +4,7 @@ use std::net::{IpAddr, SocketAddr};
use std::sync::LazyLock;
use axum::{
- extract::{connect_info::ConnectInfo, Request},
+ extract::{Request, connect_info::ConnectInfo},
http::{HeaderMap, HeaderValue},
middleware::Next,
response::Response,
diff --git a/backend/src/api/middleware/trace.rs b/backend/src/api/middleware/trace.rs
index e9b9cea5a..3ae408c58 100644
--- a/backend/src/api/middleware/trace.rs
+++ b/backend/src/api/middleware/trace.rs
@@ -10,7 +10,7 @@ use tower_http::{
classify::{ServerErrorsAsFailures, ServerErrorsFailureClass, SharedClassifier},
trace::TraceLayer,
};
-use tracing::{debug, info, warn, Span};
+use tracing::{Span, debug, info, warn};
use crate::api::middleware::request_id::{ClientIp, RequestId};
diff --git a/backend/src/api/router.rs b/backend/src/api/router.rs
index b71f558a1..127dd658a 100644
--- a/backend/src/api/router.rs
+++ b/backend/src/api/router.rs
@@ -6,7 +6,7 @@ use std::{
};
use async_trait::async_trait;
-use axum::{extract::State, http::StatusCode, middleware, routing::get, Router};
+use axum::{Router, extract::State, http::StatusCode, middleware, routing::get};
use crate::{
api::{
@@ -26,6 +26,7 @@ use crate::{
account_usage::query::AccountUsageQueryService, ops::query::OpsQueryService,
usage::query::UsageQueryService,
},
+ update::service::SystemUpdateService,
upstream::openai::{fingerprint::RuntimeFingerprint, token_client::OpenAiTokenClient},
};
@@ -49,6 +50,7 @@ pub struct ApiServices {
pub session_affinity: Arc,
pub fingerprint: RuntimeFingerprint,
pub process_control: Arc,
+ pub system_update: Arc,
}
/// 由 bootstrap 实现的进程生命周期控制端口。
diff --git a/backend/src/bootstrap/config.rs b/backend/src/bootstrap/config.rs
index 667507b7d..07452a645 100644
--- a/backend/src/bootstrap/config.rs
+++ b/backend/src/bootstrap/config.rs
@@ -1,6 +1,6 @@
use std::{collections::BTreeMap, env, path::Path};
-use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
+use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
use serde::{Deserialize, Serialize};
use url::Url;
diff --git a/backend/src/bootstrap/import_sqlite/core.rs b/backend/src/bootstrap/import_sqlite/core.rs
index ac3e854ef..33bb98e2b 100644
--- a/backend/src/bootstrap/import_sqlite/core.rs
+++ b/backend/src/bootstrap/import_sqlite/core.rs
@@ -1,11 +1,11 @@
use futures::TryStreamExt;
-use sqlx::{types::Json, Postgres, Row, SqlitePool, Transaction};
+use sqlx::{Postgres, Row, SqlitePool, Transaction, types::Json};
use crate::infra::identity::hash_credential;
use super::{
- parse_cookie_expiration, parse_json, parse_optional_json, parse_optional_timestamp,
- parse_timestamp, ImportSqliteError, ImportSqliteReport,
+ ImportSqliteError, ImportSqliteReport, parse_cookie_expiration, parse_json,
+ parse_optional_json, parse_optional_timestamp, parse_timestamp,
};
pub(super) async fn import_core_tables(
diff --git a/backend/src/bootstrap/import_sqlite/mod.rs b/backend/src/bootstrap/import_sqlite/mod.rs
index 9a1009dc6..68e15b701 100644
--- a/backend/src/bootstrap/import_sqlite/mod.rs
+++ b/backend/src/bootstrap/import_sqlite/mod.rs
@@ -8,8 +8,8 @@ use std::{collections::BTreeMap, fmt, path::Path};
use chrono::{DateTime, Utc};
use serde_json::Value;
use sqlx::{
- sqlite::{SqliteConnectOptions, SqlitePoolOptions},
PgPool, Postgres, SqlitePool, Transaction,
+ sqlite::{SqliteConnectOptions, SqlitePoolOptions},
};
use thiserror::Error;
diff --git a/backend/src/bootstrap/import_sqlite/telemetry.rs b/backend/src/bootstrap/import_sqlite/telemetry.rs
index 389967e42..a34b28402 100644
--- a/backend/src/bootstrap/import_sqlite/telemetry.rs
+++ b/backend/src/bootstrap/import_sqlite/telemetry.rs
@@ -1,9 +1,9 @@
use futures::TryStreamExt;
use serde_json::Value;
-use sqlx::{types::Json, Postgres, Row, SqlitePool, Transaction};
+use sqlx::{Postgres, Row, SqlitePool, Transaction, types::Json};
use super::{
- normalized_dimension, parse_json, parse_timestamp, ImportSqliteError, ImportSqliteReport,
+ ImportSqliteError, ImportSqliteReport, normalized_dimension, parse_json, parse_timestamp,
};
pub(super) async fn import_telemetry_tables(
diff --git a/backend/src/bootstrap/services.rs b/backend/src/bootstrap/services.rs
index 6a4a35699..7790789ba 100644
--- a/backend/src/bootstrap/services.rs
+++ b/backend/src/bootstrap/services.rs
@@ -26,7 +26,7 @@ use crate::{
infra::{
database::connect,
identity::AccountPseudonymizer,
- logging::{init_tracing, LogError, LogGuard, RotationConfig, TracingConfig},
+ logging::{LogError, LogGuard, RotationConfig, TracingConfig, init_tracing},
paths::{ensure_data_dir, load_or_create_identity_secret},
redis::RedisConnection,
},
@@ -36,7 +36,7 @@ use crate::{
store::{ModelSnapshotStore, RedisModelSnapshotStore},
types::ModelConfig,
},
- settings::{service::SettingsService, SettingsSnapshot},
+ settings::{SettingsSnapshot, service::SettingsService},
telemetry::{
account_usage::query::AccountUsageQueryService,
account_usage::store::{AccountUsageStore, PgAccountUsageStore},
@@ -45,14 +45,15 @@ use crate::{
ops::store::PgOpsErrorLogStore,
recorder::Recorder,
usage::query::UsageQueryService,
- usage::store::{PgUsageRecordStore, DEFAULT_USAGE_RECORD_CAPTURE_BODY},
+ usage::store::{DEFAULT_USAGE_RECORD_CAPTURE_BODY, PgUsageRecordStore},
},
+ update::service::SystemUpdateService,
upstream::openai::{
fingerprint::{Fingerprint, PgFingerprintStore, RuntimeFingerprint},
- token_client::{default_openai_token_client, OpenAiTokenClient, TokenClientConfig},
+ token_client::{OpenAiTokenClient, TokenClientConfig, default_openai_token_client},
transport::{
- build_reqwest_client, tls::CustomCaError, CodexBackendClient, CodexModelCatalogClient,
- CodexWebSocketPool, CodexWebSocketPoolConfig,
+ CodexBackendClient, CodexModelCatalogClient, CodexWebSocketPool,
+ CodexWebSocketPoolConfig, build_reqwest_client, tls::CustomCaError,
},
},
};
@@ -117,6 +118,7 @@ pub struct Services {
pub websocket_pool: Option>,
pub fingerprint: RuntimeFingerprint,
pub account_pseudonymizer: Arc,
+ pub system_update: Arc,
pub background_tasks: BackgroundTaskStores,
pub(crate) account_pool_static: AccountPoolStaticSettings,
}
@@ -304,6 +306,7 @@ impl Services {
account_identity,
cloudflare: cloudflare_recovery,
}));
+ let system_update = Arc::new(SystemUpdateService::from_env());
Ok(Self {
database,
@@ -327,6 +330,7 @@ impl Services {
websocket_pool,
fingerprint,
account_pseudonymizer,
+ system_update,
background_tasks: stores,
account_pool_static,
})
@@ -362,6 +366,7 @@ impl From<&Services> for crate::api::router::ApiServices {
session_affinity: services.session_affinity.clone(),
fingerprint: services.fingerprint.clone(),
process_control: Arc::new(crate::bootstrap::shutdown::RuntimeProcessControl),
+ system_update: services.system_update.clone(),
}
}
}
diff --git a/backend/src/bootstrap/shutdown.rs b/backend/src/bootstrap/shutdown.rs
index df18d9890..5644f1170 100644
--- a/backend/src/bootstrap/shutdown.rs
+++ b/backend/src/bootstrap/shutdown.rs
@@ -3,8 +3,8 @@
use std::{
path::PathBuf,
sync::{
- atomic::{AtomicBool, Ordering},
OnceLock,
+ atomic::{AtomicBool, Ordering},
},
};
diff --git a/backend/src/bootstrap/tasks/cleanup.rs b/backend/src/bootstrap/tasks/cleanup.rs
index 05ae4c7e4..a80960d0f 100644
--- a/backend/src/bootstrap/tasks/cleanup.rs
+++ b/backend/src/bootstrap/tasks/cleanup.rs
@@ -7,7 +7,7 @@ use tracing::{debug, info, warn};
use super::{
coordinator::SchedulerHandle,
- periodic::{spawn_periodic_task, PeriodicTaskConfig, PeriodicTaskRunner},
+ periodic::{PeriodicTaskConfig, PeriodicTaskRunner, spawn_periodic_task},
};
/// 支持按时间清理过期记录的存储。
diff --git a/backend/src/bootstrap/tasks/coordinator.rs b/backend/src/bootstrap/tasks/coordinator.rs
index 33572d4e5..fe3c5be98 100644
--- a/backend/src/bootstrap/tasks/coordinator.rs
+++ b/backend/src/bootstrap/tasks/coordinator.rs
@@ -7,8 +7,8 @@ use tokio::task::JoinHandle;
use super::{
cookie_cleanup::CookieCleanupTask,
fingerprint_update::{
- start_fingerprint_update_task, CODEX_DESKTOP_APPCAST_URL,
- DEFAULT_EXTRACTED_FINGERPRINT_PATH,
+ CODEX_DESKTOP_APPCAST_URL, DEFAULT_EXTRACTED_FINGERPRINT_PATH,
+ start_fingerprint_update_task,
},
model_refresh::ModelRefreshTask,
quota_refresh::QuotaRefreshTask,
diff --git a/backend/src/bootstrap/tasks/fingerprint_update.rs b/backend/src/bootstrap/tasks/fingerprint_update.rs
index 452ee3ba8..b443946d4 100644
--- a/backend/src/bootstrap/tasks/fingerprint_update.rs
+++ b/backend/src/bootstrap/tasks/fingerprint_update.rs
@@ -5,12 +5,12 @@ use std::path::PathBuf;
use tracing::{info, warn};
use crate::upstream::openai::fingerprint::{
- PgFingerprintStore, RuntimeFingerprint, UpdateChecker, APPCAST_POLL_INTERVAL,
+ APPCAST_POLL_INTERVAL, PgFingerprintStore, RuntimeFingerprint, UpdateChecker,
};
use super::{
coordinator::SchedulerHandle,
- periodic::{spawn_periodic_task, PeriodicTaskConfig, PeriodicTaskRunner},
+ periodic::{PeriodicTaskConfig, PeriodicTaskRunner, spawn_periodic_task},
};
/// Codex Desktop 官方 appcast 地址。
diff --git a/backend/src/bootstrap/tasks/model_refresh.rs b/backend/src/bootstrap/tasks/model_refresh.rs
index 94a005845..fa874c9f4 100644
--- a/backend/src/bootstrap/tasks/model_refresh.rs
+++ b/backend/src/bootstrap/tasks/model_refresh.rs
@@ -15,7 +15,7 @@ use crate::models::service::{
use super::{
coordinator::SchedulerHandle,
- periodic::{spawn_periodic_task, PeriodicTaskConfig, PeriodicTaskRunner, PeriodicTaskStartup},
+ periodic::{PeriodicTaskConfig, PeriodicTaskRunner, PeriodicTaskStartup, spawn_periodic_task},
};
/// 模型刷新任务接线器。
diff --git a/backend/src/bootstrap/tasks/quota_refresh.rs b/backend/src/bootstrap/tasks/quota_refresh.rs
index 975c03b6f..ffd52d4a5 100644
--- a/backend/src/bootstrap/tasks/quota_refresh.rs
+++ b/backend/src/bootstrap/tasks/quota_refresh.rs
@@ -19,7 +19,7 @@ use crate::{
use super::{
coordinator::SchedulerHandle,
- periodic::{spawn_periodic_task, PeriodicTaskConfig, PeriodicTaskRunner},
+ periodic::{PeriodicTaskConfig, PeriodicTaskRunner, spawn_periodic_task},
};
/// 主动配额刷新后台任务。
diff --git a/backend/src/bootstrap/tasks/retention_trim.rs b/backend/src/bootstrap/tasks/retention_trim.rs
index b21df93f0..8999a7818 100644
--- a/backend/src/bootstrap/tasks/retention_trim.rs
+++ b/backend/src/bootstrap/tasks/retention_trim.rs
@@ -13,7 +13,7 @@ use crate::{
use super::{
coordinator::SchedulerHandle,
- periodic::{spawn_periodic_task, PeriodicTaskConfig, PeriodicTaskRunner, TaskFuture},
+ periodic::{PeriodicTaskConfig, PeriodicTaskRunner, TaskFuture, spawn_periodic_task},
};
const RETENTION_TRIM_INTERVAL_SECS: u64 = 60 * 60;
diff --git a/backend/src/bootstrap/tasks/token_refresh.rs b/backend/src/bootstrap/tasks/token_refresh.rs
index 9d686a831..3c02ad945 100644
--- a/backend/src/bootstrap/tasks/token_refresh.rs
+++ b/backend/src/bootstrap/tasks/token_refresh.rs
@@ -10,7 +10,7 @@ use crate::upstream::openai::token_client::TokenRefresher;
use super::{
coordinator::SchedulerHandle,
- periodic::{spawn_periodic_task, PeriodicTaskConfig, PeriodicTaskRunner},
+ periodic::{PeriodicTaskConfig, PeriodicTaskRunner, spawn_periodic_task},
};
const DEFAULT_INTERVAL_SECS: u64 = 60;
diff --git a/backend/src/dispatch/affinity/resolve.rs b/backend/src/dispatch/affinity/resolve.rs
index 6a98915f5..d449d2c1f 100644
--- a/backend/src/dispatch/affinity/resolve.rs
+++ b/backend/src/dispatch/affinity/resolve.rs
@@ -10,7 +10,7 @@ use crate::{
},
upstream::openai::protocol::{
events::TokenUsage,
- responses::{completed_response_metadata, CodexResponsesRequest},
+ responses::{CodexResponsesRequest, completed_response_metadata},
},
};
@@ -146,12 +146,11 @@ fn build_variant_identity(request: &CodexResponsesRequest) -> Option {
if let Some(window_id) = non_empty_str(request.codex_window_id.as_deref()) {
parts.push(format!("window:{window_id}"));
}
- if request.explicit_prompt_cache_key
- || non_empty_str(request.client_conversation_id.as_deref()).is_some()
+ if (request.explicit_prompt_cache_key
+ || non_empty_str(request.client_conversation_id.as_deref()).is_some())
+ && let Some(anchor) = derive_stable_conversation_key(request)
{
- if let Some(anchor) = derive_stable_conversation_key(request) {
- parts.push(format!("anchor:{anchor}"));
- }
+ parts.push(format!("anchor:{anchor}"));
}
(!parts.is_empty()).then(|| parts.join("\0"))
diff --git a/backend/src/dispatch/errors.rs b/backend/src/dispatch/errors.rs
index 6be0deb54..5b277fb8f 100644
--- a/backend/src/dispatch/errors.rs
+++ b/backend/src/dispatch/errors.rs
@@ -1,7 +1,7 @@
//! Shared upstream error classification for dispatch routes.
use chrono::{DateTime, Duration, Utc};
-use serde_json::{json, Map, Value};
+use serde_json::{Map, Value, json};
use thiserror::Error;
use crate::{
diff --git a/backend/src/dispatch/recording.rs b/backend/src/dispatch/recording.rs
index 1a013c8c7..136c24c6c 100644
--- a/backend/src/dispatch/recording.rs
+++ b/backend/src/dispatch/recording.rs
@@ -1,19 +1,19 @@
use std::time::Instant;
-use serde_json::{json, Map, Value};
+use serde_json::{Map, Value, json};
use crate::{
dispatch::errors::{
- backend_transport_name, insert_dispatch_error_metadata,
- insert_upstream_diagnostics_metadata, upstream_error_http_status, DispatchErrorMetadata,
+ DispatchErrorMetadata, backend_transport_name, insert_dispatch_error_metadata,
+ insert_upstream_diagnostics_metadata, upstream_error_http_status,
},
infra::time::elapsed_millis_i64,
telemetry::{
ops::types::OpsErrorLog,
recorder::{
- enrich_event_route_metadata, enrich_usage_record_identity,
- event_kind as response_event_kind, reasoning_effort_from_request,
- record_dispatch_error_event, DispatchErrorLogRecord, Recorder,
+ DispatchErrorLogRecord, Recorder, enrich_event_route_metadata,
+ enrich_usage_record_identity, event_kind as response_event_kind,
+ reasoning_effort_from_request, record_dispatch_error_event,
},
usage::types::UsageRecordLevel,
},
@@ -28,10 +28,10 @@ use crate::{
use super::{
errors::{
- dispatch_error_metadata, enrich_response_dispatch_error_metadata, ResponseDispatchError,
+ ResponseDispatchError, dispatch_error_metadata, enrich_response_dispatch_error_metadata,
},
stream::{
- live::{latest_response_id, LiveResponseStreamContext},
+ live::{LiveResponseStreamContext, latest_response_id},
sse_failure::{status_code_for_stream_failure, stream_failure_metadata},
trace::{ResponseDispatchAttempt, ResponseDispatchTrace},
},
@@ -236,13 +236,13 @@ pub(super) async fn record_prefetched_response_stream_failure_event(
event.model = Some(record.model.to_string());
event.status_code = Some(event_status_code);
event.latency_ms = Some(elapsed_millis_i64(record.started_at));
- if !record.rate_limit_headers.is_empty() {
- if let Some(object) = metadata.as_object_mut() {
- object.insert(
- "rateLimitHeaders".to_string(),
- serde_json::json!(record.rate_limit_headers),
- );
- }
+ if !record.rate_limit_headers.is_empty()
+ && let Some(object) = metadata.as_object_mut()
+ {
+ object.insert(
+ "rateLimitHeaders".to_string(),
+ serde_json::json!(record.rate_limit_headers),
+ );
}
event.metadata = metadata;
if let Err(error) = record.recorder.record_error(event).await {
diff --git a/backend/src/dispatch/recovery/account_failure.rs b/backend/src/dispatch/recovery/account_failure.rs
index 36ef11a16..af9464561 100644
--- a/backend/src/dispatch/recovery/account_failure.rs
+++ b/backend/src/dispatch/recovery/account_failure.rs
@@ -12,9 +12,9 @@ use crate::{
recovery::{
auth::{auth_failure_account_status, is_auth_upstream_error},
cloudflare::{
- cloudflare_challenge_error_message, cloudflare_path_block_error_message,
- is_cloudflare_challenge_upstream_error, is_cloudflare_path_block_upstream_error,
- CloudflareRecovery,
+ CloudflareRecovery, cloudflare_challenge_error_message,
+ cloudflare_path_block_error_message, is_cloudflare_challenge_upstream_error,
+ is_cloudflare_path_block_upstream_error,
},
exhaustion::AccountExhaustionTracker,
},
@@ -26,7 +26,7 @@ use crate::{
fleet::{account::AccountStatus, pool::AccountPoolService},
upstream::openai::{
protocol::responses::ResponsesSseFailure,
- transport::{is_banned_upstream_error, CodexClientError},
+ transport::{CodexClientError, is_banned_upstream_error},
},
};
diff --git a/backend/src/dispatch/recovery/auth.rs b/backend/src/dispatch/recovery/auth.rs
index 1a051f0b2..74709e815 100644
--- a/backend/src/dispatch/recovery/auth.rs
+++ b/backend/src/dispatch/recovery/auth.rs
@@ -2,7 +2,7 @@
use crate::{
fleet::account::AccountStatus,
- upstream::openai::transport::{is_banned_auth_signal, CodexClientError},
+ upstream::openai::transport::{CodexClientError, is_banned_auth_signal},
};
pub(crate) fn is_auth_upstream_error(error: &CodexClientError) -> bool {
diff --git a/backend/src/dispatch/recovery/history.rs b/backend/src/dispatch/recovery/history.rs
index 902bcd3ff..208341aa6 100644
--- a/backend/src/dispatch/recovery/history.rs
+++ b/backend/src/dispatch/recovery/history.rs
@@ -5,9 +5,9 @@ use serde_json::Value;
use crate::{
dispatch::affinity::{
+ SessionAffinityEntry, SessionAffinityService,
service::{MAX_REPLAY_DEPTH, MAX_REPLAY_SESSION_BYTES, MAX_REPLAY_SNAPSHOT_BYTES},
types::ResponseReplaySnapshot,
- SessionAffinityEntry, SessionAffinityService,
},
upstream::openai::protocol::responses::{CodexResponsesRequest, StreamCommitPolicy},
};
diff --git a/backend/src/dispatch/service.rs b/backend/src/dispatch/service.rs
index 231cffc31..b0bd949ee 100644
--- a/backend/src/dispatch/service.rs
+++ b/backend/src/dispatch/service.rs
@@ -12,7 +12,7 @@ use std::{pin::Pin, sync::Arc, time::Instant};
use bytes::Bytes;
use chrono::Utc;
use futures::stream::Stream;
-use serde_json::{json, Value};
+use serde_json::{Value, json};
use crate::{
dispatch::{
@@ -31,9 +31,9 @@ use crate::{
history::HistoryRecoveryPlan,
},
upstream_call::{
- create_response_with_account_retrying_5xx, verify_acquired_quota_if_required,
- AccountUpstreamContext, QuotaVerificationContext, QuotaVerificationDecision,
- QUOTA_VERIFY_LIMIT_REACHED_MESSAGE,
+ AccountUpstreamContext, QUOTA_VERIFY_LIMIT_REACHED_MESSAGE, QuotaVerificationContext,
+ QuotaVerificationDecision, create_response_with_account_retrying_5xx,
+ verify_acquired_quota_if_required,
},
},
fleet::{
@@ -42,16 +42,16 @@ use crate::{
},
models::service::ModelService,
telemetry::{
- recorder::{reasoning_effort_from_request, record_response_event, Recorder},
+ recorder::{Recorder, reasoning_effort_from_request, record_response_event},
usage::types::ResponseUsageRecord,
},
upstream::openai::{
protocol::{
events::TokenUsage,
- responses::{response_from_codex_sse, CodexResponsesRequest, CollectedResponse},
+ responses::{CodexResponsesRequest, CollectedResponse, response_from_codex_sse},
},
transport::{
- backend_transport_for_response_request, CodexBackendClient, CodexBackendResponse,
+ CodexBackendClient, CodexBackendResponse, backend_transport_for_response_request,
},
},
};
@@ -59,11 +59,11 @@ use crate::{
use super::{
errors::{ResponseDispatchError, ResponseDispatchStreamError},
recording::{
- insert_response_status_metadata, insert_response_trace_metadata,
- insert_response_upstream_diagnostics, insert_websocket_pool_decision,
- record_response_dispatch_error_event, record_response_upstream_error_event,
ResponseDispatchErrorDetails, ResponseDispatchErrorEventRecord,
- ResponseUpstreamErrorEventRecord,
+ ResponseUpstreamErrorEventRecord, insert_response_status_metadata,
+ insert_response_trace_metadata, insert_response_upstream_diagnostics,
+ insert_websocket_pool_decision, record_response_dispatch_error_event,
+ record_response_upstream_error_event,
},
stream::{
sse_failure::is_history_recovery_sse_failure,
diff --git a/backend/src/dispatch/stream/lifecycle.rs b/backend/src/dispatch/stream/lifecycle.rs
index 1cf8836f0..ba56da637 100644
--- a/backend/src/dispatch/stream/lifecycle.rs
+++ b/backend/src/dispatch/stream/lifecycle.rs
@@ -7,14 +7,14 @@ use crate::{
affinity::prepare_variant_identity,
attempts::AccountAttemptLedger,
errors::{
- backend_transport_name, is_continuation_busy_error, is_history_recovery_upstream_error,
- is_retryable_account_transport_error, upstream_error_body,
- upstream_error_set_cookie_headers, ResponseDispatchError,
+ ResponseDispatchError, backend_transport_name, is_continuation_busy_error,
+ is_history_recovery_upstream_error, is_retryable_account_transport_error,
+ upstream_error_body, upstream_error_set_cookie_headers,
},
recording::{
- record_prefetched_response_stream_failure_event, record_response_upstream_error_event,
ResponseDispatchErrorDetails, ResponseStreamFailureEventRecord,
- ResponseUpstreamErrorEventRecord,
+ ResponseUpstreamErrorEventRecord, record_prefetched_response_stream_failure_event,
+ record_response_upstream_error_event,
},
recovery::{
account_failure::{isolate_rotatable_account_failure, isolate_sse_account_failure},
@@ -23,7 +23,7 @@ use crate::{
},
service::{ResponseDispatchService, ResponseDispatchStream},
stream::{
- live::{spawn_live_response_stream, LiveResponseStreamContext},
+ live::{LiveResponseStreamContext, spawn_live_response_stream},
prefetch::prefetch_first_sse_chunk,
sse_failure::{
first_sse_failure, is_history_recovery_sse_failure, sse_failure_error_body,
@@ -31,9 +31,9 @@ use crate::{
trace::ResponseDispatchTrace,
},
upstream_call::{
- create_response_stream_with_account_retrying_5xx, verify_acquired_quota_if_required,
- AccountUpstreamContext, QuotaVerificationContext, QuotaVerificationDecision,
- QUOTA_VERIFY_LIMIT_REACHED_MESSAGE,
+ AccountUpstreamContext, QUOTA_VERIFY_LIMIT_REACHED_MESSAGE, QuotaVerificationContext,
+ QuotaVerificationDecision, create_response_stream_with_account_retrying_5xx,
+ verify_acquired_quota_if_required,
},
},
fleet::pool::AccountAcquireRequest,
diff --git a/backend/src/dispatch/stream/live.rs b/backend/src/dispatch/stream/live.rs
index c348569e8..e5138feda 100644
--- a/backend/src/dispatch/stream/live.rs
+++ b/backend/src/dispatch/stream/live.rs
@@ -6,14 +6,14 @@ use std::{
};
use bytes::Bytes;
-use futures::{stream::Stream, StreamExt};
+use futures::{StreamExt, stream::Stream};
use serde_json::Value;
use tokio::sync::{mpsc, oneshot};
use crate::{
dispatch::{
- affinity::{resolve::record_response_affinity, SessionAffinityService},
- errors::{backend_transport_name, ResponseDispatchStreamError},
+ affinity::{SessionAffinityService, resolve::record_response_affinity},
+ errors::{ResponseDispatchStreamError, backend_transport_name},
recording::{
insert_first_token_ms, live_response_rate_limit_headers, live_response_turn_state,
record_live_response_stream_event,
@@ -28,13 +28,13 @@ use crate::{
protocol::{
events::extract_sse_usage,
responses::{
+ CodexResponsesRequest, CollectedResponse,
reconvert_responses_sse_event_tuple_values, response_from_codex_sse,
response_sse_event_is_terminal, update_first_response_output_ms,
- CodexResponsesRequest, CollectedResponse,
},
sse::{
- encode_sse_event, parse_sse_events, response_failed_sse_event_with_id,
- sse_body_has_done, sse_frame_end, DONE_SSE_FRAME,
+ DONE_SSE_FRAME, encode_sse_event, parse_sse_events,
+ response_failed_sse_event_with_id, sse_body_has_done, sse_frame_end,
},
},
transport::{
@@ -47,9 +47,8 @@ use crate::{
use super::{
sse_failure::{
- status_code_for_stream_failure, stream_failure_metadata, stream_failure_source,
- synthetic_stream_disconnected_detail, STREAM_DISCONNECTED_CODE,
- STREAM_DISCONNECTED_MESSAGE,
+ STREAM_DISCONNECTED_CODE, STREAM_DISCONNECTED_MESSAGE, status_code_for_stream_failure,
+ stream_failure_metadata, stream_failure_source, synthetic_stream_disconnected_detail,
},
trace::ResponseDispatchAttempt,
};
diff --git a/backend/src/dispatch/stream/prefetch.rs b/backend/src/dispatch/stream/prefetch.rs
index 9c7b375ff..9ceeb4e56 100644
--- a/backend/src/dispatch/stream/prefetch.rs
+++ b/backend/src/dispatch/stream/prefetch.rs
@@ -2,7 +2,7 @@ use bytes::Bytes;
use futures::StreamExt;
use crate::upstream::openai::{
- protocol::sse::{sse_frame_end, SseError},
+ protocol::sse::{SseError, sse_frame_end},
transport::CodexBackendSseStream,
};
diff --git a/backend/src/dispatch/stream/sse_failure.rs b/backend/src/dispatch/stream/sse_failure.rs
index 6167231a5..502d527a7 100644
--- a/backend/src/dispatch/stream/sse_failure.rs
+++ b/backend/src/dispatch/stream/sse_failure.rs
@@ -6,7 +6,7 @@ use crate::{
upstream::openai::{
protocol::{
events::TokenUsage,
- responses::{response_from_codex_sse, CollectedResponse, ResponsesSseFailure},
+ responses::{CollectedResponse, ResponsesSseFailure, response_from_codex_sse},
sse::SseError,
},
transport::is_banned_auth_signal,
diff --git a/backend/src/dispatch/upstream_call.rs b/backend/src/dispatch/upstream_call.rs
index c5e670fc1..e3efe6ad1 100644
--- a/backend/src/dispatch/upstream_call.rs
+++ b/backend/src/dispatch/upstream_call.rs
@@ -3,29 +3,30 @@
use std::{future::Future, time::Instant};
use chrono::Utc;
-use serde_json::{json, Value};
+use serde_json::{Value, json};
use crate::{
dispatch::{
affinity::{AccountIdentityService, AccountScopedIdentity},
attempts::AccountAttemptLedger,
errors::{
- is_model_unsupported_upstream_error, is_quota_exhausted_upstream_error,
- is_rate_limit_upstream_error, is_retryable_account_transport_error,
- is_retryable_upstream_5xx_error, rate_limit_cooldown_until, upstream_error_body,
- upstream_error_http_status, upstream_error_set_cookie_headers, ResponseDispatchError,
+ ResponseDispatchError, is_model_unsupported_upstream_error,
+ is_quota_exhausted_upstream_error, is_rate_limit_upstream_error,
+ is_retryable_account_transport_error, is_retryable_upstream_5xx_error,
+ rate_limit_cooldown_until, upstream_error_body, upstream_error_http_status,
+ upstream_error_set_cookie_headers,
},
recording::{
- insert_response_status_metadata, insert_response_trace_metadata,
- insert_response_upstream_diagnostics, record_response_dispatch_error_event,
- ResponseDispatchErrorEventRecord,
+ ResponseDispatchErrorEventRecord, insert_response_status_metadata,
+ insert_response_trace_metadata, insert_response_upstream_diagnostics,
+ record_response_dispatch_error_event,
},
recovery::{
auth::{auth_failure_account_status, is_auth_upstream_error},
cloudflare::{
- cloudflare_challenge_error_message, cloudflare_path_block_error_message,
- is_cloudflare_challenge_upstream_error, is_cloudflare_path_block_upstream_error,
- CloudflareRecovery,
+ CloudflareRecovery, cloudflare_challenge_error_message,
+ cloudflare_path_block_error_message, is_cloudflare_challenge_upstream_error,
+ is_cloudflare_path_block_upstream_error,
},
exhaustion::AccountExhaustionTracker,
},
@@ -50,9 +51,8 @@ use crate::{
responses::{CodexCompactRequest, CodexResponsesRequest},
},
transport::{
- is_banned_upstream_error, CodexBackendClient, CodexBackendResponse,
- CodexBackendStreamingResponse, CodexClientError, CodexCompactResponse,
- CodexRequestContext,
+ CodexBackendClient, CodexBackendResponse, CodexBackendStreamingResponse,
+ CodexClientError, CodexCompactResponse, CodexRequestContext, is_banned_upstream_error,
},
},
};
diff --git a/backend/src/fleet/import.rs b/backend/src/fleet/import.rs
index 44dc82f6e..8f140949d 100644
--- a/backend/src/fleet/import.rs
+++ b/backend/src/fleet/import.rs
@@ -4,7 +4,7 @@ use chrono::{DateTime, Utc};
use serde_json::{Map, Value};
use crate::fleet::account::AccountStatus;
-use crate::fleet::refresh::{jwt_expiry, JwtExpiry};
+use crate::fleet::refresh::{JwtExpiry, jwt_expiry};
/// 账号导入条目。
#[derive(Debug, Clone)]
diff --git a/backend/src/fleet/manage/export.rs b/backend/src/fleet/manage/export.rs
index 3c9b49ac2..531c0190e 100644
--- a/backend/src/fleet/manage/export.rs
+++ b/backend/src/fleet/manage/export.rs
@@ -1,8 +1,8 @@
use secrecy::ExposeSecret;
use super::{
- types::{ExportedAccount, ExportedAccounts},
AccountManageError, AccountManageService,
+ types::{ExportedAccount, ExportedAccounts},
};
use crate::{fleet::store::StoredAccount, infra::time::china_rfc3339_str};
diff --git a/backend/src/fleet/manage/import.rs b/backend/src/fleet/manage/import.rs
index c7312811a..a96a519bf 100644
--- a/backend/src/fleet/manage/import.rs
+++ b/backend/src/fleet/manage/import.rs
@@ -2,12 +2,12 @@ use chrono::Utc;
use secrecy::SecretString;
use super::{
- types::{
- import_quota_plan_type, import_status_from_usage_error, import_usage_plan_type,
- import_usage_string, AccountManageError, ImportSupplementalAccountInfo,
- ImportSupplementalNeeds, ImportedAccountState, ImportedAccounts, ResolvedImportTokens,
- },
AccountManageService,
+ types::{
+ AccountManageError, ImportSupplementalAccountInfo, ImportSupplementalNeeds,
+ ImportedAccountState, ImportedAccounts, ResolvedImportTokens, import_quota_plan_type,
+ import_status_from_usage_error, import_usage_plan_type, import_usage_string,
+ },
};
impl AccountManageService {
@@ -304,15 +304,14 @@ impl AccountManageService {
let chatgpt_account_id = crate::fleet::import::normalize_nonempty_str(account_id);
let chatgpt_user_id = crate::fleet::import::normalize_nonempty_str(user_id);
- if let Some(chatgpt_account_id) = chatgpt_account_id {
- if let Some(existing) = self
+ if let Some(chatgpt_account_id) = chatgpt_account_id
+ && let Some(existing) = self
.store
.find_by_chatgpt_identity(chatgpt_account_id, chatgpt_user_id)
.await
.map_err(|_| AccountManageError::Inspect)?
- {
- return Ok(existing.id);
- }
+ {
+ return Ok(existing.id);
}
Ok(provided_id.unwrap_or_else(|| crate::fleet::import::normalized_account_id(None)))
diff --git a/backend/src/fleet/manage/lifecycle.rs b/backend/src/fleet/manage/lifecycle.rs
index 6361bfa6f..21003f837 100644
--- a/backend/src/fleet/manage/lifecycle.rs
+++ b/backend/src/fleet/manage/lifecycle.rs
@@ -1,10 +1,10 @@
use std::{
- collections::{hash_map::DefaultHasher, HashSet},
+ collections::{HashSet, hash_map::DefaultHasher},
hash::{Hash, Hasher},
};
use chrono::{DateTime, Duration, Utc};
-use futures::{stream, StreamExt};
+use futures::{StreamExt, stream};
use secrecy::{ExposeSecret, SecretString};
use tokio::time::sleep;
use uuid::Uuid;
@@ -16,12 +16,12 @@ use crate::{
};
use super::{
- types::{
- parse_account_status, stored_to_admin_metadata, AccountHealthCheck, AccountManageError,
- AccountRefreshOutcome, AccountRefreshResult, AccountUpdate, BatchDeleteAccounts,
- ManagedAccount, ManualCreateTokens,
- },
AccountManageService,
+ types::{
+ AccountHealthCheck, AccountManageError, AccountRefreshOutcome, AccountRefreshResult,
+ AccountUpdate, BatchDeleteAccounts, ManagedAccount, ManualCreateTokens,
+ parse_account_status, stored_to_admin_metadata,
+ },
};
const ADMIN_REFRESH_LEASE_TTL_SECONDS: i64 = 5 * 60;
diff --git a/backend/src/fleet/manage/oauth.rs b/backend/src/fleet/manage/oauth.rs
index 545f9e1e1..a6f7e8b53 100644
--- a/backend/src/fleet/manage/oauth.rs
+++ b/backend/src/fleet/manage/oauth.rs
@@ -1,6 +1,6 @@
use std::{collections::HashMap, fmt::Write as _, sync::Arc};
-use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
+use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
use chrono::{DateTime, Duration, Utc};
use rand::Rng;
use reqwest::{Client, StatusCode, Url};
@@ -9,8 +9,8 @@ use sha2::{Digest, Sha256};
use tokio::sync::Mutex;
use super::{
- types::{AccountManageError, OAuthAuthorizeResult, OAuthExchangeInput},
AccountManageService,
+ types::{AccountManageError, OAuthAuthorizeResult, OAuthExchangeInput},
};
const OPENAI_OAUTH_AUTHORIZE_ENDPOINT: &str = "https://auth.openai.com/oauth/authorize";
diff --git a/backend/src/fleet/manage/probe.rs b/backend/src/fleet/manage/probe.rs
index b133998f7..2ef3eed3d 100644
--- a/backend/src/fleet/manage/probe.rs
+++ b/backend/src/fleet/manage/probe.rs
@@ -3,7 +3,7 @@ use std::{convert::Infallible, pin::Pin};
use bytes::Bytes;
use futures::{Stream, StreamExt};
use secrecy::ExposeSecret;
-use serde_json::{json, Value};
+use serde_json::{Value, json};
use tokio::sync::mpsc;
use crate::{
@@ -12,15 +12,15 @@ use crate::{
upstream::openai::{
protocol::{
responses::{CodexResponsesRequest, ResponsesSseFailure},
- sse::{encode_sse_event, parse_sse_events, SseEvent},
+ sse::{SseEvent, encode_sse_event, parse_sse_events},
},
transport::{
- is_banned_auth_signal, is_banned_upstream_error, CodexClientError, CodexRequestContext,
+ CodexClientError, CodexRequestContext, is_banned_auth_signal, is_banned_upstream_error,
},
},
};
-use super::{types::AccountManageError, AccountManageService};
+use super::{AccountManageService, types::AccountManageError};
pub(super) type AccountTestStream = Pin> + Send>>;
@@ -221,14 +221,14 @@ impl AccountManageService {
}
}
- if matches!(status, AccountStatus::Expired | AccountStatus::Banned) {
- if let Err(error) = self.store.set_next_refresh_at(account_id, None).await {
- tracing::warn!(
- account_id,
- error = %error,
- "failed to clear token refresh schedule after connection test"
- );
- }
+ if matches!(status, AccountStatus::Expired | AccountStatus::Banned)
+ && let Err(error) = self.store.set_next_refresh_at(account_id, None).await
+ {
+ tracing::warn!(
+ account_id,
+ error = %error,
+ "failed to clear token refresh schedule after connection test"
+ );
}
self.sync_account_pool_best_effort(account_id, "connection test")
.await;
@@ -297,10 +297,10 @@ async fn process_upstream_test_stream(
}
}
- if !buffer.trim().is_empty() {
- if let Some(outcome) = process_sse_frame(tx, &buffer).await {
- return outcome;
- }
+ if !buffer.trim().is_empty()
+ && let Some(outcome) = process_sse_frame(tx, &buffer).await
+ {
+ return outcome;
}
AccountTestOutcome::error("Stream ended before response.completed", None)
@@ -332,10 +332,10 @@ async fn process_sse_event(
};
match value.get("type").and_then(Value::as_str) {
Some("response.output_text.delta") => {
- if let Some(delta) = value.get("delta").and_then(Value::as_str) {
- if !delta.is_empty() {
- send_test_event(tx, json!({ "type": "content", "text": delta })).await;
- }
+ if let Some(delta) = value.get("delta").and_then(Value::as_str)
+ && !delta.is_empty()
+ {
+ send_test_event(tx, json!({ "type": "content", "text": delta })).await;
}
None
}
diff --git a/backend/src/fleet/manage/quota.rs b/backend/src/fleet/manage/quota.rs
index 498078198..8fb302447 100644
--- a/backend/src/fleet/manage/quota.rs
+++ b/backend/src/fleet/manage/quota.rs
@@ -1,6 +1,6 @@
use secrecy::ExposeSecret;
-use super::{types::AccountManageError, AccountManageService};
+use super::{AccountManageService, types::AccountManageError};
impl AccountManageService {
async fn usage_cookie_header(&self, account_id: &str) -> Option {
diff --git a/backend/src/fleet/pool/mod.rs b/backend/src/fleet/pool/mod.rs
index a85d890ff..e48093eb7 100644
--- a/backend/src/fleet/pool/mod.rs
+++ b/backend/src/fleet/pool/mod.rs
@@ -13,10 +13,10 @@ use indexmap::IndexMap;
use thiserror::Error;
use crate::fleet::quota::{quota_snapshot_limit_reached, quota_snapshot_reset_at};
-use crate::fleet::refresh::{jwt_expiry, JwtExpiry};
+use crate::fleet::refresh::{JwtExpiry, jwt_expiry};
use crate::fleet::scheduler::{
- candidates::{self, CandidateFilter, CandidateRequest},
AccountScheduler, ScoreWeights,
+ candidates::{self, CandidateFilter, CandidateRequest},
};
use crate::fleet::store::AccountStore;
use crate::fleet::{
@@ -27,7 +27,7 @@ use crate::telemetry::account_usage::store::{
AccountUsageDelta, AccountUsageSnapshot, AccountUsageStore, AccountUsageWindow,
};
use crate::upstream::openai::protocol::events::{
- parse_rate_limit_headers, rate_limit_quota, TokenUsage as CodexTokenUsage,
+ TokenUsage as CodexTokenUsage, parse_rate_limit_headers, rate_limit_quota,
};
pub use crate::fleet::scheduler::RotationStrategy;
@@ -457,21 +457,20 @@ impl AccountPoolService {
"failed to persist refreshed runtime account state"
);
}
- if refreshed.sync_usage_window {
- if let Err(error) = self
+ if refreshed.sync_usage_window
+ && let Err(error) = self
.usage_store
.sync_runtime_window(
&refreshed.account.id,
account_usage_window(&refreshed.account),
)
.await
- {
- tracing::error!(
- account_id = %refreshed.account.id,
- error = %error,
- "failed to persist refreshed runtime account usage window"
- );
- }
+ {
+ tracing::error!(
+ account_id = %refreshed.account.id,
+ error = %error,
+ "failed to persist refreshed runtime account usage window"
+ );
}
}
}
@@ -641,18 +640,17 @@ impl AccountPoolService {
}
};
- if let Some(reset_at) = reset_at {
- if let Err(error) = self
+ if let Some(reset_at) = reset_at
+ && let Err(error) = self
.usage_store
.sync_rate_limit_window(account_id, reset_at, limit_window_seconds)
.await
- {
- tracing::error!(
- account_id,
- error = %error,
- "failed to persist verified quota window"
- );
- }
+ {
+ tracing::error!(
+ account_id,
+ error = %error,
+ "failed to persist verified quota window"
+ );
}
let in_memory = {
diff --git a/backend/src/fleet/pool/state.rs b/backend/src/fleet/pool/state.rs
index b0c3c0103..3b3b63bac 100644
--- a/backend/src/fleet/pool/state.rs
+++ b/backend/src/fleet/pool/state.rs
@@ -451,13 +451,12 @@ impl AccountPool {
});
let mut account_ids = Vec::with_capacity(ordered.len());
- if let Some(preferred_account_id) = request.preferred_account_id.as_deref() {
- if let Some(index) = ordered
+ if let Some(preferred_account_id) = request.preferred_account_id.as_deref()
+ && let Some(index) = ordered
.iter()
.position(|account| account.id == preferred_account_id)
- {
- account_ids.push(ordered.remove(index).id);
- }
+ {
+ account_ids.push(ordered.remove(index).id);
}
account_ids.extend(ordered.into_iter().map(|account| account.id));
AccountCandidateSnapshotWithStatusRefresh {
diff --git a/backend/src/fleet/quota/runtime.rs b/backend/src/fleet/quota/runtime.rs
index e28c43c89..c7855db3b 100644
--- a/backend/src/fleet/quota/runtime.rs
+++ b/backend/src/fleet/quota/runtime.rs
@@ -4,7 +4,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
use chrono::Utc;
use thiserror::Error;
-use tokio::time::{sleep, Instant};
+use tokio::time::{Instant, sleep};
use tracing::warn;
use crate::fleet::{
diff --git a/backend/src/fleet/quota/service.rs b/backend/src/fleet/quota/service.rs
index 0888ed2d0..85473af56 100644
--- a/backend/src/fleet/quota/service.rs
+++ b/backend/src/fleet/quota/service.rs
@@ -3,7 +3,7 @@
use std::time::Duration;
use chrono::{DateTime, Utc};
-use serde_json::{json, Value};
+use serde_json::{Value, json};
const MONTH_WINDOW_MINUTES: u64 = 43_200;
const MONTH_WINDOW_SECONDS: u64 = 2_592_000;
diff --git a/backend/src/fleet/refresh/policy.rs b/backend/src/fleet/refresh/policy.rs
index 832f9bea1..8139b7fa4 100644
--- a/backend/src/fleet/refresh/policy.rs
+++ b/backend/src/fleet/refresh/policy.rs
@@ -7,10 +7,10 @@ use std::{
time::Duration as StdDuration,
};
-use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
+use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
use chrono::{DateTime, Duration, TimeZone, Utc};
use serde_json::{Map, Value};
-use tokio::sync::{watch, Semaphore};
+use tokio::sync::{Semaphore, watch};
use crate::fleet::account::{Account, AccountStatus};
use crate::upstream::openai::token_client::{RefreshFailure, TokenPair, TokenRefresher};
diff --git a/backend/src/fleet/refresh/service.rs b/backend/src/fleet/refresh/service.rs
index 4ca0a7927..9c2a32a51 100644
--- a/backend/src/fleet/refresh/service.rs
+++ b/backend/src/fleet/refresh/service.rs
@@ -5,8 +5,8 @@ use std::{
future::Future,
pin::Pin,
sync::{
- atomic::{AtomicBool, Ordering},
Arc, Mutex as StdMutex,
+ atomic::{AtomicBool, Ordering},
},
time::Duration,
};
@@ -30,12 +30,12 @@ use crate::upstream::openai::token_client::TokenRefresher;
use super::{
lease::{RedisRefreshLeaseStore, RedisRefreshLeaseStoreError},
policy::{
- default_refresh_retry_delays, duration_until, is_permanent_refresh_failure_status,
- jittered_refresh_at, jwt_expiration, same_refresh_time, scheduled_at_from_delay,
- stable_jittered_duration, token_refresh_status_eligible, RefreshError, RefreshPolicy,
- RefreshScheduler, RuntimeRefreshPolicy, TokenTimerSummary,
PERMANENT_FAILURE_CONFIRMATION_THRESHOLD, RECOVERY_DELAY_JITTER, RECOVERY_DELAY_SECONDS,
- RETRY_DELAY_JITTER,
+ RETRY_DELAY_JITTER, RefreshError, RefreshPolicy, RefreshScheduler, RuntimeRefreshPolicy,
+ TokenTimerSummary, default_refresh_retry_delays, duration_until,
+ is_permanent_refresh_failure_status, jittered_refresh_at, jwt_expiration,
+ same_refresh_time, scheduled_at_from_delay, stable_jittered_duration,
+ token_refresh_status_eligible,
},
};
diff --git a/backend/src/fleet/scheduler/feedback.rs b/backend/src/fleet/scheduler/feedback.rs
index 8ab440c9f..8c002d022 100644
--- a/backend/src/fleet/scheduler/feedback.rs
+++ b/backend/src/fleet/scheduler/feedback.rs
@@ -6,8 +6,8 @@
//! 对齐 sub2api 的设计,避免选择热路径上的锁与 DB 往返。
use std::collections::HashMap;
-use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::RwLock;
+use std::sync::atomic::{AtomicU64, Ordering};
/// EWMA 平滑系数(新样本权重)。与 sub2api 一致取 0.2。
const EWMA_ALPHA: f64 = 0.2;
@@ -59,11 +59,7 @@ impl AccountFeedback {
/// 读取 EWMA 样本;哨兵(NaN)返回 `None`。
fn load_sample(bits: &AtomicU64) -> Option {
let value = f64::from_bits(bits.load(Ordering::Relaxed));
- if value.is_nan() {
- None
- } else {
- Some(value)
- }
+ if value.is_nan() { None } else { Some(value) }
}
/// 无锁 CAS 更新 EWMA:首个样本直接落值,其后按 alpha 混合。
diff --git a/backend/src/fleet/scheduler/strategy/quota_reset.rs b/backend/src/fleet/scheduler/strategy/quota_reset.rs
index 02777b670..f79578bd2 100644
--- a/backend/src/fleet/scheduler/strategy/quota_reset.rs
+++ b/backend/src/fleet/scheduler/strategy/quota_reset.rs
@@ -12,7 +12,7 @@ use std::cmp::Ordering;
use crate::fleet::account::Account;
use crate::fleet::scheduler::{
- compare_last_used, compare_window_reset, order_by, select_by, SelectionInput,
+ SelectionInput, compare_last_used, compare_window_reset, order_by, select_by,
};
/// QuotaResetPriority 策略选择。
diff --git a/backend/src/fleet/scheduler/strategy/smart.rs b/backend/src/fleet/scheduler/strategy/smart.rs
index c43ed77cb..455ef1abc 100644
--- a/backend/src/fleet/scheduler/strategy/smart.rs
+++ b/backend/src/fleet/scheduler/strategy/smart.rs
@@ -11,7 +11,7 @@
use crate::fleet::account::Account;
use crate::fleet::scheduler::feedback::{FeedbackSample, FeedbackStats};
-use crate::fleet::scheduler::{account_window_token_count, SelectionInput};
+use crate::fleet::scheduler::{SelectionInput, account_window_token_count};
/// 配额封顶账号的惩罚分。远大于各因子加权和的理论上限(各因子 ∈ [0,1],权重和通常为
/// 个位数),保证未封顶账号总是优先于封顶账号。
@@ -202,11 +202,11 @@ impl NormalizationContext {
max_window_cached = max_window_cached.max(account.window_cached_tokens as f64);
max_window_requests = max_window_requests.max(account.window_request_count as f64);
- if let Some(ttft) = sample.ttft_ms {
- if ttft > 0.0 {
- min_ttft_ms = min_ttft_ms.min(ttft);
- max_ttft_ms = max_ttft_ms.max(ttft);
- }
+ if let Some(ttft) = sample.ttft_ms
+ && ttft > 0.0
+ {
+ min_ttft_ms = min_ttft_ms.min(ttft);
+ max_ttft_ms = max_ttft_ms.max(ttft);
}
if let Some(reset_at) = account.window_reset_at {
diff --git a/backend/src/fleet/scheduler/strategy/sticky.rs b/backend/src/fleet/scheduler/strategy/sticky.rs
index e6638c1a3..39b43714c 100644
--- a/backend/src/fleet/scheduler/strategy/sticky.rs
+++ b/backend/src/fleet/scheduler/strategy/sticky.rs
@@ -6,7 +6,7 @@
use std::cmp::Ordering;
use crate::fleet::account::Account;
-use crate::fleet::scheduler::{compare_last_used, SelectionInput};
+use crate::fleet::scheduler::{SelectionInput, compare_last_used};
/// 选中最近使用过的账号(`last_used_at` 最大者)。
///
diff --git a/backend/src/fleet/store/mod.rs b/backend/src/fleet/store/mod.rs
index 042de4339..1c1f40152 100644
--- a/backend/src/fleet/store/mod.rs
+++ b/backend/src/fleet/store/mod.rs
@@ -8,7 +8,7 @@ use sqlx::{PgPool, Postgres, QueryBuilder, Row};
use thiserror::Error;
use crate::fleet::account::{Account, AccountStatus};
-use crate::infra::json::{page_offset, NumberedPage};
+use crate::infra::json::{NumberedPage, page_offset};
mod queries;
mod rows;
@@ -249,7 +249,7 @@ pub trait AccountStore: Send + Sync + 'static {
/// 更新账号状态。
async fn set_status(&self, account_id: &str, status: AccountStatus)
- -> AccountStoreResult;
+ -> AccountStoreResult;
/// 读取账号当前配额 JSON。
async fn get_quota_json(&self, account_id: &str) -> AccountStoreResult