支持 Gemini Interactions 与 Antigravity 配额精细化

This commit is contained in:
MMEXA
2026-07-08 22:34:29 +08:00
parent 1ca83ca3fb
commit 80a6579766
41 changed files with 1072 additions and 225 deletions
+113 -1
View File
@@ -223,13 +223,19 @@ pub fn parse_antigravity_usage_response(
) -> Option<serde_json::Value> {
let models = value.get("models")?.as_object()?;
let mut quota_by_model = serde_json::Map::new();
let mut opaque_key_display_index = 1usize;
for (model_id, model_value) in models {
let mut payload = serde_json::Map::new();
if let Some(display_name) = coerce_json_string(
let upstream_display_name = coerce_json_string(
model_value
.get("displayName")
.or_else(|| model_value.get("display_name")),
);
if let Some(display_name) = friendly_quota_display_name(
upstream_display_name,
model_id,
&mut opaque_key_display_index,
) {
payload.insert("display_name".to_string(), json!(display_name));
}
@@ -270,6 +276,7 @@ pub fn parse_gemini_cli_retrieve_user_quota_response(
) -> Option<serde_json::Value> {
let buckets = value.get("buckets")?.as_array()?;
let mut quota_by_model = serde_json::Map::new();
let mut opaque_key_display_index = 1usize;
for bucket in buckets {
if !bucket.is_object() {
@@ -320,6 +327,12 @@ pub fn parse_gemini_cli_retrieve_user_quota_response(
.or_else(|| model_id.clone())
.or_else(|| token_type.clone())
.unwrap_or_else(|| quota_key.clone());
let display_name = friendly_quota_display_name(
Some(display_name),
&quota_key,
&mut opaque_key_display_index,
)
.unwrap_or_else(|| quota_key.clone());
let remaining_fraction = first_json_f64_by_paths(
bucket,
&[
@@ -456,6 +469,38 @@ pub fn parse_gemini_cli_retrieve_user_quota_response(
}))
}
fn is_opaque_reset_credit_quota_identifier(value: &str) -> bool {
value.trim().starts_with("RateLimitResetCredit_")
}
fn friendly_quota_display_name(
candidate: Option<String>,
quota_key: &str,
opaque_key_display_index: &mut usize,
) -> Option<String> {
let candidate = candidate
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty());
if let Some(candidate) = candidate.as_deref() {
if !is_opaque_reset_credit_quota_identifier(candidate) {
return Some(candidate.to_string());
}
}
if is_opaque_reset_credit_quota_identifier(quota_key)
|| candidate
.as_deref()
.is_some_and(is_opaque_reset_credit_quota_identifier)
{
let label = format!("Key-{}", *opaque_key_display_index);
*opaque_key_display_index += 1;
return Some(label);
}
candidate
}
pub fn parse_gemini_cli_v1internal_credits_response(
value: &serde_json::Value,
updated_at_unix_secs: u64,
@@ -2001,6 +2046,7 @@ mod tests {
codex_build_invalid_state, codex_runtime_invalid_reason,
normalize_codex_reset_credit_consume_outcome, parse_chatgpt_web_conversation_init_response,
parse_codex_backend_me_response, parse_codex_wham_reset_credits_detail_response,
parse_antigravity_usage_response,
parse_codex_wham_usage_response, parse_gemini_cli_retrieve_user_quota_response,
parse_gemini_cli_v1internal_credits_response, parse_windsurf_model_configs_response,
parse_windsurf_rate_limit_response, parse_windsurf_user_status_response,
@@ -2538,6 +2584,40 @@ mod tests {
assert!(parsed.get("secondary_used_percent").is_none());
}
#[test]
fn parses_antigravity_usage_response_labels_opaque_reset_credit_keys() {
let parsed = parse_antigravity_usage_response(
&json!({
"models": {
"RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb": {
"quotaInfo": {
"remainingFraction": 0.75,
"resetTime": "2030-01-01T00:00:00Z"
}
},
"gemini-3-pro-preview": {
"displayName": "Gemini 3 Pro Preview",
"quotaInfo": {
"remainingFraction": 0.25
}
}
}
}),
1_777_000_000,
)
.expect("antigravity quota should parse");
assert_eq!(
parsed["models"]["RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb"]
["display_name"],
json!("Key-1")
);
assert_eq!(
parsed["models"]["gemini-3-pro-preview"]["display_name"],
json!("Gemini 3 Pro Preview")
);
}
#[test]
fn parses_gemini_cli_retrieve_user_quota_buckets() {
let parsed = parse_gemini_cli_retrieve_user_quota_response(
@@ -2595,6 +2675,38 @@ mod tests {
);
}
#[test]
fn parses_gemini_cli_quota_buckets_labels_opaque_reset_credit_keys() {
let parsed = parse_gemini_cli_retrieve_user_quota_response(
&json!({
"buckets": [
{
"tokenType": "RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb",
"remainingFraction": 0.5
},
{
"modelId": "RateLimitResetCredit_d18b8aac4ec2472697ad747a14975ac8",
"displayName": "RateLimitResetCredit_d18b8aac4ec2472697ad747a14975ac8",
"remainingFraction": 0.25
}
]
}),
1_777_000_000,
)
.expect("gemini cli quota should parse");
assert_eq!(
parsed["quota_by_model"]["RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb"]
["display_name"],
json!("Key-1")
);
assert_eq!(
parsed["quota_by_model"]["RateLimitResetCredit_d18b8aac4ec2472697ad747a14975ac8"]
["display_name"],
json!("Key-2")
);
}
#[test]
fn parses_gemini_cli_v1internal_credits() {
let parsed = parse_gemini_cli_v1internal_credits_response(
+6
View File
@@ -805,6 +805,12 @@ const ADMIN_API_FORMAT_DEFINITIONS: &[AdminApiFormatDefinition] = &[
default_path: "/v1beta/models/{model}:{action}",
aliases: &["gemini", "google", "vertex"],
},
AdminApiFormatDefinition {
value: "gemini:interactions",
label: "Gemini Interactions",
default_path: "/v1/interactions",
aliases: &["gemini_interactions", "interactions"],
},
AdminApiFormatDefinition {
value: "gemini:embedding",
label: "Gemini Embedding",