use crate::brain::provider::OpenAIProvider;
use crate::brain::provider::Provider;
use crate::brain::provider::nonstream_compat::synthesize_stream_events;
use crate::brain::provider::types::*;
use crate::brain::provider::{LLMRequest, Message, StreamEvent};
use crate::usage::pricing::{PricingConfig, PricingEntry, ProviderBlock};
use std::collections::HashMap;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
use tokio::time::timeout;
const FLASH_GROSS_INPUT: u32 = 22_020_984;
const FLASH_OUTPUT: u32 = 107_370;
const FLASH_CACHE_READ: u32 = 21_705_152;
const FLASH_NET_INPUT: u32 = FLASH_GROSS_INPUT - FLASH_CACHE_READ;
const CALL_GROSS_INPUT: u32 = 34_906;
const CALL_OUTPUT: u32 = 71;
const CALL_CACHE_READ: u32 = 34_432;
const CALL_NET_INPUT: u32 = CALL_GROSS_INPUT - CALL_CACHE_READ;
fn zai_card() -> PricingConfig {
let mut providers = HashMap::new();
providers.insert(
"zai".to_string(),
ProviderBlock {
entries: vec![PricingEntry {
prefix: "glm-5.3-flash".to_string(),
input_per_m: 0.15,
output_per_m: 0.50,
cache_write_per_m: Some(0.0),
cache_read_per_m: Some(0.03),
}],
},
);
PricingConfig { providers }
}
#[test]
fn cached_prefix_billed_once_reproduces_the_real_flash_run() {
let card = zai_card();
let netted = card.calculate_cost_with_cache(
"glm-5.3-flash",
FLASH_NET_INPUT,
FLASH_OUTPUT,
0,
FLASH_CACHE_READ,
);
assert!(
(netted - 0.752_214_36).abs() < 1e-6,
"netted cost drifted: {netted}"
);
let double_counted = card.calculate_cost_with_cache(
"glm-5.3-flash",
FLASH_GROSS_INPUT,
FLASH_OUTPUT,
0,
FLASH_CACHE_READ,
);
assert!(
(double_counted - 4.007_987_16).abs() < 1e-6,
"as-billed cost drifted: {double_counted}"
);
assert!(
double_counted / netted > 5.0,
"ratio {}",
double_counted / netted
);
}
#[test]
fn token_total_excludes_the_cached_prefix() {
let usage = TokenUsage {
input_tokens: CALL_NET_INPUT,
output_tokens: CALL_OUTPUT,
cache_read_tokens: CALL_CACHE_READ,
..Default::default()
};
assert_eq!(usage.total(), CALL_NET_INPUT + CALL_OUTPUT);
assert_eq!(
usage.billable_input(),
CALL_NET_INPUT + CALL_CACHE_READ,
"billable input is the gross prompt, reassembled from net + cache"
);
}
async fn serve_once(listener: TcpListener, content_type: &'static str, body: String) {
let (mut sock, _) = listener.accept().await.expect("accept");
let mut buf = [0u8; 8192];
let _ = timeout(Duration::from_secs(5), sock.read(&mut buf)).await;
let resp = format!(
"HTTP/1.1 200 OK\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
content_type,
body.len(),
body
);
sock.write_all(resp.as_bytes()).await.expect("write body");
sock.flush().await.ok();
}
async fn collect_usage(provider: &OpenAIProvider) -> TokenUsage {
let req = LLMRequest::new("glm-5.3-flash", vec![Message::user("hi")]);
let mut stream = provider.stream(req).await.expect("stream opens");
let mut usage = None;
while let Some(ev) = futures::StreamExt::next(&mut stream).await {
match ev.expect("event ok") {
StreamEvent::MessageDelta { usage: u, .. } => usage = Some(u),
StreamEvent::MessageStop => break,
_ => {}
}
}
usage.expect("a MessageDelta carried usage")
}
async fn usage_from_sse(sse: String) -> TokenUsage {
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let port = listener.local_addr().unwrap().port();
tokio::spawn(serve_once(listener, "text/event-stream", sse));
let provider = OpenAIProvider::local(format!("http://127.0.0.1:{port}/chat/completions"));
timeout(Duration::from_secs(10), collect_usage(&provider))
.await
.expect("stream completes in time")
}
fn assert_netted(usage: &TokenUsage, label: &str) {
assert_eq!(
usage.input_tokens, CALL_NET_INPUT,
"{label}: input_tokens must be the GROSS prompt minus the cached prefix, \
not the gross prompt itself"
);
assert_eq!(
usage.cache_read_tokens, CALL_CACHE_READ,
"{label}: cache read"
);
assert_eq!(usage.output_tokens, CALL_OUTPUT, "{label}: output");
assert_eq!(
usage.billable_input(),
CALL_GROSS_INPUT,
"{label}: net + cache must reassemble the provider's gross prompt exactly"
);
}
#[tokio::test]
async fn stream_inline_usage_nets_the_cached_prefix() {
let chunk = format!(
r#"{{"id":"c1","object":"chat.completion.chunk","model":"glm-5.3-flash","choices":[{{"index":0,"delta":{{"content":"ok"}},"finish_reason":"stop"}}],"usage":{{"prompt_tokens":{CALL_GROSS_INPUT},"completion_tokens":{CALL_OUTPUT},"prompt_tokens_details":{{"cached_tokens":{CALL_CACHE_READ}}}}}}}"#
);
let sse = format!("data: {chunk}\n\ndata: [DONE]\n\n");
assert_netted(&usage_from_sse(sse).await, "inline");
}
#[tokio::test]
async fn stream_usage_only_chunk_nets_the_cached_prefix() {
let finish = r#"{"id":"c2","object":"chat.completion.chunk","model":"glm-5.3-flash","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}"#;
let usage_chunk = format!(
r#"{{"id":"c2","object":"chat.completion.chunk","model":"glm-5.3-flash","choices":[],"usage":{{"prompt_tokens":{CALL_GROSS_INPUT},"completion_tokens":{CALL_OUTPUT},"prompt_tokens_details":{{"cached_tokens":{CALL_CACHE_READ}}}}}}}"#
);
let sse = format!("data: {finish}\n\ndata: {usage_chunk}\n\ndata: [DONE]\n\n");
assert_netted(&usage_from_sse(sse).await, "usage-only");
}
#[tokio::test]
async fn anthropic_style_cache_read_field_also_nets() {
let chunk = format!(
r#"{{"id":"c3","object":"chat.completion.chunk","model":"glm-5.3-flash","choices":[{{"index":0,"delta":{{"content":"ok"}},"finish_reason":"stop"}}],"usage":{{"prompt_tokens":{CALL_GROSS_INPUT},"completion_tokens":{CALL_OUTPUT},"cache_read_input_tokens":{CALL_CACHE_READ}}}}}"#
);
let sse = format!("data: {chunk}\n\ndata: [DONE]\n\n");
assert_netted(&usage_from_sse(sse).await, "anthropic-style field");
}
#[tokio::test]
async fn cache_read_larger_than_prompt_clamps_to_zero() {
let chunk = r#"{"id":"c4","object":"chat.completion.chunk","model":"glm-5.3-flash","choices":[{"index":0,"delta":{"content":"ok"},"finish_reason":"stop"}],"usage":{"prompt_tokens":100,"completion_tokens":5,"prompt_tokens_details":{"cached_tokens":500}}}"#;
let sse = format!("data: {chunk}\n\ndata: [DONE]\n\n");
let usage = usage_from_sse(sse).await;
assert_eq!(usage.input_tokens, 0);
assert_eq!(usage.cache_read_tokens, 500);
}
#[tokio::test]
async fn no_cache_split_leaves_the_prompt_untouched() {
let chunk = r#"{"id":"c5","object":"chat.completion.chunk","model":"glm-5.3-flash","choices":[{"index":0,"delta":{"content":"ok"},"finish_reason":"stop"}],"usage":{"prompt_tokens":1200,"completion_tokens":34}}"#;
let sse = format!("data: {chunk}\n\ndata: [DONE]\n\n");
let usage = usage_from_sse(sse).await;
assert_eq!(usage.input_tokens, 1200);
assert_eq!(usage.cache_read_tokens, 0);
}
#[tokio::test]
async fn non_stream_response_nets_the_cached_prefix() {
let body = format!(
r#"{{"id":"n1","object":"chat.completion","created":0,"model":"glm-5.3-flash","choices":[{{"index":0,"finish_reason":"stop","message":{{"role":"assistant","content":"ok"}}}}],"usage":{{"prompt_tokens":{CALL_GROSS_INPUT},"completion_tokens":{CALL_OUTPUT},"prompt_tokens_details":{{"cached_tokens":{CALL_CACHE_READ}}}}}}}"#
);
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let port = listener.local_addr().unwrap().port();
tokio::spawn(serve_once(listener, "application/json", body));
let provider = OpenAIProvider::local(format!("http://127.0.0.1:{port}/chat/completions"));
let req = LLMRequest::new("glm-5.3-flash", vec![Message::user("hi")]);
let resp = timeout(Duration::from_secs(10), provider.complete(req))
.await
.expect("completes in time")
.expect("response ok");
assert_netted(&resp.usage, "non-stream");
}
#[test]
fn nonstream_compat_nets_the_cached_prefix() {
let json = format!(
r#"{{"id":"g1","object":"chat.completion","created":0,"model":"glm-5.3-flash","choices":[{{"index":0,"finish_reason":"stop","message":{{"role":"assistant","content":"ok"}}}}],"usage":{{"prompt_tokens":{CALL_GROSS_INPUT},"completion_tokens":{CALL_OUTPUT},"prompt_tokens_details":{{"cached_tokens":{CALL_CACHE_READ}}}}}}}"#
);
let events = synthesize_stream_events(&json).expect("synthesizes");
let usage = events
.iter()
.find_map(|e| match e {
Ok(StreamEvent::MessageDelta { usage, .. }) => Some(*usage),
_ => None,
})
.expect("a MessageDelta carried usage");
assert_netted(&usage, "nonstream_compat");
}
#[tokio::test]
async fn done_fires_nothing_after_reported_usage_and_input_stays_netted() {
let chunk = format!(
r#"{{"id":"c6","object":"chat.completion.chunk","model":"glm-5.3-flash","choices":[{{"index":0,"delta":{{"content":"ok"}},"finish_reason":"stop"}}],"usage":{{"prompt_tokens":{CALL_GROSS_INPUT},"completion_tokens":{CALL_OUTPUT},"prompt_tokens_details":{{"cached_tokens":{CALL_CACHE_READ}}}}}}}"#
);
let sse = format!("data: {chunk}\n\ndata: [DONE]\n\n");
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let port = listener.local_addr().unwrap().port();
tokio::spawn(serve_once(listener, "text/event-stream", sse));
let provider = OpenAIProvider::local(format!("http://127.0.0.1:{port}/chat/completions"));
let req = LLMRequest::new("glm-5.3-flash", vec![Message::user("hi")]);
let mut stream = provider.stream(req).await.expect("stream opens");
let mut deltas = Vec::new();
while let Some(ev) = futures::StreamExt::next(&mut stream).await {
if let StreamEvent::MessageDelta { usage, .. } = ev.expect("event ok") {
deltas.push(usage);
}
}
assert_eq!(
deltas.len(),
1,
"expected exactly the reported delta, got {} (a second delta here is the #1738 duplicate finalize)",
deltas.len()
);
assert_eq!(
deltas[0].input_tokens, CALL_NET_INPUT,
"the reported delta must carry the netted input, not the gross prompt: {:?}",
deltas[0]
);
}