feat(usage): 在 CanonicalUsage 中追加缓存 token 字段并在标准化流程透传

- 扩展 CanonicalUsage 支持 cache_creation/cache_read 及 5m/1h 明细
- OpenAI/Claude/Gemini 解析器提取缓存 token 并计入 total
- usage_mapper 补齐 Claude 缓存字段映射
- 新增 gateway pricing 集成测试覆盖三家同步/流式缓存计费
This commit is contained in:
fawney19
2026-04-19 03:55:55 +08:00
parent 2f487fda66
commit 0329b7b784
7 changed files with 1443 additions and 7 deletions

View File

@@ -66,6 +66,22 @@ fn base_mapping(api_format: &str) -> BTreeMap<String, String> {
mapping.insert("completion_tokens".to_string(), "output_tokens".to_string());
mapping.insert("input_tokens".to_string(), "input_tokens".to_string());
mapping.insert("output_tokens".to_string(), "output_tokens".to_string());
mapping.insert(
"cache_creation_input_tokens".to_string(),
"cache_creation_tokens".to_string(),
);
mapping.insert(
"cache_creation.ephemeral_5m_input_tokens".to_string(),
"cache_creation_ephemeral_5m_tokens".to_string(),
);
mapping.insert(
"cache_creation.ephemeral_1h_input_tokens".to_string(),
"cache_creation_ephemeral_1h_tokens".to_string(),
);
mapping.insert(
"cache_read_input_tokens".to_string(),
"cache_read_tokens".to_string(),
);
mapping.insert(
"prompt_tokens_details.cached_tokens".to_string(),
"cache_read_tokens".to_string(),
@@ -252,6 +268,26 @@ mod tests {
assert_eq!(usage.reasoning_tokens, 1);
}
#[test]
fn maps_openai_responses_with_top_level_cache_fields() {
let usage = map_usage_from_response(
&serde_json::json!({
"usage": {
"input_tokens": 6,
"output_tokens": 20,
"cache_creation_input_tokens": 42_262,
"cache_read_input_tokens": 0
}
}),
"openai:chat",
);
assert_eq!(usage.input_tokens, 6);
assert_eq!(usage.output_tokens, 20);
assert_eq!(usage.cache_creation_tokens, 42_262);
assert_eq!(usage.cache_read_tokens, 0);
}
#[test]
fn maps_openai_responses_usage_from_stream_chunks() {
let usage = map_usage_from_response(