mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
fix(proxy): correct usage accounting on format-conversion paths
Audited all proxy format-conversion paths (Chat<->Message, Chat<->Response, Gemini<->Message) for usage/cache metering. Five issues found and fixed. The dedup mechanism (request_id PK, proxy/session source isolation) is untouched, so no double-counting is introduced. - A (Claude + openai_chat, streaming): inject stream_options.include_usage so OpenAI-compatible upstreams emit usage in the SSE tail. Without it the converted Anthropic message_delta was all-zero and the whole request's input/output/cache was dropped. Same root cause as the already-fixed Codex Chat path; the injection is extracted into a shared helper (transform::inject_openai_stream_include_usage) reused by both paths. - C (Claude + gemini_native): subtract cachedContentTokenCount from input_tokens in build_anthropic_usage so input becomes fresh input (Anthropic semantics). Previously the cache-hit tokens were billed twice because this path meters as app_type="claude" (input_includes_cache_read = false) while Gemini's promptTokenCount includes the cache. - D (Codex + openai_chat, streaming): gate log_usage on has_billable_tokens() to skip the synthetic all-zero usage the converter emits when a non-compliant upstream omits usage, preventing empty-row request-count inflation. - P2 (from_claude_stream_events): use has_billable_tokens() for the return gate instead of input>0||output>0, so a fully-cached streamed request (cache_read>0, input==output==0) is still recorded. Affects all Claude-streaming paths, not just Gemini. - P3 (Codex Chat->Responses, non-streaming): apply the same has_billable_tokens() filter the streaming branch got, since the synthesized all-zero usage makes from_codex_response return Some and bypass the `if let Some` guard. Add TokenUsage::has_billable_tokens() as the unified predicate. New tests cover include_usage injection, gemini input subtraction, the gate itself, cache-only stream recording, and synthetic all-zero codex usage. Full lib suite: 1569 passed.
This commit is contained in:
@@ -336,21 +336,8 @@ pub fn responses_to_chat_completions_with_reasoning(
|
||||
// include_usage 才会在末尾吐 usage chunk。Codex CLI 用 Responses 协议、
|
||||
// 自身不带 stream_options,缺这一注入会导致 kimi/MiniMax 等第三方流式请求的
|
||||
// token/成本/缓存命中率全部漏记(input/output/cache 全为 0)。
|
||||
let is_stream = result
|
||||
.get("stream")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
if is_stream {
|
||||
match result.get_mut("stream_options") {
|
||||
// 保留客户端可能透传的其它 stream_options 字段,仅补 include_usage。
|
||||
Some(Value::Object(opts)) => {
|
||||
opts.insert("include_usage".to_string(), json!(true));
|
||||
}
|
||||
_ => {
|
||||
result["stream_options"] = json!({ "include_usage": true });
|
||||
}
|
||||
}
|
||||
}
|
||||
// 与 Claude→openai_chat 路径共用同一 helper,保证两个客户端方向一致。
|
||||
super::transform::inject_openai_stream_include_usage(&mut result);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user