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;
|
data["message"]["usage"]["input_tokens"].as_u64().unwrap_or(0) as u32;
|
||||||
}
|
}
|
||||||
"message_delta" => {
|
"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() {
|
if let Some(out) = data["usage"]["output_tokens"].as_u64() {
|
||||||
yield LlmEvent::Usage {
|
yield LlmEvent::Usage {
|
||||||
input_tokens,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn setup_tokens_are_distinguished_from_api_keys() {
|
fn setup_tokens_are_distinguished_from_api_keys() {
|
||||||
assert!(is_setup_token("sk-ant-oat01-abc"));
|
assert!(is_setup_token("sk-ant-oat01-abc"));
|
||||||
|
|||||||
Reference in New Issue
Block a user