@oxy.so/contracts 1.0.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.
Files changed (147) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/dist/cjs/.tsbuildinfo +1 -0
  4. package/dist/cjs/accountGraph.js +489 -0
  5. package/dist/cjs/agency.js +439 -0
  6. package/dist/cjs/browserHub.js +215 -0
  7. package/dist/cjs/civic.js +163 -0
  8. package/dist/cjs/commonsSignIn.js +59 -0
  9. package/dist/cjs/deviceBoot.js +50 -0
  10. package/dist/cjs/deviceDirectory.js +189 -0
  11. package/dist/cjs/devicePairing.js +138 -0
  12. package/dist/cjs/deviceSession.js +164 -0
  13. package/dist/cjs/emailAgentContext.js +32 -0
  14. package/dist/cjs/followGraph.js +28 -0
  15. package/dist/cjs/identity.js +258 -0
  16. package/dist/cjs/inboxPush.js +24 -0
  17. package/dist/cjs/index.js +618 -0
  18. package/dist/cjs/inference/accountBilling.js +334 -0
  19. package/dist/cjs/inference/aliaModelRelease.js +262 -0
  20. package/dist/cjs/inference/attribution.js +106 -0
  21. package/dist/cjs/inference/catalogue.js +487 -0
  22. package/dist/cjs/inference/entitlement.js +217 -0
  23. package/dist/cjs/inference/errors.js +309 -0
  24. package/dist/cjs/inference/identifiers.js +224 -0
  25. package/dist/cjs/inference/inbox.js +105 -0
  26. package/dist/cjs/inference/modelDocumentation.js +433 -0
  27. package/dist/cjs/inference/money.js +188 -0
  28. package/dist/cjs/inference/priceVersion.js +110 -0
  29. package/dist/cjs/inference/providerConnection.js +455 -0
  30. package/dist/cjs/inference/request.js +477 -0
  31. package/dist/cjs/inference/routingPolicy.js +318 -0
  32. package/dist/cjs/inference/streamEvents.js +258 -0
  33. package/dist/cjs/inference/usage.js +329 -0
  34. package/dist/cjs/inference/version.js +105 -0
  35. package/dist/cjs/keyRecovery.js +91 -0
  36. package/dist/cjs/keyRotation.js +75 -0
  37. package/dist/cjs/links.js +68 -0
  38. package/dist/cjs/moderationReputation.js +298 -0
  39. package/dist/cjs/oauth.js +66 -0
  40. package/dist/cjs/oxyRecordTypes.js +71 -0
  41. package/dist/cjs/protocol.js +53 -0
  42. package/dist/cjs/recommendations.js +168 -0
  43. package/dist/cjs/reputation.js +297 -0
  44. package/dist/cjs/sessionStatus.js +121 -0
  45. package/dist/cjs/transparency.js +89 -0
  46. package/dist/cjs/updates.js +252 -0
  47. package/dist/cjs/userInvalidation.js +89 -0
  48. package/dist/cjs/userResponse.js +245 -0
  49. package/dist/cjs/username.js +290 -0
  50. package/dist/cjs/webauthn.js +71 -0
  51. package/dist/esm/.tsbuildinfo +1 -0
  52. package/dist/esm/accountGraph.js +480 -0
  53. package/dist/esm/agency.js +436 -0
  54. package/dist/esm/browserHub.js +212 -0
  55. package/dist/esm/civic.js +160 -0
  56. package/dist/esm/commonsSignIn.js +56 -0
  57. package/dist/esm/deviceBoot.js +47 -0
  58. package/dist/esm/deviceDirectory.js +186 -0
  59. package/dist/esm/devicePairing.js +135 -0
  60. package/dist/esm/deviceSession.js +161 -0
  61. package/dist/esm/emailAgentContext.js +29 -0
  62. package/dist/esm/followGraph.js +27 -0
  63. package/dist/esm/identity.js +255 -0
  64. package/dist/esm/inboxPush.js +21 -0
  65. package/dist/esm/index.js +172 -0
  66. package/dist/esm/inference/accountBilling.js +331 -0
  67. package/dist/esm/inference/aliaModelRelease.js +259 -0
  68. package/dist/esm/inference/attribution.js +103 -0
  69. package/dist/esm/inference/catalogue.js +484 -0
  70. package/dist/esm/inference/entitlement.js +214 -0
  71. package/dist/esm/inference/errors.js +306 -0
  72. package/dist/esm/inference/identifiers.js +221 -0
  73. package/dist/esm/inference/inbox.js +102 -0
  74. package/dist/esm/inference/modelDocumentation.js +430 -0
  75. package/dist/esm/inference/money.js +185 -0
  76. package/dist/esm/inference/priceVersion.js +107 -0
  77. package/dist/esm/inference/providerConnection.js +452 -0
  78. package/dist/esm/inference/request.js +474 -0
  79. package/dist/esm/inference/routingPolicy.js +315 -0
  80. package/dist/esm/inference/streamEvents.js +255 -0
  81. package/dist/esm/inference/usage.js +326 -0
  82. package/dist/esm/inference/version.js +102 -0
  83. package/dist/esm/keyRecovery.js +88 -0
  84. package/dist/esm/keyRotation.js +72 -0
  85. package/dist/esm/links.js +65 -0
  86. package/dist/esm/moderationReputation.js +295 -0
  87. package/dist/esm/oauth.js +63 -0
  88. package/dist/esm/oxyRecordTypes.js +68 -0
  89. package/dist/esm/protocol.js +50 -0
  90. package/dist/esm/recommendations.js +165 -0
  91. package/dist/esm/reputation.js +293 -0
  92. package/dist/esm/sessionStatus.js +118 -0
  93. package/dist/esm/transparency.js +86 -0
  94. package/dist/esm/updates.js +249 -0
  95. package/dist/esm/userInvalidation.js +85 -0
  96. package/dist/esm/userResponse.js +240 -0
  97. package/dist/esm/username.js +283 -0
  98. package/dist/esm/webauthn.js +68 -0
  99. package/dist/types/.tsbuildinfo +1 -0
  100. package/dist/types/accountGraph.d.ts +378 -0
  101. package/dist/types/agency.d.ts +2162 -0
  102. package/dist/types/browserHub.d.ts +856 -0
  103. package/dist/types/civic.d.ts +338 -0
  104. package/dist/types/commonsSignIn.d.ts +58 -0
  105. package/dist/types/deviceBoot.d.ts +74 -0
  106. package/dist/types/deviceDirectory.d.ts +1317 -0
  107. package/dist/types/devicePairing.d.ts +130 -0
  108. package/dist/types/deviceSession.d.ts +411 -0
  109. package/dist/types/emailAgentContext.d.ts +248 -0
  110. package/dist/types/followGraph.d.ts +150 -0
  111. package/dist/types/identity.d.ts +402 -0
  112. package/dist/types/inboxPush.d.ts +30 -0
  113. package/dist/types/index.d.ts +100 -0
  114. package/dist/types/inference/accountBilling.d.ts +738 -0
  115. package/dist/types/inference/aliaModelRelease.d.ts +609 -0
  116. package/dist/types/inference/attribution.d.ts +176 -0
  117. package/dist/types/inference/catalogue.d.ts +1618 -0
  118. package/dist/types/inference/entitlement.d.ts +519 -0
  119. package/dist/types/inference/errors.d.ts +242 -0
  120. package/dist/types/inference/identifiers.d.ts +182 -0
  121. package/dist/types/inference/inbox.d.ts +374 -0
  122. package/dist/types/inference/modelDocumentation.d.ts +1603 -0
  123. package/dist/types/inference/money.d.ts +185 -0
  124. package/dist/types/inference/priceVersion.d.ts +182 -0
  125. package/dist/types/inference/providerConnection.d.ts +968 -0
  126. package/dist/types/inference/request.d.ts +2800 -0
  127. package/dist/types/inference/routingPolicy.d.ts +616 -0
  128. package/dist/types/inference/streamEvents.d.ts +950 -0
  129. package/dist/types/inference/usage.d.ts +1164 -0
  130. package/dist/types/inference/version.d.ts +102 -0
  131. package/dist/types/keyRecovery.d.ts +138 -0
  132. package/dist/types/keyRotation.d.ts +103 -0
  133. package/dist/types/links.d.ts +96 -0
  134. package/dist/types/moderationReputation.d.ts +487 -0
  135. package/dist/types/oauth.d.ts +86 -0
  136. package/dist/types/oxyRecordTypes.d.ts +62 -0
  137. package/dist/types/protocol.d.ts +86 -0
  138. package/dist/types/recommendations.d.ts +542 -0
  139. package/dist/types/reputation.d.ts +457 -0
  140. package/dist/types/sessionStatus.d.ts +231 -0
  141. package/dist/types/transparency.d.ts +392 -0
  142. package/dist/types/updates.d.ts +545 -0
  143. package/dist/types/userInvalidation.d.ts +94 -0
  144. package/dist/types/userResponse.d.ts +1706 -0
  145. package/dist/types/username.d.ts +265 -0
  146. package/dist/types/webauthn.d.ts +77 -0
  147. package/package.json +87 -0
@@ -0,0 +1,433 @@
1
+ "use strict";
2
+ /**
3
+ * Model documentation: what a first-party release must DECLARE, what a
4
+ * downstream developer may READ, and the request that ingests both.
5
+ *
6
+ * Issue #972 §12, the three items under "Future Alia model
7
+ * publication/compliance" that `aliaModelRelease.ts` deliberately left open:
8
+ * accepting the documentation set, publicising the customer-safe half of it, and
9
+ * preserving the metadata an EU AI Act / GPAI documentation workflow needs.
10
+ *
11
+ * ## The section's own scope is what makes the compliance claim falsifiable
12
+ *
13
+ * The issue section is titled "Future Alia model publication/compliance", so the
14
+ * obligations in play are the ones binding a PROVIDER of a general-purpose AI
15
+ * model — Oxy/Alia, for an `alia/*` release it trained or derived. Oxy's position
16
+ * on third-party weights is a different one (it received documentation rather
17
+ * than produced it) with different obligations, and nothing here claims to
18
+ * discharge those. Every field below names the obligation it serves; a field
19
+ * whose obligation could not be named is not here, and two are listed at the
20
+ * bottom as deliberately absent.
21
+ *
22
+ * References are to Regulation (EU) 2024/1689 (the AI Act): Article 50(2)
23
+ * (marking synthetic output), Article 51 (classification as a model with
24
+ * systemic risk), Article 53 (obligations of providers of general-purpose AI
25
+ * models), Article 55 (additional obligations for systemic-risk models), Annex XI
26
+ * (the technical documentation), Annex XII (the information for downstream
27
+ * providers).
28
+ *
29
+ * ## Two shapes, and the Act itself draws the line between them
30
+ *
31
+ * {@link modelGpaiDocumentationSchema} is the whole record. {@link
32
+ * modelDownstreamDocumentationSchema} is the subset served publicly. The split
33
+ * is NOT editorial taste: Annex XI is documentation a provider keeps and
34
+ * provides to the AI Office and national competent authorities on request, while
35
+ * Annex XII is information a provider MAKES AVAILABLE to downstream providers.
36
+ * Training compute, training time, energy consumption and the adversarial-testing
37
+ * report are Annex XI Section 2 and Article 55(1)(a) — the first audience — so
38
+ * they are in the record and not in the public projection, and
39
+ * `db/schema/protectedColumns.ts` says the same thing a second time at the type
40
+ * level.
41
+ *
42
+ * ## The conditionals are the Act's, not a convenience
43
+ *
44
+ * Article 53(2) exempts a model released under a free and open-source licence
45
+ * from 53(1)(a) and 53(1)(b) — the Annex XI and Annex XII sets — UNLESS it is a
46
+ * model with systemic risk. It does not exempt 53(1)(c) or 53(1)(d). So the
47
+ * copyright policy and the training-content summary are required of every
48
+ * release here, while the Annex XI/XII set is required of every release that is
49
+ * not covered by that exemption. Writing it the other way round — everything
50
+ * optional, checked by a human — is what makes a compliance record a field nobody
51
+ * filled in.
52
+ *
53
+ * ## What is deliberately NOT here
54
+ *
55
+ * **The modality and FORMAT of inputs and outputs (Annex XI §1(6), Annex XII
56
+ * §1(b)).** The modality half is already stored, as `inference_models`'
57
+ * `input_modalities` / `output_modalities`. The format half is a property of the
58
+ * Oxy API — one request envelope, one set of endpoints, identical for every model
59
+ * — so a per-model column would record the same value on every row and invite a
60
+ * reader to believe it could differ.
61
+ *
62
+ * **The technical means required for integration (Annex XII §1(c)).** Same
63
+ * reason: for a model served over the Oxy API that is Oxy's own API
64
+ * documentation, not a fact about the weights.
65
+ *
66
+ * **A verification finding for a release signature.** See
67
+ * `aliaModelRelease.ts`: whether a signature checked out is Oxy's finding about
68
+ * the document and not a claim the document makes, and no verifier exists yet
69
+ * because what signs is undecided. The ingestion path stores the signatures and
70
+ * the manifest as received so a verifier that lands later can check them; it
71
+ * records no finding, because there is none.
72
+ *
73
+ * Decided in: docs/adr/0008-catalogue-concept-separation.md, issue #972 §12.
74
+ */
75
+ Object.defineProperty(exports, "__esModule", { value: true });
76
+ exports.modelDocumentationSchema = exports.modelReleaseIngestionResultSchema = exports.modelReleaseIngestionRequestSchema = exports.modelLineDeclarationSchema = exports.modelGpaiDocumentationSchema = exports.modelDownstreamDocumentationSchema = exports.SYSTEMIC_RISK_COMPUTE_THRESHOLD_FLOPS = exports.trainingComputeFlopsSchema = exports.modelSystemicRiskTierSchema = exports.modelDistributionMethodSchema = void 0;
77
+ const zod_1 = require("zod");
78
+ const aliaModelRelease_1 = require("./aliaModelRelease");
79
+ const catalogue_1 = require("./catalogue");
80
+ const identifiers_1 = require("./identifiers");
81
+ /* -------------------------------------------------------------------------- */
82
+ /* Vocabulary */
83
+ /* -------------------------------------------------------------------------- */
84
+ /**
85
+ * How a release reaches the people who use it — Annex XI §1(4) and Annex XII
86
+ * §1(a), "methods of distribution".
87
+ *
88
+ * TWO members, and both exist today: a release is served through the Oxy API, or
89
+ * its weights are published for download, or both. A third channel is a
90
+ * distribution decision somebody would have to make, and a closed enum gaining a
91
+ * member is a MINOR contract-set change the handshake surfaces (`version.ts`),
92
+ * which is the right amount of ceremony for it.
93
+ *
94
+ * `downloadable_weights` is also what the Article 53(2) free-and-open-source
95
+ * exemption is assessed against — that exemption requires the model to be
96
+ * "released under a free and open-source licence that allows for the access,
97
+ * usage, modification and distribution of the model" — so it is required even
98
+ * where the Annex XI set it belongs to is exempt.
99
+ */
100
+ exports.modelDistributionMethodSchema = zod_1.z.enum(['oxy_api', 'downloadable_weights']);
101
+ /**
102
+ * Whether this is a model with systemic risk, and on what basis — Article 51.
103
+ *
104
+ * Three states, because the two ways a model acquires the classification have
105
+ * different evidence and a record that flattened them could not be checked:
106
+ *
107
+ * - `not_designated` — neither presumed nor designated.
108
+ * - `presumed_by_training_compute` — Article 51(2): the cumulative compute used
109
+ * for training exceeds 10^25 floating point operations, which the Act makes a
110
+ * presumption of high-impact capabilities. The FIGURE is what creates it, so
111
+ * {@link modelGpaiDocumentationSchema} requires the figure alongside this
112
+ * value.
113
+ * - `designated_by_commission` — Article 51(1)(b): a Commission decision, ex
114
+ * officio or following a qualified alert, that the model has capabilities
115
+ * equivalent to the presumption. Not derivable from anything Oxy holds, which
116
+ * is exactly why it is a declared value.
117
+ */
118
+ exports.modelSystemicRiskTierSchema = zod_1.z.enum([
119
+ 'not_designated',
120
+ 'presumed_by_training_compute',
121
+ 'designated_by_commission',
122
+ ]);
123
+ /**
124
+ * Cumulative training compute in floating point operations — Annex XI §2(b).
125
+ *
126
+ * TEXT, in the same spirit as `modelEvaluationResultSchema.score` and for a
127
+ * sharper reason: this is a PUBLISHED figure (`4.2e25`, `2.5e26`), the numbers
128
+ * involved are far outside the exactly-representable integer range, and the
129
+ * value is never arithmetic Oxy performs on a customer's behalf. A JSON number
130
+ * would round it silently and make two records of one published figure compare
131
+ * unequal.
132
+ *
133
+ * The one comparison that IS made — against Article 51(2)'s 10^25 threshold — is
134
+ * a magnitude test, and `Number()` on a string this regex admits is exact enough
135
+ * for a magnitude test by a factor of about 10^9. The refinement that performs
136
+ * it is on {@link modelGpaiDocumentationSchema}.
137
+ */
138
+ exports.trainingComputeFlopsSchema = zod_1.z
139
+ .string()
140
+ .max(40)
141
+ .regex(/^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:e\+?(?:0|[1-9][0-9]?))?$/, 'training compute must be a decimal or scientific figure, e.g. 4.2e25');
142
+ /**
143
+ * Article 51(2)'s presumption threshold, as a number.
144
+ *
145
+ * Named rather than inlined so the refinement that applies it and the enum
146
+ * member that describes it (`presumed_by_training_compute`) cannot come to mean
147
+ * different things.
148
+ */
149
+ exports.SYSTEMIC_RISK_COMPUTE_THRESHOLD_FLOPS = 1e25;
150
+ /* -------------------------------------------------------------------------- */
151
+ /* The record */
152
+ /* -------------------------------------------------------------------------- */
153
+ /**
154
+ * The subset of the documentation set that is served to downstream developers —
155
+ * Annex XII, plus the two Article 53(1) items that are public by their own terms.
156
+ *
157
+ * Rides inside {@link modelDocumentationSchema} and inherits its version.
158
+ *
159
+ * Every field here is one a developer integrating the model needs in order to
160
+ * decide whether they may use it and what they must say about it: what it is for,
161
+ * how it is distributed, what it is built out of, where the training-content
162
+ * summary and the copyright policy are, and whether it carries the systemic-risk
163
+ * classification that puts obligations on them too.
164
+ *
165
+ * The optional members are optional for a REASON stated in the parent record's
166
+ * refinement — Article 53(2) — and not because a value may be skipped.
167
+ */
168
+ exports.modelDownstreamDocumentationSchema = zod_1.z
169
+ .object({
170
+ /** Annex XI §1(2), Annex XII §1(a): the tasks the model is intended for. */
171
+ intendedTasks: zod_1.z.string().min(1).max(2000).optional(),
172
+ /** Annex XI §1(4), Annex XII §1(a). */
173
+ distributionMethods: zod_1.z.array(exports.modelDistributionMethodSchema).min(1),
174
+ /** Annex XI §1(5), reachable through Annex XII §1(a) ("points 1 to 5"). */
175
+ architecture: zod_1.z.string().min(1).max(500).optional(),
176
+ /** Annex XI §1(5): the number of parameters. */
177
+ parameterCount: zod_1.z.number().int().positive().safe().optional(),
178
+ /** Article 53(1)(d): the publicly available summary of training content. */
179
+ trainingDataSummaryUrl: identifiers_1.inferenceHttpsUrlSchema,
180
+ /**
181
+ * Article 53(1)(c): the policy for complying with Union copyright law,
182
+ * including the reservation of rights under Article 4(3) of Directive
183
+ * (EU) 2019/790. Required of every release — Article 53(2) does not exempt it.
184
+ */
185
+ copyrightPolicyUrl: identifiers_1.inferenceHttpsUrlSchema,
186
+ /** Article 51. */
187
+ systemicRisk: exports.modelSystemicRiskTierSchema,
188
+ /**
189
+ * Whether the release is under a free and open-source licence in the sense
190
+ * of Article 53(2). Distinct from `modelLicenseSchema.commercialUseAllowed`,
191
+ * which answers whether OXY may serve the model — a licence can permit
192
+ * commercial use and still not permit access, modification and
193
+ * redistribution of the weights, and it is the second question the exemption
194
+ * turns on.
195
+ */
196
+ freeAndOpenSourceRelease: zod_1.z.boolean(),
197
+ })
198
+ .strict();
199
+ /**
200
+ * The whole documentation record for one revision, as ingested.
201
+ *
202
+ * `.strict()`, because this is a compliance record arriving over the wire: a
203
+ * field silently dropped at the parse is a field the record does not contain,
204
+ * and "we accepted your documentation" would then be true of less than was sent.
205
+ *
206
+ * Not versioned on its own — it rides inside
207
+ * {@link modelReleaseIngestionRequestSchema} on the way in and inside
208
+ * {@link modelDocumentationSchema} on the way out, and inherits whichever
209
+ * message carries it.
210
+ */
211
+ exports.modelGpaiDocumentationSchema = exports.modelDownstreamDocumentationSchema
212
+ .extend({
213
+ /** Annex XI §2(b): the computational resources used for training. */
214
+ trainingComputeFlops: exports.trainingComputeFlopsSchema.optional(),
215
+ /** Annex XI §2(b): the training time. */
216
+ trainingTimeHours: zod_1.z.number().positive().safe().optional(),
217
+ /**
218
+ * Annex XI §2(c): the known or ESTIMATED energy consumption. The Act asks
219
+ * for an estimate where the figure is not known, so absence here means the
220
+ * Annex XI set is exempt rather than that the number was hard to obtain.
221
+ */
222
+ energyConsumptionMwh: zod_1.z.number().nonnegative().safe().optional(),
223
+ /**
224
+ * Article 55(1)(a): the model evaluation, including adversarial testing,
225
+ * performed for a model with systemic risk. A pointer, like every other
226
+ * document reference here — the catalogue holds no report.
227
+ */
228
+ adversarialTestingReportUrl: identifiers_1.inferenceHttpsUrlSchema.optional(),
229
+ })
230
+ .strict()
231
+ .superRefine((documentation, ctx) => {
232
+ // Article 53(2): the free-and-open-source exemption from 53(1)(a) and (b)
233
+ // does not apply to a model with systemic risk. So the Annex XI/XII set is
234
+ // required of everything else, and the ONE state that may omit it is a
235
+ // free-and-open-source release that is not designated.
236
+ const exempt = documentation.freeAndOpenSourceRelease && documentation.systemicRisk === 'not_designated';
237
+ if (!exempt) {
238
+ const required = [
239
+ 'intendedTasks',
240
+ 'architecture',
241
+ 'parameterCount',
242
+ 'trainingTimeHours',
243
+ 'energyConsumptionMwh',
244
+ ];
245
+ for (const field of required) {
246
+ if (documentation[field] === undefined) {
247
+ ctx.addIssue({
248
+ code: zod_1.z.ZodIssueCode.custom,
249
+ path: [field],
250
+ message: 'required by Annex XI unless the Article 53(2) free-and-open-source exemption applies, which it does not for this release',
251
+ });
252
+ }
253
+ }
254
+ }
255
+ // The presumption IS the compute figure (Article 51(2)). Declaring the tier
256
+ // without the figure asserts a threshold was crossed while withholding the
257
+ // only thing that says so.
258
+ if (documentation.systemicRisk === 'presumed_by_training_compute' &&
259
+ documentation.trainingComputeFlops === undefined) {
260
+ ctx.addIssue({
261
+ code: zod_1.z.ZodIssueCode.custom,
262
+ path: ['trainingComputeFlops'],
263
+ message: 'a systemic-risk presumption under Article 51(2) is the training-compute figure; declare it',
264
+ });
265
+ }
266
+ // The other direction, which is the one that matters: a release whose own
267
+ // declared compute is past the threshold cannot also declare that no
268
+ // classification applies. Without this the field pair would let the record
269
+ // contradict itself and still parse.
270
+ if (documentation.trainingComputeFlops !== undefined &&
271
+ documentation.systemicRisk === 'not_designated' &&
272
+ Number(documentation.trainingComputeFlops) >= exports.SYSTEMIC_RISK_COMPUTE_THRESHOLD_FLOPS) {
273
+ ctx.addIssue({
274
+ code: zod_1.z.ZodIssueCode.custom,
275
+ path: ['systemicRisk'],
276
+ message: 'training compute at or above 10^25 FLOP is presumed to be a model with systemic risk under Article 51(2)',
277
+ });
278
+ }
279
+ // Article 55(1)(a) applies to every model with systemic risk, however it
280
+ // acquired the classification.
281
+ if (documentation.systemicRisk !== 'not_designated' &&
282
+ documentation.adversarialTestingReportUrl === undefined) {
283
+ ctx.addIssue({
284
+ code: zod_1.z.ZodIssueCode.custom,
285
+ path: ['adversarialTestingReportUrl'],
286
+ message: 'a model with systemic risk documents its evaluation including adversarial testing (Article 55(1)(a))',
287
+ });
288
+ }
289
+ });
290
+ /* -------------------------------------------------------------------------- */
291
+ /* Ingestion */
292
+ /* -------------------------------------------------------------------------- */
293
+ /**
294
+ * What OXY states about the model line a release belongs to.
295
+ *
296
+ * A signed release manifest carries a revision, a licence, a provenance block,
297
+ * evaluations, safety metadata and an artifact inventory. It carries no
298
+ * CAPABILITY SHEET — no modalities, no `maxContextTokens`, none of the
299
+ * tool/streaming flags — and every one of those is required to create a model
300
+ * line at all.
301
+ *
302
+ * That is not a gap in the manifest. A capability sheet is a statement about what
303
+ * the Oxy API will serve, which is Oxy's to make and not the signer's: the same
304
+ * weights behind a different gateway answer a different set of these questions.
305
+ * So it travels beside the manifest, like the documentation record, and the
306
+ * signature keeps covering exactly the document its signer wrote.
307
+ *
308
+ * Ignored when the model line already exists — a release does not edit a model.
309
+ * The licence and provenance in the MANIFEST are checked against the stored ones
310
+ * instead, because those are claims about somebody's rights rather than Oxy's own
311
+ * editorial choices.
312
+ */
313
+ exports.modelLineDeclarationSchema = zod_1.z
314
+ .object({
315
+ displayName: zod_1.z.string().min(1).max(200),
316
+ description: zod_1.z.string().max(4000).optional(),
317
+ capabilities: catalogue_1.modelCapabilitiesSchema,
318
+ knowledgeCutoff: identifiers_1.inferenceDateSchema.optional(),
319
+ releasedOn: identifiers_1.inferenceDateSchema.optional(),
320
+ })
321
+ .strict();
322
+ /**
323
+ * The body of the release-ingestion request.
324
+ *
325
+ * The documentation and the capability sheet travel BESIDE the manifest rather
326
+ * than inside it, and that is the whole reason this wrapper exists.
327
+ * `aliaModelReleaseManifestSchema` is a SIGNED document: adding a field to it
328
+ * would change the bytes a signer covers and the version the data plane and Alia
329
+ * compile against, for records that are Oxy's own rather than the signer's.
330
+ * Keeping them separate means the signature still covers exactly what it covered.
331
+ *
332
+ * A signer that later chooses to cover the documentation too can: it would
333
+ * become a second signed document with its own manifest, which is a contract
334
+ * addition rather than a change to this one.
335
+ */
336
+ exports.modelReleaseIngestionRequestSchema = zod_1.z
337
+ .object({
338
+ /** See `version.ts`: an ingestion payload is a whole message on the wire. */
339
+ schemaVersion: zod_1.z.literal(1),
340
+ manifest: aliaModelRelease_1.aliaModelReleaseManifestSchema,
341
+ gpaiDocumentation: exports.modelGpaiDocumentationSchema,
342
+ model: exports.modelLineDeclarationSchema,
343
+ })
344
+ .strict();
345
+ /**
346
+ * What ingestion reports back.
347
+ *
348
+ * COUNTS for the artifacts and signatures rather than echoing them: the caller
349
+ * sent them and the interesting fact is that all of them landed. Echoing a
350
+ * signature would also make this response a place a credential-shaped value gets
351
+ * logged, for no gain.
352
+ *
353
+ * No verification field — see this module's header, and `aliaModelRelease.ts`.
354
+ */
355
+ exports.modelReleaseIngestionResultSchema = zod_1.z
356
+ .object({
357
+ /** See `version.ts`: served on its own, so it is versioned. */
358
+ schemaVersion: zod_1.z.literal(1),
359
+ releaseId: zod_1.z.string().min(1).max(128),
360
+ modelId: identifiers_1.modelIdSchema,
361
+ revision: identifiers_1.modelRevisionLabelSchema,
362
+ reference: identifiers_1.modelReferenceSchema,
363
+ /** Whether this request created the release, or found it already ingested. */
364
+ outcome: zod_1.z.enum(['ingested', 'already_ingested']),
365
+ artifactCount: zod_1.z.number().int().positive().safe(),
366
+ signatureCount: zod_1.z.number().int().positive().safe(),
367
+ evaluationCount: zod_1.z.number().int().nonnegative().safe(),
368
+ ingestedAt: identifiers_1.inferenceTimestampSchema,
369
+ })
370
+ .strict();
371
+ /* -------------------------------------------------------------------------- */
372
+ /* The customer-safe documentation view */
373
+ /* -------------------------------------------------------------------------- */
374
+ /**
375
+ * The documentation for ONE revision, as a downstream developer reads it.
376
+ *
377
+ * Revision-scoped, and that is the point of it existing beside
378
+ * `modelCatalogueEntrySchema`. The catalogue entry carries the documentation of
379
+ * whichever revision is CURRENT, so a customer who pinned
380
+ * `<publisher>/<model>@<revision>` — which the catalogue invites, and which the
381
+ * immutability trigger on `inference_model_revisions` exists to make meaningful —
382
+ * had no way to read the model card, evaluations or safety metadata of the
383
+ * revision they are actually calling. A model card that only describes the
384
+ * newest weights is the exact conflation ADR 0008 separates revisions to prevent.
385
+ *
386
+ * `license` and `provenance` are the MODEL's, repeated here rather than linked,
387
+ * for the same reason `modelCatalogueEntrySchema` repeats its fields: a
388
+ * projection that nests the operational descriptors is one accident of nesting
389
+ * away from serving an internal identifier.
390
+ */
391
+ exports.modelDocumentationSchema = zod_1.z
392
+ .object({
393
+ /** See `version.ts`: this is a public response shape. */
394
+ schemaVersion: zod_1.z.literal(1),
395
+ modelId: identifiers_1.modelIdSchema,
396
+ revision: identifiers_1.modelRevisionLabelSchema,
397
+ /** The exact string a customer pins. */
398
+ reference: identifiers_1.modelReferenceSchema,
399
+ /** Whether a bare `<publisher>/<model>` resolves to this revision today. */
400
+ isCurrentRevision: zod_1.z.boolean(),
401
+ releasedAt: identifiers_1.inferenceTimestampSchema,
402
+ retiredAt: identifiers_1.inferenceTimestampSchema.optional(),
403
+ modelCardUrl: identifiers_1.inferenceHttpsUrlSchema.optional(),
404
+ /**
405
+ * The digest of the served artifact, where Oxy hosts the weights.
406
+ *
407
+ * Customer-safe, deliberately: it is the one field on this view that lets a
408
+ * developer check that the weights they were handed are the weights the
409
+ * documentation describes, and a digest discloses nothing but the identity of
410
+ * bytes Oxy is already serving them.
411
+ */
412
+ artifactDigest: identifiers_1.sha256DigestSchema.optional(),
413
+ license: catalogue_1.modelLicenseSchema,
414
+ provenance: catalogue_1.modelProvenanceSchema,
415
+ evaluations: zod_1.z.array(catalogue_1.modelEvaluationResultSchema).default([]),
416
+ safety: catalogue_1.modelSafetyMetadataSchema.optional(),
417
+ /** Absent for a revision with no documentation record — i.e. every one Oxy did not release. */
418
+ gpai: exports.modelDownstreamDocumentationSchema.optional(),
419
+ })
420
+ .strict()
421
+ .superRefine((documentation, ctx) => {
422
+ // The same check `modelRevisionSchema` makes, and it is load-bearing for a
423
+ // different reason here: this view exists so a customer can read the
424
+ // documentation of the revision they PINNED, so a reference that resolves
425
+ // elsewhere would attach a model card to weights nobody is calling.
426
+ if (documentation.reference !== `${documentation.modelId}@${documentation.revision}`) {
427
+ ctx.addIssue({
428
+ code: zod_1.z.ZodIssueCode.custom,
429
+ path: ['reference'],
430
+ message: 'reference must be exactly <modelId>@<revision>',
431
+ });
432
+ }
433
+ });
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ /**
3
+ * Money and usage units for the inference contracts.
4
+ *
5
+ * The non-negotiable invariant this file exists to make structural: **customer
6
+ * charges never use floating-point values as the financial source of truth.**
7
+ * A JS `number` cannot represent `0.1 + 0.2` exactly, and an inference ledger
8
+ * adds millions of small amounts, so a float total is wrong by construction
9
+ * rather than by accident.
10
+ *
11
+ * Every amount and every price is one representation: {@link exactDecimalSchema},
12
+ * an exact decimal STRING at the scale ADR 0009 declares. Amounts are not
13
+ * integer minor units, and that is the ADR's decision rather than an oversight:
14
+ * one token costs several orders of magnitude less than one cent, so rounding
15
+ * per request would make a customer's bill depend on how their client chunked
16
+ * its work. Rounding happens ONCE, at the invoice boundary, and is itself a
17
+ * ledger entry.
18
+ *
19
+ * A string is also what the driver hands back — `postgres.js` decodes `NUMERIC`
20
+ * as a string — so keeping it a string on the wire means accidental JS
21
+ * arithmetic fails loudly instead of silently losing precision. Money
22
+ * arithmetic happens in SQL or in a decimal type, never in a JS `number`.
23
+ *
24
+ * Units are carried separately from money in every shape: a receipt says both
25
+ * "204 output tokens" and "0.003060000000 USD", and neither is derived from the
26
+ * other at read time. That separation is what lets a price version change
27
+ * without rewriting settled history.
28
+ *
29
+ * Decided in: docs/adr/0009-usage-reservation-and-settlement.md.
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.unitPriceSchema = exports.usageSourceSchema = exports.USAGE_SOURCES = exports.usageQuantitySchema = exports.usageUnitSchema = exports.USAGE_UNITS = exports.moneySchema = exports.exactDecimalSchema = exports.INFERENCE_MONEY_SCALE = exports.currencyCodeSchema = void 0;
33
+ const zod_1 = require("zod");
34
+ /* -------------------------------------------------------------------------- */
35
+ /* Money */
36
+ /* -------------------------------------------------------------------------- */
37
+ /** ISO 4217 alpha-3 currency code, e.g. `USD`. */
38
+ exports.currencyCodeSchema = zod_1.z
39
+ .string()
40
+ .regex(/^[A-Z]{3}$/, 'currency must be an ISO 4217 alpha-3 code');
41
+ /**
42
+ * The declared fractional scale of every amount and price in this contract, and
43
+ * of the `NUMERIC` columns the ledger stores them in.
44
+ *
45
+ * Twelve digits is sub-minor-unit precision by a wide margin: at $3 per million
46
+ * input tokens, one token costs `0.000003000000`, which this scale represents
47
+ * exactly. Amounts are compared and summed NUMERICALLY, never as text — `3.0`
48
+ * and `3.000000000000` are one amount written two ways.
49
+ */
50
+ exports.INFERENCE_MONEY_SCALE = 12;
51
+ /**
52
+ * An exact non-negative decimal, carried as a STRING so no parse step can turn
53
+ * it into a float on the way past. Up to 18 integer digits and
54
+ * {@link INFERENCE_MONEY_SCALE} fractional digits.
55
+ *
56
+ * Non-negative: direction is carried by the SHAPE — a receipt debits, a refund
57
+ * credits — so a stray sign can never silently invert an entry.
58
+ *
59
+ * No exponent form: `1e-6` and `0.000001` are the same number, but only one of
60
+ * them survives a naive string comparison, a cache key or a log grep intact.
61
+ *
62
+ * Branded, so a bare `string` is not assignable and an amount cannot arrive
63
+ * from string concatenation that was never checked. Producers construct one
64
+ * with `exactDecimalSchema.parse(value)`.
65
+ */
66
+ exports.exactDecimalSchema = zod_1.z
67
+ .string()
68
+ .regex(/^(?:0|[1-9][0-9]{0,17})(?:\.[0-9]{1,12})?$/, 'must be an exact non-negative decimal string without an exponent')
69
+ .brand();
70
+ /**
71
+ * An amount of money: the exact decimal plus the currency it is in.
72
+ *
73
+ * `.strict()` so a payload carrying a convenience float beside the exact value
74
+ * (`{ amount: '18.06', amountFloat: 18.06 }`) is REJECTED rather than stripped.
75
+ * A stripped float is the more dangerous outcome: it disappears silently here
76
+ * and survives in the producer, where it is the value somebody eventually
77
+ * displays.
78
+ */
79
+ exports.moneySchema = zod_1.z
80
+ .object({
81
+ amount: exports.exactDecimalSchema,
82
+ currency: exports.currencyCodeSchema,
83
+ })
84
+ .strict();
85
+ /* -------------------------------------------------------------------------- */
86
+ /* Usage units */
87
+ /* -------------------------------------------------------------------------- */
88
+ /**
89
+ * The closed set of units inference is metered in.
90
+ *
91
+ * **The units PARTITION a request: every unit counts material no other unit
92
+ * counts.** `cached_input_tokens` is not part of `input_tokens`, and
93
+ * `reasoning_tokens` is not part of `output_tokens` — they are siblings, not
94
+ * subsets. A request whose 10 000-token prompt was served 9 000 tokens from
95
+ * cache is reported as `input_tokens: 1000` beside `cached_input_tokens: 9000`,
96
+ * never as `input_tokens: 10000` beside it.
97
+ *
98
+ * That belongs to the definition rather than to a convention somewhere else,
99
+ * because settlement applies a price to EVERY reported unit and sums them
100
+ * (`inferenceLedger.service.ts`'s `computeCharge`). Under the partition rule
101
+ * that sum IS the request's cost, and a cached token can carry its own — lower
102
+ * — price. Under the nested reading the same sum charges the cached and
103
+ * reasoning tokens twice: once inside their parent and once on their own line.
104
+ * It fails silently, because every total still looks plausible and the receipt
105
+ * is still internally consistent, and on a reasoning model the reasoning tokens
106
+ * can dominate the completion, so the error is not marginal.
107
+ *
108
+ * **Every OpenAI-compatible provider reports the other way round**:
109
+ * `prompt_tokens` INCLUDES `prompt_tokens_details.cached_tokens`, and
110
+ * `completion_tokens` INCLUDES `completion_tokens_details.reasoning_tokens`.
111
+ * Normalising is the data plane's job and it is subtraction:
112
+ *
113
+ * ```text
114
+ * input_tokens = prompt_tokens - prompt_tokens_details.cached_tokens
115
+ * output_tokens = completion_tokens - completion_tokens_details.reasoning_tokens
116
+ * ```
117
+ *
118
+ * No refinement in this package can enforce it, and saying so is part of the
119
+ * rule: a nested report and a disjoint one are the same four non-negative
120
+ * integers, so no predicate over a single report can tell them apart. The two
121
+ * structural guards that DO exist — refining `cached <= input` and
122
+ * `reasoning <= output`, or deriving the parents instead of reporting them —
123
+ * both encode the nested reading, which is the one this rule rejects. What IS
124
+ * enforceable is the arithmetic that depends on the rule, and that is where the
125
+ * enforcement lives — `inferenceLedger.service.test.ts` prices a report in
126
+ * which cached and reasoning tokens are both non-zero and asserts the exact
127
+ * total, which the nested reading cannot produce.
128
+ *
129
+ * Where the public surface has to speak a nested dialect, the sum is put back
130
+ * at the boundary rather than the internal reading being bent to it
131
+ * (`routes/inferenceEdge.ts` renders `prompt_tokens` as
132
+ * `input_tokens + cached_input_tokens`).
133
+ *
134
+ * Time is carried in integer MILLISECONDS rather than seconds so that no unit
135
+ * quantity is ever fractional: a 12.5-second transcription is `12500`, exactly,
136
+ * and the "units are integers" rule holds for every modality instead of holding
137
+ * for tokens and being quietly broken by audio.
138
+ */
139
+ exports.USAGE_UNITS = [
140
+ 'input_tokens',
141
+ 'cached_input_tokens',
142
+ 'output_tokens',
143
+ 'reasoning_tokens',
144
+ 'requests',
145
+ 'images',
146
+ 'audio_input_milliseconds',
147
+ 'audio_output_milliseconds',
148
+ 'video_milliseconds',
149
+ 'characters',
150
+ 'embeddings',
151
+ ];
152
+ exports.usageUnitSchema = zod_1.z.enum(exports.USAGE_UNITS);
153
+ /**
154
+ * A metered quantity of ONE unit. Never money — a quantity carries no price and
155
+ * no currency, so a consumer cannot mistake a token count for an amount owed.
156
+ */
157
+ exports.usageQuantitySchema = zod_1.z
158
+ .object({
159
+ unit: exports.usageUnitSchema,
160
+ quantity: zod_1.z.number().int().nonnegative().safe(),
161
+ })
162
+ .strict();
163
+ /**
164
+ * Where a metered quantity came from.
165
+ *
166
+ * Kept explicit because the three are not interchangeable when a charge is
167
+ * disputed: `provider_reported` is the upstream's own count, `oxy_measured` is
168
+ * counted by the platform (streamed bytes, wall-clock milliseconds), and
169
+ * `estimated` is a reconstruction used when a provider returned no usage at
170
+ * all. An estimate that is indistinguishable from a reported number is an
171
+ * estimate nobody can later reconcile or refund against.
172
+ */
173
+ exports.USAGE_SOURCES = ['provider_reported', 'oxy_measured', 'estimated'];
174
+ exports.usageSourceSchema = zod_1.z.enum(exports.USAGE_SOURCES);
175
+ /**
176
+ * A price for one unit, as `amount` per `per` units — `per` because a price
177
+ * quoted per single token would need more fractional digits than it is worth
178
+ * ("$3.00 per 1000000 input_tokens" is how every provider quotes it, and how
179
+ * every customer reads it).
180
+ */
181
+ exports.unitPriceSchema = zod_1.z
182
+ .object({
183
+ unit: exports.usageUnitSchema,
184
+ amount: exports.exactDecimalSchema,
185
+ per: zod_1.z.number().int().positive().safe(),
186
+ currency: exports.currencyCodeSchema,
187
+ })
188
+ .strict();