mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
fix(usage): 修复缓存创建 token 展示并在总量中计入缓存组件
- gateway/admin 用量载荷在 cache_creation_input_tokens 为 0 时回填 ephemeral_5m/1h 合计 - 标准化用量写入时将 cache_creation/cache_read token 计入 total_tokens - 前端列表与轮询同步新增分类字段,修复缓存 token 列显示
This commit is contained in:
@@ -1366,10 +1366,7 @@ fn apply_standardized_usage(
|
||||
if usage.cache_read_tokens > 0 {
|
||||
data.cache_read_input_tokens = Some(usage.cache_read_tokens as u64);
|
||||
}
|
||||
let total_tokens = usage
|
||||
.input_tokens
|
||||
.saturating_add(usage.output_tokens)
|
||||
.max(0) as u64;
|
||||
let total_tokens = standardized_usage_total_tokens(&usage);
|
||||
if total_tokens > 0 {
|
||||
data.total_tokens = Some(total_tokens);
|
||||
}
|
||||
@@ -1396,15 +1393,23 @@ fn apply_standardized_usage_seed(usage: &StandardizedUsage, data: &mut UsageEven
|
||||
if usage.cache_read_tokens > 0 {
|
||||
data.cache_read_input_tokens = Some(usage.cache_read_tokens as u64);
|
||||
}
|
||||
let total_tokens = usage
|
||||
.input_tokens
|
||||
.saturating_add(usage.output_tokens)
|
||||
.max(0) as u64;
|
||||
let total_tokens = standardized_usage_total_tokens(usage);
|
||||
if total_tokens > 0 {
|
||||
data.total_tokens = Some(total_tokens);
|
||||
}
|
||||
}
|
||||
|
||||
fn standardized_usage_total_tokens(usage: &StandardizedUsage) -> u64 {
|
||||
positive_usage_component(usage.input_tokens)
|
||||
.saturating_add(positive_usage_component(usage.output_tokens))
|
||||
.saturating_add(positive_usage_component(usage.cache_creation_tokens))
|
||||
.saturating_add(positive_usage_component(usage.cache_read_tokens))
|
||||
}
|
||||
|
||||
fn positive_usage_component(value: i64) -> u64 {
|
||||
value.max(0) as u64
|
||||
}
|
||||
|
||||
fn headers_to_json(headers: &BTreeMap<String, String>) -> Value {
|
||||
Value::Object(Map::from_iter(headers.iter().map(|(key, value)| {
|
||||
(key.clone(), Value::String(mask_header_value(key, value)))
|
||||
@@ -1661,10 +1666,20 @@ fn extract_token_counts_from_json(value: &Value) -> Option<(u64, u64, u64)> {
|
||||
.or_else(|| usage.get("completion_tokens"))
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or_default();
|
||||
let total = usage
|
||||
let raw_total = usage
|
||||
.get("total_tokens")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or(input + output);
|
||||
let cache_creation = extract_cache_creation_tokens_from_usage_object(usage);
|
||||
let cache_read = extract_cache_read_tokens_from_usage_object(usage);
|
||||
let total = if cache_creation > 0 || cache_read > 0 {
|
||||
input
|
||||
.saturating_add(output)
|
||||
.saturating_add(cache_creation)
|
||||
.saturating_add(cache_read)
|
||||
} else {
|
||||
raw_total
|
||||
};
|
||||
return Some((input, output, total));
|
||||
}
|
||||
|
||||
@@ -1677,10 +1692,19 @@ fn extract_token_counts_from_json(value: &Value) -> Option<(u64, u64, u64)> {
|
||||
.get("candidatesTokenCount")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or_default();
|
||||
let total = usage
|
||||
let raw_total = usage
|
||||
.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));
|
||||
}
|
||||
|
||||
@@ -1703,6 +1727,59 @@ fn extract_token_counts_from_json(value: &Value) -> Option<(u64, u64, u64)> {
|
||||
None
|
||||
}
|
||||
|
||||
fn extract_cache_creation_tokens_from_usage_object(usage: &serde_json::Map<String, Value>) -> u64 {
|
||||
let direct = usage
|
||||
.get("cache_creation_input_tokens")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or_default();
|
||||
if direct > 0 {
|
||||
return direct;
|
||||
}
|
||||
|
||||
let breakdown = usage
|
||||
.get("cache_creation")
|
||||
.and_then(Value::as_object)
|
||||
.map(|cache_creation| {
|
||||
cache_creation
|
||||
.get("ephemeral_5m_input_tokens")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or_default()
|
||||
.saturating_add(
|
||||
cache_creation
|
||||
.get("ephemeral_1h_input_tokens")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
if breakdown > 0 {
|
||||
return breakdown;
|
||||
}
|
||||
|
||||
usage
|
||||
.get("input_tokens_details")
|
||||
.or_else(|| usage.get("prompt_tokens_details"))
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|details| details.get("cached_creation_tokens"))
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn extract_cache_read_tokens_from_usage_object(usage: &serde_json::Map<String, Value>) -> u64 {
|
||||
usage
|
||||
.get("cache_read_input_tokens")
|
||||
.and_then(Value::as_u64)
|
||||
.or_else(|| {
|
||||
usage
|
||||
.get("input_tokens_details")
|
||||
.or_else(|| usage.get("prompt_tokens_details"))
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|details| details.get("cached_tokens"))
|
||||
.and_then(Value::as_u64)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn empty_to_none(value: Option<String>) -> Option<String> {
|
||||
value
|
||||
.map(|value| value.trim().to_string())
|
||||
@@ -1746,6 +1823,39 @@ mod tests {
|
||||
assert_eq!(tokens, (3, 5, 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_openai_usage_tokens_with_cache_components() {
|
||||
let tokens = extract_token_counts_from_json(&json!({
|
||||
"usage": {
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 5,
|
||||
"total_tokens": 8,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 2,
|
||||
"cached_creation_tokens": 1
|
||||
}
|
||||
}
|
||||
}))
|
||||
.expect("tokens should exist");
|
||||
|
||||
assert_eq!(tokens, (3, 5, 11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_claude_usage_tokens_with_cache_components() {
|
||||
let tokens = extract_token_counts_from_json(&json!({
|
||||
"usage": {
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 20,
|
||||
"cache_creation_input_tokens": 41857,
|
||||
"cache_read_input_tokens": 0
|
||||
}
|
||||
}))
|
||||
.expect("tokens should exist");
|
||||
|
||||
assert_eq!(tokens, (6, 20, 41883));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_upsert_record_from_terminal_event() {
|
||||
let record = build_upsert_usage_record_from_event(&UsageEvent {
|
||||
@@ -2117,7 +2227,7 @@ mod tests {
|
||||
|
||||
assert_eq!(event.data.input_tokens, Some(3));
|
||||
assert_eq!(event.data.output_tokens, Some(5));
|
||||
assert_eq!(event.data.total_tokens, Some(8));
|
||||
assert_eq!(event.data.total_tokens, Some(11));
|
||||
assert_eq!(event.data.cache_creation_input_tokens, Some(1));
|
||||
assert_eq!(event.data.cache_read_input_tokens, Some(2));
|
||||
assert_eq!(
|
||||
@@ -2531,7 +2641,7 @@ mod tests {
|
||||
|
||||
assert_eq!(event.data.input_tokens, Some(3));
|
||||
assert_eq!(event.data.output_tokens, Some(5));
|
||||
assert_eq!(event.data.total_tokens, Some(8));
|
||||
assert_eq!(event.data.total_tokens, Some(11));
|
||||
assert_eq!(event.data.cache_creation_input_tokens, Some(1));
|
||||
assert_eq!(event.data.cache_read_input_tokens, Some(2));
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user