From 9f14f1db7923a1f4cf39ef43cc6f1e418d6f7a2d Mon Sep 17 00:00:00 2001 From: Dev Agent Date: Wed, 26 Aug 2026 22:35:47 +0000 Subject: [PATCH] fix(runner): preserve accounting uncertainty Co-Authored-By: Paperclip --- .../crates/runner-core/src/provider_events.rs | 44 +++++++++++++++++++ .../native-codex-runner.integration.test.ts | 4 ++ 2 files changed, 48 insertions(+) diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs b/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs index 471847562e3..06f664a085b 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs @@ -133,6 +133,7 @@ fn measurement(value: &Value) -> Value { let has_token_usage = input_tokens > 0 || output_tokens > 0 || cache_read_tokens > 0 || cache_write_tokens > 0; let (requests, request_count_source, request_count_exact) = match reported_requests { + Some(0) if has_token_usage => (1, "token_bearing_turn_minimum", false), Some(requests) => (requests, "provider_reported", true), None if has_token_usage => (1, "token_bearing_turn_minimum", false), None => (0, "unavailable", false), @@ -507,11 +508,44 @@ mod tests { assert_eq!(usage[0].priority, EventPriority::P0); } + #[test] + fn treats_zero_requests_with_positive_tokens_as_an_inexact_lower_bound() { + let usage = normalize_codex_notification( + "thread/tokenUsage/updated", + &json!({ + "turnId": "provider-turn-with-usage", + "tokenUsage": { + "total": {"inputTokens": 12, "outputTokens": 3, "requests": 0} + } + }), + ); + + assert_eq!( + usage[0].payload["providerTurnId"], + "provider-turn-with-usage" + ); + assert_eq!(usage[0].payload["cumulative"]["requests"], 1); + assert_eq!( + usage[0].payload["cumulative"]["requestCountSource"], + "token_bearing_turn_minimum" + ); + assert_eq!(usage[0].payload["cumulative"]["requestCountExact"], false); + assert_eq!( + usage[0].payload["cumulative"]["providerCostStatus"], + "unpriced" + ); + assert_eq!( + usage[0].payload["cumulative"]["providerCostUnavailableReason"], + "codex_app_server_does_not_report_per_turn_cost" + ); + } + #[test] fn preserves_provider_reported_request_and_cost_receipts() { let usage = normalize_codex_notification( "thread/tokenUsage/updated", &json!({ + "turnId": "provider-turn-with-two-requests", "responseId": "resp_123", "tokenUsage": { "total": {"inputTokens": 12, "outputTokens": 3, "requests": 2, "providerCostUsd": 0.04}, @@ -519,6 +553,16 @@ mod tests { } }), ); + assert_eq!( + usage[0].payload["providerTurnId"], + "provider-turn-with-two-requests" + ); + assert_eq!(usage[0].payload["cumulative"]["requests"], 2); + assert_eq!( + usage[0].payload["cumulative"]["requestCountSource"], + "provider_reported" + ); + assert_eq!(usage[0].payload["cumulative"]["requestCountExact"], true); assert_eq!(usage[0].payload["providerRequestId"], "resp_123"); assert_eq!( usage[0].payload["providerRequestIdUnavailableReason"], diff --git a/server/src/services/native-runtime/native-codex-runner.integration.test.ts b/server/src/services/native-runtime/native-codex-runner.integration.test.ts index c89a18a277d..2f5e6d852db 100644 --- a/server/src/services/native-runtime/native-codex-runner.integration.test.ts +++ b/server/src/services/native-runtime/native-codex-runner.integration.test.ts @@ -399,6 +399,10 @@ describeEmbeddedPostgres("native Codex server vertical slice", () => { providerCostStatus: finalCumulative?.providerCostStatus ?? null, providerCostUnavailableReason: finalCumulative?.providerCostUnavailableReason ?? null, + inputTokens: finalCumulative?.inputTokens ?? null, + outputTokens: finalCumulative?.outputTokens ?? null, + cacheReadTokens: finalCumulative?.cacheReadTokens ?? null, + cacheWriteTokens: finalCumulative?.cacheWriteTokens ?? null, }, usageReports, }));