@oxyhq/contracts 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -85,6 +85,49 @@ export const moneySchema = z
85
85
  /**
86
86
  * The closed set of units inference is metered in.
87
87
  *
88
+ * **The units PARTITION a request: every unit counts material no other unit
89
+ * counts.** `cached_input_tokens` is not part of `input_tokens`, and
90
+ * `reasoning_tokens` is not part of `output_tokens` — they are siblings, not
91
+ * subsets. A request whose 10 000-token prompt was served 9 000 tokens from
92
+ * cache is reported as `input_tokens: 1000` beside `cached_input_tokens: 9000`,
93
+ * never as `input_tokens: 10000` beside it.
94
+ *
95
+ * That belongs to the definition rather than to a convention somewhere else,
96
+ * because settlement applies a price to EVERY reported unit and sums them
97
+ * (`inferenceLedger.service.ts`'s `computeCharge`). Under the partition rule
98
+ * that sum IS the request's cost, and a cached token can carry its own — lower
99
+ * — price. Under the nested reading the same sum charges the cached and
100
+ * reasoning tokens twice: once inside their parent and once on their own line.
101
+ * It fails silently, because every total still looks plausible and the receipt
102
+ * is still internally consistent, and on a reasoning model the reasoning tokens
103
+ * can dominate the completion, so the error is not marginal.
104
+ *
105
+ * **Every OpenAI-compatible provider reports the other way round**:
106
+ * `prompt_tokens` INCLUDES `prompt_tokens_details.cached_tokens`, and
107
+ * `completion_tokens` INCLUDES `completion_tokens_details.reasoning_tokens`.
108
+ * Normalising is the data plane's job and it is subtraction:
109
+ *
110
+ * ```text
111
+ * input_tokens = prompt_tokens - prompt_tokens_details.cached_tokens
112
+ * output_tokens = completion_tokens - completion_tokens_details.reasoning_tokens
113
+ * ```
114
+ *
115
+ * No refinement in this package can enforce it, and saying so is part of the
116
+ * rule: a nested report and a disjoint one are the same four non-negative
117
+ * integers, so no predicate over a single report can tell them apart. The two
118
+ * structural guards that DO exist — refining `cached <= input` and
119
+ * `reasoning <= output`, or deriving the parents instead of reporting them —
120
+ * both encode the nested reading, which is the one this rule rejects. What IS
121
+ * enforceable is the arithmetic that depends on the rule, and that is where the
122
+ * enforcement lives — `inferenceLedger.service.test.ts` prices a report in
123
+ * which cached and reasoning tokens are both non-zero and asserts the exact
124
+ * total, which the nested reading cannot produce.
125
+ *
126
+ * Where the public surface has to speak a nested dialect, the sum is put back
127
+ * at the boundary rather than the internal reading being bent to it
128
+ * (`routes/inferenceEdge.ts` renders `prompt_tokens` as
129
+ * `input_tokens + cached_input_tokens`).
130
+ *
88
131
  * Time is carried in integer MILLISECONDS rather than seconds so that no unit
89
132
  * quantity is ever fractional: a 12.5-second transcription is `12500`, exactly,
90
133
  * and the "units are integers" rule holds for every modality instead of holding
@@ -128,6 +128,12 @@ export const inferenceRequestOutcomeSchema = z.enum([
128
128
  * `usageSource` is load-bearing when a provider returns no usage at all: the
129
129
  * report still arrives, marked `estimated`, so settlement can apply the
130
130
  * estimation policy knowingly instead of treating a reconstruction as fact.
131
+ *
132
+ * `units` is a PARTITION of what the request consumed, not a set of totals with
133
+ * details hanging off them — see `USAGE_UNITS` in `money.ts`. Reporting a provider's
134
+ * nested `prompt_tokens`/`completion_tokens` verbatim charges the cached and
135
+ * reasoning tokens twice, so subtracting the children out is part of what
136
+ * "normalized" means in this shape's name.
131
137
  */
132
138
  export const normalizedUsageReportSchema = z
133
139
  .object({
@@ -34,6 +34,34 @@
34
34
  * field is additive and does not bump it, because a consumer on the previous
35
35
  * version parses the message correctly and simply does not read the new field.
36
36
  *
37
+ * ## Which shapes reject an unknown field
38
+ *
39
+ * That last rule is why the shapes EXCHANGED WITH THE DATA PLANE are not
40
+ * `.strict()` at their top level — the request envelope, the four usage records,
41
+ * the stream events, the error body, the catalogue descriptors, the price
42
+ * version. The split is a decision rather than an omission: `.strict()` and
43
+ * "adding an optional field is additive" cannot both hold on one shape, because
44
+ * a producer one minor version ahead would have its whole message REFUSED
45
+ * rather than its new field ignored. For a usage report that means a request
46
+ * already served upstream can never be settled and Oxy absorbs its cost, which
47
+ * is a worse failure than the one strictness would have caught.
48
+ *
49
+ * Their LEAVES are strict, and that is where the protection lives: a stripped
50
+ * field is the worse outcome exactly where it would be a leak or a second
51
+ * source of truth, because it disappears at this parse and survives in the
52
+ * producer, which is where somebody eventually reads it. So
53
+ * `clientRequestMetadataSchema` (no IP, ever), `moneySchema` (no convenience
54
+ * float beside the exact decimal), `providerErrorPassthroughSchema` (no
55
+ * upstream request or headers beside the message), `usageQuantitySchema` and
56
+ * `unitPriceSchema` all refuse an unknown field, while the envelope carrying
57
+ * them tolerates an additive one.
58
+ *
59
+ * A shape Oxy does NOT exchange with the data plane is strict at its top level
60
+ * too, since nothing there can run ahead of this package:
61
+ * `providerConnectionSchema`, where an unknown field is how a BYOK credential
62
+ * escapes, and the billing and entitlement records, where one is a second
63
+ * number beside an exact amount.
64
+ *
37
65
  * Decided in: docs/adr/0006-oxy-relay-boundary.md, docs/adr/0010-public-api-compatibility.md.
38
66
  */
39
67
  /**