Refactor provider transport modules

This commit is contained in:
fawney19
2026-05-08 02:34:45 +08:00
parent c52ef1993f
commit 080784cd9f
15 changed files with 307 additions and 72 deletions

View File

@@ -0,0 +1,595 @@
use std::collections::BTreeMap;
use serde_json::Value;
use crate::antigravity::is_antigravity_provider_transport;
use crate::auth::{
build_complete_passthrough_headers, build_complete_passthrough_headers_with_auth,
resolve_local_gemini_auth, resolve_local_standard_auth,
};
use crate::claude_code::build_claude_code_passthrough_headers;
use crate::claude_code::local_claude_code_transport_unsupported_reason_with_network;
use crate::kiro::{
build_kiro_provider_headers, build_kiro_provider_request_body, is_kiro_provider_transport,
local_kiro_request_transport_unsupported_reason_with_network, KiroAuthConfig,
KiroProviderHeadersInput,
};
use crate::policy::{
local_gemini_transport_unsupported_reason_with_network,
local_standard_transport_unsupported_reason_with_network,
};
use crate::rules::{
apply_local_body_rules_with_request_headers, apply_local_header_rules_with_request_headers,
};
use crate::snapshot::GatewayProviderTransportSnapshot;
use crate::vertex::{
is_vertex_api_key_transport_context,
local_vertex_api_key_gemini_transport_unsupported_reason_with_network,
};
use crate::{build_transport_request_url, ensure_upstream_auth_header, TransportRequestUrlParams};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SameFormatProviderFamily {
Standard,
Gemini,
}
#[derive(Debug, Clone, Copy)]
pub struct SameFormatProviderRequestBehaviorParams {
pub require_streaming: bool,
pub report_kind: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SameFormatProviderRequestBehavior {
pub is_antigravity: bool,
pub is_claude_code: bool,
pub is_vertex: bool,
pub is_kiro: bool,
pub upstream_is_stream: bool,
pub report_kind: &'static str,
}
#[derive(Debug, Clone, Copy)]
pub struct SameFormatProviderRequestBodyInput<'a> {
pub body_json: &'a Value,
pub mapped_model: &'a str,
pub client_api_format: &'a str,
pub provider_api_format: &'a str,
pub source_model: Option<&'a str>,
pub family: SameFormatProviderFamily,
pub body_rules: Option<&'a Value>,
pub request_headers: Option<&'a http::HeaderMap>,
pub upstream_is_stream: bool,
pub kiro_auth_config: Option<&'a KiroAuthConfig>,
pub is_claude_code: bool,
pub enable_model_directives: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct SameFormatProviderUpstreamUrlParams<'a> {
pub provider_api_format: &'a str,
pub mapped_model: &'a str,
pub upstream_is_stream: bool,
pub request_query: Option<&'a str>,
pub kiro_api_region: Option<&'a str>,
}
#[derive(Debug, Clone, Copy)]
pub struct SameFormatProviderHeadersInput<'a> {
pub headers: &'a http::HeaderMap,
pub provider_request_body: &'a Value,
pub original_request_body: &'a Value,
pub header_rules: Option<&'a Value>,
pub behavior: SameFormatProviderRequestBehavior,
pub auth_header: Option<&'a str>,
pub auth_value: Option<&'a str>,
pub extra_headers: &'a BTreeMap<String, String>,
pub key_fingerprint: Option<&'a Value>,
pub kiro_auth_config: Option<&'a KiroAuthConfig>,
pub kiro_machine_id: Option<&'a str>,
}
pub fn classify_same_format_provider_request_behavior(
transport: &GatewayProviderTransportSnapshot,
params: SameFormatProviderRequestBehaviorParams,
) -> SameFormatProviderRequestBehavior {
let is_antigravity = is_antigravity_provider_transport(transport);
let is_claude_code = transport
.provider
.provider_type
.trim()
.eq_ignore_ascii_case("claude_code");
let is_vertex = is_vertex_api_key_transport_context(transport);
let is_kiro = is_kiro_provider_transport(transport);
let upstream_is_stream = is_kiro || is_antigravity || params.require_streaming;
let report_kind = if is_kiro && !params.require_streaming {
"claude_cli_sync_finalize"
} else if is_antigravity && !params.require_streaming {
match params.report_kind {
"gemini_chat_sync_success" => "gemini_chat_sync_finalize",
"gemini_cli_sync_success" => "gemini_cli_sync_finalize",
_ => params.report_kind,
}
} else {
params.report_kind
};
SameFormatProviderRequestBehavior {
is_antigravity,
is_claude_code,
is_vertex,
is_kiro,
upstream_is_stream,
report_kind,
}
}
pub fn build_same_format_provider_request_body(
input: SameFormatProviderRequestBodyInput<'_>,
) -> Option<Value> {
if let Some(kiro_auth_config) = input.kiro_auth_config {
return build_kiro_provider_request_body(
input.body_json,
input.mapped_model,
kiro_auth_config,
input.body_rules,
input.request_headers,
);
}
let mut provider_request_body = if aether_ai_formats::api_format_alias_matches(
input.client_api_format,
input.provider_api_format,
) {
let request_body_object = input.body_json.as_object()?;
serde_json::Map::from_iter(
request_body_object
.iter()
.map(|(key, value)| (key.clone(), value.clone())),
)
} else {
aether_ai_formats::convert_request(
input.client_api_format,
input.provider_api_format,
input.body_json,
&aether_ai_formats::FormatContext::default().with_mapped_model(input.mapped_model),
)
.ok()?
.as_object()?
.clone()
};
match input.family {
SameFormatProviderFamily::Standard => {
provider_request_body.insert(
"model".to_string(),
Value::String(input.mapped_model.to_string()),
);
if input.upstream_is_stream {
provider_request_body.insert("stream".to_string(), Value::Bool(true));
}
}
SameFormatProviderFamily::Gemini => {
provider_request_body.remove("model");
}
}
let mut provider_request_body = Value::Object(provider_request_body);
if input.is_claude_code {
crate::claude_code::sanitize_claude_code_request_body(&mut provider_request_body);
}
if input.enable_model_directives {
if let Some(source_model) = input.source_model {
aether_ai_formats::apply_model_directive_overrides_from_model(
&mut provider_request_body,
input.provider_api_format,
input.mapped_model,
source_model,
);
}
}
if !apply_local_body_rules_with_request_headers(
&mut provider_request_body,
input.body_rules,
Some(input.body_json),
input.request_headers,
) {
return None;
}
Some(provider_request_body)
}
pub fn build_same_format_provider_upstream_url(
transport: &GatewayProviderTransportSnapshot,
params: SameFormatProviderUpstreamUrlParams<'_>,
) -> Option<String> {
build_transport_request_url(
transport,
TransportRequestUrlParams {
provider_api_format: params.provider_api_format,
mapped_model: Some(params.mapped_model),
upstream_is_stream: params.upstream_is_stream,
request_query: params.request_query,
kiro_api_region: params.kiro_api_region,
},
)
}
pub fn build_same_format_provider_headers(
input: SameFormatProviderHeadersInput<'_>,
) -> Option<BTreeMap<String, String>> {
if let Some(kiro_auth_config) = input.kiro_auth_config {
return build_kiro_provider_headers(KiroProviderHeadersInput {
headers: input.headers,
provider_request_body: input.provider_request_body,
original_request_body: input.original_request_body,
header_rules: input.header_rules,
auth_header: input.auth_header.unwrap_or_default(),
auth_value: input.auth_value.unwrap_or_default(),
auth_config: kiro_auth_config,
machine_id: input.kiro_machine_id.unwrap_or_default(),
});
}
let auth_header = input.auth_header.unwrap_or_default();
let auth_value = input.auth_value.unwrap_or_default();
let mut provider_request_headers = if input.behavior.is_claude_code {
build_claude_code_passthrough_headers(
input.headers,
auth_header,
auth_value,
input.extra_headers,
input.behavior.upstream_is_stream,
input.key_fingerprint,
)
} else if input.behavior.is_vertex {
build_complete_passthrough_headers(
input.headers,
input.extra_headers,
Some("application/json"),
)
} else {
build_complete_passthrough_headers_with_auth(
input.headers,
auth_header,
auth_value,
input.extra_headers,
Some("application/json"),
)
};
let protected_headers = input
.auth_header
.filter(|value| !value.trim().is_empty())
.map(|value| vec![value, "content-type"])
.unwrap_or_else(|| vec!["content-type"]);
if !apply_local_header_rules_with_request_headers(
&mut provider_request_headers,
input.header_rules,
&protected_headers,
input.provider_request_body,
Some(input.original_request_body),
Some(input.headers),
) {
return None;
}
if let (Some(auth_header), Some(auth_value)) = (input.auth_header, input.auth_value) {
ensure_upstream_auth_header(&mut provider_request_headers, auth_header, auth_value);
}
if input.behavior.upstream_is_stream {
provider_request_headers.insert("accept".to_string(), "text/event-stream".to_string());
}
Some(provider_request_headers)
}
pub fn same_format_provider_transport_supported(
behavior: &SameFormatProviderRequestBehavior,
transport: &GatewayProviderTransportSnapshot,
family: SameFormatProviderFamily,
api_format: &str,
) -> bool {
same_format_provider_transport_unsupported_reason(behavior, transport, family, api_format)
.is_none()
}
pub fn same_format_provider_transport_unsupported_reason(
behavior: &SameFormatProviderRequestBehavior,
transport: &GatewayProviderTransportSnapshot,
family: SameFormatProviderFamily,
api_format: &str,
) -> Option<&'static str> {
if behavior.is_kiro {
local_kiro_request_transport_unsupported_reason_with_network(transport)
} else if behavior.is_antigravity {
None
} else if behavior.is_claude_code {
local_claude_code_transport_unsupported_reason_with_network(transport, api_format)
} else if behavior.is_vertex {
local_vertex_api_key_gemini_transport_unsupported_reason_with_network(transport)
} else {
match family {
SameFormatProviderFamily::Standard => {
local_standard_transport_unsupported_reason_with_network(transport, api_format)
}
SameFormatProviderFamily::Gemini => {
local_gemini_transport_unsupported_reason_with_network(transport, api_format)
}
}
}
}
pub fn same_format_provider_transport_unsupported_reason_for_trace(
transport: &GatewayProviderTransportSnapshot,
provider_api_format: &str,
) -> Option<&'static str> {
let normalized_api_format =
match aether_ai_formats::normalize_api_format_alias(provider_api_format).as_str() {
"openai:chat" => "openai:chat",
"openai:responses" => "openai:responses",
"openai:responses:compact" => "openai:responses:compact",
"claude:messages" => "claude:messages",
"gemini:generate_content" => "gemini:generate_content",
_ => return Some("transport_api_format_unsupported"),
};
let behavior = classify_same_format_provider_request_behavior(
transport,
SameFormatProviderRequestBehaviorParams {
require_streaming: false,
report_kind: "trace_candidate_metadata",
},
);
if !behavior.is_antigravity
&& !behavior.is_claude_code
&& !behavior.is_vertex
&& !behavior.is_kiro
{
return None;
}
let family = if normalized_api_format.starts_with("gemini:") {
SameFormatProviderFamily::Gemini
} else {
SameFormatProviderFamily::Standard
};
same_format_provider_transport_unsupported_reason(
&behavior,
transport,
family,
normalized_api_format,
)
}
pub fn should_try_same_format_provider_oauth_auth(
behavior: &SameFormatProviderRequestBehavior,
transport: &GatewayProviderTransportSnapshot,
family: SameFormatProviderFamily,
) -> bool {
behavior.is_kiro
|| matches!(family, SameFormatProviderFamily::Standard)
&& resolve_local_standard_auth(transport).is_none()
|| matches!(family, SameFormatProviderFamily::Gemini)
&& !behavior.is_vertex
&& resolve_local_gemini_auth(transport).is_none()
}
pub fn resolve_same_format_provider_direct_auth(
behavior: &SameFormatProviderRequestBehavior,
transport: &GatewayProviderTransportSnapshot,
family: SameFormatProviderFamily,
) -> Option<(String, String)> {
if behavior.is_vertex {
None
} else {
match family {
SameFormatProviderFamily::Standard => resolve_local_standard_auth(transport),
SameFormatProviderFamily::Gemini => resolve_local_gemini_auth(transport),
}
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
use crate::snapshot::{
GatewayProviderTransportEndpoint, GatewayProviderTransportKey,
GatewayProviderTransportProvider,
};
use serde_json::json;
fn sample_transport(provider_type: &str) -> GatewayProviderTransportSnapshot {
GatewayProviderTransportSnapshot {
provider: GatewayProviderTransportProvider {
id: "provider-1".to_string(),
name: "Provider".to_string(),
provider_type: provider_type.to_string(),
website: None,
is_active: true,
keep_priority_on_conversion: false,
enable_format_conversion: true,
concurrent_limit: None,
max_retries: None,
proxy: None,
request_timeout_secs: None,
stream_first_byte_timeout_secs: None,
config: None,
},
endpoint: GatewayProviderTransportEndpoint {
id: "endpoint-1".to_string(),
provider_id: "provider-1".to_string(),
api_format: "openai:chat".to_string(),
api_family: None,
endpoint_kind: None,
is_active: true,
base_url: "https://api.example.test".to_string(),
header_rules: None,
body_rules: None,
max_retries: None,
custom_path: None,
config: None,
format_acceptance_config: None,
proxy: None,
},
key: GatewayProviderTransportKey {
id: "key-1".to_string(),
provider_id: "provider-1".to_string(),
name: "key".to_string(),
auth_type: "api_key".to_string(),
is_active: true,
api_formats: None,
auth_type_by_format: None,
allow_auth_channel_mismatch_formats: None,
allowed_models: None,
capabilities: None,
rate_multipliers: None,
global_priority_by_format: None,
expires_at_unix_secs: None,
proxy: None,
fingerprint: None,
decrypted_api_key: "secret".to_string(),
decrypted_auth_config: None,
},
}
}
#[test]
fn classifies_streaming_and_report_kind_for_provider_private_transports() {
let kiro = sample_transport("kiro");
let behavior = classify_same_format_provider_request_behavior(
&kiro,
SameFormatProviderRequestBehaviorParams {
require_streaming: false,
report_kind: "claude_chat_sync_success",
},
);
assert!(behavior.is_kiro);
assert!(behavior.upstream_is_stream);
assert_eq!(behavior.report_kind, "claude_cli_sync_finalize");
let antigravity = sample_transport("antigravity");
let behavior = classify_same_format_provider_request_behavior(
&antigravity,
SameFormatProviderRequestBehaviorParams {
require_streaming: false,
report_kind: "gemini_chat_sync_success",
},
);
assert!(behavior.is_antigravity);
assert!(behavior.upstream_is_stream);
assert_eq!(behavior.report_kind, "gemini_chat_sync_finalize");
}
#[test]
fn resolves_direct_auth_except_vertex() {
let transport = sample_transport("openai");
let behavior = classify_same_format_provider_request_behavior(
&transport,
SameFormatProviderRequestBehaviorParams {
require_streaming: false,
report_kind: "openai_chat_sync_success",
},
);
assert_eq!(
resolve_same_format_provider_direct_auth(
&behavior,
&transport,
SameFormatProviderFamily::Standard,
),
Some(("x-api-key".to_string(), "secret".to_string()))
);
}
#[test]
fn builds_same_format_standard_body_with_mapped_model_and_stream_flag() {
let body = build_same_format_provider_request_body(SameFormatProviderRequestBodyInput {
body_json: &json!({
"model": "client-model",
"messages": [{"role": "user", "content": "hello"}]
}),
mapped_model: "upstream-model",
client_api_format: "openai:chat",
provider_api_format: "openai:chat",
source_model: Some("client-model"),
family: SameFormatProviderFamily::Standard,
body_rules: None,
request_headers: None,
upstream_is_stream: true,
kiro_auth_config: None,
is_claude_code: false,
enable_model_directives: false,
})
.expect("body should build");
assert_eq!(body.get("model"), Some(&json!("upstream-model")));
assert_eq!(body.get("stream"), Some(&json!(true)));
}
#[test]
fn same_format_body_applies_model_directive_before_body_rules() {
let body = build_same_format_provider_request_body(SameFormatProviderRequestBodyInput {
body_json: &json!({
"model": "gpt-5.4-high",
"messages": [{"role": "user", "content": "hello"}],
"reasoning_effort": "low"
}),
mapped_model: "upstream-model",
client_api_format: "openai:chat",
provider_api_format: "openai:chat",
source_model: Some("gpt-5.4-high"),
family: SameFormatProviderFamily::Standard,
body_rules: Some(&json!([
{"action":"set","path":"metadata.body_rule_seen","value":true}
])),
request_headers: None,
upstream_is_stream: false,
kiro_auth_config: None,
is_claude_code: false,
enable_model_directives: true,
})
.expect("body should build");
assert_eq!(body["model"], "upstream-model");
assert_eq!(body["reasoning_effort"], "high");
assert_eq!(body["metadata"]["body_rule_seen"], true);
}
#[test]
fn builds_same_format_headers_with_auth_and_stream_accept() {
let provider_request_body = json!({"model": "upstream-model"});
let original_request_body = json!({"model": "client-model"});
let headers = build_same_format_provider_headers(SameFormatProviderHeadersInput {
headers: &http::HeaderMap::new(),
provider_request_body: &provider_request_body,
original_request_body: &original_request_body,
header_rules: None,
behavior: SameFormatProviderRequestBehavior {
is_antigravity: false,
is_claude_code: false,
is_vertex: false,
is_kiro: false,
upstream_is_stream: true,
report_kind: "openai_chat_stream_success",
},
auth_header: Some("x-api-key"),
auth_value: Some("secret"),
extra_headers: &BTreeMap::new(),
key_fingerprint: None,
kiro_auth_config: None,
kiro_machine_id: None,
})
.expect("headers should build");
assert_eq!(headers.get("x-api-key").map(String::as_str), Some("secret"));
assert_eq!(
headers.get("content-type").map(String::as_str),
Some("application/json")
);
assert_eq!(
headers.get("accept").map(String::as_str),
Some("text/event-stream")
);
}
}