Track prompt cache write token usage (#33454)

## What changed

- Parse `cache_write_tokens` from response input token details and carry it through token usage aggregation.
- Expose `cache_write_input_tokens` in protocol, app-server, exec, and TypeScript SDK usage events, defaulting it to zero for compatibility with older payloads.
- Report cache-write usage in analytics, metrics, traces, and rollout data alongside existing cache-read usage.

## Testing

- Add coverage for response parsing, usage event output, aggregation, analytics, metrics, and telemetry fields.

GitOrigin-RevId: 634e4fbe3086a8923c0ee13b622573e7d9d221bd
This commit is contained in:
Eric Traut authored and copyberry committed 2026-07-16 00:22:53 +00:00
1 parent 78d4a563f9
commit 2edad72de3
46 files changed
+207 -31

No files matched your search

@@ -377,6 +377,7 @@ fn sample_turn_token_usage_fact(thread_id: &str, turn_id: &str) -> TurnTokenUsag
total_tokens: 321,
input_tokens: 123,
cached_input_tokens: 45,
cache_write_input_tokens: 7,
output_tokens: 140,
reasoning_output_tokens: 13,
},
@@ -1317,6 +1318,7 @@ fn compaction_event_serializes_expected_shape() {
retained_image_count: None,
compaction_summary_tokens: None,
cached_input_tokens: None,
cache_write_input_tokens: Some(456),
started_at: 100,
completed_at: 106,
duration_ms: Some(6543),
@@ -1369,6 +1371,7 @@ fn compaction_event_serializes_expected_shape() {
"retained_image_count": null,
"compaction_summary_tokens": null,
"cached_input_tokens": null,
"cache_write_input_tokens": 456,
"started_at": 100,
"completed_at": 106,
"duration_ms": 6543
@@ -1867,6 +1870,7 @@ async fn thread_originator_overrides_shared_connection_across_thread_events() {
retained_image_count: None,
compaction_summary_tokens: None,
cached_input_tokens: None,
cache_write_input_tokens: None,
started_at: 100,
completed_at: 101,
duration_ms: Some(1200),
@@ -2045,6 +2049,7 @@ async fn compaction_event_ingests_custom_fact() {
retained_image_count: None,
compaction_summary_tokens: None,
cached_input_tokens: None,
cache_write_input_tokens: None,
started_at: 100,
completed_at: 101,
duration_ms: Some(1200),
@@ -2186,6 +2191,7 @@ async fn guardian_review_event_ingests_custom_fact_with_optional_target_item() {
completed_at: Some(190),
input_tokens: None,
cached_input_tokens: None,
cache_write_input_tokens: None,
output_tokens: None,
reasoning_output_tokens: None,
total_tokens: None,
@@ -3011,6 +3017,7 @@ async fn subagent_events_keep_thread_originator_with_explicit_turn_connection()
retained_image_count: None,
compaction_summary_tokens: None,
cached_input_tokens: None,
cache_write_input_tokens: None,
started_at: 100,
completed_at: 101,
duration_ms: Some(1200),
@@ -3939,6 +3946,7 @@ fn turn_event_serializes_expected_shape() {
image_generation_count: None,
input_tokens: None,
cached_input_tokens: None,
cache_write_input_tokens: None,
output_tokens: None,
reasoning_output_tokens: None,
total_tokens: None,
@@ -4011,6 +4019,7 @@ fn turn_event_serializes_expected_shape() {
"image_generation_count": null,
"input_tokens": null,
"cached_input_tokens": null,
"cache_write_input_tokens": null,
"output_tokens": null,
"reasoning_output_tokens": null,
"total_tokens": null,
@@ -4342,6 +4351,10 @@ async fn turn_lifecycle_emits_turn_event() {
assert_eq!(payload["event_params"]["duration_ms"], json!(1234));
assert_eq!(payload["event_params"]["input_tokens"], json!(123));
assert_eq!(payload["event_params"]["cached_input_tokens"], json!(45));
assert_eq!(
payload["event_params"]["cache_write_input_tokens"],
json!(7)
);
assert_eq!(payload["event_params"]["output_tokens"], json!(140));
assert_eq!(
payload["event_params"]["reasoning_output_tokens"],