mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
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:
@@ -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)
|
||||
}
|
||||
|
||||
879
apps/aether-gateway/src/handlers/proxy/mod.rs
Normal file
879
apps/aether-gateway/src/handlers/proxy/mod.rs
Normal file
@@ -0,0 +1,879 @@
|
||||
pub(crate) use super::*;
|
||||
|
||||
mod local;
|
||||
|
||||
use self::local::{
|
||||
maybe_build_local_admin_proxy_response, maybe_build_local_internal_proxy_response,
|
||||
};
|
||||
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 =
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path, and Python fallback has been removed";
|
||||
const OPENAI_RESPONSES_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
|
||||
"OpenAI responses execution runtime miss did not match a Rust execution path, and Python fallback has been removed";
|
||||
const OPENAI_COMPACT_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
|
||||
"OpenAI compact execution runtime miss did not match a Rust execution path, and Python fallback has been removed";
|
||||
const OPENAI_VIDEO_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
|
||||
"OpenAI video execution runtime miss did not match a Rust execution path, and Python fallback has been removed";
|
||||
const CLAUDE_MESSAGES_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
|
||||
"Claude messages execution runtime miss did not match a Rust execution path, and Python fallback has been removed";
|
||||
const GEMINI_PUBLIC_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
|
||||
"Gemini public execution runtime miss did not match a Rust execution path, and Python fallback has been removed";
|
||||
const GEMINI_FILES_PYTHON_FALLBACK_REMOVED_DETAIL: &str =
|
||||
"Gemini files execution runtime miss did not match a Rust execution path, and Python fallback has been removed";
|
||||
const EXECUTION_PATH_TUNNEL_AFFINITY_FORWARD: &str = "tunnel_affinity_forward";
|
||||
|
||||
fn execution_runtime_candidate_header_value(decision: &GatewayControlDecision) -> &'static str {
|
||||
if decision.is_execution_runtime_candidate() {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
}
|
||||
}
|
||||
|
||||
async fn maybe_forward_public_request_to_tunnel_owner(
|
||||
state: &AppState,
|
||||
remote_addr: &std::net::SocketAddr,
|
||||
request_context: &GatewayPublicRequestContext,
|
||||
parts: &http::request::Parts,
|
||||
buffered_body: Option<&Bytes>,
|
||||
) -> Result<Option<Response<Body>>, GatewayError> {
|
||||
let Some(decision) = request_context.control_decision.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
if decision.route_class.as_deref() != Some("ai_public")
|
||||
|| !decision.is_execution_runtime_candidate()
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
if parts
|
||||
.headers
|
||||
.get(TUNNEL_AFFINITY_FORWARDED_BY_HEADER)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::trim)
|
||||
.is_some_and(|value| !value.is_empty())
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(auth_context) = decision.auth_context.as_ref().filter(|auth_context| {
|
||||
auth_context.access_allowed && !auth_context.api_key_id.trim().is_empty()
|
||||
}) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(api_format) = decision
|
||||
.auth_endpoint_signature
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let empty_body = Bytes::new();
|
||||
let Some(requested_model) = crate::gateway::extract_requested_model(
|
||||
decision,
|
||||
&parts.uri,
|
||||
&parts.headers,
|
||||
buffered_body.unwrap_or(&empty_body),
|
||||
) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(target) = crate::gateway::scheduler::read_cached_scheduler_affinity_target(
|
||||
state,
|
||||
&auth_context.api_key_id,
|
||||
api_format,
|
||||
&requested_model,
|
||||
) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let transport = match state
|
||||
.read_provider_transport_snapshot(&target.provider_id, &target.endpoint_id, &target.key_id)
|
||||
.await
|
||||
{
|
||||
Ok(Some(transport)) => transport,
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => {
|
||||
warn!(
|
||||
trace_id = %request_context.trace_id,
|
||||
provider_id = %target.provider_id,
|
||||
endpoint_id = %target.endpoint_id,
|
||||
key_id = %target.key_id,
|
||||
error = ?err,
|
||||
"gateway failed to read provider transport for tunnel affinity forward"
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
let Some(proxy) =
|
||||
crate::gateway::provider_transport::resolve_transport_proxy_snapshot(&transport)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
if proxy.enabled == Some(false) {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(node_id) = proxy
|
||||
.node_id
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
if state.tunnel.has_local_proxy(node_id) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(owner) = state
|
||||
.tunnel
|
||||
.lookup_attachment_owner(state.data.as_ref(), node_id)
|
||||
.await
|
||||
.map_err(GatewayError::Internal)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
if owner.gateway_instance_id == state.tunnel.local_instance_id() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let owner_url = format!(
|
||||
"{}{}",
|
||||
owner.relay_base_url.trim_end_matches('/'),
|
||||
request_context.request_path_and_query()
|
||||
);
|
||||
let mut upstream_request = state.client.request(parts.method.clone(), owner_url);
|
||||
for (name, value) in &parts.headers {
|
||||
if should_skip_request_header(name.as_str()) || name == http::header::HOST {
|
||||
continue;
|
||||
}
|
||||
if should_strip_forwarded_provider_credential_header(Some(decision), name) {
|
||||
continue;
|
||||
}
|
||||
if should_strip_forwarded_trusted_admin_header(Some(decision), name) {
|
||||
continue;
|
||||
}
|
||||
upstream_request = upstream_request.header(name, value);
|
||||
}
|
||||
if let Some(host) = request_context.host_header.as_deref() {
|
||||
if !parts.headers.contains_key(FORWARDED_HOST_HEADER) {
|
||||
upstream_request = upstream_request.header(FORWARDED_HOST_HEADER, host);
|
||||
}
|
||||
}
|
||||
if !parts.headers.contains_key(FORWARDED_FOR_HEADER) {
|
||||
upstream_request =
|
||||
upstream_request.header(FORWARDED_FOR_HEADER, remote_addr.ip().to_string());
|
||||
}
|
||||
if !parts.headers.contains_key(FORWARDED_PROTO_HEADER) {
|
||||
upstream_request = upstream_request.header(FORWARDED_PROTO_HEADER, "http");
|
||||
}
|
||||
if !parts.headers.contains_key(TRACE_ID_HEADER) {
|
||||
upstream_request = upstream_request.header(TRACE_ID_HEADER, &request_context.trace_id);
|
||||
}
|
||||
upstream_request = upstream_request
|
||||
.header(GATEWAY_HEADER, "rust-phase3b-affinity")
|
||||
.header(
|
||||
TUNNEL_AFFINITY_FORWARDED_BY_HEADER,
|
||||
state.tunnel.local_instance_id(),
|
||||
)
|
||||
.header(
|
||||
TUNNEL_AFFINITY_OWNER_INSTANCE_HEADER,
|
||||
owner.gateway_instance_id.as_str(),
|
||||
)
|
||||
.header(TRUSTED_AUTH_USER_ID_HEADER, &auth_context.user_id)
|
||||
.header(TRUSTED_AUTH_API_KEY_ID_HEADER, &auth_context.api_key_id)
|
||||
.header(TRUSTED_AUTH_ACCESS_ALLOWED_HEADER, "true");
|
||||
if let Some(balance_remaining) = auth_context.balance_remaining {
|
||||
upstream_request =
|
||||
upstream_request.header(TRUSTED_AUTH_BALANCE_HEADER, balance_remaining.to_string());
|
||||
}
|
||||
|
||||
let upstream_response = upstream_request
|
||||
.body(buffered_body.cloned().unwrap_or_default())
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| GatewayError::UpstreamUnavailable {
|
||||
trace_id: request_context.trace_id.clone(),
|
||||
message: format!("owner gateway affinity forward failed: {err}"),
|
||||
})?;
|
||||
|
||||
let mut response = ai_finalize::build_client_response(
|
||||
upstream_response,
|
||||
&request_context.trace_id,
|
||||
Some(decision),
|
||||
)?;
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static(TUNNEL_AFFINITY_OWNER_INSTANCE_HEADER),
|
||||
HeaderValue::from_str(owner.gateway_instance_id.as_str())
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?,
|
||||
);
|
||||
Ok(Some(response))
|
||||
}
|
||||
|
||||
pub(crate) async fn proxy_request(
|
||||
State(state): State<AppState>,
|
||||
ConnectInfo(remote_addr): ConnectInfo<std::net::SocketAddr>,
|
||||
request: Request,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
let started_at = Instant::now();
|
||||
let mut request_permit = match state.try_acquire_request_permit().await {
|
||||
Ok(permit) => permit,
|
||||
Err(crate::gateway::RequestAdmissionError::Local(
|
||||
aether_runtime::ConcurrencyError::Saturated { gate, limit },
|
||||
)) => {
|
||||
let trace_id = extract_or_generate_trace_id(request.headers());
|
||||
let response = build_local_overloaded_response(&trace_id, None, gate, limit)?;
|
||||
return Ok(finalize_gateway_response(
|
||||
&state,
|
||||
response,
|
||||
&trace_id,
|
||||
&remote_addr,
|
||||
request.method(),
|
||||
request
|
||||
.uri()
|
||||
.path_and_query()
|
||||
.map(|value| value.as_str())
|
||||
.unwrap_or("/"),
|
||||
None,
|
||||
EXECUTION_PATH_LOCAL_OVERLOADED,
|
||||
&started_at,
|
||||
None,
|
||||
));
|
||||
}
|
||||
Err(crate::gateway::RequestAdmissionError::Local(
|
||||
aether_runtime::ConcurrencyError::Closed { gate },
|
||||
)) => {
|
||||
return Err(GatewayError::Internal(format!(
|
||||
"gateway request concurrency gate {gate} is closed"
|
||||
)));
|
||||
}
|
||||
Err(crate::gateway::RequestAdmissionError::Distributed(
|
||||
aether_runtime::DistributedConcurrencyError::Saturated { gate, limit },
|
||||
))
|
||||
| Err(crate::gateway::RequestAdmissionError::Distributed(
|
||||
aether_runtime::DistributedConcurrencyError::Unavailable { gate, limit, .. },
|
||||
)) => {
|
||||
let trace_id = extract_or_generate_trace_id(request.headers());
|
||||
let response = build_local_overloaded_response(&trace_id, None, gate, limit)?;
|
||||
return Ok(finalize_gateway_response(
|
||||
&state,
|
||||
response,
|
||||
&trace_id,
|
||||
&remote_addr,
|
||||
request.method(),
|
||||
request
|
||||
.uri()
|
||||
.path_and_query()
|
||||
.map(|value| value.as_str())
|
||||
.unwrap_or("/"),
|
||||
None,
|
||||
EXECUTION_PATH_DISTRIBUTED_OVERLOADED,
|
||||
&started_at,
|
||||
None,
|
||||
));
|
||||
}
|
||||
Err(crate::gateway::RequestAdmissionError::Distributed(
|
||||
aether_runtime::DistributedConcurrencyError::InvalidConfiguration(message),
|
||||
)) => return Err(GatewayError::Internal(message)),
|
||||
};
|
||||
let (parts, body) = request.into_parts();
|
||||
let trace_id = extract_or_generate_trace_id(&parts.headers);
|
||||
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
let request_context = crate::gateway::control::resolve_public_request_context(
|
||||
&state,
|
||||
&parts.method,
|
||||
&parts.uri,
|
||||
&parts.headers,
|
||||
&trace_id,
|
||||
)
|
||||
.await?;
|
||||
let mut request_body = Some(body);
|
||||
let local_proxy_body = if local_proxy_route_requires_buffered_body(&request_context) {
|
||||
Some(
|
||||
to_bytes(
|
||||
request_body
|
||||
.take()
|
||||
.expect("local proxy body buffering should own request body"),
|
||||
usize::MAX,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let method = request_context.request_method.clone();
|
||||
let request_path_and_query = request_context.request_path_and_query();
|
||||
let path_and_query = request_path_and_query.as_str();
|
||||
let control_decision = request_context.control_decision.as_ref();
|
||||
let legacy_internal_gateway_allowed = request_enables_control_execute(&parts.headers);
|
||||
if let Some(response) = maybe_build_local_internal_proxy_response(
|
||||
&state,
|
||||
&request_context,
|
||||
&remote_addr,
|
||||
local_proxy_body.as_ref(),
|
||||
legacy_internal_gateway_allowed,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
let execution_path =
|
||||
resolve_local_proxy_execution_path(&response, EXECUTION_PATH_PUBLIC_PROXY_PASSTHROUGH);
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
execution_path,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
if let Some(response) =
|
||||
maybe_build_local_admin_proxy_response(&state, &request_context, local_proxy_body.as_ref())
|
||||
.await?
|
||||
{
|
||||
let execution_path =
|
||||
resolve_local_proxy_execution_path(&response, EXECUTION_PATH_PUBLIC_PROXY_PASSTHROUGH);
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
execution_path,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
if let Some(response) = super::public::maybe_build_local_public_support_response(
|
||||
&state,
|
||||
&request_context,
|
||||
&parts.headers,
|
||||
local_proxy_body.as_ref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
let execution_path =
|
||||
resolve_local_proxy_execution_path(&response, EXECUTION_PATH_PUBLIC_PROXY_PASSTHROUGH);
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
execution_path,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
if let Some(buffered_body) = local_proxy_body {
|
||||
request_body = Some(Body::from(buffered_body));
|
||||
}
|
||||
let should_try_control_execute = control_decision
|
||||
.map(|decision| {
|
||||
decision.is_execution_runtime_candidate()
|
||||
&& decision.route_class.as_deref() == Some("ai_public")
|
||||
})
|
||||
.unwrap_or(false);
|
||||
let should_buffer_for_local_auth =
|
||||
should_buffer_request_for_local_auth(control_decision, &parts.headers);
|
||||
let should_buffer_body = should_try_control_execute || should_buffer_for_local_auth;
|
||||
|
||||
let allow_control_execute_fallback = should_try_control_execute
|
||||
&& control_decision.is_some_and(allows_control_execute_emergency)
|
||||
&& request_enables_control_execute(&parts.headers);
|
||||
|
||||
let buffered_body = if should_buffer_body {
|
||||
Some(
|
||||
to_bytes(
|
||||
request_body
|
||||
.take()
|
||||
.expect("buffered auth/execution runtime path should own request body"),
|
||||
usize::MAX,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(response) = maybe_forward_public_request_to_tunnel_owner(
|
||||
&state,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
&parts,
|
||||
buffered_body.as_ref(),
|
||||
)
|
||||
.await?
|
||||
{
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_TUNNEL_AFFINITY_FORWARD,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(rejection) = trusted_auth_local_rejection(control_decision, &parts.headers) {
|
||||
let response =
|
||||
build_local_auth_rejection_response(&trace_id, control_decision, &rejection)?;
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_LOCAL_AUTH_DENIED,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(buffered_body) = buffered_body.as_ref() {
|
||||
if let Some(rejection) = request_model_local_rejection(
|
||||
control_decision,
|
||||
&parts.uri,
|
||||
&parts.headers,
|
||||
buffered_body,
|
||||
) {
|
||||
let response =
|
||||
build_local_auth_rejection_response(&trace_id, control_decision, &rejection)?;
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_LOCAL_AUTH_DENIED,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let rate_limit_outcome = state
|
||||
.frontdoor_user_rpm()
|
||||
.check_and_consume(&state, control_decision)
|
||||
.await?;
|
||||
if let FrontdoorUserRpmOutcome::Rejected(rejection) = &rate_limit_outcome {
|
||||
let response =
|
||||
build_local_user_rpm_limited_response(&trace_id, control_decision, rejection)?;
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_LOCAL_RATE_LIMITED,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
|
||||
let upstream_path_and_query =
|
||||
sanitize_upstream_path_and_query(control_decision, path_and_query);
|
||||
let target_url = format!("{}{}", state.upstream_base_url, upstream_path_and_query);
|
||||
let mut upstream_request = state.client.request(method.clone(), &target_url);
|
||||
for (name, value) in &parts.headers {
|
||||
if should_skip_request_header(name.as_str()) {
|
||||
continue;
|
||||
}
|
||||
// Once Rust has produced trusted auth headers, Python should not need the raw
|
||||
// provider credential anymore. Keep the bridge boundary explicit.
|
||||
if should_strip_forwarded_provider_credential_header(control_decision, name) {
|
||||
continue;
|
||||
}
|
||||
if should_strip_forwarded_trusted_admin_header(control_decision, name) {
|
||||
continue;
|
||||
}
|
||||
upstream_request = upstream_request.header(name, value);
|
||||
}
|
||||
|
||||
if let Some(host) = request_context.host_header.as_deref() {
|
||||
if !parts.headers.contains_key(FORWARDED_HOST_HEADER) {
|
||||
upstream_request = upstream_request.header(FORWARDED_HOST_HEADER, host);
|
||||
}
|
||||
}
|
||||
|
||||
if !parts.headers.contains_key(FORWARDED_FOR_HEADER) {
|
||||
upstream_request =
|
||||
upstream_request.header(FORWARDED_FOR_HEADER, remote_addr.ip().to_string());
|
||||
}
|
||||
|
||||
if !parts.headers.contains_key(FORWARDED_PROTO_HEADER) {
|
||||
upstream_request = upstream_request.header(FORWARDED_PROTO_HEADER, "http");
|
||||
}
|
||||
|
||||
if !parts.headers.contains_key(TRACE_ID_HEADER) {
|
||||
upstream_request = upstream_request.header(TRACE_ID_HEADER, &trace_id);
|
||||
}
|
||||
|
||||
if let Some(decision) = control_decision {
|
||||
let execution_runtime_candidate = execution_runtime_candidate_header_value(decision);
|
||||
upstream_request = upstream_request
|
||||
.header(
|
||||
CONTROL_ROUTE_CLASS_HEADER,
|
||||
decision.route_class.as_deref().unwrap_or("passthrough"),
|
||||
)
|
||||
.header(
|
||||
CONTROL_LEGACY_EXECUTION_RUNTIME_HEADER,
|
||||
execution_runtime_candidate,
|
||||
)
|
||||
.header(
|
||||
CONTROL_EXECUTION_RUNTIME_HEADER,
|
||||
execution_runtime_candidate,
|
||||
);
|
||||
if let Some(route_family) = decision.route_family.as_deref() {
|
||||
upstream_request = upstream_request.header(CONTROL_ROUTE_FAMILY_HEADER, route_family);
|
||||
}
|
||||
if let Some(route_kind) = decision.route_kind.as_deref() {
|
||||
upstream_request = upstream_request.header(CONTROL_ROUTE_KIND_HEADER, route_kind);
|
||||
}
|
||||
if let Some(endpoint_signature) = decision.auth_endpoint_signature.as_deref() {
|
||||
upstream_request =
|
||||
upstream_request.header(CONTROL_ENDPOINT_SIGNATURE_HEADER, endpoint_signature);
|
||||
}
|
||||
if let Some(auth_context) = decision.auth_context.as_ref() {
|
||||
upstream_request = upstream_request
|
||||
.header(TRUSTED_AUTH_USER_ID_HEADER, &auth_context.user_id)
|
||||
.header(TRUSTED_AUTH_API_KEY_ID_HEADER, &auth_context.api_key_id)
|
||||
.header(
|
||||
TRUSTED_AUTH_ACCESS_ALLOWED_HEADER,
|
||||
if auth_context.access_allowed {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
},
|
||||
);
|
||||
if let Some(balance_remaining) = auth_context.balance_remaining {
|
||||
upstream_request = upstream_request
|
||||
.header(TRUSTED_AUTH_BALANCE_HEADER, balance_remaining.to_string());
|
||||
}
|
||||
}
|
||||
if let Some(admin_principal) = decision.admin_principal.as_ref() {
|
||||
upstream_request = upstream_request
|
||||
.header(TRUSTED_ADMIN_USER_ID_HEADER, &admin_principal.user_id)
|
||||
.header(TRUSTED_ADMIN_USER_ROLE_HEADER, &admin_principal.user_role);
|
||||
if let Some(session_id) = admin_principal.session_id.as_deref() {
|
||||
upstream_request =
|
||||
upstream_request.header(TRUSTED_ADMIN_SESSION_ID_HEADER, session_id);
|
||||
}
|
||||
if let Some(token_id) = admin_principal.management_token_id.as_deref() {
|
||||
upstream_request =
|
||||
upstream_request.header(TRUSTED_ADMIN_MANAGEMENT_TOKEN_ID_HEADER, token_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches!(rate_limit_outcome, FrontdoorUserRpmOutcome::Allowed) {
|
||||
upstream_request = upstream_request.header(TRUSTED_RATE_LIMIT_PREFLIGHT_HEADER, "true");
|
||||
}
|
||||
|
||||
upstream_request = upstream_request.header(GATEWAY_HEADER, "rust-phase3b");
|
||||
|
||||
let upstream_response = if should_try_control_execute {
|
||||
let buffered_body = buffered_body
|
||||
.as_ref()
|
||||
.expect("execution runtime/control auth gate should have buffered request body");
|
||||
let stream_request = request_wants_stream(&request_context, buffered_body);
|
||||
if stream_request {
|
||||
if let Some(execution_runtime_response) = ai_runtime::maybe_execute_stream_request(
|
||||
&state,
|
||||
&parts,
|
||||
buffered_body,
|
||||
&trace_id,
|
||||
control_decision,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
execution_runtime_response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_EXECUTION_RUNTIME_STREAM,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(execution_runtime_response) = ai_runtime::maybe_execute_sync_request(
|
||||
&state,
|
||||
&parts,
|
||||
buffered_body,
|
||||
&trace_id,
|
||||
control_decision,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
execution_runtime_response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_EXECUTION_RUNTIME_SYNC,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
if parts.method != http::Method::POST {
|
||||
if let Some(execution_runtime_response) = ai_runtime::maybe_execute_stream_request(
|
||||
&state,
|
||||
&parts,
|
||||
buffered_body,
|
||||
&trace_id,
|
||||
control_decision,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
execution_runtime_response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_EXECUTION_RUNTIME_STREAM,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if allow_control_execute_fallback {
|
||||
if let Some(control_response) = maybe_execute_via_control(
|
||||
&state,
|
||||
&parts,
|
||||
buffered_body.clone(),
|
||||
&trace_id,
|
||||
control_decision,
|
||||
stream_request,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
let reason = GatewayFallbackReason::ControlExecuteEmergency;
|
||||
let control_execution_path = if stream_request {
|
||||
EXECUTION_PATH_CONTROL_EXECUTE_STREAM
|
||||
} else {
|
||||
EXECUTION_PATH_CONTROL_EXECUTE_SYNC
|
||||
};
|
||||
state.record_fallback_metric(
|
||||
GatewayFallbackMetricKind::ControlExecuteFallback,
|
||||
control_decision,
|
||||
None,
|
||||
Some(control_execution_path),
|
||||
reason,
|
||||
);
|
||||
state.record_fallback_metric(
|
||||
GatewayFallbackMetricKind::PythonExecuteEmergency,
|
||||
control_decision,
|
||||
None,
|
||||
Some(control_execution_path),
|
||||
reason,
|
||||
);
|
||||
let mut control_response = control_response;
|
||||
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
control_response.headers_mut().insert(
|
||||
HeaderName::from_static(PYTHON_DEPENDENCY_REASON_HEADER),
|
||||
HeaderValue::from_static(reason.as_label_value()),
|
||||
);
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
control_response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
control_execution_path,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
}
|
||||
let local_execution_runtime_miss_detail =
|
||||
local_execution_runtime_miss_detail_after_python_fallback_removal(control_decision);
|
||||
state.record_fallback_metric(
|
||||
if local_execution_runtime_miss_detail.is_some() {
|
||||
GatewayFallbackMetricKind::LocalExecutionRuntimeMiss
|
||||
} else {
|
||||
GatewayFallbackMetricKind::PublicProxyAfterExecutionRuntimeMiss
|
||||
},
|
||||
control_decision,
|
||||
None,
|
||||
Some(if local_execution_runtime_miss_detail.is_some() {
|
||||
EXECUTION_PATH_LOCAL_EXECUTION_RUNTIME_MISS
|
||||
} else {
|
||||
EXECUTION_PATH_PUBLIC_PROXY_AFTER_EXECUTION_RUNTIME_MISS
|
||||
}),
|
||||
if local_execution_runtime_miss_detail.is_some() {
|
||||
GatewayFallbackReason::PythonFallbackRemoved
|
||||
} else {
|
||||
GatewayFallbackReason::ExecutionRuntimeMiss
|
||||
},
|
||||
);
|
||||
if let Some(local_execution_runtime_miss_detail) = local_execution_runtime_miss_detail {
|
||||
let local_execution_runtime_miss_diagnostic =
|
||||
state.take_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
if let Some(diagnostic) = local_execution_runtime_miss_diagnostic.as_ref() {
|
||||
warn!(
|
||||
trace_id = %trace_id,
|
||||
local_execution_runtime_miss_reason = %diagnostic.reason,
|
||||
route_family = diagnostic.route_family.as_deref().unwrap_or_default(),
|
||||
route_kind = diagnostic.route_kind.as_deref().unwrap_or_default(),
|
||||
public_path = diagnostic.public_path.as_deref().unwrap_or_default(),
|
||||
plan_kind = diagnostic.plan_kind.as_deref().unwrap_or_default(),
|
||||
requested_model = diagnostic.requested_model.as_deref().unwrap_or_default(),
|
||||
candidate_count = diagnostic.candidate_count.unwrap_or(0),
|
||||
skipped_candidate_count = diagnostic.skipped_candidate_count.unwrap_or(0),
|
||||
skip_reasons = diagnostic.skip_reasons_summary().unwrap_or_default(),
|
||||
"gateway local execution runtime miss"
|
||||
);
|
||||
}
|
||||
let mut response = build_local_http_error_response(
|
||||
&trace_id,
|
||||
control_decision,
|
||||
http::StatusCode::SERVICE_UNAVAILABLE,
|
||||
local_execution_runtime_miss_detail,
|
||||
)?;
|
||||
if let Some(diagnostic) = local_execution_runtime_miss_diagnostic {
|
||||
if !diagnostic.reason.trim().is_empty() {
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static(LOCAL_LEGACY_EXECUTION_RUNTIME_MISS_REASON_HEADER),
|
||||
HeaderValue::from_str(diagnostic.reason.as_str())
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?,
|
||||
);
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static(LOCAL_EXECUTION_RUNTIME_MISS_REASON_HEADER),
|
||||
HeaderValue::from_str(diagnostic.reason.as_str())
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?,
|
||||
);
|
||||
}
|
||||
}
|
||||
return Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
EXECUTION_PATH_LOCAL_EXECUTION_RUNTIME_MISS,
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
));
|
||||
}
|
||||
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
upstream_request = upstream_request.header(
|
||||
EXECUTION_PATH_HEADER,
|
||||
EXECUTION_PATH_PUBLIC_PROXY_AFTER_EXECUTION_RUNTIME_MISS,
|
||||
);
|
||||
upstream_request
|
||||
.body(buffered_body.clone())
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| GatewayError::UpstreamUnavailable {
|
||||
trace_id: trace_id.clone(),
|
||||
message: err.to_string(),
|
||||
})?
|
||||
} else {
|
||||
state.record_fallback_metric(
|
||||
GatewayFallbackMetricKind::PublicProxyPassthrough,
|
||||
control_decision,
|
||||
None,
|
||||
Some(EXECUTION_PATH_PUBLIC_PROXY_PASSTHROUGH),
|
||||
GatewayFallbackReason::ProxyPassthrough,
|
||||
);
|
||||
upstream_request = upstream_request.header(
|
||||
EXECUTION_PATH_HEADER,
|
||||
EXECUTION_PATH_PUBLIC_PROXY_PASSTHROUGH,
|
||||
);
|
||||
if let Some(buffered_body) = buffered_body {
|
||||
upstream_request
|
||||
.body(buffered_body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| GatewayError::UpstreamUnavailable {
|
||||
trace_id: trace_id.clone(),
|
||||
message: err.to_string(),
|
||||
})?
|
||||
} else {
|
||||
let request_body_stream = request_body
|
||||
.take()
|
||||
.expect("streaming passthrough path should retain request body")
|
||||
.into_data_stream()
|
||||
.map_err(|err| std::io::Error::other(err.to_string()));
|
||||
upstream_request
|
||||
.body(reqwest::Body::wrap_stream(request_body_stream))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| GatewayError::UpstreamUnavailable {
|
||||
trace_id: trace_id.clone(),
|
||||
message: err.to_string(),
|
||||
})?
|
||||
}
|
||||
};
|
||||
|
||||
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
|
||||
let mut response =
|
||||
ai_finalize::build_client_response(upstream_response, &trace_id, control_decision)?;
|
||||
if control_decision.and_then(|decision| decision.route_family.as_deref())
|
||||
== Some("gateway_legacy")
|
||||
{
|
||||
response = attach_legacy_internal_gateway_deprecation_headers(response);
|
||||
}
|
||||
let python_dependency_reason = if should_try_control_execute {
|
||||
GatewayFallbackReason::ExecutionRuntimeMiss
|
||||
} else {
|
||||
GatewayFallbackReason::ProxyPassthrough
|
||||
};
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static(PYTHON_DEPENDENCY_REASON_HEADER),
|
||||
HeaderValue::from_static(python_dependency_reason.as_label_value()),
|
||||
);
|
||||
Ok(finalize_gateway_response_with_context(
|
||||
&state,
|
||||
response,
|
||||
&remote_addr,
|
||||
&request_context,
|
||||
if should_try_control_execute {
|
||||
EXECUTION_PATH_PUBLIC_PROXY_AFTER_EXECUTION_RUNTIME_MISS
|
||||
} else {
|
||||
EXECUTION_PATH_PUBLIC_PROXY_PASSTHROUGH
|
||||
},
|
||||
&started_at,
|
||||
request_permit.take(),
|
||||
))
|
||||
}
|
||||
|
||||
fn local_execution_runtime_miss_detail_after_python_fallback_removal(
|
||||
decision: Option<&GatewayControlDecision>,
|
||||
) -> Option<&'static str> {
|
||||
let decision = decision?;
|
||||
if decision.route_class.as_deref() != Some("ai_public") {
|
||||
return None;
|
||||
}
|
||||
let public_path = decision.public_path.as_str();
|
||||
match public_path {
|
||||
"/v1/chat/completions" => Some(OPENAI_CHAT_PYTHON_FALLBACK_REMOVED_DETAIL),
|
||||
"/v1/responses" => Some(OPENAI_RESPONSES_PYTHON_FALLBACK_REMOVED_DETAIL),
|
||||
"/v1/responses/compact" => Some(OPENAI_COMPACT_PYTHON_FALLBACK_REMOVED_DETAIL),
|
||||
"/v1/messages" => Some(CLAUDE_MESSAGES_PYTHON_FALLBACK_REMOVED_DETAIL),
|
||||
path if path.starts_with("/v1/videos") => Some(OPENAI_VIDEO_PYTHON_FALLBACK_REMOVED_DETAIL),
|
||||
path if path.starts_with("/upload/v1beta/files") || path.starts_with("/v1beta/files") => {
|
||||
Some(GEMINI_FILES_PYTHON_FALLBACK_REMOVED_DETAIL)
|
||||
}
|
||||
path if decision.route_family.as_deref() == Some("gemini")
|
||||
&& (path.starts_with("/v1beta/models/") || path.starts_with("/v1/models/")) =>
|
||||
{
|
||||
Some(GEMINI_PUBLIC_PYTHON_FALLBACK_REMOVED_DETAIL)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[path = "finalize.rs"]
|
||||
mod finalize;
|
||||
|
||||
use self::finalize::{
|
||||
finalize_gateway_response, finalize_gateway_response_with_context, request_wants_stream,
|
||||
};
|
||||
|
||||
pub(super) use self::finalize::*;
|
||||
Reference in New Issue
Block a user