Skip to content

Commit edc597a

Browse files
authored
修复 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期望值匹配去重逻辑
1 parent 955ea26 commit edc597a

2 files changed

Lines changed: 14 additions & 8 deletions

File tree

src-tauri/src/proxy/handlers.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ async fn handle_claude_transform(
333333
let usage_collector = if usage_logging_enabled(state) {
334334
let state = state.clone();
335335
let provider_id = ctx.provider.id.clone();
336-
let model = ctx.request_model.clone();
336+
let request_model = ctx.request_model.clone();
337337
let status_code = status.as_u16();
338338
let start_time = ctx.start_time;
339339
let session_id = ctx.session_id.clone();
@@ -343,19 +343,20 @@ async fn handle_claude_transform(
343343
Some(claude_stream_usage_event_filter),
344344
move |events, first_token_ms| {
345345
if let Some(usage) = TokenUsage::from_claude_stream_events(&events) {
346+
let model = usage.model.clone().unwrap_or(request_model.clone());
346347
let latency_ms = start_time.elapsed().as_millis() as u64;
347348
let state = state.clone();
348349
let provider_id = provider_id.clone();
349-
let model = model.clone();
350350
let session_id = session_id.clone();
351+
let request_model = request_model.clone();
351352

352353
tokio::spawn(async move {
353354
log_usage(
354355
&state,
355356
&provider_id,
356357
"claude",
357358
&model,
358-
&model,
359+
&request_model,
359360
usage,
360361
latency_ms,
361362
first_token_ms,

src-tauri/src/proxy/providers/streaming.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,14 @@ struct ToolBlockState {
100100
const INFINITE_WHITESPACE_THRESHOLD: usize = 500;
101101

102102
fn build_anthropic_usage_json(usage: &Usage) -> Value {
103+
// OpenAI prompt_tokens 含缓存,Anthropic input_tokens 不含,需减去
104+
let cached = extract_cache_read_tokens(usage).unwrap_or(0);
105+
let input_tokens = usage.prompt_tokens.saturating_sub(cached);
103106
let mut usage_json = json!({
104-
"input_tokens": usage.prompt_tokens,
107+
"input_tokens": input_tokens,
105108
"output_tokens": usage.completion_tokens
106109
});
107-
if let Some(cached) = extract_cache_read_tokens(usage) {
110+
if cached > 0 {
108111
usage_json["cache_read_input_tokens"] = json!(cached);
109112
}
110113
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>(
223226
"output_tokens": 0
224227
});
225228
if let Some(u) = &chunk.usage {
226-
start_usage["input_tokens"] = json!(u.prompt_tokens);
227-
if let Some(cached) = extract_cache_read_tokens(u) {
229+
let cached = extract_cache_read_tokens(u).unwrap_or(0);
230+
let input = u.prompt_tokens.saturating_sub(cached);
231+
start_usage["input_tokens"] = json!(input);
232+
if cached > 0 {
228233
start_usage["cache_read_input_tokens"] = json!(cached);
229234
}
230235
if let Some(created) = u.cache_creation_input_tokens {
@@ -1022,7 +1027,7 @@ mod tests {
10221027
message_delta
10231028
.pointer("/usage/input_tokens")
10241029
.and_then(|v| v.as_u64()),
1025-
Some(13312)
1030+
Some(13212)
10261031
);
10271032
assert_eq!(
10281033
message_delta

0 commit comments

Comments
 (0)