mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
refactor: separate request auth channel from route kind
Extract request_auth_channel as a distinct routing dimension to distinguish between api_key and bearer_like authentication methods. This improves routing clarity by decoupling authentication mechanism from route classification.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
classified, is_claude_cli_request, is_gemini_cli_request, is_gemini_models_route,
|
||||
is_gemini_operation_route, ClassifiedRoute,
|
||||
classified, classified_with_request_auth_channel, is_claude_cli_request, is_gemini_cli_request,
|
||||
is_gemini_models_route, is_gemini_operation_route, ClassifiedRoute,
|
||||
};
|
||||
|
||||
pub(super) fn classify_ai_public_route(
|
||||
@@ -58,15 +58,16 @@ pub(super) fn classify_ai_public_route(
|
||||
false,
|
||||
))
|
||||
} else if method == http::Method::POST && normalized_path == "/v1/messages" {
|
||||
let route_kind = if is_claude_cli_request(headers) {
|
||||
"cli"
|
||||
let request_auth_channel = if is_claude_cli_request(headers) {
|
||||
"bearer_like"
|
||||
} else {
|
||||
"messages"
|
||||
"api_key"
|
||||
};
|
||||
Some(classified(
|
||||
Some(classified_with_request_auth_channel(
|
||||
"ai_public",
|
||||
"claude",
|
||||
route_kind,
|
||||
"messages",
|
||||
request_auth_channel,
|
||||
"claude:messages",
|
||||
true,
|
||||
))
|
||||
@@ -88,18 +89,20 @@ pub(super) fn classify_ai_public_route(
|
||||
true,
|
||||
))
|
||||
} else if is_gemini_cli_request(headers) {
|
||||
Some(classified(
|
||||
Some(classified_with_request_auth_channel(
|
||||
"ai_public",
|
||||
"gemini",
|
||||
"cli",
|
||||
"generate_content",
|
||||
"bearer_like",
|
||||
"gemini:generate_content",
|
||||
true,
|
||||
))
|
||||
} else {
|
||||
Some(classified(
|
||||
Some(classified_with_request_auth_channel(
|
||||
"ai_public",
|
||||
"gemini",
|
||||
"generate_content",
|
||||
"api_key",
|
||||
"gemini:generate_content",
|
||||
true,
|
||||
))
|
||||
|
||||
@@ -19,6 +19,7 @@ pub(crate) struct GatewayControlDecision {
|
||||
pub(crate) route_class: Option<String>,
|
||||
pub(crate) route_family: Option<String>,
|
||||
pub(crate) route_kind: Option<String>,
|
||||
pub(crate) request_auth_channel: Option<String>,
|
||||
pub(crate) auth_endpoint_signature: Option<String>,
|
||||
pub(crate) execution_runtime_candidate: bool,
|
||||
pub(crate) auth_context: Option<GatewayControlAuthContext>,
|
||||
@@ -40,6 +41,7 @@ impl GatewayControlDecision {
|
||||
route_class,
|
||||
route_family,
|
||||
route_kind,
|
||||
request_auth_channel: None,
|
||||
auth_endpoint_signature,
|
||||
execution_runtime_candidate: false,
|
||||
auth_context: None,
|
||||
@@ -75,6 +77,7 @@ pub(super) struct ClassifiedRoute {
|
||||
route_class: &'static str,
|
||||
route_family: &'static str,
|
||||
route_kind: &'static str,
|
||||
request_auth_channel: Option<&'static str>,
|
||||
auth_endpoint_signature: String,
|
||||
execution_runtime_candidate: bool,
|
||||
}
|
||||
@@ -90,6 +93,25 @@ pub(super) fn classified(
|
||||
route_class,
|
||||
route_family,
|
||||
route_kind,
|
||||
request_auth_channel: None,
|
||||
auth_endpoint_signature: auth_endpoint_signature.into(),
|
||||
execution_runtime_candidate,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn classified_with_request_auth_channel(
|
||||
route_class: &'static str,
|
||||
route_family: &'static str,
|
||||
route_kind: &'static str,
|
||||
request_auth_channel: &'static str,
|
||||
auth_endpoint_signature: impl Into<String>,
|
||||
execution_runtime_candidate: bool,
|
||||
) -> ClassifiedRoute {
|
||||
ClassifiedRoute {
|
||||
route_class,
|
||||
route_family,
|
||||
route_kind,
|
||||
request_auth_channel: Some(request_auth_channel),
|
||||
auth_endpoint_signature: auth_endpoint_signature.into(),
|
||||
execution_runtime_candidate,
|
||||
}
|
||||
@@ -103,6 +125,7 @@ impl ClassifiedRoute {
|
||||
route_class: Some(self.route_class.to_string()),
|
||||
route_family: Some(self.route_family.to_string()),
|
||||
route_kind: Some(self.route_kind.to_string()),
|
||||
request_auth_channel: self.request_auth_channel.map(str::to_string),
|
||||
auth_endpoint_signature: Some(self.auth_endpoint_signature),
|
||||
execution_runtime_candidate: self.execution_runtime_candidate,
|
||||
auth_context: None,
|
||||
|
||||
@@ -44,7 +44,11 @@ fn classifies_claude_messages_cli_when_bearer_without_api_key() {
|
||||
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
|
||||
|
||||
assert_eq!(decision.route_family.as_deref(), Some("claude"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("cli"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("messages"));
|
||||
assert_eq!(
|
||||
decision.request_auth_channel.as_deref(),
|
||||
Some("bearer_like")
|
||||
);
|
||||
assert_eq!(
|
||||
decision.auth_endpoint_signature.as_deref(),
|
||||
Some("claude:messages")
|
||||
@@ -63,7 +67,11 @@ fn classifies_claude_messages_cli_when_bearer_is_present_even_with_api_key() {
|
||||
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
|
||||
|
||||
assert_eq!(decision.route_family.as_deref(), Some("claude"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("cli"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("messages"));
|
||||
assert_eq!(
|
||||
decision.request_auth_channel.as_deref(),
|
||||
Some("bearer_like")
|
||||
);
|
||||
assert_eq!(
|
||||
decision.auth_endpoint_signature.as_deref(),
|
||||
Some("claude:messages")
|
||||
@@ -80,6 +88,7 @@ fn classifies_claude_messages_when_api_key_without_bearer() {
|
||||
|
||||
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.auth_endpoint_signature.as_deref(),
|
||||
Some("claude:messages")
|
||||
@@ -97,7 +106,30 @@ fn classifies_gemini_cli_generate_content_when_x_app_contains_cli() {
|
||||
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
|
||||
|
||||
assert_eq!(decision.route_family.as_deref(), Some("gemini"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("cli"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("generate_content"));
|
||||
assert_eq!(
|
||||
decision.request_auth_channel.as_deref(),
|
||||
Some("bearer_like")
|
||||
);
|
||||
assert_eq!(
|
||||
decision.auth_endpoint_signature.as_deref(),
|
||||
Some("gemini:generate_content")
|
||||
);
|
||||
assert!(decision.is_execution_runtime_candidate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifies_gemini_generate_content_api_key_without_cli_marker() {
|
||||
let headers = headers(&[("x-goog-api-key", "gemini-key")]);
|
||||
let uri: Uri = "/v1beta/models/gemini-2.5-pro:generateContent"
|
||||
.parse()
|
||||
.expect("uri should parse");
|
||||
let decision =
|
||||
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
|
||||
|
||||
assert_eq!(decision.route_family.as_deref(), Some("gemini"));
|
||||
assert_eq!(decision.route_kind.as_deref(), Some("generate_content"));
|
||||
assert_eq!(decision.request_auth_channel.as_deref(), Some("api_key"));
|
||||
assert_eq!(
|
||||
decision.auth_endpoint_signature.as_deref(),
|
||||
Some("gemini:generate_content")
|
||||
|
||||
@@ -61,7 +61,14 @@ fn builds_public_request_context_from_request_parts() {
|
||||
.control_decision
|
||||
.as_ref()
|
||||
.and_then(|value| value.route_kind.as_deref()),
|
||||
Some("cli")
|
||||
Some("generate_content")
|
||||
);
|
||||
assert_eq!(
|
||||
context
|
||||
.control_decision
|
||||
.as_ref()
|
||||
.and_then(|value| value.request_auth_channel.as_deref()),
|
||||
Some("bearer_like")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user