Merge origin/main into fix/gemini-cli-v1internal

This commit is contained in:
Mas0nShi
2026-05-28 11:58:00 +08:00
410 changed files with 38026 additions and 6621 deletions
+211 -7
View File
@@ -17,8 +17,8 @@ use serde_json::{json, Value};
use sha2::Sha256;
use crate::logic::{
extract_error_message, parse_models_response_page, parse_windsurf_model_configs_response,
preset_models_for_provider,
aggregate_models_for_cache, extract_error_message, parse_models_response_page,
parse_windsurf_model_configs_response, preset_models_for_provider,
};
use crate::transport::{
build_antigravity_fetch_available_models_plan, build_gemini_cli_load_code_assist_plan,
@@ -204,7 +204,8 @@ async fn fetch_standard_models(
}
}
Ok(build_success_outcome(all_models, None, has_success).with_errors(errors))
let merged_models = aggregate_models_for_cache(&all_models);
Ok(build_success_outcome(merged_models, None, has_success).with_errors(errors))
}
async fn fetch_standard_models_for_transport(
@@ -438,7 +439,7 @@ async fn fetch_vertex_api_key_models(
for base_url in iter_vertex_base_urls(transports) {
let url = build_vertex_google_list_url(&base_url, api_key, None);
let outcome = fetch_vertex_models_from_url(
let outcome = match fetch_vertex_models_from_url(
runtime,
reference_transport,
&url,
@@ -447,7 +448,14 @@ async fn fetch_vertex_api_key_models(
"gemini:generate_content",
None,
)
.await?;
.await
{
Ok(outcome) => outcome,
Err(err) => {
hard_errors.push(format!("{base_url}: {err}"));
continue;
}
};
has_success |= outcome.has_success;
if let Some(error) = outcome.error {
if is_soft_not_found(&error) {
@@ -512,7 +520,7 @@ async fn fetch_vertex_service_account_models(
("anthropic", claude_transport, "claude:messages"),
] {
let url = build_vertex_service_account_list_url(&base, publisher, None);
let outcome = fetch_vertex_models_from_url(
let outcome = match fetch_vertex_models_from_url(
runtime,
transport,
&url,
@@ -521,7 +529,14 @@ async fn fetch_vertex_service_account_models(
api_format,
Some(("authorization".to_string(), format!("Bearer {token}"))),
)
.await?;
.await
{
Ok(outcome) => outcome,
Err(err) => {
hard_errors.push(format!("{url}: {err}"));
continue;
}
};
has_success |= outcome.has_success;
if let Some(error) = outcome.error {
let labeled = format!("{url}: {error}");
@@ -1339,12 +1354,20 @@ mod tests {
use crate::fetch_models_from_transports;
use crate::transport::ModelFetchTransportRuntime;
type RouteResult = Result<(u16, Value), String>;
type ModelFetchRoute = (String, RouteResult);
struct TestRuntime {
executed_urls: Arc<Mutex<Vec<String>>>,
response_body: Value,
status_code: u16,
}
struct RoutingTestRuntime {
executed_urls: Arc<Mutex<Vec<String>>>,
routes: Vec<ModelFetchRoute>,
}
#[async_trait]
impl ModelFetchTransportRuntime for TestRuntime {
async fn resolve_local_oauth_request_auth(
@@ -1385,6 +1408,57 @@ mod tests {
}
}
#[async_trait]
impl ModelFetchTransportRuntime for RoutingTestRuntime {
async fn resolve_local_oauth_request_auth(
&self,
_transport: &GatewayProviderTransportSnapshot,
) -> Result<Option<aether_provider_transport::LocalResolvedOAuthRequestAuth>, String>
{
Ok(None)
}
async fn resolve_model_fetch_proxy(
&self,
_transport: &GatewayProviderTransportSnapshot,
) -> Option<aether_contracts::ProxySnapshot> {
None
}
async fn execute_model_fetch_execution_plan(
&self,
plan: &aether_contracts::ExecutionPlan,
) -> Result<ExecutionResult, String> {
self.executed_urls
.lock()
.expect("executed_urls lock")
.push(plan.url.clone());
let Some((_, route_result)) = self
.routes
.iter()
.find(|(url_part, _)| plan.url.contains(url_part))
else {
return Err(format!("unexpected models fetch URL {}", plan.url));
};
let (status_code, response_body) = match route_result {
Ok((status_code, response_body)) => (*status_code, response_body.clone()),
Err(err) => return Err(err.clone()),
};
Ok(ExecutionResult {
request_id: plan.request_id.clone(),
candidate_id: plan.candidate_id.clone(),
status_code,
headers: BTreeMap::new(),
body: Some(ResponseBody {
json_body: Some(response_body),
body_bytes_b64: None,
}),
telemetry: None,
error: None,
})
}
}
fn sample_custom_aiplatform_transport() -> GatewayProviderTransportSnapshot {
GatewayProviderTransportSnapshot {
provider: GatewayProviderTransportProvider {
@@ -1507,6 +1581,27 @@ mod tests {
transport
}
fn sample_openai_transport(
endpoint_id: &str,
api_format: &str,
base_url: &str,
) -> GatewayProviderTransportSnapshot {
let mut transport = sample_custom_aiplatform_transport();
transport.provider.provider_type = "custom".to_string();
transport.provider.name = "OpenAI Compat".to_string();
transport.endpoint.id = endpoint_id.to_string();
transport.endpoint.api_format = api_format.to_string();
transport.endpoint.api_family = Some("openai".to_string());
transport.endpoint.endpoint_kind = api_format
.split_once(':')
.map(|(_, endpoint_kind)| endpoint_kind.to_string());
transport.endpoint.base_url = base_url.to_string();
transport.endpoint.custom_path = None;
transport.key.api_formats = Some(vec![api_format.to_string()]);
transport.key.decrypted_api_key = "openai-secret".to_string();
transport
}
#[test]
fn strategy_selection_keeps_codex_on_standard_transport_fetch() {
let strategy = select_model_fetch_strategy(&[sample_codex_transport()])
@@ -1577,6 +1672,115 @@ mod tests {
);
}
#[tokio::test]
async fn standard_transport_merges_successful_endpoint_models_when_one_endpoint_fails() {
let executed_urls = Arc::new(Mutex::new(Vec::new()));
let runtime = RoutingTestRuntime {
executed_urls: Arc::clone(&executed_urls),
routes: vec![
(
"https://bad.example.com/v1/models".to_string(),
Err("connection reset".to_string()),
),
(
"https://chat.example.com/v1/models".to_string(),
Ok((
200,
json!({
"data": [{ "id": "shared-model" }]
}),
)),
),
(
"https://responses.example.com/v1/models".to_string(),
Ok((
200,
json!({
"data": [
{ "id": "shared-model" },
{ "id": "responses-only" }
]
}),
)),
),
],
};
let transports = vec![
sample_openai_transport("endpoint-bad", "openai:chat", "https://bad.example.com"),
sample_openai_transport("endpoint-chat", "openai:chat", "https://chat.example.com"),
sample_openai_transport(
"endpoint-responses",
"openai:responses",
"https://responses.example.com",
),
];
let outcome = fetch_models_from_transports(&runtime, &transports)
.await
.expect("models fetch should keep successful endpoint results");
assert!(outcome.has_success);
assert_eq!(
outcome.fetched_model_ids,
vec!["responses-only", "shared-model"]
);
assert_eq!(outcome.cached_models.len(), 2);
assert_eq!(outcome.errors.len(), 1);
assert!(outcome.errors[0].contains("connection reset"));
let shared_model = outcome
.cached_models
.iter()
.find(|model| model.get("id").and_then(Value::as_str) == Some("shared-model"))
.expect("shared model should be cached once");
assert_eq!(
shared_model.get("api_formats"),
Some(&json!(["openai:chat", "openai:responses"]))
);
}
#[tokio::test]
async fn vertex_models_fetch_continues_when_one_base_url_errors() {
let executed_urls = Arc::new(Mutex::new(Vec::new()));
let runtime = RoutingTestRuntime {
executed_urls: Arc::clone(&executed_urls),
routes: vec![
(
"https://us-central1-aiplatform.googleapis.com/v1beta1/publishers/google/models"
.to_string(),
Err("connect timeout".to_string()),
),
(
"https://aiplatform.googleapis.com/v1beta1/publishers/google/models".to_string(),
Ok((
200,
json!({
"models": [{
"name": "publishers/google/models/gemini-3.1-pro-preview"
}]
}),
)),
),
],
};
let mut failing_transport = sample_custom_aiplatform_transport();
failing_transport.endpoint.base_url =
"https://us-central1-aiplatform.googleapis.com".to_string();
let mut successful_transport = sample_custom_aiplatform_transport();
successful_transport.endpoint.id = "endpoint-2".to_string();
successful_transport.endpoint.base_url = "https://aiplatform.googleapis.com".to_string();
let outcome =
fetch_models_from_transports(&runtime, &[failing_transport, successful_transport])
.await
.expect("vertex models fetch should keep successful base URL results");
assert!(outcome.has_success);
assert_eq!(outcome.fetched_model_ids, vec!["gemini-3.1-pro-preview"]);
assert_eq!(outcome.cached_models.len(), 1);
assert_eq!(outcome.errors.len(), 1);
assert!(outcome.errors[0].contains("connect timeout"));
}
#[test]
fn vertex_model_fetch_uses_model_garden_list_endpoint() {
assert_eq!(