refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构

- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/
- 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层
- 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构
- 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations)
- 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image
- 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
This commit is contained in:
fawney19
2026-04-03 16:26:16 +08:00
parent 8f26e1a31f
commit 1d9c77522a
868 changed files with 1735 additions and 2433 deletions

View File

@@ -53,7 +53,7 @@ fn build_admin_model_catalog_maintenance_response() -> Response<Body> {
.into_response()
}
pub(super) async fn maybe_build_local_admin_core_response(
pub(crate) async fn maybe_build_local_admin_core_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -9,7 +9,7 @@ mod endpoints_routes;
#[path = "endpoints/rpm.rs"]
mod endpoints_rpm;
pub(super) async fn maybe_build_local_admin_endpoints_response(
pub(crate) async fn maybe_build_local_admin_endpoints_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -0,0 +1,74 @@
pub(crate) use super::*;
mod adaptive;
mod api_keys;
mod billing;
mod catalog_write_helpers;
mod core;
mod endpoints;
mod endpoints_health_helpers;
mod gemini_files;
mod global_models;
mod ldap;
mod misc_helpers;
mod models_helpers;
mod monitoring;
mod oauth_helpers;
mod payments;
mod pool;
mod provider_models;
#[path = "provider_oauth/dispatch.rs"]
mod provider_oauth_dispatch;
#[path = "provider_oauth/quota.rs"]
mod provider_oauth_quota;
#[path = "provider_oauth/refresh.rs"]
mod provider_oauth_refresh;
#[path = "provider_oauth/state.rs"]
mod provider_oauth_state;
mod provider_ops;
mod provider_query;
mod provider_strategy;
mod providers;
mod providers_helpers;
mod proxy_nodes;
mod security;
mod stats;
mod usage;
mod users;
mod video_tasks;
mod wallets;
pub(crate) use self::adaptive::*;
pub(crate) use self::api_keys::*;
pub(crate) use self::billing::*;
pub(crate) use self::catalog_write_helpers::*;
pub(crate) use self::core::*;
pub(crate) use self::endpoints::*;
pub(crate) use self::endpoints_health_helpers::*;
pub(crate) use self::gemini_files::*;
pub(crate) use self::global_models::*;
pub(crate) use self::ldap::*;
pub(crate) use self::misc_helpers::*;
pub(crate) use self::models_helpers::*;
pub(crate) use self::monitoring::maybe_build_local_admin_monitoring_root_response as maybe_build_local_admin_monitoring_response;
pub(crate) use self::oauth_helpers::*;
pub(crate) use self::payments::*;
pub(crate) use self::pool::*;
pub(crate) use self::provider_models::*;
pub(crate) use self::provider_oauth_dispatch::*;
pub(crate) use self::provider_oauth_quota::*;
pub(crate) use self::provider_oauth_refresh::*;
pub(crate) use self::provider_oauth_state::*;
pub(crate) use self::provider_ops::admin_provider_ops_local_action_response;
pub(crate) use self::provider_ops::maybe_build_local_admin_provider_ops_response;
pub(crate) use self::provider_query::*;
pub(crate) use self::provider_strategy::*;
pub(crate) use self::providers::*;
pub(crate) use self::providers_helpers::*;
pub(crate) use self::proxy_nodes::*;
pub(crate) use self::security::*;
pub(crate) use self::stats::*;
pub(crate) use self::usage::*;
pub(crate) use self::users::*;
pub(crate) use self::video_tasks::*;
pub(crate) use self::wallets::*;

View File

@@ -19,7 +19,7 @@ mod provider_models_list;
#[path = "provider_models/update.rs"]
mod provider_models_update;
pub(super) async fn maybe_build_local_admin_provider_models_response(
pub(crate) async fn maybe_build_local_admin_provider_models_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -15,7 +15,7 @@ mod dispatch_start;
#[path = "dispatch/tasks.rs"]
mod dispatch_tasks;
pub(super) async fn maybe_build_local_admin_provider_oauth_response(
pub(crate) async fn maybe_build_local_admin_provider_oauth_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&axum::body::Bytes>,

View File

@@ -0,0 +1,6 @@
pub(crate) use super::*;
mod gateway_helpers;
pub(crate) use self::gateway_helpers::*;
mod gateway;
pub(crate) use self::gateway::maybe_build_local_internal_proxy_response_impl;

View File

@@ -0,0 +1,145 @@
use std::collections::{BTreeMap, BTreeSet};
use std::fs;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use aether_contracts::{ExecutionPlan, ExecutionResult, ExecutionTimeouts, RequestBody};
#[cfg(test)]
use aether_crypto::DEVELOPMENT_ENCRYPTION_KEY;
use aether_crypto::{decrypt_python_fernet_ciphertext, encrypt_python_fernet_plaintext};
use aether_data::redis::{RedisKeyspace, RedisKvRunner};
use aether_data::repository::candidate_selection::StoredMinimalCandidateSelectionRow;
use aether_data::repository::candidates::{
PublicHealthTimelineBucket, RequestCandidateStatus, StoredRequestCandidate,
};
use aether_data::repository::global_models::{
AdminGlobalModelListQuery, AdminProviderModelListQuery, CreateAdminGlobalModelRecord,
PublicGlobalModelQuery, StoredAdminGlobalModel, StoredAdminProviderModel,
StoredPublicGlobalModel, UpdateAdminGlobalModelRecord, UpsertAdminProviderModelRecord,
};
use aether_data::repository::management_tokens::{
CreateManagementTokenRecord, ManagementTokenListQuery, RegenerateManagementTokenSecret,
StoredManagementToken, StoredManagementTokenUserSummary, UpdateManagementTokenRecord,
};
use aether_data::repository::oauth_providers::{
EncryptedSecretUpdate, UpsertOAuthProviderConfigRecord,
};
use aether_data::repository::provider_catalog::{
StoredProviderCatalogEndpoint, StoredProviderCatalogKey, StoredProviderCatalogProvider,
};
use aether_data::repository::proxy_nodes::{
ProxyNodeHeartbeatMutation, ProxyNodeTunnelStatusMutation, StoredProxyNode,
StoredProxyNodeEvent,
};
use aether_runtime::{maybe_hold_axum_response_permit, AdmissionPermit};
use axum::body::{to_bytes, Body, Bytes};
use axum::extract::{ConnectInfo, Request, State};
use axum::http::header::{HeaderName, HeaderValue};
use axum::http::Response;
use axum::response::IntoResponse;
use axum::Json;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine as _;
use chrono::{Datelike, SecondsFormat, Utc};
use futures_util::TryStreamExt;
use regex::Regex;
pub(crate) use serde::Deserialize;
pub(crate) use serde_json::{json, Value};
use sha2::{Digest, Sha256};
use tracing::info;
pub(crate) use tracing::warn;
use url::form_urlencoded;
use url::Url;
use uuid::Uuid;
use crate::gateway::api::ai::{
admin_default_body_rules_for_signature, admin_endpoint_signature_parts,
public_api_format_local_path,
};
use crate::gateway::api::response::{
build_client_response, build_local_auth_rejection_response, build_local_http_error_response,
build_local_overloaded_response, build_local_user_rpm_limited_response,
};
use crate::gateway::constants::*;
use crate::gateway::headers::{
extract_or_generate_trace_id, header_value_str, should_skip_request_header,
};
use crate::gateway::provider_transport::{
fixed_provider_template, provider_type_enables_format_conversion_by_default,
provider_type_is_fixed,
};
use crate::gateway::scheduler::{
count_recent_rpm_requests_for_provider_key_since, is_provider_key_circuit_open,
provider_key_health_score,
};
use crate::gateway::{
allows_control_execute_emergency, execute_execution_runtime_stream,
execute_execution_runtime_sync, maybe_build_stream_decision_payload,
maybe_build_stream_plan_payload, maybe_build_sync_decision_payload,
maybe_build_sync_finalize_outcome, maybe_build_sync_plan_payload, maybe_execute_via_control,
maybe_execute_via_execution_runtime_stream, maybe_execute_via_execution_runtime_sync,
record_shadow_result_non_blocking, request_model_local_rejection,
resolve_public_request_context, should_buffer_request_for_local_auth,
trusted_auth_local_rejection, AppState, FrontdoorUserRpmOutcome, GatewayControlDecision,
GatewayError, GatewayFallbackMetricKind, GatewayFallbackReason, GatewayPublicRequestContext,
LocalProviderDeleteTaskState,
};
const ADMIN_PROVIDER_MAPPING_PREVIEW_MAX_KEYS: usize = 200;
const ADMIN_PROVIDER_MAPPING_PREVIEW_MAX_MODELS: usize = 500;
const ADMIN_PROVIDER_MAPPING_PREVIEW_FETCH_LIMIT: usize = 10_000;
const ADMIN_PROVIDER_POOL_SCAN_BATCH: u64 = 200;
const ADMIN_EXTERNAL_MODELS_CACHE_KEY: &str = "aether:external:models_dev";
const ADMIN_EXTERNAL_MODELS_CACHE_TTL_SECS: u64 = 15 * 60;
const ADMIN_PROVIDER_OAUTH_RUST_BACKEND_DETAIL: &str =
"Admin provider OAuth requires Rust maintenance backend";
pub(crate) mod admin;
pub(crate) mod internal;
pub(crate) mod proxy;
pub(crate) mod public;
pub(crate) mod shared;
pub(crate) use admin::*;
pub(crate) use internal::*;
pub(crate) use public::*;
pub(crate) use proxy::proxy_request;
pub(crate) use shared::*;
const OFFICIAL_EXTERNAL_MODEL_PROVIDERS: &[&str] = &[
"anthropic",
"openai",
"google",
"google-vertex",
"azure",
"amazon-bedrock",
"xai",
"meta",
"deepseek",
"mistral",
"cohere",
"zhipuai",
"alibaba",
"minimax",
"moonshot",
"baichuan",
"ai21",
];
#[derive(Debug, Clone, Copy)]
pub(crate) struct AdminProviderPoolConfig {
pub(crate) lru_enabled: bool,
pub(crate) cost_window_seconds: u64,
pub(crate) cost_limit_per_key_tokens: Option<u64>,
}
#[derive(Debug, Default)]
pub(crate) struct AdminProviderPoolRuntimeState {
pub(crate) total_sticky_sessions: usize,
pub(crate) sticky_sessions_by_key: BTreeMap<String, usize>,
pub(crate) cooldown_reason_by_key: BTreeMap<String, String>,
pub(crate) cooldown_ttl_by_key: BTreeMap<String, u64>,
pub(crate) cost_window_usage_by_key: BTreeMap<String, u64>,
pub(crate) lru_score_by_key: BTreeMap<String, f64>,
}
pub(crate) use self::shared::*;

View File

@@ -1,3 +1,4 @@
use super::super::{admin, internal, public};
use super::*;
pub(super) async fn maybe_build_local_internal_proxy_response(
@@ -7,7 +8,7 @@ pub(super) async fn maybe_build_local_internal_proxy_response(
request_body: Option<&axum::body::Bytes>,
legacy_internal_gateway_allowed: bool,
) -> Result<Option<Response<Body>>, GatewayError> {
let response = maybe_build_local_internal_proxy_response_impl(
let response = internal::maybe_build_local_internal_proxy_response_impl(
state,
request_context,
remote_addr,
@@ -22,7 +23,7 @@ pub(super) async fn maybe_build_local_internal_proxy_response(
.and_then(|decision| decision.route_family.as_deref())
== Some("gateway_legacy")
{
return Ok(response.map(attach_legacy_internal_gateway_deprecation_headers));
return Ok(response.map(internal::attach_legacy_internal_gateway_deprecation_headers));
}
Ok(response)
@@ -44,27 +45,33 @@ pub(super) async fn maybe_build_local_admin_proxy_response(
}
if let Some(response) =
super::admin_provider_oauth_dispatch::maybe_build_local_admin_provider_oauth_response(
state,
request_context,
request_body,
)
.await?
admin::maybe_build_local_admin_provider_oauth_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = super::public_support::maybe_build_local_admin_announcements_response(
state,
request_context,
request_body,
)
.await?
if let Some(response) =
public::maybe_build_local_admin_announcements_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = super::admin_core::maybe_build_local_admin_core_response(
if let Some(response) =
admin::maybe_build_local_admin_core_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_global_models_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = admin::maybe_build_local_admin_provider_models_response(
state,
request_context,
request_body,
@@ -75,127 +82,24 @@ pub(super) async fn maybe_build_local_admin_proxy_response(
}
if let Some(response) =
super::admin_global_models::maybe_build_local_admin_global_models_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
super::admin_provider_models::maybe_build_local_admin_provider_models_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = super::admin_providers::maybe_build_local_admin_providers_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_provider_ops_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_adaptive_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_provider_strategy_response(state, request_context, request_body)
admin::maybe_build_local_admin_providers_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_pool_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_billing_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_payments_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_provider_query_response(state, request_context, request_body)
admin::maybe_build_local_admin_provider_ops_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_security_response(state, request_context, request_body).await?
admin::maybe_build_local_admin_adaptive_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) = maybe_build_local_admin_stats_response(state, request_context).await? {
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_monitoring_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_usage_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_video_tasks_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_proxy_nodes_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_wallets_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_api_keys_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_ldap_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_gemini_files_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
maybe_build_local_admin_users_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) = super::admin_endpoints::maybe_build_local_admin_endpoints_response(
if let Some(response) = admin::maybe_build_local_admin_provider_strategy_response(
state,
request_context,
request_body,
@@ -204,6 +108,94 @@ pub(super) async fn maybe_build_local_admin_proxy_response(
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_pool_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_billing_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_payments_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_provider_query_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_security_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_stats_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_monitoring_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_usage_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_video_tasks_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_proxy_nodes_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_wallets_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_api_keys_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_ldap_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_gemini_files_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_users_response(state, request_context, request_body).await?
{
return Ok(Some(response));
}
if let Some(response) =
admin::maybe_build_local_admin_endpoints_response(state, request_context, request_body)
.await?
{
return Ok(Some(response));
}
Ok(None)
}

View File

@@ -1,27 +1,14 @@
pub(crate) use super::*;
#[path = "proxy/local.rs"]
mod local;
#[path = "admin/core.rs"]
mod admin_core;
#[path = "admin/endpoints.rs"]
mod admin_endpoints;
#[path = "admin/global_models.rs"]
mod admin_global_models;
#[path = "admin/provider_models.rs"]
mod admin_provider_models;
#[path = "admin/provider_oauth/dispatch.rs"]
mod admin_provider_oauth_dispatch;
#[path = "admin/providers.rs"]
mod admin_providers;
#[path = "public/support.rs"]
mod public_support;
use self::local::{
maybe_build_local_admin_proxy_response, maybe_build_local_internal_proxy_response,
};
pub(crate) use self::public_support::matches_model_mapping_for_models;
use super::internal::{
attach_legacy_internal_gateway_deprecation_headers, resolve_local_proxy_execution_path,
};
pub(crate) use super::public::matches_model_mapping_for_models;
use crate::gateway::ai_pipeline::{finalize as ai_finalize, runtime as ai_runtime};
const OPENAI_CHAT_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
@@ -362,7 +349,7 @@ pub(crate) async fn proxy_request(
request_permit.take(),
));
}
if let Some(response) = public_support::maybe_build_local_public_support_response(
if let Some(response) = super::public::maybe_build_local_public_support_response(
&state,
&request_context,
&parts.headers,
@@ -882,7 +869,7 @@ fn local_execution_runtime_miss_detail_after_python_fallback_removal(
}
}
#[path = "proxy/finalize.rs"]
#[path = "finalize.rs"]
mod finalize;
use self::finalize::{

View File

@@ -0,0 +1,9 @@
pub(crate) use super::*;
mod catalog_helpers;
mod support;
mod system_modules_helpers;
pub(crate) use self::catalog_helpers::*;
pub(crate) use self::support::*;
pub(crate) use self::system_modules_helpers::*;

View File

@@ -19,7 +19,7 @@ mod support_user_me;
#[path = "support/wallet.rs"]
mod support_wallet;
pub(super) use self::support_announcements::maybe_build_local_admin_announcements_response;
pub(crate) use self::support_announcements::maybe_build_local_admin_announcements_response;
pub(crate) use self::support_models::matches_model_mapping_for_models;
use self::support_announcements::{
@@ -57,7 +57,7 @@ fn build_public_support_maintenance_response(detail: &str) -> Response<Body> {
.into_response()
}
pub(super) async fn maybe_build_local_public_support_response(
pub(crate) async fn maybe_build_local_public_support_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
headers: &http::HeaderMap,

View File

@@ -1,12 +1,8 @@
pub(crate) use super::*;
#[path = "shared/admin_paths.rs"]
mod admin_paths;
#[path = "shared/catalog.rs"]
mod catalog;
#[path = "shared/payloads.rs"]
mod payloads;
#[path = "shared/request_utils.rs"]
mod request_utils;
pub(crate) use self::admin_paths::*;