mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
fix(usage): 修复 Gemini total 重复累加 cache_read 并补充测试
- write.rs: Gemini usage 提取 total 时不再叠加 cachedContentTokenCount - 补充 Claude 大 cache_read 场景下 input_tokens 不被扣减的测试 - 补充前端 getEffectiveInputTokens 对 Claude 格式不减 cache_read 的测试
This commit is contained in:
@@ -1340,6 +1340,31 @@ mod tests {
|
||||
assert_eq!(payload["cache_creation_ephemeral_1h_input_tokens"], 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_usage_payload_keeps_claude_effective_input_when_cache_read_is_large() {
|
||||
let item = StoredRequestUsageAudit {
|
||||
provider_name: "Claude".to_string(),
|
||||
model: "claude-sonnet-4-5".to_string(),
|
||||
api_format: Some("claude:chat".to_string()),
|
||||
api_family: Some("claude".to_string()),
|
||||
endpoint_api_format: Some("claude:chat".to_string()),
|
||||
provider_api_family: Some("claude".to_string()),
|
||||
input_tokens: 4941,
|
||||
output_tokens: 973,
|
||||
total_tokens: 59474,
|
||||
cache_creation_input_tokens: 687,
|
||||
cache_read_input_tokens: 52873,
|
||||
..sample_usage("completed")
|
||||
};
|
||||
|
||||
let payload = build_users_me_usage_record_payload(&item, false, &BTreeMap::new(), false);
|
||||
|
||||
assert_eq!(payload["input_tokens"], 4941);
|
||||
assert_eq!(payload["effective_input_tokens"], 4941);
|
||||
assert_eq!(payload["cache_creation_input_tokens"], 687);
|
||||
assert_eq!(payload["cache_read_input_tokens"], 52873);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_usage_active_pending_with_failure_signal_is_not_active() {
|
||||
let item = StoredRequestUsageAudit {
|
||||
|
||||
@@ -418,6 +418,54 @@ mod tests {
|
||||
assert_eq!(usage.cache_read_tokens, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maps_claude_usage_with_large_cache_read_tokens_without_subtracting_input() {
|
||||
let usage = map_usage(
|
||||
&serde_json::json!({
|
||||
"input_tokens": 4941,
|
||||
"cache_creation_input_tokens": 687,
|
||||
"cache_read_input_tokens": 52873,
|
||||
"output_tokens": 973
|
||||
}),
|
||||
"claude:chat",
|
||||
);
|
||||
|
||||
assert_eq!(usage.input_tokens, 4941);
|
||||
assert_eq!(usage.cache_creation_tokens, 687);
|
||||
assert_eq!(usage.cache_read_tokens, 52873);
|
||||
assert_eq!(usage.output_tokens, 973);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maps_claude_usage_with_cache_creation_total_and_zero_ttl_breakdown() {
|
||||
let usage = map_usage(
|
||||
&serde_json::json!({
|
||||
"cache_creation": {
|
||||
"ephemeral_1h_input_tokens": 0,
|
||||
"ephemeral_5m_input_tokens": 0
|
||||
},
|
||||
"cache_creation_input_tokens": 2051,
|
||||
"cache_read_input_tokens": 2051,
|
||||
"inference_geo": "inference_geo",
|
||||
"input_tokens": 2095,
|
||||
"output_tokens": 503,
|
||||
"server_tool_use": {
|
||||
"web_fetch_requests": 2,
|
||||
"web_search_requests": 0
|
||||
},
|
||||
"service_tier": "standard"
|
||||
}),
|
||||
"claude:chat",
|
||||
);
|
||||
|
||||
assert_eq!(usage.input_tokens, 2095);
|
||||
assert_eq!(usage.cache_creation_tokens, 2051);
|
||||
assert_eq!(usage.cache_creation_ephemeral_5m_tokens, 0);
|
||||
assert_eq!(usage.cache_creation_ephemeral_1h_tokens, 0);
|
||||
assert_eq!(usage.cache_read_tokens, 2051);
|
||||
assert_eq!(usage.output_tokens, 503);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maps_claude_usage_with_ephemeral_cache_breakdown() {
|
||||
let usage = map_usage(
|
||||
|
||||
@@ -2180,16 +2180,7 @@ fn extract_token_counts_from_json(value: &Value) -> Option<(u64, u64, u64)> {
|
||||
.get("totalTokenCount")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or(input + output);
|
||||
let cache_read = usage
|
||||
.get("cachedContentTokenCount")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or_default();
|
||||
let total = if cache_read > 0 {
|
||||
input.saturating_add(output).saturating_add(cache_read)
|
||||
} else {
|
||||
raw_total
|
||||
};
|
||||
return Some((input, output, total));
|
||||
return Some((input, output, raw_total));
|
||||
}
|
||||
|
||||
if let Some(chunks) = value.get("chunks").and_then(Value::as_array) {
|
||||
@@ -2353,6 +2344,21 @@ mod tests {
|
||||
assert_eq!(tokens, (6, 20, 41883));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_gemini_usage_tokens_without_adding_cached_content_twice() {
|
||||
let tokens = extract_token_counts_from_json(&json!({
|
||||
"usageMetadata": {
|
||||
"promptTokenCount": 14,
|
||||
"candidatesTokenCount": 6,
|
||||
"cachedContentTokenCount": 2,
|
||||
"totalTokenCount": 20
|
||||
}
|
||||
}))
|
||||
.expect("tokens should exist");
|
||||
|
||||
assert_eq!(tokens, (14, 6, 20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_usage_tokens_from_last_matching_chunk() {
|
||||
let tokens = extract_token_counts_from_json(&json!({
|
||||
|
||||
@@ -26,4 +26,14 @@ describe('usage token normalization', () => {
|
||||
api_format: 'openai:chat',
|
||||
})).toBe(80)
|
||||
})
|
||||
|
||||
it('does not subtract cache read tokens for Claude usage', () => {
|
||||
expect(getEffectiveInputTokens({
|
||||
input_tokens: 4941,
|
||||
cache_creation_input_tokens: 687,
|
||||
cache_read_input_tokens: 52873,
|
||||
output_tokens: 973,
|
||||
api_format: 'claude:chat',
|
||||
})).toBe(4941)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user