mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 18:59:50 +08:00
feat(gateway): harden provider request execution
Preserve exact request payloads and model client surface and API operation explicitly. Add Anthropic compatibility profiles, bounded stream commitment, and scoped OAuth retry behavior across provider transports.
This commit is contained in:
@@ -15,8 +15,9 @@ use tracing::warn;
|
||||
|
||||
use crate::ai_serving::planner::common::extract_standard_requested_model;
|
||||
use crate::ai_serving::{
|
||||
ExecutionRuntimeAuthContext, GatewayAuthApiKeySnapshot, GatewayProviderTransportSnapshot,
|
||||
PlannerAppState, CODEX_RESPONSES_LITE_HEADER,
|
||||
ClientSurface, ExecutionRuntimeAuthContext, GatewayAuthApiKeySnapshot,
|
||||
GatewayCredentialCarrier, GatewayProviderTransportSnapshot, PlannerAppState,
|
||||
CODEX_RESPONSES_LITE_HEADER,
|
||||
};
|
||||
use crate::cache::CacheLoadObserver;
|
||||
use crate::client_session_affinity::client_session_affinity_from_api_request;
|
||||
@@ -52,6 +53,8 @@ pub(crate) struct LocalRequestedModelDecisionInput {
|
||||
pub(crate) auth_snapshot: GatewayAuthApiKeySnapshot,
|
||||
pub(crate) required_capabilities: Option<serde_json::Value>,
|
||||
pub(crate) request_auth_channel: Option<String>,
|
||||
pub(crate) client_surface: Option<ClientSurface>,
|
||||
pub(crate) gateway_credential_carrier: Option<GatewayCredentialCarrier>,
|
||||
pub(crate) client_session_affinity: Option<ClientSessionAffinity>,
|
||||
pub(crate) routing_policy: Option<ResolvedRoutingPolicy>,
|
||||
pub(crate) routing_trace_seed: Option<RoutingDecisionTrace>,
|
||||
@@ -378,6 +381,8 @@ pub(crate) fn build_local_requested_model_decision_input(
|
||||
auth_snapshot: resolved_input.auth_snapshot,
|
||||
required_capabilities: resolved_input.required_capabilities,
|
||||
request_auth_channel: None,
|
||||
client_surface: None,
|
||||
gateway_credential_carrier: None,
|
||||
client_session_affinity: None,
|
||||
routing_policy: None,
|
||||
routing_trace_seed: None,
|
||||
@@ -1128,6 +1133,8 @@ mod tests {
|
||||
auth_snapshot: sample_auth_snapshot(),
|
||||
required_capabilities: None,
|
||||
request_auth_channel: None,
|
||||
client_surface: None,
|
||||
gateway_credential_carrier: None,
|
||||
client_session_affinity: None,
|
||||
routing_policy: None,
|
||||
routing_trace_seed: None,
|
||||
@@ -1323,6 +1330,8 @@ mod tests {
|
||||
auth_snapshot: sample_auth_snapshot(),
|
||||
required_capabilities: None,
|
||||
request_auth_channel: None,
|
||||
client_surface: None,
|
||||
gateway_credential_carrier: None,
|
||||
client_session_affinity: None,
|
||||
routing_policy: None,
|
||||
routing_trace_seed: None,
|
||||
@@ -1390,6 +1399,8 @@ mod tests {
|
||||
auth_snapshot: sample_auth_snapshot(),
|
||||
required_capabilities: None,
|
||||
request_auth_channel: None,
|
||||
client_surface: None,
|
||||
gateway_credential_carrier: None,
|
||||
client_session_affinity: None,
|
||||
routing_policy: None,
|
||||
routing_trace_seed: None,
|
||||
@@ -1459,6 +1470,59 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_request_routing_policy_cannot_restore_credentials_or_aether_internal_headers() {
|
||||
for header_name in [
|
||||
"authorization",
|
||||
"proxy-authorization",
|
||||
"api-key",
|
||||
"x-api-key",
|
||||
"x-goog-api-key",
|
||||
"cookie",
|
||||
"cookie2",
|
||||
"set-cookie",
|
||||
"x-aether-auth-user-id",
|
||||
"x-aether-control-future",
|
||||
] {
|
||||
let mut input = sample_decision_input();
|
||||
set_provider_request_rules(
|
||||
&mut input,
|
||||
&["gpt-5"],
|
||||
json!([{
|
||||
"type": "patch_headers",
|
||||
"patch": [{
|
||||
"op": "set",
|
||||
"name": header_name,
|
||||
"value": "must-not-reach-upstream"
|
||||
}]
|
||||
}]),
|
||||
);
|
||||
let mut decision = sample_decision();
|
||||
|
||||
let error =
|
||||
apply_provider_request_routing_policy_to_decision(&input, &mut decision, None)
|
||||
.expect_err("reserved provider header mutation should fail closed");
|
||||
|
||||
assert!(
|
||||
matches!(
|
||||
&error,
|
||||
GatewayError::Client {
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
..
|
||||
}
|
||||
),
|
||||
"unexpected error for {header_name}: {error:?}"
|
||||
);
|
||||
assert!(
|
||||
!decision
|
||||
.provider_request_headers
|
||||
.keys()
|
||||
.any(|name| name.eq_ignore_ascii_case(header_name)),
|
||||
"reserved header reached the provider decision: {header_name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_prompt_cache_identity_headers_are_terminal_after_routing_mutations() {
|
||||
let mut input = sample_decision_input();
|
||||
|
||||
Reference in New Issue
Block a user