In this article
Final usage is an accounting event rather than another generation. Validate its shape and order for the specific endpoint, preserving implementation differences.
Request counters often arrive after text has finished. Tests can then close the result on finish_reason and stop reading, or treat the following frame as another answer. Final-usage validation should connect text, choice completion and accounting into one operation while preserving them as distinct events. Compare specific interfaces rather than an abstract family-level compatibility label.
Record both sides of the comparison
The OpenRouter streaming documentation describes the current external Chat Completions shape. A final usage frame precedes DONE and contains nonempty choices with a content-free delta and repeated completion reason. The page explicitly identifies this as a difference from the original OpenAI shape.
That does not make this shape a universal upstream-provider requirement. Describe the engine-facing input contract and service-facing output contract separately. Test any conversion with its own fixture instead of silently adjusting expectations while reading responses.
Do not count the frame as new work
The OpenRouter schema overview associates usage with the request and describes prompt_tokens, completion_tokens and total_tokens. Receiving an accounting frame must not increase completed-generation count. The result counter changes state once; accounting supplements the known operation.
Save request ID and observed events. When an operation has two terminal-looking frames, inspect their roles and shapes. Declaring every repeated finish_reason a duplicate request would flag a documented valid stream incorrectly.
Define structural invariants
A fixture should expect usage to match the request, appear in an allowed position, survive the read loop and avoid being summed with its own repetition. Define units and missing-field rules. Zero is different from an unknown counter.
Do not require positive completion_tokens for every valid outcome. A request may finish differently, and visible content does not always describe all generated work. Keep structural validation separate from arithmetic checks and commercial settlement, which may need additional evidence.
Test complete and incomplete delivery
Build a small synthetic stream containing content, terminal reason, usage and DONE. Then remove accounting, and separately truncate before DONE. Expected classifications should identify what is missing rather than promote the last received object into complete success.
Add a client canceling reads immediately after text. It reveals an observation boundary: missing client-side usage does not prove the engine failed to count work. Investigation needs a server-record link where the implementation provides one. The fixture needs no real paid traffic.
Preserve endpoint-specific contracts
Responses and other interfaces can report usage through different terminal events. A shared parser must choose validation by endpoint rather than search every JSON object for one magic field. Store fixture and adapter versions together so schema changes appear in release checks.
Related guides cover usage reconciliation and incremental stream reading. The result here is an accounting-order and completeness test, without claims of active integration or universal requirements imposed by an external platform.