mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
fix: allow empty provider secrets to attempt requests
This commit is contained in:
@@ -82,3 +82,116 @@ pub(crate) async fn resolve_candidate_oauth_auth(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use aether_provider_transport::snapshot::{
|
||||
GatewayProviderTransportEndpoint, GatewayProviderTransportKey,
|
||||
GatewayProviderTransportProvider, GatewayProviderTransportSnapshot,
|
||||
};
|
||||
use aether_scheduler_core::SchedulerMinimalCandidateSelectionCandidate;
|
||||
|
||||
use super::{prepare_header_authenticated_candidate, OauthPreparationContext};
|
||||
use crate::ai_pipeline::PlannerAppState;
|
||||
|
||||
fn sample_transport() -> GatewayProviderTransportSnapshot {
|
||||
GatewayProviderTransportSnapshot {
|
||||
provider: GatewayProviderTransportProvider {
|
||||
id: "provider-1".to_string(),
|
||||
name: "provider".to_string(),
|
||||
provider_type: "custom".to_string(),
|
||||
website: None,
|
||||
is_active: true,
|
||||
keep_priority_on_conversion: false,
|
||||
enable_format_conversion: false,
|
||||
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: Some("openai".to_string()),
|
||||
endpoint_kind: Some("chat".to_string()),
|
||||
is_active: true,
|
||||
base_url: "https://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: Some(vec!["openai:chat".to_string()]),
|
||||
auth_type_by_format: 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: String::new(),
|
||||
decrypted_auth_config: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_candidate() -> SchedulerMinimalCandidateSelectionCandidate {
|
||||
SchedulerMinimalCandidateSelectionCandidate {
|
||||
provider_id: "provider-1".to_string(),
|
||||
provider_name: "provider".to_string(),
|
||||
provider_type: "custom".to_string(),
|
||||
provider_priority: 1,
|
||||
endpoint_id: "endpoint-1".to_string(),
|
||||
endpoint_api_format: "openai:chat".to_string(),
|
||||
key_id: "key-1".to_string(),
|
||||
key_name: "key".to_string(),
|
||||
key_auth_type: "api_key".to_string(),
|
||||
key_internal_priority: 1,
|
||||
key_global_priority_for_format: None,
|
||||
key_capabilities: None,
|
||||
model_id: "model-1".to_string(),
|
||||
global_model_id: "global-model-1".to_string(),
|
||||
global_model_name: "gpt-test".to_string(),
|
||||
selected_provider_model_name: "gpt-test-upstream".to_string(),
|
||||
mapping_matched_model: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn header_auth_preparation_allows_empty_auth_value() {
|
||||
let state = crate::AppState::new().expect("state should build");
|
||||
let transport = sample_transport();
|
||||
let candidate = sample_candidate();
|
||||
|
||||
let prepared = prepare_header_authenticated_candidate(
|
||||
PlannerAppState::new(&state),
|
||||
&transport,
|
||||
&candidate,
|
||||
Some(("authorization".to_string(), String::new())),
|
||||
OauthPreparationContext {
|
||||
trace_id: "trace-empty-auth",
|
||||
api_format: "openai:chat",
|
||||
operation: "test",
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("empty auth value should still prepare the candidate");
|
||||
|
||||
assert_eq!(prepared.auth_header, "authorization");
|
||||
assert_eq!(prepared.auth_value, "");
|
||||
assert_eq!(prepared.mapped_model, "gpt-test-upstream");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,22 +242,23 @@ pub(super) async fn maybe_build_local_test_connection_route_response(
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut provider_request_headers = BTreeMap::from([
|
||||
("content-type".to_string(), "application/json".to_string()),
|
||||
(auth_header.clone(), auth_value.clone()),
|
||||
]);
|
||||
let mut provider_request_headers =
|
||||
BTreeMap::from([("content-type".to_string(), "application/json".to_string())]);
|
||||
if !auth_header.trim().is_empty() && !auth_value.trim().is_empty() {
|
||||
provider_request_headers.insert(auth_header.clone(), auth_value.clone());
|
||||
}
|
||||
if uses_vertex_query_auth {
|
||||
provider_request_headers.remove("x-goog-api-key");
|
||||
}
|
||||
let protected_headers = if uses_vertex_query_auth {
|
||||
&["content-type"][..]
|
||||
let protected_headers = if uses_vertex_query_auth || auth_value.trim().is_empty() {
|
||||
vec!["content-type"]
|
||||
} else {
|
||||
&[auth_header.as_str(), "content-type"][..]
|
||||
vec![auth_header.as_str(), "content-type"]
|
||||
};
|
||||
if !crate::provider_transport::apply_local_header_rules(
|
||||
&mut provider_request_headers,
|
||||
transport.endpoint.header_rules.as_ref(),
|
||||
protected_headers,
|
||||
&protected_headers,
|
||||
&provider_request_body,
|
||||
None,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user