修复 Completions转Anthropic时不记录实际返回模型、Input token记录错误问题 (#2774)

* fix(proxy): 修复completions转claude格式流式响应未记录实际命中模型

* style: cargo fmt fix

* fix(proxy): 修复completions转claude格式时input与cache_read重复计费

* fix(proxy): 修复完全缓存命中时input_tokens计算错误

* test: 更新input_tokens期望值匹配去重逻辑
This commit is contained in:
LaoYueHanNi
2026-06-09 20:30:05 +08:00
committed by GitHub
parent 955ea26da9
commit edc597ab23
2 changed files with 14 additions and 8 deletions
+4 -3
View File
@@ -333,7 +333,7 @@ async fn handle_claude_transform(
let usage_collector = if usage_logging_enabled(state) {
let state = state.clone();
let provider_id = ctx.provider.id.clone();
let model = ctx.request_model.clone();
let request_model = ctx.request_model.clone();
let status_code = status.as_u16();
let start_time = ctx.start_time;
let session_id = ctx.session_id.clone();
@@ -343,11 +343,12 @@ async fn handle_claude_transform(
Some(claude_stream_usage_event_filter),
move |events, first_token_ms| {
if let Some(usage) = TokenUsage::from_claude_stream_events(&events) {
let model = usage.model.clone().unwrap_or(request_model.clone());
let latency_ms = start_time.elapsed().as_millis() as u64;
let state = state.clone();
let provider_id = provider_id.clone();
let model = model.clone();
let session_id = session_id.clone();
let request_model = request_model.clone();
tokio::spawn(async move {
log_usage(
@@ -355,7 +356,7 @@ async fn handle_claude_transform(
&provider_id,
"claude",
&model,
&model,
&request_model,
usage,
latency_ms,
first_token_ms,
+10 -5
View File
@@ -100,11 +100,14 @@ struct ToolBlockState {
const INFINITE_WHITESPACE_THRESHOLD: usize = 500;
fn build_anthropic_usage_json(usage: &Usage) -> Value {
// OpenAI prompt_tokens 含缓存,Anthropic input_tokens 不含,需减去
let cached = extract_cache_read_tokens(usage).unwrap_or(0);
let input_tokens = usage.prompt_tokens.saturating_sub(cached);
let mut usage_json = json!({
"input_tokens": usage.prompt_tokens,
"input_tokens": input_tokens,
"output_tokens": usage.completion_tokens
});
if let Some(cached) = extract_cache_read_tokens(usage) {
if cached > 0 {
usage_json["cache_read_input_tokens"] = json!(cached);
}
if let Some(created) = usage.cache_creation_input_tokens {
@@ -223,8 +226,10 @@ pub fn create_anthropic_sse_stream<E: std::error::Error + Send + 'static>(
"output_tokens": 0
});
if let Some(u) = &chunk.usage {
start_usage["input_tokens"] = json!(u.prompt_tokens);
if let Some(cached) = extract_cache_read_tokens(u) {
let cached = extract_cache_read_tokens(u).unwrap_or(0);
let input = u.prompt_tokens.saturating_sub(cached);
start_usage["input_tokens"] = json!(input);
if cached > 0 {
start_usage["cache_read_input_tokens"] = json!(cached);
}
if let Some(created) = u.cache_creation_input_tokens {
@@ -1022,7 +1027,7 @@ mod tests {
message_delta
.pointer("/usage/input_tokens")
.and_then(|v| v.as_u64()),
Some(13312)
Some(13212)
);
assert_eq!(
message_delta