Implement transport profile routing

This commit is contained in:
fawney19
2026-05-05 22:21:23 +08:00
parent aacab1a90c
commit f959f02d40
86 changed files with 860 additions and 318 deletions

View File

@@ -130,7 +130,7 @@ pub fn build_ai_execution_plan_from_decision(
provider_api_format: parts.core.provider_api_format,
model_name: payload.model_name.take(),
proxy: payload.proxy.take(),
tls_profile: payload.tls_profile.take(),
transport_profile: payload.transport_profile.take(),
timeouts: payload.timeouts.take(),
}
}
@@ -156,7 +156,7 @@ pub fn build_ai_execution_decision_from_plan(
provider_api_format,
model_name,
proxy,
tls_profile,
transport_profile,
timeouts,
} = parts.plan;
let auth_pair = parts
@@ -209,7 +209,7 @@ pub fn build_ai_execution_decision_from_plan(
provider_request_body_base64: body_bytes_b64,
content_type,
proxy,
tls_profile,
transport_profile,
timeouts,
upstream_is_stream: stream,
report_kind: parts.report_kind,
@@ -451,7 +451,7 @@ mod tests {
provider_api_format: "claude:messages".to_string(),
model_name: Some("mapped".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
@@ -512,7 +512,7 @@ mod tests {
provider_request_body_base64: None,
content_type: None,
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
upstream_is_stream: false,
report_kind: None,

View File

@@ -4,7 +4,7 @@ use aether_ai_formats::api::{
ExecutionRuntimeAuthContext, EXECUTION_RUNTIME_STREAM_DECISION_ACTION,
EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
};
use aether_contracts::{ExecutionTimeouts, ProxySnapshot};
use aether_contracts::{ExecutionTimeouts, ProxySnapshot, ResolvedTransportProfile};
use crate::{AiExecutionDecision, ConversionMode, ExecutionStrategy};
@@ -34,7 +34,7 @@ pub struct AiExecutionDecisionResponseParts {
pub provider_request_body_base64: Option<String>,
pub content_type: Option<String>,
pub proxy: Option<ProxySnapshot>,
pub tls_profile: Option<String>,
pub transport_profile: Option<ResolvedTransportProfile>,
pub timeouts: Option<ExecutionTimeouts>,
pub upstream_is_stream: bool,
pub report_kind: Option<String>,
@@ -74,7 +74,7 @@ pub fn build_ai_execution_decision_response(
provider_request_body_base64: parts.provider_request_body_base64,
content_type: parts.content_type,
proxy: parts.proxy,
tls_profile: parts.tls_profile,
transport_profile: parts.transport_profile,
timeouts: parts.timeouts,
upstream_is_stream: parts.upstream_is_stream,
report_kind: parts.report_kind,

View File

@@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use aether_ai_formats::api::ExecutionRuntimeAuthContext;
use aether_contracts::{ExecutionPlan, ExecutionTimeouts, ProxySnapshot};
use aether_contracts::{ExecutionPlan, ExecutionTimeouts, ProxySnapshot, ResolvedTransportProfile};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
@@ -117,7 +117,7 @@ pub struct AiExecutionDecision {
#[serde(default)]
pub proxy: Option<ProxySnapshot>,
#[serde(default)]
pub tls_profile: Option<String>,
pub transport_profile: Option<ResolvedTransportProfile>,
#[serde(default)]
pub timeouts: Option<ExecutionTimeouts>,
#[serde(default)]
@@ -217,7 +217,7 @@ mod tests {
provider_request_body_base64: None,
content_type: None,
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
upstream_is_stream: false,
report_kind: None,

View File

@@ -101,7 +101,7 @@ mod tests {
provider_api_format: "openai:chat".to_string(),
model_name: Some("model".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
}
}

View File

@@ -8,8 +8,10 @@ mod usage;
pub use error::{ExecutionError, ExecutionErrorKind, ExecutionPhase};
pub use frame::{StreamFrame, StreamFramePayload, StreamFrameType};
pub use plan::{
ExecutionPlan, ExecutionTimeouts, ProxySnapshot, RequestBody,
ExecutionPlan, ExecutionTimeouts, ProxySnapshot, RequestBody, ResolvedTransportProfile,
EXECUTION_REQUEST_FOLLOW_REDIRECTS_HEADER, EXECUTION_REQUEST_HTTP1_ONLY_HEADER,
TRANSPORT_BACKEND_HYPER_RUSTLS, TRANSPORT_BACKEND_REQWEST_RUSTLS, TRANSPORT_HTTP_MODE_AUTO,
TRANSPORT_HTTP_MODE_HTTP1_ONLY, TRANSPORT_POOL_SCOPE_KEY,
};
pub use result::{ExecutionResult, ExecutionTelemetry, ResponseBody};
pub use usage::{ExecutionStreamTerminalSummary, StandardizedUsage};

View File

@@ -59,6 +59,47 @@ pub struct ProxySnapshot {
pub extra: Option<Value>,
}
pub const TRANSPORT_BACKEND_REQWEST_RUSTLS: &str = "reqwest_rustls";
pub const TRANSPORT_BACKEND_HYPER_RUSTLS: &str = "hyper_rustls";
pub const TRANSPORT_HTTP_MODE_AUTO: &str = "auto";
pub const TRANSPORT_HTTP_MODE_HTTP1_ONLY: &str = "http1_only";
pub const TRANSPORT_POOL_SCOPE_KEY: &str = "key";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct ResolvedTransportProfile {
pub profile_id: String,
pub backend: String,
pub http_mode: String,
pub pool_scope: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra: Option<Value>,
}
impl Default for ResolvedTransportProfile {
fn default() -> Self {
Self {
profile_id: String::new(),
backend: TRANSPORT_BACKEND_REQWEST_RUSTLS.to_string(),
http_mode: TRANSPORT_HTTP_MODE_AUTO.to_string(),
pool_scope: TRANSPORT_POOL_SCOPE_KEY.to_string(),
extra: None,
}
}
}
impl ResolvedTransportProfile {
pub fn from_legacy_tls_profile(profile_id: impl Into<String>) -> Self {
Self {
profile_id: profile_id.into(),
backend: TRANSPORT_BACKEND_REQWEST_RUSTLS.to_string(),
http_mode: TRANSPORT_HTTP_MODE_AUTO.to_string(),
pool_scope: TRANSPORT_POOL_SCOPE_KEY.to_string(),
extra: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ExecutionPlan {
pub request_id: String,
@@ -88,7 +129,7 @@ pub struct ExecutionPlan {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proxy: Option<ProxySnapshot>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tls_profile: Option<String>,
pub transport_profile: Option<ResolvedTransportProfile>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeouts: Option<ExecutionTimeouts>,
}
@@ -117,7 +158,7 @@ mod tests {
provider_api_format: "openai:chat".into(),
model_name: Some("gpt-test".into()),
proxy: None,
tls_profile: Some("chrome".into()),
transport_profile: None,
timeouts: Some(ExecutionTimeouts {
connect_ms: Some(30_000),
read_ms: Some(3_600_000),

View File

@@ -171,6 +171,12 @@ pub enum ProtocolError {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RequestMeta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub endpoint_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key_id: Option<String>,
pub method: String,
pub url: String,
pub headers: std::collections::HashMap<String, String>,
@@ -180,6 +186,8 @@ pub struct RequestMeta {
pub follow_redirects: Option<bool>,
#[serde(default, skip_serializing_if = "is_false")]
pub http1_only: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transport_profile: Option<crate::ResolvedTransportProfile>,
}
fn default_timeout() -> u64 {

View File

@@ -4,7 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use aether_contracts::{ExecutionPlan, ExecutionResult, RequestBody};
use aether_provider_transport::{
is_vertex_api_key_transport_context, resolve_transport_execution_timeouts,
resolve_transport_tls_profile, GatewayProviderTransportSnapshot,
resolve_transport_profile, GatewayProviderTransportSnapshot,
};
use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
use base64::Engine as _;
@@ -499,6 +499,8 @@ async fn exchange_vertex_service_account_token(
let body = format!(
"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion={assertion}"
);
let transport_profile = resolve_transport_profile(transport);
let plan = ExecutionPlan {
request_id: format!("req-model-fetch-{}-vertex-sa-token", transport.key.id),
candidate_id: None,
@@ -524,7 +526,7 @@ async fn exchange_vertex_service_account_token(
provider_api_format: "vertex_ai:service_account_token".to_string(),
model_name: Some("token".to_string()),
proxy: runtime.resolve_model_fetch_proxy(transport).await,
tls_profile: resolve_transport_tls_profile(transport),
transport_profile,
timeouts: resolve_transport_execution_timeouts(transport),
};
let result = runtime.execute_model_fetch_execution_plan(&plan).await?;

View File

@@ -11,7 +11,7 @@ use aether_provider_transport::auth::{
};
use aether_provider_transport::vertex::resolve_local_vertex_api_key_query_auth;
use aether_provider_transport::{
apply_local_header_rules, resolve_transport_execution_timeouts, resolve_transport_tls_profile,
apply_local_header_rules, resolve_transport_execution_timeouts, resolve_transport_profile,
GatewayProviderTransportSnapshot, LocalResolvedOAuthRequestAuth,
};
use async_trait::async_trait;
@@ -295,6 +295,8 @@ async fn build_execution_plan(
model_name,
} = request;
let transport_profile = resolve_transport_profile(transport);
Ok(ExecutionPlan {
request_id: format!(
"req-model-fetch-{}-{}",
@@ -317,7 +319,7 @@ async fn build_execution_plan(
provider_api_format,
model_name,
proxy: runtime.resolve_model_fetch_proxy(transport).await,
tls_profile: resolve_transport_tls_profile(transport),
transport_profile,
timeouts: resolve_transport_execution_timeouts(transport),
})
}

View File

@@ -1,7 +1,7 @@
use super::super::snapshot::GatewayProviderTransportSnapshot;
use super::super::{
body_rules_are_locally_supported, header_rules_are_locally_supported,
resolve_transport_tls_profile, supports_local_oauth_request_auth_resolution,
resolve_transport_profile, supports_local_oauth_request_auth_resolution,
transport_proxy_is_locally_supported,
};
use super::auth::supports_local_claude_code_auth;
@@ -52,7 +52,7 @@ pub fn local_claude_code_transport_unsupported_reason_with_network(
if !transport_proxy_is_locally_supported(transport) {
return Some("transport_proxy_unsupported");
}
if transport.key.fingerprint.is_some() && resolve_transport_tls_profile(transport).is_none() {
if transport.key.fingerprint.is_some() && resolve_transport_profile(transport).is_none() {
return Some("transport_tls_profile_unsupported");
}

View File

@@ -8,7 +8,9 @@ use crate::conversion::{
request_conversion_enabled_for_transport, request_conversion_transport_unsupported_reason,
request_pair_allowed_for_transport,
};
use crate::network::{resolve_transport_tls_profile, transport_proxy_is_locally_supported};
use crate::network::{
resolve_transport_profile, resolve_transport_tls_profile, transport_proxy_is_locally_supported,
};
use crate::policy::{
local_gemini_transport_unsupported_reason_with_network,
local_openai_chat_transport_unsupported_reason,
@@ -67,6 +69,9 @@ pub fn build_transport_diagnostics(
provider_api_format: &str,
) -> Value {
let resolved_tls_profile = resolve_transport_tls_profile(transport);
let resolved_transport_profile = resolve_transport_profile(transport)
.and_then(|profile| serde_json::to_value(profile).ok())
.unwrap_or(Value::Null);
let configured_tls_profile = transport
.key
.fingerprint
@@ -75,6 +80,23 @@ pub fn build_transport_diagnostics(
.and_then(|value| value.get("tls_profile"))
.cloned()
.unwrap_or(Value::Null);
let configured_key_transport_profile = transport
.key
.fingerprint
.as_ref()
.and_then(Value::as_object)
.and_then(|value| value.get("transport_profile"))
.cloned()
.unwrap_or(Value::Null);
let configured_provider_transport_profile = transport
.provider
.config
.as_ref()
.and_then(|value| value.get("fingerprint"))
.and_then(Value::as_object)
.and_then(|value| value.get("transport_profile"))
.cloned()
.unwrap_or(Value::Null);
let has_oauth_config = transport.key.decrypted_auth_config.is_some();
let oauth_resolution_supported =
!has_oauth_config || crate::supports_local_oauth_request_auth_resolution(transport);
@@ -111,7 +133,10 @@ pub fn build_transport_diagnostics(
},
"fingerprint": transport.key.fingerprint,
"configured_tls_profile": configured_tls_profile,
"configured_key_transport_profile": configured_key_transport_profile,
"configured_provider_transport_profile": configured_provider_transport_profile,
"resolved_tls_profile": resolved_tls_profile,
"resolved_transport_profile": resolved_transport_profile,
"request_pair": {
"client_api_format": client_api_format,
"provider_api_format": provider_api_format,

View File

@@ -1,5 +1,5 @@
use super::super::snapshot::GatewayProviderTransportSnapshot;
use super::super::{resolve_transport_tls_profile, transport_proxy_is_locally_supported};
use super::super::{resolve_transport_profile, transport_proxy_is_locally_supported};
use super::{
body_rules_are_locally_supported, header_rules_are_locally_supported,
supports_local_kiro_request_auth_resolution, supports_local_kiro_request_shape, PROVIDER_TYPE,
@@ -48,7 +48,7 @@ pub fn local_kiro_request_transport_unsupported_reason_with_network(
if !transport_proxy_is_locally_supported(transport) {
return Some("transport_proxy_unsupported");
}
if transport.key.fingerprint.is_some() && resolve_transport_tls_profile(transport).is_none() {
if transport.key.fingerprint.is_some() && resolve_transport_profile(transport).is_none() {
return Some("transport_tls_profile_unsupported");
}

View File

@@ -46,10 +46,10 @@ pub use generic_oauth::{
};
pub use headers::{should_skip_request_header, should_skip_upstream_passthrough_header};
pub use network::{
resolve_transport_execution_timeouts, resolve_transport_proxy_snapshot,
resolve_transport_proxy_snapshot_with_tunnel_affinity, resolve_transport_tls_profile,
transport_proxy_is_locally_supported, TransportTunnelAffinityLookup,
TransportTunnelAttachmentOwner,
resolve_transport_execution_timeouts, resolve_transport_profile,
resolve_transport_proxy_snapshot, resolve_transport_proxy_snapshot_with_tunnel_affinity,
resolve_transport_tls_profile, transport_proxy_is_locally_supported,
TransportTunnelAffinityLookup, TransportTunnelAttachmentOwner,
};
pub use oauth_refresh::{
supports_local_oauth_request_auth_resolution, CachedOAuthEntry, LocalOAuthHttpExecutor,

View File

@@ -1,4 +1,7 @@
use aether_contracts::{ExecutionTimeouts, ProxySnapshot};
use aether_contracts::{
ExecutionTimeouts, ProxySnapshot, ResolvedTransportProfile, TRANSPORT_BACKEND_REQWEST_RUSTLS,
TRANSPORT_HTTP_MODE_AUTO, TRANSPORT_POOL_SCOPE_KEY,
};
use async_trait::async_trait;
use serde_json::{json, Map, Value};
use tracing::warn;
@@ -141,15 +144,83 @@ pub fn transport_proxy_is_locally_supported(transport: &GatewayProviderTransport
pub fn resolve_transport_tls_profile(
transport: &GatewayProviderTransportSnapshot,
) -> Option<String> {
transport
.key
.fingerprint
.as_ref()
.and_then(|value| value.get("tls_profile"))
resolve_transport_profile(transport).map(|profile| profile.profile_id)
}
pub fn resolve_transport_profile(
transport: &GatewayProviderTransportSnapshot,
) -> Option<ResolvedTransportProfile> {
resolve_transport_profile_from_fingerprint(transport.key.fingerprint.as_ref()).or_else(|| {
resolve_transport_profile_from_provider_config(transport.provider.config.as_ref())
})
}
fn resolve_transport_profile_from_provider_config(
config: Option<&Value>,
) -> Option<ResolvedTransportProfile> {
let fingerprint = config?.get("fingerprint");
resolve_transport_profile_from_fingerprint(fingerprint)
}
fn resolve_transport_profile_from_fingerprint(
fingerprint: Option<&Value>,
) -> Option<ResolvedTransportProfile> {
let fingerprint = fingerprint?;
if let Some(profile) = fingerprint.get("transport_profile") {
if let Some(resolved) = parse_transport_profile_value(profile) {
return Some(resolved);
}
}
fingerprint
.get("tls_profile")
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
.map(ResolvedTransportProfile::from_legacy_tls_profile)
}
fn parse_transport_profile_value(value: &Value) -> Option<ResolvedTransportProfile> {
if let Some(profile_id) = value
.as_str()
.map(str::trim)
.filter(|value| !value.is_empty())
{
return Some(ResolvedTransportProfile {
profile_id: profile_id.to_string(),
backend: TRANSPORT_BACKEND_REQWEST_RUSTLS.to_string(),
http_mode: TRANSPORT_HTTP_MODE_AUTO.to_string(),
pool_scope: TRANSPORT_POOL_SCOPE_KEY.to_string(),
extra: None,
});
}
let object = value.as_object()?;
let profile_id = json_string_field(object, "profile_id")
.or_else(|| json_string_field(object, "id"))
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())?;
let backend = json_string_field(object, "backend")
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| TRANSPORT_BACKEND_REQWEST_RUSTLS.to_string());
let http_mode = json_string_field(object, "http_mode")
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| TRANSPORT_HTTP_MODE_AUTO.to_string());
let pool_scope = json_string_field(object, "pool_scope")
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| TRANSPORT_POOL_SCOPE_KEY.to_string());
let extra = object.get("extra").cloned();
Some(ResolvedTransportProfile {
profile_id,
backend,
http_mode,
pool_scope,
extra,
})
}
fn effective_proxy_config(transport: &GatewayProviderTransportSnapshot) -> Option<&Value> {
@@ -225,9 +296,10 @@ mod tests {
GatewayProviderTransportProvider, GatewayProviderTransportSnapshot,
};
use super::{
resolve_transport_proxy_snapshot, resolve_transport_proxy_snapshot_with_tunnel_affinity,
resolve_transport_tls_profile, transport_proxy_is_locally_supported,
TransportTunnelAffinityLookup, TransportTunnelAttachmentOwner,
resolve_transport_profile, resolve_transport_proxy_snapshot,
resolve_transport_proxy_snapshot_with_tunnel_affinity, resolve_transport_tls_profile,
transport_proxy_is_locally_supported, TransportTunnelAffinityLookup,
TransportTunnelAttachmentOwner,
};
#[derive(Default)]
@@ -391,4 +463,60 @@ mod tests {
);
assert!(transport_proxy_is_locally_supported(&sample_transport()));
}
#[test]
fn resolves_transport_profile_from_key_fingerprint_before_provider_default() {
let mut transport = sample_transport();
transport.provider.config = Some(json!({
"fingerprint": {"transport_profile": "provider_profile"}
}));
transport.key.fingerprint = Some(json!({
"transport_profile": {
"profile_id": "key_profile",
"backend": "reqwest_rustls",
"http_mode": "http1_only"
}
}));
let profile = resolve_transport_profile(&transport).expect("profile");
assert_eq!(profile.profile_id, "key_profile");
assert_eq!(profile.backend, "reqwest_rustls");
assert_eq!(profile.http_mode, "http1_only");
assert_eq!(profile.pool_scope, "key");
}
#[test]
fn resolves_transport_profile_from_provider_default() {
let mut transport = sample_transport();
transport.key.fingerprint = None;
transport.provider.config = Some(json!({
"fingerprint": {"transport_profile": "provider_profile"}
}));
let profile = resolve_transport_profile(&transport).expect("profile");
assert_eq!(profile.profile_id, "provider_profile");
assert_eq!(profile.backend, "reqwest_rustls");
}
#[test]
fn maps_legacy_tls_profile_to_transport_profile() {
let profile = resolve_transport_profile(&sample_transport()).expect("profile");
assert_eq!(profile.profile_id, "chrome_136");
assert_eq!(profile.backend, "reqwest_rustls");
assert_eq!(profile.http_mode, "auto");
assert_eq!(profile.pool_scope, "key");
}
#[test]
fn resolves_no_transport_profile_without_fingerprint_configuration() {
let mut transport = sample_transport();
transport.key.fingerprint = None;
transport.provider.config = None;
assert!(resolve_transport_profile(&transport).is_none());
assert!(resolve_transport_tls_profile(&transport).is_none());
}
}

View File

@@ -6,7 +6,7 @@ use super::provider_types::{
use super::snapshot::GatewayProviderTransportSnapshot;
use super::{
body_rules_are_locally_supported, header_rules_are_locally_supported,
resolve_transport_tls_profile, supports_local_oauth_request_auth_resolution,
resolve_transport_profile, supports_local_oauth_request_auth_resolution,
transport_proxy_is_locally_supported,
};
@@ -76,7 +76,7 @@ pub fn local_openai_chat_transport_unsupported_reason(
if !transport_proxy_is_locally_supported(transport) {
return Some("transport_proxy_unsupported");
}
if transport.key.fingerprint.is_some() && resolve_transport_tls_profile(transport).is_none() {
if transport.key.fingerprint.is_some() && resolve_transport_profile(transport).is_none() {
return Some("transport_tls_profile_unsupported");
}
if !provider_type_supports_local_openai_chat_transport(&transport.provider.provider_type) {
@@ -175,8 +175,7 @@ fn local_same_format_transport_unsupported_reason(
if !transport_proxy_is_locally_supported(transport) {
return Some("transport_proxy_unsupported");
}
if transport.key.fingerprint.is_some() && resolve_transport_tls_profile(transport).is_none()
{
if transport.key.fingerprint.is_some() && resolve_transport_profile(transport).is_none() {
return Some("transport_tls_profile_unsupported");
}
} else if transport.provider.proxy.is_some()

View File

@@ -1,7 +1,7 @@
use super::super::snapshot::GatewayProviderTransportSnapshot;
use super::super::{
body_rules_are_locally_supported, header_rules_are_locally_supported,
resolve_transport_tls_profile, transport_proxy_is_locally_supported,
resolve_transport_profile, resolve_transport_tls_profile, transport_proxy_is_locally_supported,
};
use super::auth::resolve_local_vertex_api_key_query_auth;
@@ -46,7 +46,7 @@ pub fn local_vertex_api_key_gemini_transport_unsupported_reason_with_network(
if !transport_proxy_is_locally_supported(transport) {
return Some("transport_proxy_unsupported");
}
if transport.key.fingerprint.is_some() && resolve_transport_tls_profile(transport).is_none() {
if transport.key.fingerprint.is_some() && resolve_transport_profile(transport).is_none() {
return Some("transport_tls_profile_unsupported");
}
@@ -133,8 +133,7 @@ fn supports_local_vertex_api_key_same_format_transport(
if !transport_proxy_is_locally_supported(transport) {
return false;
}
if transport.key.fingerprint.is_some() && resolve_transport_tls_profile(transport).is_none()
{
if transport.key.fingerprint.is_some() && resolve_transport_profile(transport).is_none() {
return false;
}
} else if transport.provider.proxy.is_some()

View File

@@ -818,7 +818,7 @@ mod tests {
provider_api_format: "openai:chat".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
}
}

View File

@@ -536,7 +536,7 @@ fn execution_plan(url: String, stream: bool) -> ExecutionPlan {
provider_api_format: "openai:chat".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: Some(ExecutionTimeouts {
connect_ms: Some(2_000),
read_ms: Some(10_000),
@@ -608,6 +608,10 @@ fn relay_envelope() -> Vec<u8> {
timeout: 30,
follow_redirects: None,
http1_only: false,
provider_id: None,
endpoint_id: None,
key_id: None,
transport_profile: None,
};
let meta_json = serde_json::to_vec(&meta).expect("hub relay metadata should serialize");
let body = br#"{"model":"gpt-5","messages":[{"role":"user","content":"hello"}]}"#;

View File

@@ -100,6 +100,10 @@ fn relay_envelope() -> Vec<u8> {
timeout: 30,
follow_redirects: None,
http1_only: false,
provider_id: None,
endpoint_id: None,
key_id: None,
transport_profile: None,
};
let meta_json = serde_json::to_vec(&meta).expect("tunnel relay metadata should serialize");
let body = br#"{"model":"gpt-5","messages":[{"role":"user","content":"hello"}]}"#;

View File

@@ -386,7 +386,7 @@ fn execution_plan(url: String) -> ExecutionPlan {
provider_api_format: "openai:chat".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: Some(ExecutionTimeouts {
connect_ms: Some(2_000),
read_ms: Some(10_000),

View File

@@ -256,6 +256,10 @@ fn relay_envelope() -> Vec<u8> {
timeout: 30,
follow_redirects: None,
http1_only: false,
provider_id: None,
endpoint_id: None,
key_id: None,
transport_profile: None,
};
let meta_json = serde_json::to_vec(&meta).expect("owner relay metadata should serialize");
let body = br#"{"model":"gpt-5","messages":[{"role":"user","content":"owner relay"}]}"#;

View File

@@ -257,7 +257,7 @@ fn execution_plan(url: String, stream: bool) -> ExecutionPlan {
provider_api_format: "openai:chat".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: Some(ExecutionTimeouts {
connect_ms: Some(2_000),
read_ms: Some(10_000),

View File

@@ -376,7 +376,7 @@ mod tests {
provider_api_format: "openai:chat".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
}
}

View File

@@ -2483,7 +2483,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
@@ -2550,7 +2550,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
@@ -2595,7 +2595,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
@@ -2652,7 +2652,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
@@ -2715,7 +2715,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewayStreamReportRequest {
@@ -2810,7 +2810,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let mut standardized_usage = StandardizedUsage::new();
@@ -2881,7 +2881,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let mut partial_summary_usage = StandardizedUsage::new();
@@ -2987,7 +2987,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let sse_body = concat!(
@@ -3111,7 +3111,7 @@ mod tests {
provider_api_format: "gemini:generate_content".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
@@ -3218,7 +3218,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
@@ -3326,7 +3326,7 @@ mod tests {
provider_api_format: "gemini:generate_content".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
@@ -3398,7 +3398,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
@@ -3461,7 +3461,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewayStreamReportRequest {
@@ -3547,7 +3547,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewayStreamReportRequest {
@@ -3613,7 +3613,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
@@ -3667,7 +3667,7 @@ mod tests {
provider_api_format: "openai:responses".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
@@ -3915,7 +3915,7 @@ mod tests {
provider_api_format: "openai:chat".to_string(),
model_name: Some("gpt-5".to_string()),
proxy: None,
tls_profile: None,
transport_profile: None,
timeouts: None,
};

View File

@@ -168,7 +168,7 @@ impl GeminiVideoTaskSeed {
provider_api_format: "gemini:video".to_string(),
model_name: Some(self.model.clone()),
proxy: self.transport.proxy.clone(),
tls_profile: self.transport.tls_profile.clone(),
transport_profile: None,
timeouts: self.transport.timeouts.clone(),
})
}
@@ -228,7 +228,7 @@ impl GeminiVideoTaskSeed {
provider_api_format: "gemini:video".to_string(),
model_name: Some(self.model.clone()),
proxy: self.transport.proxy.clone(),
tls_profile: self.transport.tls_profile.clone(),
transport_profile: None,
timeouts: self.transport.timeouts.clone(),
},
report_kind: Some("gemini_video_cancel_sync_finalize".to_string()),

View File

@@ -235,7 +235,7 @@ impl OpenAiVideoTaskSeed {
.clone()
.or_else(|| self.transport.model_name.clone()),
proxy: self.transport.proxy.clone(),
tls_profile: self.transport.tls_profile.clone(),
transport_profile: None,
timeouts: self.transport.timeouts.clone(),
},
)))
@@ -340,7 +340,7 @@ impl OpenAiVideoTaskSeed {
provider_api_format: "openai:video".to_string(),
model_name: model_name.clone(),
proxy: self.transport.proxy.clone(),
tls_profile: self.transport.tls_profile.clone(),
transport_profile: None,
timeouts: self.transport.timeouts.clone(),
},
report_kind: Some("openai_video_delete_sync_finalize".to_string()),
@@ -405,7 +405,7 @@ impl OpenAiVideoTaskSeed {
.clone()
.or_else(|| self.transport.model_name.clone()),
proxy: self.transport.proxy.clone(),
tls_profile: self.transport.tls_profile.clone(),
transport_profile: None,
timeouts: self.transport.timeouts.clone(),
})
}
@@ -466,7 +466,7 @@ impl OpenAiVideoTaskSeed {
provider_api_format: "openai:video".to_string(),
model_name: model_name.clone(),
proxy: self.transport.proxy.clone(),
tls_profile: self.transport.tls_profile.clone(),
transport_profile: None,
timeouts: self.transport.timeouts.clone(),
},
report_kind: Some("openai_video_cancel_sync_finalize".to_string()),
@@ -561,7 +561,7 @@ impl OpenAiVideoTaskSeed {
provider_api_format: "openai:video".to_string(),
model_name,
proxy: self.transport.proxy.clone(),
tls_profile: self.transport.tls_profile.clone(),
transport_profile: None,
timeouts: self.transport.timeouts.clone(),
},
report_kind: Some("openai_video_remix_sync_finalize".to_string()),

View File

@@ -278,7 +278,7 @@ pub fn build_internal_finalize_video_plan(
url: None,
extra: None,
}),
tls_profile: None,
transport_profile: None,
timeouts: None,
})
}

View File

@@ -30,7 +30,7 @@ impl LocalVideoTaskTransport {
content_type: plan.content_type.clone(),
model_name: plan.model_name.clone(),
proxy: plan.proxy.clone(),
tls_profile: plan.tls_profile.clone(),
tls_profile: None,
timeouts: plan.timeouts.clone(),
})
}