mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 18:07:47 +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:
@@ -1,9 +1,11 @@
|
||||
use aether_ai_formats::{ApiOperation, ClientSurface};
|
||||
use http::Uri;
|
||||
|
||||
use super::super::auth::GatewayCredentialCarrier;
|
||||
use super::{classify_control_route, headers};
|
||||
|
||||
#[test]
|
||||
fn classifies_claude_count_tokens_as_non_execution_runtime_public_route() {
|
||||
fn classifies_claude_count_tokens_as_execution_runtime_operation() {
|
||||
let headers = headers(&[("x-api-key", "sk-test")]);
|
||||
let uri: Uri = "/v1/messages/count_tokens"
|
||||
.parse()
|
||||
@@ -17,7 +19,11 @@ fn classifies_claude_count_tokens_as_non_execution_runtime_public_route() {
|
||||
decision.auth_endpoint_signature.as_deref(),
|
||||
Some("claude:messages")
|
||||
);
|
||||
assert!(!decision.is_execution_runtime_candidate());
|
||||
assert!(decision.is_execution_runtime_candidate());
|
||||
assert_eq!(
|
||||
decision.api_operation,
|
||||
Some(ApiOperation::ClaudeCountTokens)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -141,7 +147,7 @@ fn classifies_models_list_as_claude_when_headers_match() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifies_claude_messages_cli_when_bearer_without_api_key() {
|
||||
fn bearer_auth_does_not_imply_claude_code_client_surface() {
|
||||
let headers = headers(&[("authorization", "Bearer token-123")]);
|
||||
let uri: Uri = "/v1/messages".parse().expect("uri should parse");
|
||||
let decision =
|
||||
@@ -149,6 +155,10 @@ fn classifies_claude_messages_cli_when_bearer_without_api_key() {
|
||||
|
||||
assert_eq!(decision.route_family.as_deref(), Some("claude"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("messages"));
|
||||
assert_eq!(
|
||||
decision.client_surface,
|
||||
Some(ClientSurface::GenericCompatible)
|
||||
);
|
||||
assert_eq!(
|
||||
decision.request_auth_channel.as_deref(),
|
||||
Some("bearer_like")
|
||||
@@ -161,7 +171,7 @@ fn classifies_claude_messages_cli_when_bearer_without_api_key() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifies_claude_messages_cli_when_bearer_is_present_even_with_api_key() {
|
||||
fn claude_api_key_carrier_keeps_precedence_over_bearer() {
|
||||
let headers = headers(&[
|
||||
("authorization", "Bearer token-123"),
|
||||
("x-api-key", "sk-client"),
|
||||
@@ -172,9 +182,10 @@ fn classifies_claude_messages_cli_when_bearer_is_present_even_with_api_key() {
|
||||
|
||||
assert_eq!(decision.route_family.as_deref(), Some("claude"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("messages"));
|
||||
assert_eq!(decision.request_auth_channel.as_deref(), Some("api_key"));
|
||||
assert_eq!(
|
||||
decision.request_auth_channel.as_deref(),
|
||||
Some("bearer_like")
|
||||
decision.gateway_credential_carrier,
|
||||
Some(GatewayCredentialCarrier::XApiKey)
|
||||
);
|
||||
assert_eq!(
|
||||
decision.auth_endpoint_signature.as_deref(),
|
||||
@@ -183,6 +194,63 @@ fn classifies_claude_messages_cli_when_bearer_is_present_even_with_api_key() {
|
||||
assert!(decision.is_execution_runtime_candidate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_claude_code_independently_from_bearer_auth() {
|
||||
let headers = headers(&[
|
||||
("authorization", "Bearer token-123"),
|
||||
("user-agent", "Claude-Code/2.1.0"),
|
||||
]);
|
||||
let uri: Uri = "/v1/messages".parse().expect("uri should parse");
|
||||
let decision =
|
||||
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
|
||||
|
||||
assert_eq!(decision.client_surface, Some(ClientSurface::ClaudeCode));
|
||||
assert_eq!(
|
||||
decision.gateway_credential_carrier,
|
||||
Some(GatewayCredentialCarrier::AuthorizationBearer)
|
||||
);
|
||||
assert_eq!(
|
||||
decision.api_operation,
|
||||
Some(ApiOperation::ClaudeMessagesCreate)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_current_claude_cli_user_agent() {
|
||||
let headers = headers(&[
|
||||
("x-api-key", "sk-client"),
|
||||
("user-agent", "claude-cli/2.1.161 (external, cli)"),
|
||||
]);
|
||||
let uri: Uri = "/v1/messages".parse().expect("uri should parse");
|
||||
let decision =
|
||||
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
|
||||
|
||||
assert_eq!(decision.client_surface, Some(ClientSurface::ClaudeCode));
|
||||
assert_eq!(decision.request_auth_channel.as_deref(), Some("api_key"));
|
||||
assert_eq!(
|
||||
decision.gateway_credential_carrier,
|
||||
Some(GatewayCredentialCarrier::XApiKey)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_claude_code_from_explicit_x_app_signal() {
|
||||
let headers = headers(&[
|
||||
("x-api-key", "sk-client"),
|
||||
("user-agent", "rewritten-by-proxy"),
|
||||
("x-app", "cli"),
|
||||
]);
|
||||
let uri: Uri = "/v1/messages".parse().expect("uri should parse");
|
||||
let decision =
|
||||
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
|
||||
|
||||
assert_eq!(decision.client_surface, Some(ClientSurface::ClaudeCode));
|
||||
assert_eq!(
|
||||
decision.api_operation,
|
||||
Some(ApiOperation::ClaudeMessagesCreate)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifies_claude_messages_when_api_key_without_bearer() {
|
||||
let headers = headers(&[("x-api-key", "sk-client")]);
|
||||
|
||||
Reference in New Issue
Block a user