fix(llm): read input tokens from the frame that carries them
The first judge-spend rows recorded by 248948c came back with input_tokens = 0
on both passes of mission 01a09dfc — 7 and 9 requests, 5940 and 2109 output
tokens, and nothing on the side that actually empties the plan. Probed z.ai's
Anthropic-compatible stream directly: `message_start` carries
`"input_tokens": 0`, and the real figure arrives in `message_delta.usage`
beside output_tokens. Anthropic proper does it the other way round, which is
the shape the parser was written for.
A nonzero figure in the delta now wins; otherwise the start's figure stands,
so the Anthropic path is byte-for-byte unchanged. The decision is a pure
function with the three shapes as its test — including a delta that says 0,
which must not erase what the start said.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
This commit is contained in:
co-authored by
Claude Opus 5
parent
248948cc84
commit
736b6a9a82
@@ -242,6 +242,14 @@ impl LlmProvider for AnthropicProvider {
|
||||
data["message"]["usage"]["input_tokens"].as_u64().unwrap_or(0) as u32;
|
||||
}
|
||||
"message_delta" => {
|
||||
// Anthropic reports input_tokens in `message_start`;
|
||||
// z.ai's Anthropic-compatible endpoint sends 0 there and
|
||||
// the real count here, beside output_tokens (measured
|
||||
// 2026-09-14: start `input_tokens: 0`, delta
|
||||
// `input_tokens: 14`). Prefer the delta's figure when
|
||||
// it carries one — the judge's input, which is the
|
||||
// number that empties a plan, read as zero until then.
|
||||
input_tokens = input_tokens_after_delta(input_tokens, &data["usage"]);
|
||||
if let Some(out) = data["usage"]["output_tokens"].as_u64() {
|
||||
yield LlmEvent::Usage {
|
||||
input_tokens,
|
||||
@@ -266,10 +274,38 @@ impl LlmProvider for AnthropicProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/// The input-token figure to report once a `message_delta` has arrived.
|
||||
///
|
||||
/// `start` is what `message_start` said. Anthropic puts the real count there
|
||||
/// and nothing in the delta; z.ai's Anthropic-compatible endpoint puts 0 there
|
||||
/// and the real count in the delta's `usage` (measured 2026-09-14). A nonzero
|
||||
/// delta figure wins; anything else keeps what `message_start` said, so the
|
||||
/// Anthropic path is unchanged.
|
||||
fn input_tokens_after_delta(start: u32, delta_usage: &serde_json::Value) -> u32 {
|
||||
match delta_usage["input_tokens"].as_u64() {
|
||||
Some(n) if n > 0 => n as u32,
|
||||
_ => start,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// z.ai reports input in the delta; Anthropic reports it at the start.
|
||||
/// Before this the judge's input tokens — the number that empties a
|
||||
/// plan — read as zero on every GLM verdict.
|
||||
#[test]
|
||||
fn input_tokens_come_from_whichever_frame_carries_them() {
|
||||
use serde_json::json;
|
||||
// z.ai shape: start says 0, delta says 14.
|
||||
assert_eq!(input_tokens_after_delta(0, &json!({"input_tokens": 14, "output_tokens": 16})), 14);
|
||||
// Anthropic shape: start said 812, delta has no input figure.
|
||||
assert_eq!(input_tokens_after_delta(812, &json!({"output_tokens": 40})), 812);
|
||||
// A delta that explicitly says 0 must not erase the start's figure.
|
||||
assert_eq!(input_tokens_after_delta(812, &json!({"input_tokens": 0, "output_tokens": 40})), 812);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn setup_tokens_are_distinguished_from_api_keys() {
|
||||
assert!(is_setup_token("sk-ant-oat01-abc"));
|
||||
|
||||
Reference in New Issue
Block a user