opencode/packages/llm/test
Kit Langton b9451175a6 refactor(llm): make LLM.Usage a fully-additive contract
Defines a single invariant for `LLM.Usage`: every field is non-negative
and every meaningful aggregate is a *sum*, never a difference. Total
billable input = inputTokens + cacheReadInputTokens + cacheWriteInputTokens.
Total billable output = outputTokens + reasoningTokens. Adding two
non-negatives cannot underflow, so consumers can no longer reproduce the
underflow-then-clamp bug class fixed by #26620.

Each protocol mapper now enforces the contract at the provider boundary
via `ProviderShared.subtractTokens`, which clamps with `Math.max(0, …)`
for defense against provider bugs:

- OpenAI Chat / Responses: pull `cached_tokens` out of `prompt_tokens` /
  `input_tokens`; pull `reasoning_tokens` out of `completion_tokens` /
  `output_tokens`. The provider's `total_tokens` is preserved verbatim.
- Gemini: pull `cachedContentTokenCount` out of `promptTokenCount`.
  Gemini already split visible candidates from thoughts.
- Bedrock: pull `cacheReadInputTokens` and `cacheWriteInputTokens` out of
  `inputTokens`, matching AWS prompt-caching docs.
- Anthropic: already non-overlapping per the Messages API; pass through.

Adds `Usage.totalInput` / `Usage.totalOutput` helpers for callers that
want the merged view, and a regression test covering the clamp behavior.

The reasoning underflow fixed in #26620 was the most visible symptom of
a broader semantic inconsistency in this package: providers also disagreed
on whether `inputTokens` includes cache reads (Anthropic excluded;
OpenAI/Gemini/Bedrock included), which would silently double-subtract
the moment v2 wired LLM.Usage into Session.getUsage. Normalizing now,
pre-integration, closes both holes in one move.
2026-05-10 13:07:58 -04:00
..
fixtures/recordings chore: generate 2026-05-08 20:57:36 +00:00
lib Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
provider refactor(llm): make LLM.Usage a fully-additive contract 2026-05-10 13:07:58 -04:00
adapter.test.ts Add explicit LLM stream lifecycle events (#26722) 2026-05-10 12:19:13 -04:00
auth-options.types.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
auth.test.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
endpoint.test.ts chore: generate 2026-05-08 20:57:36 +00:00
executor.test.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
exports.test.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
generate-object.test.ts chore: generate 2026-05-08 20:57:36 +00:00
llm.test.ts Add explicit LLM stream lifecycle events (#26722) 2026-05-10 12:19:13 -04:00
provider.types.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
recorded-golden.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
recorded-runner.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
recorded-scenarios.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
recorded-test.ts refactor(http-recorder): hide cassette format behind Cassette seam (#26725) 2026-05-10 12:29:55 -04:00
recorded-utils.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
recorded-websocket.ts feat(http-recorder): default mode to "auto" (#26719) 2026-05-10 16:05:11 +00:00
schema.test.ts refactor(llm): make LLM.Usage a fully-additive contract 2026-05-10 13:07:58 -04:00
tool-runtime.test.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
tool-stream.test.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00
tool.types.ts Add native LLM core foundation (#24712) 2026-05-08 16:56:20 -04:00