mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
fix(provider): 将rust分支的gemini cli端点行为对齐到python分支 (#321)
* fix(provider): 对齐 Vertex/Gemini 上游发包与 Python master - provider-transport: 为 custom+aiplatform 推断 Vertex API key 上下文并统一 URL 构建顺序,复用共享 request_url 构建最终上游地址 - ai-pipeline/gateway: Vertex Gemini 路径改为仅使用 URL query key,不再向上游附带 x-goog-api-key header;同步对齐 standard/admin/test-connection/runtime miss 摘要中的最终 URL - gemini conversion: 按 Python master 输出 Gemini 请求体,补齐 system_instruction / generation_config / tool_config / function_declarations 形态,并移植 Gemini schema 清洗逻辑 - scheduler/executor: 将最终 upstream_url、mapped_model、key_name 写入候选 extra_data,运行时 miss 诊断优先展示真实展开后的上游 URL 便于服务器排障 * fix(provider): 修复 Vertex provider 测试与本地调度链路 * fix(provider): 对齐 Vertex 本地执行与 Rust CI
This commit is contained in:
@@ -308,7 +308,11 @@ fn provider_query_transport_supports_standard_test_execution(
|
||||
)
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => {
|
||||
state.supports_local_gemini_transport_with_network(transport, api_format)
|
||||
if crate::provider_transport::is_vertex_api_key_transport_context(transport) {
|
||||
aether_provider_transport::vertex::supports_local_vertex_api_key_gemini_transport_with_network(transport)
|
||||
} else {
|
||||
state.supports_local_gemini_transport_with_network(transport, api_format)
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
@@ -1018,6 +1022,13 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
}
|
||||
};
|
||||
|
||||
let uses_vertex_query_auth =
|
||||
crate::provider_transport::uses_vertex_api_key_query_auth(&transport, provider_api_format);
|
||||
let vertex_query_auth = if uses_vertex_query_auth {
|
||||
aether_provider_transport::vertex::resolve_local_vertex_api_key_query_auth(&transport)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let oauth_auth = match provider_api_format {
|
||||
"openai:chat" | "openai:cli" | "claude:chat" | "claude:cli" | "gemini:chat"
|
||||
| "gemini:cli" => state.resolve_local_oauth_header_auth(&transport).await?,
|
||||
@@ -1031,14 +1042,24 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
"claude:chat" | "claude:cli" => {
|
||||
crate::provider_transport::auth::resolve_local_standard_auth(&transport).or(oauth_auth)
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => state.resolve_local_gemini_auth(&transport).or(oauth_auth),
|
||||
"gemini:chat" | "gemini:cli" => {
|
||||
if uses_vertex_query_auth {
|
||||
oauth_auth
|
||||
} else {
|
||||
state.resolve_local_gemini_auth(&transport).or(oauth_auth)
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let Some((auth_header, auth_value)) = auth else {
|
||||
return Ok(provider_query_skipped_execution_outcome(
|
||||
provider_request_body,
|
||||
format!("Provider auth is unavailable for {provider_api_format}"),
|
||||
));
|
||||
let (auth_header, auth_value) = match auth {
|
||||
Some((auth_header, auth_value)) => (Some(auth_header), Some(auth_value)),
|
||||
None if uses_vertex_query_auth && vertex_query_auth.is_some() => (None, None),
|
||||
None => {
|
||||
return Ok(provider_query_skipped_execution_outcome(
|
||||
provider_request_body,
|
||||
format!("Provider auth is unavailable for {provider_api_format}"),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let mut synthetic_request = http::Request::builder()
|
||||
@@ -1048,91 +1069,16 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
*synthetic_request.headers_mut() = provider_query_extract_request_headers(payload);
|
||||
let (parts, _) = synthetic_request.into_parts();
|
||||
|
||||
let request_url = match provider_api_format {
|
||||
"openai:chat" => {
|
||||
let custom_path = transport
|
||||
.endpoint
|
||||
.custom_path
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
match custom_path {
|
||||
Some(path) => state.build_passthrough_path_url(
|
||||
&transport.endpoint.base_url,
|
||||
path,
|
||||
parts.uri.query(),
|
||||
&[],
|
||||
),
|
||||
None => Some(
|
||||
state.build_openai_chat_url(&transport.endpoint.base_url, parts.uri.query()),
|
||||
),
|
||||
}
|
||||
}
|
||||
"claude:chat" | "claude:cli" => {
|
||||
let custom_path = transport
|
||||
.endpoint
|
||||
.custom_path
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
match custom_path {
|
||||
Some(path) => state.build_passthrough_path_url(
|
||||
&transport.endpoint.base_url,
|
||||
path,
|
||||
parts.uri.query(),
|
||||
&[],
|
||||
),
|
||||
None => Some(
|
||||
state
|
||||
.build_claude_messages_url(&transport.endpoint.base_url, parts.uri.query()),
|
||||
),
|
||||
}
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => {
|
||||
let custom_path = transport
|
||||
.endpoint
|
||||
.custom_path
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
match custom_path {
|
||||
Some(path) => state.build_passthrough_path_url(
|
||||
&transport.endpoint.base_url,
|
||||
path,
|
||||
parts.uri.query(),
|
||||
&["key"],
|
||||
),
|
||||
None => state.build_gemini_content_url(
|
||||
&transport.endpoint.base_url,
|
||||
&candidate.effective_model,
|
||||
false,
|
||||
parts.uri.query(),
|
||||
),
|
||||
}
|
||||
}
|
||||
"openai:cli" => {
|
||||
let custom_path = transport
|
||||
.endpoint
|
||||
.custom_path
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
match custom_path {
|
||||
Some(path) => state.build_passthrough_path_url(
|
||||
&transport.endpoint.base_url,
|
||||
path,
|
||||
parts.uri.query(),
|
||||
&[],
|
||||
),
|
||||
None => Some(build_openai_cli_url(
|
||||
&transport.endpoint.base_url,
|
||||
parts.uri.query(),
|
||||
false,
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let request_url = crate::provider_transport::build_transport_request_url(
|
||||
&transport,
|
||||
crate::provider_transport::TransportRequestUrlParams {
|
||||
provider_api_format,
|
||||
mapped_model: Some(candidate.effective_model.as_str()),
|
||||
upstream_is_stream: false,
|
||||
request_query: parts.uri.query(),
|
||||
kiro_api_region: None,
|
||||
},
|
||||
);
|
||||
let Some(request_url) = request_url else {
|
||||
return Ok(provider_query_skipped_execution_outcome(
|
||||
provider_request_body,
|
||||
@@ -1144,8 +1090,8 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
"claude:chat" | "claude:cli" => {
|
||||
crate::provider_transport::auth::build_claude_passthrough_headers(
|
||||
&parts.headers,
|
||||
&auth_header,
|
||||
&auth_value,
|
||||
auth_header.as_deref().unwrap_or_default(),
|
||||
auth_value.as_deref().unwrap_or_default(),
|
||||
&BTreeMap::new(),
|
||||
Some("application/json"),
|
||||
)
|
||||
@@ -1153,26 +1099,41 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
"openai:cli" => {
|
||||
crate::provider_transport::auth::build_complete_passthrough_headers_with_auth(
|
||||
&parts.headers,
|
||||
&auth_header,
|
||||
&auth_value,
|
||||
auth_header.as_deref().unwrap_or_default(),
|
||||
auth_value.as_deref().unwrap_or_default(),
|
||||
&BTreeMap::new(),
|
||||
Some("application/json"),
|
||||
)
|
||||
}
|
||||
_ => state.build_passthrough_headers_with_auth(
|
||||
&parts.headers,
|
||||
&auth_header,
|
||||
&auth_value,
|
||||
&BTreeMap::new(),
|
||||
),
|
||||
_ => match (auth_header.as_deref(), auth_value.as_deref()) {
|
||||
(Some(auth_header), Some(auth_value)) => state.build_passthrough_headers_with_auth(
|
||||
&parts.headers,
|
||||
auth_header,
|
||||
auth_value,
|
||||
&BTreeMap::new(),
|
||||
),
|
||||
_ => crate::provider_transport::auth::build_passthrough_headers(
|
||||
&parts.headers,
|
||||
&BTreeMap::new(),
|
||||
Some("application/json"),
|
||||
),
|
||||
},
|
||||
};
|
||||
if uses_vertex_query_auth {
|
||||
request_headers.remove("x-goog-api-key");
|
||||
}
|
||||
request_headers
|
||||
.entry("content-type".to_string())
|
||||
.or_insert_with(|| "application/json".to_string());
|
||||
let protected_headers = if uses_vertex_query_auth {
|
||||
vec!["content-type"]
|
||||
} else {
|
||||
vec![auth_header.as_deref().unwrap_or_default(), "content-type"]
|
||||
};
|
||||
if !state.apply_local_header_rules(
|
||||
&mut request_headers,
|
||||
transport.endpoint.header_rules.as_ref(),
|
||||
&[auth_header.as_str(), "content-type"],
|
||||
&protected_headers,
|
||||
&provider_request_body,
|
||||
Some(&request_body),
|
||||
) {
|
||||
@@ -1200,11 +1161,17 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
transport.key.decrypted_auth_config.as_deref(),
|
||||
);
|
||||
}
|
||||
crate::provider_transport::ensure_upstream_auth_header(
|
||||
&mut request_headers,
|
||||
&auth_header,
|
||||
&auth_value,
|
||||
);
|
||||
if !uses_vertex_query_auth {
|
||||
if let (Some(auth_header), Some(auth_value)) =
|
||||
(auth_header.as_deref(), auth_value.as_deref())
|
||||
{
|
||||
crate::provider_transport::ensure_upstream_auth_header(
|
||||
&mut request_headers,
|
||||
auth_header,
|
||||
auth_value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let plan = ExecutionPlan {
|
||||
request_id: trace_id.to_string(),
|
||||
|
||||
@@ -219,50 +219,21 @@ pub(super) async fn maybe_build_local_test_connection_route_response(
|
||||
let Some((auth_header, auth_value)) = auth else {
|
||||
return None;
|
||||
};
|
||||
let uses_vertex_query_auth = crate::provider_transport::uses_vertex_api_key_query_auth(
|
||||
&transport,
|
||||
format_value.as_str(),
|
||||
);
|
||||
|
||||
let upstream_url = {
|
||||
let custom_path = transport
|
||||
.endpoint
|
||||
.custom_path
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
match (format_value.as_str(), custom_path) {
|
||||
("openai:chat", Some(path)) | ("claude:chat", Some(path)) => {
|
||||
crate::provider_transport::url::build_passthrough_path_url(
|
||||
&transport.endpoint.base_url,
|
||||
path,
|
||||
None,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
("gemini:chat", Some(path)) => {
|
||||
crate::provider_transport::url::build_passthrough_path_url(
|
||||
&transport.endpoint.base_url,
|
||||
path,
|
||||
None,
|
||||
&["key"],
|
||||
)
|
||||
}
|
||||
("openai:chat", None) => Some(crate::provider_transport::url::build_openai_chat_url(
|
||||
&transport.endpoint.base_url,
|
||||
None,
|
||||
)),
|
||||
("claude:chat", None) => {
|
||||
Some(crate::provider_transport::url::build_claude_messages_url(
|
||||
&transport.endpoint.base_url,
|
||||
None,
|
||||
))
|
||||
}
|
||||
("gemini:chat", None) => crate::provider_transport::url::build_gemini_content_url(
|
||||
&transport.endpoint.base_url,
|
||||
&model,
|
||||
false,
|
||||
None,
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
let upstream_url = crate::provider_transport::build_transport_request_url(
|
||||
&transport,
|
||||
crate::provider_transport::TransportRequestUrlParams {
|
||||
provider_api_format: format_value.as_str(),
|
||||
mapped_model: Some(model.as_str()),
|
||||
upstream_is_stream: false,
|
||||
request_query: None,
|
||||
kiro_api_region: None,
|
||||
},
|
||||
);
|
||||
let Some(upstream_url) = upstream_url else {
|
||||
return None;
|
||||
};
|
||||
@@ -271,20 +242,30 @@ pub(super) async fn maybe_build_local_test_connection_route_response(
|
||||
("content-type".to_string(), "application/json".to_string()),
|
||||
(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"][..]
|
||||
} else {
|
||||
&[auth_header.as_str(), "content-type"][..]
|
||||
};
|
||||
if !crate::provider_transport::apply_local_header_rules(
|
||||
&mut provider_request_headers,
|
||||
transport.endpoint.header_rules.as_ref(),
|
||||
&[auth_header.as_str(), "content-type"],
|
||||
protected_headers,
|
||||
&provider_request_body,
|
||||
None,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
crate::provider_transport::ensure_upstream_auth_header(
|
||||
&mut provider_request_headers,
|
||||
&auth_header,
|
||||
&auth_value,
|
||||
);
|
||||
if !uses_vertex_query_auth {
|
||||
crate::provider_transport::ensure_upstream_auth_header(
|
||||
&mut provider_request_headers,
|
||||
&auth_header,
|
||||
&auth_value,
|
||||
);
|
||||
}
|
||||
|
||||
let mut upstream_request = state.client.post(&upstream_url);
|
||||
for (name, value) in &provider_request_headers {
|
||||
|
||||
Reference in New Issue
Block a user