@kaana.ai/contracts 0.0.0-stage → 4.4.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 (79) hide show
  1. package/LICENSE +673 -0
  2. package/NOTICE +21 -0
  3. package/dist/index.d.ts +29 -0
  4. package/dist/index.js +29 -0
  5. package/dist/internal/attribution.d.ts +116 -0
  6. package/dist/internal/attribution.js +82 -0
  7. package/dist/internal/canonical.d.ts +31 -0
  8. package/dist/internal/canonical.js +57 -0
  9. package/dist/internal/catalogue.d.ts +1059 -0
  10. package/dist/internal/catalogue.js +389 -0
  11. package/dist/internal/credentialControl.d.ts +329 -0
  12. package/dist/internal/credentialControl.js +256 -0
  13. package/dist/internal/decisions.d.ts +245 -0
  14. package/dist/internal/decisions.js +69 -0
  15. package/dist/internal/economics.d.ts +27 -0
  16. package/dist/internal/economics.js +26 -0
  17. package/dist/internal/embeddings.d.ts +164 -0
  18. package/dist/internal/embeddings.js +59 -0
  19. package/dist/internal/errors.d.ts +185 -0
  20. package/dist/internal/errors.js +211 -0
  21. package/dist/internal/grant.d.ts +699 -0
  22. package/dist/internal/grant.js +445 -0
  23. package/dist/internal/identifiers.d.ts +63 -0
  24. package/dist/internal/identifiers.js +65 -0
  25. package/dist/internal/privateAutoExecution.d.ts +158 -0
  26. package/dist/internal/privateAutoExecution.js +188 -0
  27. package/dist/internal/realtime.d.ts +1005 -0
  28. package/dist/internal/realtime.js +187 -0
  29. package/dist/internal/request.d.ts +1079 -0
  30. package/dist/internal/request.js +404 -0
  31. package/dist/internal/routing.d.ts +90 -0
  32. package/dist/internal/routing.js +139 -0
  33. package/dist/internal/scopedExecution.d.ts +74 -0
  34. package/dist/internal/scopedExecution.js +61 -0
  35. package/dist/internal/settlement.d.ts +205 -0
  36. package/dist/internal/settlement.js +71 -0
  37. package/dist/internal/streamEvents.d.ts +422 -0
  38. package/dist/internal/streamEvents.js +171 -0
  39. package/dist/internal/usage.d.ts +127 -0
  40. package/dist/internal/usage.js +109 -0
  41. package/dist/internal.d.ts +30 -0
  42. package/dist/internal.js +30 -0
  43. package/dist/public/audio.d.ts +347 -0
  44. package/dist/public/audio.js +236 -0
  45. package/dist/public/audioFormats.d.ts +64 -0
  46. package/dist/public/audioFormats.js +60 -0
  47. package/dist/public/catalogue.d.ts +914 -0
  48. package/dist/public/catalogue.js +516 -0
  49. package/dist/public/chatCompletions.d.ts +381 -0
  50. package/dist/public/chatCompletions.js +321 -0
  51. package/dist/public/decisions.d.ts +182 -0
  52. package/dist/public/decisions.js +257 -0
  53. package/dist/public/embeddings.d.ts +34 -0
  54. package/dist/public/embeddings.js +66 -0
  55. package/dist/public/errors.d.ts +195 -0
  56. package/dist/public/errors.js +274 -0
  57. package/dist/public/generations.d.ts +330 -0
  58. package/dist/public/generations.js +121 -0
  59. package/dist/public/identifiers.d.ts +139 -0
  60. package/dist/public/identifiers.js +160 -0
  61. package/dist/public/images.d.ts +36 -0
  62. package/dist/public/images.js +36 -0
  63. package/dist/public/messages.d.ts +297 -0
  64. package/dist/public/messages.js +251 -0
  65. package/dist/public/money.d.ts +308 -0
  66. package/dist/public/money.js +237 -0
  67. package/dist/public/policy.d.ts +19 -0
  68. package/dist/public/policy.js +20 -0
  69. package/dist/public/realtime.d.ts +2006 -0
  70. package/dist/public/realtime.js +797 -0
  71. package/dist/public/responses.d.ts +253 -0
  72. package/dist/public/responses.js +95 -0
  73. package/dist/public/streamEvents.d.ts +372 -0
  74. package/dist/public/streamEvents.js +157 -0
  75. package/dist/public/version.d.ts +184 -0
  76. package/dist/public/version.js +184 -0
  77. package/openapi.json +6967 -0
  78. package/package.json +44 -4
  79. package/README.md +0 -3
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Messages, content parts, tools and generation controls shared by every
3
+ * public request and by the signed envelope.
4
+ *
5
+ * Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/request.ts`) by Kaana ADR 0002.
6
+ */
7
+ import { z } from 'zod';
8
+ import { reasoningEffortSchema } from './catalogue.js';
9
+ /**
10
+ * Where binary or remote content comes from.
11
+ *
12
+ * `url` is fetched by the data plane; `inline` carries base64 the customer sent
13
+ * with the request. Both are transient: neither is persisted by default, and
14
+ * neither appears in a receipt, a log line or a telemetry event.
15
+ */
16
+ export const inferenceContentSourceSchema = z.discriminatedUnion('kind', [
17
+ z.object({ kind: z.literal('url'), url: z.string().min(1).max(4096) }).strict(),
18
+ z
19
+ .object({
20
+ kind: z.literal('inline'),
21
+ mediaType: z.string().min(1).max(255),
22
+ data: z.string().min(1),
23
+ })
24
+ .strict(),
25
+ ]);
26
+ /**
27
+ * One part of a message's content. A message is always a list of parts.
28
+ *
29
+ * ## `refusal` is a member and `reasoning` is not, and the asymmetry is the point
30
+ *
31
+ * A model that DECLINES says why, and those words are meant for the customer:
32
+ * they are the difference between "rephrase this" and "stop asking". Before this
33
+ * member existed there was nowhere in an {@link InferenceMessage} to put them, so
34
+ * a non-streaming fold kept `finishReason: 'refusal'` and dropped the sentence —
35
+ * the customer learned they were refused and not why, while a streaming caller of
36
+ * the same request got the whole explanation on the `refusal` delta channel.
37
+ *
38
+ * Reasoning is the opposite case and stays absent. It is the model's private
39
+ * working, and a `text` part is where somebody would put it — which renders
40
+ * private reasoning to the customer AS the answer, the product bug the delta
41
+ * channels exist to prevent. An opaque per-block reasoning blob crossing this
42
+ * boundary needs a home nobody has chosen yet, and inventing one here would
43
+ * choose it by accident.
44
+ *
45
+ * Both public dialects can carry a refusal, which is what separates the two
46
+ * cases at the boundary as well as in principle: `refusal` is OpenAI's OWN field
47
+ * in both of its shapes (`delta.refusal` streaming, `message.refusal`
48
+ * non-streaming), while reasoning has no OpenAI field at all — the
49
+ * `reasoning_content`/`reasoning` spellings an OpenAI-compatible provider emits
50
+ * are provider extensions. So carrying the refusal costs no dialect its
51
+ * standard-client parseability, and carrying reasoning would.
52
+ *
53
+ * The part is a MEMBER rather than a field because a refusal need not have text:
54
+ * an Anthropic `stop_reason: "refusal"` maps to the finish reason and separates
55
+ * no words from the answer, while an OpenAI-compatible `refusal` does. A required
56
+ * field would force the first provider to invent a sentence.
57
+ */
58
+ export const inferenceContentPartSchema = z.discriminatedUnion('type', [
59
+ z.object({ type: z.literal('text'), text: z.string() }).strict(),
60
+ z
61
+ .object({
62
+ type: z.literal('image'),
63
+ source: inferenceContentSourceSchema,
64
+ /** Provider-independent hint; providers that ignore it are unaffected. */
65
+ detail: z.enum(['auto', 'low', 'high']).optional(),
66
+ })
67
+ .strict(),
68
+ z.object({ type: z.literal('audio'), source: inferenceContentSourceSchema }).strict(),
69
+ z
70
+ .object({
71
+ type: z.literal('file'),
72
+ source: inferenceContentSourceSchema,
73
+ filename: z.string().max(255).optional(),
74
+ })
75
+ .strict(),
76
+ /**
77
+ * A model's explanation for declining. Its own part, never a `text` one, so no
78
+ * renderer can present a refusal as the answer that was asked for.
79
+ */
80
+ z.object({ type: z.literal('refusal'), text: z.string() }).strict(),
81
+ ]);
82
+ /**
83
+ * A tool call an assistant made, in the normalized form.
84
+ *
85
+ * `arguments` is the JSON TEXT the model emitted, not a parsed object: models
86
+ * emit invalid JSON often enough that parsing it here would turn a recoverable
87
+ * model mistake into a rejected message.
88
+ */
89
+ export const inferenceToolCallSchema = z
90
+ .object({
91
+ id: z.string().min(1).max(128),
92
+ name: z.string().min(1).max(128),
93
+ arguments: z.string(),
94
+ })
95
+ .strict();
96
+ export const inferenceMessageRoleSchema = z.enum([
97
+ 'system',
98
+ 'developer',
99
+ 'user',
100
+ 'assistant',
101
+ 'tool',
102
+ ]);
103
+ /**
104
+ * One normalized message.
105
+ *
106
+ * The role-specific fields are refined rather than modelled as a discriminated
107
+ * union so the shape stays the one a caller recognises from the OpenAI-style
108
+ * dialects; the refinement is what stops `toolCallId` from riding on a user
109
+ * message, where every provider would silently ignore it.
110
+ */
111
+ export const inferenceMessageSchema = z
112
+ .object({
113
+ role: inferenceMessageRoleSchema,
114
+ content: z.array(inferenceContentPartSchema),
115
+ /** Participant name, where the dialect supports naming participants. */
116
+ name: z.string().max(128).optional(),
117
+ /** The tool call this message answers. Required on, and only on, `tool`. */
118
+ toolCallId: z.string().min(1).max(128).optional(),
119
+ /** Tool calls the assistant made. Only on `assistant`. */
120
+ toolCalls: z.array(inferenceToolCallSchema).optional(),
121
+ })
122
+ .strict()
123
+ .superRefine((message, ctx) => {
124
+ if (message.role === 'tool' && message.toolCallId === undefined) {
125
+ ctx.addIssue({
126
+ code: 'custom',
127
+ path: ['toolCallId'],
128
+ message: 'a tool message must name the tool call it answers',
129
+ });
130
+ }
131
+ if (message.role !== 'tool' && message.toolCallId !== undefined) {
132
+ ctx.addIssue({
133
+ code: 'custom',
134
+ path: ['toolCallId'],
135
+ message: 'only a tool message answers a tool call',
136
+ });
137
+ }
138
+ if (message.role !== 'assistant' && message.toolCalls !== undefined) {
139
+ ctx.addIssue({
140
+ code: 'custom',
141
+ path: ['toolCalls'],
142
+ message: 'only an assistant message makes tool calls',
143
+ });
144
+ }
145
+ // Same rule as `toolCalls`, for the same reason: only the assistant can
146
+ // decline, so a refusal on any other role is a part every provider would
147
+ // silently ignore — and one a renderer might not.
148
+ if (message.role !== 'assistant') {
149
+ for (const [index, part] of message.content.entries()) {
150
+ if (part.type === 'refusal') {
151
+ ctx.addIssue({
152
+ code: 'custom',
153
+ path: ['content', index, 'type'],
154
+ message: 'only an assistant message carries a refusal',
155
+ });
156
+ }
157
+ }
158
+ }
159
+ });
160
+ /** Sampling parameters, all optional: absent means the route's own default. */
161
+ export const samplingParametersSchema = z
162
+ .object({
163
+ temperature: z.number().min(0).max(2).optional(),
164
+ topP: z.number().min(0).max(1).optional(),
165
+ topK: z.number().int().positive().safe().optional(),
166
+ frequencyPenalty: z.number().min(-2).max(2).optional(),
167
+ presencePenalty: z.number().min(-2).max(2).optional(),
168
+ /** A seed makes a request reproducible on providers that honour one. */
169
+ seed: z.number().int().safe().optional(),
170
+ stopSequences: z.array(z.string().min(1).max(256)).max(8).optional(),
171
+ })
172
+ .strict();
173
+ /**
174
+ * A tool the model may call. `parameters` is a JSON Schema document, carried
175
+ * as an opaque object: validating the customer's JSON Schema against a meta
176
+ * schema here would reject documents providers accept.
177
+ */
178
+ export const toolDefinitionSchema = z
179
+ .object({
180
+ type: z.literal('function'),
181
+ name: z.string().min(1).max(128),
182
+ description: z.string().max(2000).optional(),
183
+ parameters: z.record(z.string(), z.unknown()),
184
+ /** Ask the provider to enforce the schema, where it supports enforcement. */
185
+ strict: z.boolean().optional(),
186
+ })
187
+ .strict();
188
+ /** Whether, and which, tool the model must call. */
189
+ export const toolChoiceSchema = z.union([
190
+ z.enum(['auto', 'none', 'required']),
191
+ z.object({ type: z.literal('function'), name: z.string().min(1).max(128) }).strict(),
192
+ ]);
193
+ /** Structured-output request: free text, any JSON object, or a named schema. */
194
+ export const responseFormatSchema = z.discriminatedUnion('type', [
195
+ z.object({ type: z.literal('text') }).strict(),
196
+ z.object({ type: z.literal('json_object') }).strict(),
197
+ z
198
+ .object({
199
+ type: z.literal('json_schema'),
200
+ name: z.string().min(1).max(128),
201
+ schema: z.record(z.string(), z.unknown()),
202
+ strict: z.boolean(),
203
+ })
204
+ .strict(),
205
+ ]);
206
+ /**
207
+ * The caller's reasoning control. Strict, like every leaf: a provider-specific
208
+ * knob (`budget_tokens`, `summary`) is refused rather than silently dropped.
209
+ *
210
+ * Oxy forwards it only after checking the resolved model advertises the effort
211
+ * in its catalogue `reasoningEfforts`; Kaana translates it per provider.
212
+ */
213
+ export const inferenceReasoningSchema = z.object({ effort: reasoningEffortSchema }).strict();
214
+ /** Parameters for text-to-speech, preserved in the signed request. */
215
+ export const inferenceSpeechParametersSchema = z
216
+ .object({
217
+ voice: z.string().min(1).max(64),
218
+ responseFormat: z.enum(['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm']),
219
+ speed: z.number().min(0.25).max(4).optional(),
220
+ })
221
+ .strict();
222
+ /**
223
+ * Spoken output from a CONVERSATIONAL model (contract set 3.2.0) — an audio chat
224
+ * model answering in its own voice, as opposed to `speech`, which reads given
225
+ * text aloud. Presence is the request: a chat request carrying this asks for
226
+ * the answer as audio AND its transcript, which is the only combination the
227
+ * audio chat models produce (OpenAI's `modalities: ["text", "audio"]`).
228
+ *
229
+ * The audio streams as `audio` events and the words as `delta` events on the
230
+ * `output_audio_transcript` channel — never as `output_text`, because a
231
+ * transcript of speech the customer is already hearing is not a second answer.
232
+ * `pcm` is the only format an audio chat model can STREAM; the refinement on the
233
+ * envelope holds that rather than leaving a provider to refuse it mid-flight.
234
+ */
235
+ export const inferenceAudioOutputParametersSchema = z
236
+ .object({
237
+ voice: z.string().min(1).max(64),
238
+ format: z.enum(['wav', 'mp3', 'flac', 'opus', 'pcm']),
239
+ })
240
+ .strict();
241
+ /**
242
+ * Customer-supplied cost-attribution labels, echoed on the generation record.
243
+ *
244
+ * Bounded in both directions because they are stored and read back: sixteen
245
+ * keys is a team/feature/environment tag set, not a place to smuggle a payload.
246
+ *
247
+ * Moved from the Oxy edge (`packages/api/src/schemas/inferenceEdge.schemas.ts`).
248
+ */
249
+ export const requestLabelsSchema = z
250
+ .record(z.string(), z.string().max(256))
251
+ .refine((labels) => Object.keys(labels).length <= 16, 'at most 16 labels may be attached to one request');
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Money, prices and usage units.
3
+ *
4
+ * Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/money.ts`) by Kaana ADR 0002.
5
+ * `priceSnapshotSchema` comes from `priceVersion.ts`; the price VERSION record
6
+ * itself stays in `@oxy.so/contracts`, because Oxy owns the price lists.
7
+ */
8
+ import { z } from 'zod';
9
+ /** ISO 4217 alpha-3 currency code, e.g. `USD`. */
10
+ export declare const currencyCodeSchema: z.ZodString;
11
+ /**
12
+ * The declared fractional scale of every amount and price in this contract, and
13
+ * of the `NUMERIC` columns the ledger stores them in.
14
+ *
15
+ * Twelve digits is sub-minor-unit precision by a wide margin: at $3 per million
16
+ * input tokens, one token costs `0.000003000000`, which this scale represents
17
+ * exactly. Amounts are compared and summed NUMERICALLY, never as text — `3.0`
18
+ * and `3.000000000000` are one amount written two ways.
19
+ */
20
+ export declare const INFERENCE_MONEY_SCALE = 12;
21
+ /**
22
+ * An exact non-negative decimal, carried as a STRING so no parse step can turn
23
+ * it into a float on the way past. Up to 18 integer digits and
24
+ * {@link INFERENCE_MONEY_SCALE} fractional digits.
25
+ *
26
+ * Non-negative: direction is carried by the SHAPE — a receipt debits, a refund
27
+ * credits — so a stray sign can never silently invert an entry.
28
+ *
29
+ * No exponent form: `1e-6` and `0.000001` are the same number, but only one of
30
+ * them survives a naive string comparison, a cache key or a log grep intact.
31
+ *
32
+ * Branded, so a bare `string` is not assignable and an amount cannot arrive
33
+ * from string concatenation that was never checked. Producers construct one
34
+ * with `exactDecimalSchema.parse(value)`.
35
+ */
36
+ export declare const exactDecimalSchema: z.core.$ZodBranded<z.ZodString, "ExactDecimal", "out">;
37
+ /**
38
+ * An amount of money: the exact decimal plus the currency it is in.
39
+ *
40
+ * `.strict()` so a payload carrying a convenience float beside the exact value
41
+ * (`{ amount: '18.06', amountFloat: 18.06 }`) is REJECTED rather than stripped.
42
+ * A stripped float is the more dangerous outcome: it disappears silently here
43
+ * and survives in the producer, where it is the value somebody eventually
44
+ * displays.
45
+ */
46
+ export declare const moneySchema: z.ZodObject<{
47
+ amount: z.core.$ZodBranded<z.ZodString, "ExactDecimal", "out">;
48
+ currency: z.ZodString;
49
+ }, z.core.$strict>;
50
+ /**
51
+ * The closed set of units inference is metered in.
52
+ *
53
+ * **The units PARTITION a request: every unit counts material no other unit
54
+ * counts.** `cached_input_tokens` is not part of `input_tokens`, and
55
+ * `reasoning_tokens` is not part of `output_tokens` — they are siblings, not
56
+ * subsets. A request whose 10 000-token prompt was served 9 000 tokens from
57
+ * cache is reported as `input_tokens: 1000` beside `cached_input_tokens: 9000`,
58
+ * never as `input_tokens: 10000` beside it.
59
+ *
60
+ * That belongs to the definition rather than to a convention somewhere else,
61
+ * because settlement applies a price to EVERY reported unit and sums them
62
+ * (`inferenceLedger.service.ts`'s `computeCharge`). Under the partition rule
63
+ * that sum IS the request's cost, and a cached token can carry its own — lower
64
+ * — price. Under the nested reading the same sum charges the cached and
65
+ * reasoning tokens twice: once inside their parent and once on their own line.
66
+ * It fails silently, because every total still looks plausible and the receipt
67
+ * is still internally consistent, and on a reasoning model the reasoning tokens
68
+ * can dominate the completion, so the error is not marginal.
69
+ *
70
+ * **Every OpenAI-compatible provider reports the other way round**:
71
+ * `prompt_tokens` INCLUDES `prompt_tokens_details.cached_tokens`, and
72
+ * `completion_tokens` INCLUDES `completion_tokens_details.reasoning_tokens`.
73
+ * Normalising is the data plane's job and it is subtraction:
74
+ *
75
+ * ```text
76
+ * input_tokens = prompt_tokens - prompt_tokens_details.cached_tokens
77
+ * output_tokens = completion_tokens - completion_tokens_details.reasoning_tokens
78
+ * ```
79
+ *
80
+ * **Audio tokens are siblings too** (added in contract set 3.2.0). A model that
81
+ * consumes or produces audio AS TOKENS — OpenAI's audio chat and Realtime
82
+ * models — reports them inside the same totals, and they are priced several
83
+ * times higher than text tokens, so folding them into `input_tokens` would
84
+ * under-charge by exactly the audio. They are subtracted out the same way:
85
+ *
86
+ * ```text
87
+ * cached_audio_input_tokens = prompt_tokens_details.cached_tokens_details.audio_tokens
88
+ * audio_input_tokens = prompt_tokens_details.audio_tokens - cached_audio_input_tokens
89
+ * input_tokens = prompt_tokens - (text AND audio cached) - audio_input_tokens
90
+ * audio_output_tokens = completion_tokens_details.audio_tokens
91
+ * output_tokens = completion_tokens - reasoning_tokens - audio_output_tokens
92
+ * ```
93
+ *
94
+ * `audio_input_milliseconds` and `audio_output_milliseconds` remain the units
95
+ * for audio metered by DURATION (file transcription, speech synthesis). A
96
+ * report carries one reading of a given audio, never both: the same audio
97
+ * reported as tokens and as milliseconds is the double charge this rule exists
98
+ * to prevent.
99
+ *
100
+ * **`session_milliseconds` is session wall-clock time** (added in contract set
101
+ * 3.3.0): wall-clock milliseconds a realtime session was open with the upstream
102
+ * provider; reported only by a provider that bills session time, and never
103
+ * together with a per-audio reading of the same span. xAI's Voice Agent API
104
+ * bills a session using server-side voice activity detection for its whole
105
+ * duration, speech or silence, which is neither audio sent nor audio received:
106
+ * reporting that clock as `audio_input_milliseconds` would misdescribe the
107
+ * charge, and reporting both would bill the same span twice. It is measured by
108
+ * the data plane from the upstream session's open to its close, never
109
+ * estimated from the audio, and a provider billed by the audio it carries (or
110
+ * by tokens) never reports it.
111
+ *
112
+ * **`input_transcription_milliseconds` is a second model's reading** (added in
113
+ * contract set 4.1.0): milliseconds of a realtime session's input audio that
114
+ * the deployment's own TRANSCRIPTION model transcribed, when the provider bills
115
+ * that model apart from the session model. OpenAI runs input transcription on
116
+ * a separate ASR model "billed according to the ASR model's pricing rather
117
+ * than the realtime model's", so the session model's `audio_input_tokens` and
118
+ * this unit are two charges for two models, each read once — which is why the
119
+ * partition rule holds: no other unit counts the transcription model's work.
120
+ * Reporting the ASR duration as `audio_input_milliseconds` beside the session
121
+ * model's audio tokens would be the "same audio as tokens and as milliseconds"
122
+ * this rule forbids, and folding it into the tokens would price it at the
123
+ * wrong model's rate. A provider whose session price already includes the
124
+ * transcription (xAI's Voice Agent) never reports it. A transcription model
125
+ * billed by TOKENS is a charge this unit does not express: the data plane
126
+ * declares only duration-billed transcription models and ends a session whose
127
+ * provider reports tokens for one, rather than approximate the charge.
128
+ *
129
+ * No refinement in this package can enforce it, and saying so is part of the
130
+ * rule: a nested report and a disjoint one are the same four non-negative
131
+ * integers, so no predicate over a single report can tell them apart. The two
132
+ * structural guards that DO exist — refining `cached <= input` and
133
+ * `reasoning <= output`, or deriving the parents instead of reporting them —
134
+ * both encode the nested reading, which is the one this rule rejects. What IS
135
+ * enforceable is the arithmetic that depends on the rule, and that is where the
136
+ * enforcement lives — `inferenceLedger.service.test.ts` prices a report in
137
+ * which cached and reasoning tokens are both non-zero and asserts the exact
138
+ * total, which the nested reading cannot produce.
139
+ *
140
+ * Where the public surface has to speak a nested dialect, the sum is put back
141
+ * at the boundary rather than the internal reading being bent to it
142
+ * (`routes/inferenceEdge.ts` renders `prompt_tokens` as
143
+ * `input_tokens + cached_input_tokens`).
144
+ *
145
+ * Time is carried in integer MILLISECONDS rather than seconds so that no unit
146
+ * quantity is ever fractional: a 12.5-second transcription is `12500`, exactly,
147
+ * and the "units are integers" rule holds for every modality instead of holding
148
+ * for tokens and being quietly broken by audio.
149
+ */
150
+ export declare const USAGE_UNITS: readonly ["input_tokens", "cached_input_tokens", "output_tokens", "reasoning_tokens", "requests", "images", "audio_input_milliseconds", "audio_output_milliseconds", "video_milliseconds", "characters", "embeddings", "audio_input_tokens", "cached_audio_input_tokens", "audio_output_tokens", "session_milliseconds", "input_transcription_milliseconds"];
151
+ export declare const usageUnitSchema: z.ZodEnum<{
152
+ input_tokens: "input_tokens";
153
+ cached_input_tokens: "cached_input_tokens";
154
+ output_tokens: "output_tokens";
155
+ reasoning_tokens: "reasoning_tokens";
156
+ requests: "requests";
157
+ images: "images";
158
+ audio_input_milliseconds: "audio_input_milliseconds";
159
+ audio_output_milliseconds: "audio_output_milliseconds";
160
+ video_milliseconds: "video_milliseconds";
161
+ characters: "characters";
162
+ embeddings: "embeddings";
163
+ audio_input_tokens: "audio_input_tokens";
164
+ cached_audio_input_tokens: "cached_audio_input_tokens";
165
+ audio_output_tokens: "audio_output_tokens";
166
+ session_milliseconds: "session_milliseconds";
167
+ input_transcription_milliseconds: "input_transcription_milliseconds";
168
+ }>;
169
+ /**
170
+ * A metered quantity of ONE unit. Never money — a quantity carries no price and
171
+ * no currency, so a consumer cannot mistake a token count for an amount owed.
172
+ */
173
+ export declare const usageQuantitySchema: z.ZodObject<{
174
+ unit: z.ZodEnum<{
175
+ input_tokens: "input_tokens";
176
+ cached_input_tokens: "cached_input_tokens";
177
+ output_tokens: "output_tokens";
178
+ reasoning_tokens: "reasoning_tokens";
179
+ requests: "requests";
180
+ images: "images";
181
+ audio_input_milliseconds: "audio_input_milliseconds";
182
+ audio_output_milliseconds: "audio_output_milliseconds";
183
+ video_milliseconds: "video_milliseconds";
184
+ characters: "characters";
185
+ embeddings: "embeddings";
186
+ audio_input_tokens: "audio_input_tokens";
187
+ cached_audio_input_tokens: "cached_audio_input_tokens";
188
+ audio_output_tokens: "audio_output_tokens";
189
+ session_milliseconds: "session_milliseconds";
190
+ input_transcription_milliseconds: "input_transcription_milliseconds";
191
+ }>;
192
+ quantity: z.ZodNumber;
193
+ }, z.core.$strict>;
194
+ /**
195
+ * Where a metered quantity came from.
196
+ *
197
+ * Kept explicit because the three are not interchangeable when a charge is
198
+ * disputed: `provider_reported` is the upstream's own count, `oxy_measured` is
199
+ * counted by the platform (streamed bytes, wall-clock milliseconds), and
200
+ * `estimated` is a reconstruction used when a provider returned no usage at
201
+ * all. An estimate that is indistinguishable from a reported number is an
202
+ * estimate nobody can later reconcile or refund against.
203
+ */
204
+ export declare const USAGE_SOURCES: readonly ["provider_reported", "oxy_measured", "estimated"];
205
+ export declare const usageSourceSchema: z.ZodEnum<{
206
+ provider_reported: "provider_reported";
207
+ oxy_measured: "oxy_measured";
208
+ estimated: "estimated";
209
+ }>;
210
+ /**
211
+ * A price for one unit, as `amount` per `per` units — `per` because a price
212
+ * quoted per single token would need more fractional digits than it is worth
213
+ * ("$3.00 per 1000000 input_tokens" is how every provider quotes it, and how
214
+ * every customer reads it).
215
+ */
216
+ export declare const unitPriceSchema: z.ZodObject<{
217
+ unit: z.ZodEnum<{
218
+ input_tokens: "input_tokens";
219
+ cached_input_tokens: "cached_input_tokens";
220
+ output_tokens: "output_tokens";
221
+ reasoning_tokens: "reasoning_tokens";
222
+ requests: "requests";
223
+ images: "images";
224
+ audio_input_milliseconds: "audio_input_milliseconds";
225
+ audio_output_milliseconds: "audio_output_milliseconds";
226
+ video_milliseconds: "video_milliseconds";
227
+ characters: "characters";
228
+ embeddings: "embeddings";
229
+ audio_input_tokens: "audio_input_tokens";
230
+ cached_audio_input_tokens: "cached_audio_input_tokens";
231
+ audio_output_tokens: "audio_output_tokens";
232
+ session_milliseconds: "session_milliseconds";
233
+ input_transcription_milliseconds: "input_transcription_milliseconds";
234
+ }>;
235
+ amount: z.core.$ZodBranded<z.ZodString, "ExactDecimal", "out">;
236
+ per: z.ZodNumber;
237
+ currency: z.ZodString;
238
+ }, z.core.$strict>;
239
+ /**
240
+ * The price snapshot a settled receipt keeps.
241
+ *
242
+ * The unit prices are COPIED onto the receipt, not just referenced, so a receipt
243
+ * remains readable even if the price version record is later archived, and so
244
+ * that a mistake in the copy is visible as a disagreement with the version it
245
+ * names rather than silently invisible.
246
+ */
247
+ export declare const priceSnapshotSchema: z.ZodObject<{
248
+ priceVersionId: z.ZodString;
249
+ currency: z.ZodString;
250
+ unitPrices: z.ZodArray<z.ZodObject<{
251
+ unit: z.ZodEnum<{
252
+ input_tokens: "input_tokens";
253
+ cached_input_tokens: "cached_input_tokens";
254
+ output_tokens: "output_tokens";
255
+ reasoning_tokens: "reasoning_tokens";
256
+ requests: "requests";
257
+ images: "images";
258
+ audio_input_milliseconds: "audio_input_milliseconds";
259
+ audio_output_milliseconds: "audio_output_milliseconds";
260
+ video_milliseconds: "video_milliseconds";
261
+ characters: "characters";
262
+ embeddings: "embeddings";
263
+ audio_input_tokens: "audio_input_tokens";
264
+ cached_audio_input_tokens: "cached_audio_input_tokens";
265
+ audio_output_tokens: "audio_output_tokens";
266
+ session_milliseconds: "session_milliseconds";
267
+ input_transcription_milliseconds: "input_transcription_milliseconds";
268
+ }>;
269
+ amount: z.core.$ZodBranded<z.ZodString, "ExactDecimal", "out">;
270
+ per: z.ZodNumber;
271
+ currency: z.ZodString;
272
+ }, z.core.$strict>>;
273
+ }, z.core.$strict>;
274
+ /**
275
+ * What a request consumed, as a public response reports it: one quantity per
276
+ * unit, each unit at most once, the units partitioning the request (see
277
+ * {@link USAGE_UNITS}). New in contract set 4.0.0.
278
+ */
279
+ export declare const usageSchema: z.ZodArray<z.ZodObject<{
280
+ unit: z.ZodEnum<{
281
+ input_tokens: "input_tokens";
282
+ cached_input_tokens: "cached_input_tokens";
283
+ output_tokens: "output_tokens";
284
+ reasoning_tokens: "reasoning_tokens";
285
+ requests: "requests";
286
+ images: "images";
287
+ audio_input_milliseconds: "audio_input_milliseconds";
288
+ audio_output_milliseconds: "audio_output_milliseconds";
289
+ video_milliseconds: "video_milliseconds";
290
+ characters: "characters";
291
+ embeddings: "embeddings";
292
+ audio_input_tokens: "audio_input_tokens";
293
+ cached_audio_input_tokens: "cached_audio_input_tokens";
294
+ audio_output_tokens: "audio_output_tokens";
295
+ session_milliseconds: "session_milliseconds";
296
+ input_transcription_milliseconds: "input_transcription_milliseconds";
297
+ }>;
298
+ quantity: z.ZodNumber;
299
+ }, z.core.$strict>>;
300
+ export type CurrencyCode = z.infer<typeof currencyCodeSchema>;
301
+ export type ExactDecimal = z.infer<typeof exactDecimalSchema>;
302
+ export type Money = z.infer<typeof moneySchema>;
303
+ export type UsageUnit = z.infer<typeof usageUnitSchema>;
304
+ export type UsageSource = z.infer<typeof usageSourceSchema>;
305
+ export type UsageQuantity = z.infer<typeof usageQuantitySchema>;
306
+ export type UnitPrice = z.infer<typeof unitPriceSchema>;
307
+ export type PriceSnapshot = z.infer<typeof priceSnapshotSchema>;
308
+ export type Usage = z.infer<typeof usageSchema>;