@kaana.ai/contracts 0.0.0-stage → 4.3.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.
- package/LICENSE +673 -0
- package/NOTICE +21 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +29 -0
- package/dist/internal/attribution.d.ts +116 -0
- package/dist/internal/attribution.js +82 -0
- package/dist/internal/canonical.d.ts +31 -0
- package/dist/internal/canonical.js +57 -0
- package/dist/internal/catalogue.d.ts +1030 -0
- package/dist/internal/catalogue.js +349 -0
- package/dist/internal/credentialControl.d.ts +329 -0
- package/dist/internal/credentialControl.js +256 -0
- package/dist/internal/decisions.d.ts +245 -0
- package/dist/internal/decisions.js +69 -0
- package/dist/internal/economics.d.ts +27 -0
- package/dist/internal/economics.js +26 -0
- package/dist/internal/embeddings.d.ts +164 -0
- package/dist/internal/embeddings.js +59 -0
- package/dist/internal/errors.d.ts +185 -0
- package/dist/internal/errors.js +211 -0
- package/dist/internal/grant.d.ts +696 -0
- package/dist/internal/grant.js +437 -0
- package/dist/internal/identifiers.d.ts +63 -0
- package/dist/internal/identifiers.js +65 -0
- package/dist/internal/privateAutoExecution.d.ts +158 -0
- package/dist/internal/privateAutoExecution.js +188 -0
- package/dist/internal/realtime.d.ts +1005 -0
- package/dist/internal/realtime.js +187 -0
- package/dist/internal/request.d.ts +1079 -0
- package/dist/internal/request.js +404 -0
- package/dist/internal/routing.d.ts +90 -0
- package/dist/internal/routing.js +139 -0
- package/dist/internal/scopedExecution.d.ts +74 -0
- package/dist/internal/scopedExecution.js +61 -0
- package/dist/internal/settlement.d.ts +205 -0
- package/dist/internal/settlement.js +71 -0
- package/dist/internal/streamEvents.d.ts +422 -0
- package/dist/internal/streamEvents.js +171 -0
- package/dist/internal/usage.d.ts +127 -0
- package/dist/internal/usage.js +109 -0
- package/dist/internal.d.ts +30 -0
- package/dist/internal.js +30 -0
- package/dist/public/audio.d.ts +347 -0
- package/dist/public/audio.js +236 -0
- package/dist/public/audioFormats.d.ts +64 -0
- package/dist/public/audioFormats.js +60 -0
- package/dist/public/catalogue.d.ts +914 -0
- package/dist/public/catalogue.js +516 -0
- package/dist/public/chatCompletions.d.ts +381 -0
- package/dist/public/chatCompletions.js +321 -0
- package/dist/public/decisions.d.ts +182 -0
- package/dist/public/decisions.js +257 -0
- package/dist/public/embeddings.d.ts +34 -0
- package/dist/public/embeddings.js +66 -0
- package/dist/public/errors.d.ts +195 -0
- package/dist/public/errors.js +274 -0
- package/dist/public/generations.d.ts +330 -0
- package/dist/public/generations.js +121 -0
- package/dist/public/identifiers.d.ts +139 -0
- package/dist/public/identifiers.js +160 -0
- package/dist/public/images.d.ts +36 -0
- package/dist/public/images.js +36 -0
- package/dist/public/messages.d.ts +297 -0
- package/dist/public/messages.js +251 -0
- package/dist/public/money.d.ts +308 -0
- package/dist/public/money.js +237 -0
- package/dist/public/policy.d.ts +19 -0
- package/dist/public/policy.js +20 -0
- package/dist/public/realtime.d.ts +2006 -0
- package/dist/public/realtime.js +797 -0
- package/dist/public/responses.d.ts +253 -0
- package/dist/public/responses.js +95 -0
- package/dist/public/streamEvents.d.ts +372 -0
- package/dist/public/streamEvents.js +157 -0
- package/dist/public/version.d.ts +177 -0
- package/dist/public/version.js +177 -0
- package/openapi.json +6967 -0
- package/package.json +44 -4
- package/README.md +0 -3
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The grant: how Oxy decides who may spend, for every billable request Kaana
|
|
3
|
+
* serves on `https://kaana.ai/v1` (Kaana ADR 0002, decisions 3 and 4).
|
|
4
|
+
*
|
|
5
|
+
* Kaana parses and validates the public request, then asks
|
|
6
|
+
* `POST https://api.oxy.so/internal/kaana/v1/authorizations` with the caller's
|
|
7
|
+
* credential VERBATIM and content-free facts — never the prompt, the audio or
|
|
8
|
+
* the output. Oxy resolves the principal and lane, applies the Console policy,
|
|
9
|
+
* capacity and rate limits, quotes and RESERVES with its ledger, and answers
|
|
10
|
+
* with a grant signed under {@link GRANT_SIGNATURE_DOMAIN}. Kaana verifies the
|
|
11
|
+
* signature, the {@link GRANT_EXECUTION_WINDOW_MS} window and `bodySha256`, and
|
|
12
|
+
* executes inside the grant; without a valid grant no provider is called.
|
|
13
|
+
*
|
|
14
|
+
* The grant carries the funding model as it is today, field for field: it
|
|
15
|
+
* invents no policy. New in contract set 4.0.0; it replaces what the v1–v4
|
|
16
|
+
* envelopes signed (`authorizedRoutes`, the scoped and Private Auto bindings).
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { powerLevelSchema, realtimeSessionKindSchema } from '../public/catalogue.js';
|
|
20
|
+
import { generationRecordSchema } from '../public/generations.js';
|
|
21
|
+
import { delegatedUserIdSchema, generationIdSchema, idempotencyKeySchema, inferenceEnvironmentSchema, inferenceRegionSchema, inferenceTimestampSchema, modelIdSchema, modelReferenceSchema, modelRevisionLabelSchema, oxyApplicationIdSchema, oxyCredentialIdSchema, requestIdSchema, } from '../public/identifiers.js';
|
|
22
|
+
import { requestLabelsSchema } from '../public/messages.js';
|
|
23
|
+
import { moneySchema } from '../public/money.js';
|
|
24
|
+
import { routingPolicyReferenceSchema } from '../public/policy.js';
|
|
25
|
+
import { inferenceScopeSchema } from './attribution.js';
|
|
26
|
+
import { inferenceEconomicTreatmentSchema } from './economics.js';
|
|
27
|
+
import { deploymentIdSchema, inferenceFundingTierSchema, inferenceProviderSlugSchema, oxyAccountIdSchema, } from './identifiers.js';
|
|
28
|
+
import { authorizedRouteSchema } from './routing.js';
|
|
29
|
+
/* -------------------------------------------------------------------------- */
|
|
30
|
+
/* Signing */
|
|
31
|
+
/* -------------------------------------------------------------------------- */
|
|
32
|
+
/** The signature domain of a grant. Same Ed25519 keys as the envelope (`KAANA_EDGE_PUBLIC_KEYS`). */
|
|
33
|
+
export const GRANT_SIGNATURE_DOMAIN = 'oxy-kaana-grant:v1';
|
|
34
|
+
/** How long after `issuedAt` Kaana may START executing a grant. */
|
|
35
|
+
export const GRANT_EXECUTION_WINDOW_MS = 60_000;
|
|
36
|
+
/**
|
|
37
|
+
* The bytes a signature covers, the scheme every signed Oxy↔Kaana body already
|
|
38
|
+
* uses (`internal/edgeauth`): the domain, the key id, the Unix-millisecond
|
|
39
|
+
* timestamp and the lowercase hex SHA-256 of the exact body bytes, joined by
|
|
40
|
+
* `\n` with no trailing newline. The key id, timestamp and signature travel in
|
|
41
|
+
* the `X-Oxy-Kaana-Key-Id`, `X-Oxy-Kaana-Timestamp` and `X-Oxy-Kaana-Signature`
|
|
42
|
+
* headers of the response (grant) or request (settlement) that carries the body.
|
|
43
|
+
*/
|
|
44
|
+
export function signatureInput(domain, keyId, timestampMillis, bodySha256) {
|
|
45
|
+
if (!/^[0-9a-f]{64}$/.test(bodySha256)) {
|
|
46
|
+
throw new Error('bodySha256 must be 64 lowercase hex characters');
|
|
47
|
+
}
|
|
48
|
+
return [domain, keyId, String(timestampMillis), bodySha256].join('\n');
|
|
49
|
+
}
|
|
50
|
+
/** A lowercase hex SHA-256. */
|
|
51
|
+
const sha256HexSchema = z.string().regex(/^[0-9a-f]{64}$/, 'must be 64 lowercase hex characters');
|
|
52
|
+
/* -------------------------------------------------------------------------- */
|
|
53
|
+
/* The request Kaana makes */
|
|
54
|
+
/* -------------------------------------------------------------------------- */
|
|
55
|
+
/** What a request asks to do, as the authorization names it. */
|
|
56
|
+
export const grantOperationSchema = z.enum([
|
|
57
|
+
'responses',
|
|
58
|
+
'chat_completions',
|
|
59
|
+
'embeddings',
|
|
60
|
+
'audio_speech',
|
|
61
|
+
'audio_transcriptions',
|
|
62
|
+
'images_generations',
|
|
63
|
+
'decisions',
|
|
64
|
+
'realtime_session',
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* What the caller asked to be served: a model line, optionally pinned to a
|
|
68
|
+
* revision, or a power level. Absent on the request means the application's
|
|
69
|
+
* default from its Console policy.
|
|
70
|
+
*/
|
|
71
|
+
export const grantTargetSchema = z.discriminatedUnion('kind', [
|
|
72
|
+
z
|
|
73
|
+
.object({
|
|
74
|
+
kind: z.literal('model'),
|
|
75
|
+
modelLine: modelIdSchema,
|
|
76
|
+
/** Present when the caller pinned `publisher/model@revision`. */
|
|
77
|
+
revision: modelRevisionLabelSchema.optional(),
|
|
78
|
+
})
|
|
79
|
+
.strict(),
|
|
80
|
+
z.object({ kind: z.literal('level'), level: powerLevelSchema }).strict(),
|
|
81
|
+
]);
|
|
82
|
+
/**
|
|
83
|
+
* The content-free facts Oxy sizes a hold from. Counts, never content: the
|
|
84
|
+
* prompt, the audio and the output never leave Kaana.
|
|
85
|
+
*/
|
|
86
|
+
export const grantRequestFactsSchema = z
|
|
87
|
+
.object({
|
|
88
|
+
/** Kaana's estimate of the input, in tokens. */
|
|
89
|
+
estimatedInputTokens: z.number().int().nonnegative().safe().optional(),
|
|
90
|
+
maxOutputTokens: z.number().int().positive().safe().optional(),
|
|
91
|
+
/** Speech: the characters to read, UTF-16 code units. */
|
|
92
|
+
inputCharacters: z.number().int().nonnegative().safe().optional(),
|
|
93
|
+
/**
|
|
94
|
+
* Transcription: the duration of the uploaded audio, in milliseconds rounded
|
|
95
|
+
* up, as Kaana read it from the file's own container (`internal/audioprobe`)
|
|
96
|
+
* before asking: the WAV data chunk, the MPEG and ADTS frames, the FLAC
|
|
97
|
+
* stream info, the last Ogg granule, the Matroska/WebM blocks or the MP4
|
|
98
|
+
* sample tables. Never estimated from the byte count: an upload whose
|
|
99
|
+
* duration Kaana cannot read (raw `audio/pcm`, a damaged or mislabelled
|
|
100
|
+
* file) is refused `invalid_request` naming `file`, and no grant is asked
|
|
101
|
+
* for. Settlement still charges the provider's own measurement.
|
|
102
|
+
*/
|
|
103
|
+
audioInputMilliseconds: z.number().int().nonnegative().safe().optional(),
|
|
104
|
+
/** Embeddings: how many inputs. Images: how many images. */
|
|
105
|
+
embeddingInputs: z.number().int().positive().max(2048).optional(),
|
|
106
|
+
images: z.number().int().positive().max(10).optional(),
|
|
107
|
+
/** How many tools the request declares; their definitions stay in Kaana. */
|
|
108
|
+
declaredTools: z.number().int().nonnegative().max(128),
|
|
109
|
+
stream: z.boolean(),
|
|
110
|
+
/** Realtime: the session kind being opened. */
|
|
111
|
+
realtimeSessionKind: realtimeSessionKindSchema.optional(),
|
|
112
|
+
})
|
|
113
|
+
.strict();
|
|
114
|
+
const credentialSchema = z.string().min(1).max(8192);
|
|
115
|
+
/**
|
|
116
|
+
* `POST /internal/kaana/v1/authorizations` for an inference request. Kaana
|
|
117
|
+
* authenticates as a workload (Oxy ADR 0026); `credential` is the caller's
|
|
118
|
+
* `Authorization: Bearer` value, forwarded opaque and never stored.
|
|
119
|
+
*/
|
|
120
|
+
export const inferenceAuthorizationRequestSchema = z
|
|
121
|
+
.object({
|
|
122
|
+
schemaVersion: z.literal(1),
|
|
123
|
+
kind: z.literal('inference'),
|
|
124
|
+
credential: credentialSchema,
|
|
125
|
+
/** Minted by Kaana on admission (`Kaana-Request-Id`). */
|
|
126
|
+
requestId: requestIdSchema,
|
|
127
|
+
operation: grantOperationSchema,
|
|
128
|
+
target: grantTargetSchema.optional(),
|
|
129
|
+
facts: grantRequestFactsSchema,
|
|
130
|
+
idempotencyKey: idempotencyKeySchema.optional(),
|
|
131
|
+
/** `X-Oxy-User-Id`: attribution only, honoured only with a service token. */
|
|
132
|
+
onBehalfOfUserId: delegatedUserIdSchema.optional(),
|
|
133
|
+
/** SHA-256 of the normalized request body; the grant is bound to it. */
|
|
134
|
+
bodySha256: sha256HexSchema,
|
|
135
|
+
/**
|
|
136
|
+
* For an operation under a source approval (scoped execution, Private Auto,
|
|
137
|
+
* the Jev classifier): `canonicalInputSha256(input)`, which Oxy compares
|
|
138
|
+
* with the hash the approval fixed.
|
|
139
|
+
*/
|
|
140
|
+
canonicalInputSha256: sha256HexSchema.optional(),
|
|
141
|
+
client: z
|
|
142
|
+
.object({
|
|
143
|
+
/** The public path, e.g. `/v1/responses`. */
|
|
144
|
+
endpoint: z.string().min(1).max(256),
|
|
145
|
+
clientRequestId: z.string().min(1).max(128).optional(),
|
|
146
|
+
receivedAt: inferenceTimestampSchema,
|
|
147
|
+
labels: requestLabelsSchema.optional(),
|
|
148
|
+
})
|
|
149
|
+
.strict(),
|
|
150
|
+
})
|
|
151
|
+
.strict();
|
|
152
|
+
/**
|
|
153
|
+
* Authorization to read the catalogue (`GET /v1/models`, `/v1/power-levels`):
|
|
154
|
+
* no reservation, cached for 60 s per credential.
|
|
155
|
+
*/
|
|
156
|
+
export const catalogueAuthorizationRequestSchema = z
|
|
157
|
+
.object({
|
|
158
|
+
schemaVersion: z.literal(1),
|
|
159
|
+
kind: z.literal('catalogue.read'),
|
|
160
|
+
credential: credentialSchema,
|
|
161
|
+
requestId: requestIdSchema,
|
|
162
|
+
})
|
|
163
|
+
.strict();
|
|
164
|
+
/**
|
|
165
|
+
* Authorization to read a generation record (`inference:usage:read`); Oxy
|
|
166
|
+
* answers with the record itself, amount included.
|
|
167
|
+
*/
|
|
168
|
+
export const receiptAuthorizationRequestSchema = z
|
|
169
|
+
.object({
|
|
170
|
+
schemaVersion: z.literal(1),
|
|
171
|
+
kind: z.literal('receipt.read'),
|
|
172
|
+
credential: credentialSchema,
|
|
173
|
+
requestId: requestIdSchema,
|
|
174
|
+
generationId: generationIdSchema.optional(),
|
|
175
|
+
idempotencyKey: idempotencyKeySchema.optional(),
|
|
176
|
+
/** With `idempotencyKey`: the original delegated attribution, if any. */
|
|
177
|
+
onBehalfOfUserId: delegatedUserIdSchema.optional(),
|
|
178
|
+
})
|
|
179
|
+
.strict()
|
|
180
|
+
.superRefine((request, ctx) => {
|
|
181
|
+
if ((request.generationId === undefined) === (request.idempotencyKey === undefined)) {
|
|
182
|
+
ctx.addIssue({
|
|
183
|
+
code: 'custom',
|
|
184
|
+
path: ['generationId'],
|
|
185
|
+
message: 'name exactly one of generationId or idempotencyKey',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
export const authorizationRequestSchema = z.discriminatedUnion('kind', [
|
|
190
|
+
inferenceAuthorizationRequestSchema,
|
|
191
|
+
catalogueAuthorizationRequestSchema,
|
|
192
|
+
receiptAuthorizationRequestSchema,
|
|
193
|
+
]);
|
|
194
|
+
/* -------------------------------------------------------------------------- */
|
|
195
|
+
/* The grant Oxy signs */
|
|
196
|
+
/* -------------------------------------------------------------------------- */
|
|
197
|
+
/** How the caller authenticated, which decides who pays. */
|
|
198
|
+
export const grantLaneSchema = z.enum(['machine_credential', 'service_token', 'product_session']);
|
|
199
|
+
/**
|
|
200
|
+
* Who pays and who called. `billingAccountId` is the owner of the
|
|
201
|
+
* authenticated application, UNRESOLVED: the ledger resolves the billable
|
|
202
|
+
* ancestor. The end user never pays.
|
|
203
|
+
*/
|
|
204
|
+
export const grantPrincipalSchema = z
|
|
205
|
+
.object({
|
|
206
|
+
billingAccountId: oxyAccountIdSchema,
|
|
207
|
+
applicationId: oxyApplicationIdSchema,
|
|
208
|
+
credentialId: oxyCredentialIdSchema,
|
|
209
|
+
environment: inferenceEnvironmentSchema,
|
|
210
|
+
scopes: z.array(inferenceScopeSchema),
|
|
211
|
+
lane: grantLaneSchema,
|
|
212
|
+
})
|
|
213
|
+
.strict();
|
|
214
|
+
/** A model line Oxy admits for a step, with the price it settles at. */
|
|
215
|
+
export const admittedModelLineSchema = z
|
|
216
|
+
.object({
|
|
217
|
+
modelLine: modelIdSchema,
|
|
218
|
+
priceVersionId: z.string().min(1).max(128),
|
|
219
|
+
})
|
|
220
|
+
.strict();
|
|
221
|
+
/**
|
|
222
|
+
* What bounds one step: the Console policy, already applied by Oxy, as values
|
|
223
|
+
* Kaana filters by. Kaana never re-derives a policy: it intersects what it can
|
|
224
|
+
* serve with `admittedModelLines` and walks keys free → credit → paid within
|
|
225
|
+
* `maxFundingTier`.
|
|
226
|
+
*/
|
|
227
|
+
export const grantStepLimitsSchema = z
|
|
228
|
+
.object({
|
|
229
|
+
/** The most one request may cost under this step. */
|
|
230
|
+
maxPricePerRequest: moneySchema.optional(),
|
|
231
|
+
/** The dearest funding tier Kaana may spend; absent means no ceiling. */
|
|
232
|
+
maxFundingTier: inferenceFundingTierSchema.optional(),
|
|
233
|
+
/** The ONLY model lines this step may serve, each with its settling price. */
|
|
234
|
+
admittedModelLines: z.array(admittedModelLineSchema).min(1),
|
|
235
|
+
providerAllowlist: z.array(inferenceProviderSlugSchema).default([]),
|
|
236
|
+
providerDenylist: z.array(inferenceProviderSlugSchema).default([]),
|
|
237
|
+
allowedRegions: z.array(inferenceRegionSchema).default([]),
|
|
238
|
+
deniedRegions: z.array(inferenceRegionSchema).default([]),
|
|
239
|
+
requireZeroDataRetention: z.boolean(),
|
|
240
|
+
prohibitTrainingOnCustomerData: z.boolean(),
|
|
241
|
+
allowedLicenseIds: z.array(z.string().min(1).max(128)).default([]),
|
|
242
|
+
requireCommercialUseRights: z.boolean(),
|
|
243
|
+
})
|
|
244
|
+
.strict()
|
|
245
|
+
.superRefine((limits, ctx) => {
|
|
246
|
+
const lines = limits.admittedModelLines.map((line) => line.modelLine);
|
|
247
|
+
if (new Set(lines).size !== lines.length) {
|
|
248
|
+
ctx.addIssue({
|
|
249
|
+
code: 'custom',
|
|
250
|
+
path: ['admittedModelLines'],
|
|
251
|
+
message: 'each model line is admitted once, at one price',
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
const denied = new Set(limits.providerDenylist);
|
|
255
|
+
if (limits.providerAllowlist.some((provider) => denied.has(provider))) {
|
|
256
|
+
ctx.addIssue({
|
|
257
|
+
code: 'custom',
|
|
258
|
+
path: ['providerAllowlist'],
|
|
259
|
+
message: 'a provider cannot be both allowed and denied',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const deniedRegions = new Set(limits.deniedRegions);
|
|
263
|
+
if (limits.allowedRegions.some((region) => deniedRegions.has(region))) {
|
|
264
|
+
ctx.addIssue({
|
|
265
|
+
code: 'custom',
|
|
266
|
+
path: ['allowedRegions'],
|
|
267
|
+
message: 'a region cannot be both allowed and denied',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
/**
|
|
272
|
+
* One step: a target and its limits. Kaana tries the steps IN ORDER and moves
|
|
273
|
+
* to the next only when no admissible route of this one is left — before any
|
|
274
|
+
* output, never after. Alia's Pro/Ultra lane is two steps: the level under
|
|
275
|
+
* `maxFundingTier: 'credit'`, then `xhigh` on the pilot tuples.
|
|
276
|
+
*/
|
|
277
|
+
export const grantStepSchema = z
|
|
278
|
+
.object({
|
|
279
|
+
target: grantTargetSchema,
|
|
280
|
+
limits: grantStepLimitsSchema,
|
|
281
|
+
})
|
|
282
|
+
.strict()
|
|
283
|
+
.superRefine((step, ctx) => {
|
|
284
|
+
const target = step.target;
|
|
285
|
+
if (target.kind === 'model' &&
|
|
286
|
+
!step.limits.admittedModelLines.some((line) => line.modelLine === target.modelLine)) {
|
|
287
|
+
ctx.addIssue({
|
|
288
|
+
code: 'custom',
|
|
289
|
+
path: ['limits', 'admittedModelLines'],
|
|
290
|
+
message: 'a step that names a model line admits it',
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
/**
|
|
295
|
+
* The exact route of an operation with a source approval (scoped execution,
|
|
296
|
+
* Private Auto, the Mention classifier): the deployment, provider, key and
|
|
297
|
+
* price the approval fixed, and the input it was approved for.
|
|
298
|
+
*/
|
|
299
|
+
export const grantExactRouteSchema = z
|
|
300
|
+
.object({
|
|
301
|
+
approval: z.enum(['scoped_execution', 'private_auto', 'mention_classifier']),
|
|
302
|
+
deploymentId: deploymentIdSchema,
|
|
303
|
+
provider: inferenceProviderSlugSchema,
|
|
304
|
+
/** The provider key the approval fixed (opaque Kaana key identity). */
|
|
305
|
+
keyId: z.string().min(1).max(256),
|
|
306
|
+
/** Always revision-pinned. */
|
|
307
|
+
modelReference: modelReferenceSchema,
|
|
308
|
+
priceVersionId: z.string().min(1).max(128),
|
|
309
|
+
/** `canonicalInputSha256` of the approved input. */
|
|
310
|
+
inputSha256: sha256HexSchema,
|
|
311
|
+
})
|
|
312
|
+
.strict()
|
|
313
|
+
.superRefine((route, ctx) => {
|
|
314
|
+
if (!route.modelReference.includes('@')) {
|
|
315
|
+
ctx.addIssue({
|
|
316
|
+
code: 'custom',
|
|
317
|
+
path: ['modelReference'],
|
|
318
|
+
message: 'an exact route pins an immutable revision',
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
/** The BYOK binding, exactly as an authorized route carries it today. */
|
|
323
|
+
const customerProviderCredentialBinding = authorizedRouteSchema.options[0].shape.customerProviderCredential;
|
|
324
|
+
/**
|
|
325
|
+
* The signed grant: the body of a successful authorization for an inference
|
|
326
|
+
* operation. Kaana verifies it and executes inside it; Oxy settles against it.
|
|
327
|
+
*/
|
|
328
|
+
export const grantSchema = z
|
|
329
|
+
.object({
|
|
330
|
+
schemaVersion: z.literal(1),
|
|
331
|
+
grantId: z.string().min(1).max(128),
|
|
332
|
+
requestId: requestIdSchema,
|
|
333
|
+
operation: grantOperationSchema,
|
|
334
|
+
issuedAt: inferenceTimestampSchema,
|
|
335
|
+
/** Kaana must start executing by this instant: `issuedAt` + 60 s. */
|
|
336
|
+
executeBy: inferenceTimestampSchema,
|
|
337
|
+
/** The request body this grant authorizes; Kaana re-hashes and compares. */
|
|
338
|
+
bodySha256: sha256HexSchema,
|
|
339
|
+
principal: grantPrincipalSchema,
|
|
340
|
+
/** Delegated or session user. Attribution only. */
|
|
341
|
+
userId: delegatedUserIdSchema.optional(),
|
|
342
|
+
economics: z
|
|
343
|
+
.object({
|
|
344
|
+
treatment: inferenceEconomicTreatmentSchema,
|
|
345
|
+
policyVersion: z.string().min(1).max(128),
|
|
346
|
+
relationshipId: z.string().min(1).max(128).optional(),
|
|
347
|
+
})
|
|
348
|
+
.strict(),
|
|
349
|
+
/** The hold. Absent for `internal_metered`, which holds nothing. */
|
|
350
|
+
reservation: z
|
|
351
|
+
.object({
|
|
352
|
+
reservationId: z.string().min(1).max(128),
|
|
353
|
+
/** The most the hold covers. Shown, never spent by Kaana. */
|
|
354
|
+
ceiling: moneySchema,
|
|
355
|
+
/** `promotional-only` for a scoped permit. */
|
|
356
|
+
fundingRestriction: z.literal('promotional-only').optional(),
|
|
357
|
+
})
|
|
358
|
+
.strict()
|
|
359
|
+
.optional(),
|
|
360
|
+
/** The capacity slot an internal relationship claimed (e.g. `alia-kaana`). */
|
|
361
|
+
capacity: z
|
|
362
|
+
.object({ meteredUsageId: z.string().min(1).max(128) })
|
|
363
|
+
.strict()
|
|
364
|
+
.optional(),
|
|
365
|
+
steps: z.array(grantStepSchema).min(1).max(8),
|
|
366
|
+
exactRoute: grantExactRouteSchema.optional(),
|
|
367
|
+
/** BYOK: the customer's own provider key. Excludes `maxFundingTier`. */
|
|
368
|
+
customerProviderCredential: customerProviderCredentialBinding,
|
|
369
|
+
idempotencyKey: idempotencyKeySchema.optional(),
|
|
370
|
+
client: z.object({ labels: requestLabelsSchema.optional() }).strict(),
|
|
371
|
+
/** The customer's routing-policy revision, as provenance. */
|
|
372
|
+
routingPolicy: routingPolicyReferenceSchema,
|
|
373
|
+
})
|
|
374
|
+
.strict()
|
|
375
|
+
.superRefine((grant, ctx) => {
|
|
376
|
+
const issue = (path, message) => ctx.addIssue({ code: 'custom', path, message });
|
|
377
|
+
const issued = Date.parse(grant.issuedAt);
|
|
378
|
+
const executeBy = Date.parse(grant.executeBy);
|
|
379
|
+
if (executeBy <= issued || executeBy - issued > GRANT_EXECUTION_WINDOW_MS) {
|
|
380
|
+
issue(['executeBy'], 'a grant is executable for at most 60 seconds after it is issued');
|
|
381
|
+
}
|
|
382
|
+
const metered = grant.economics.treatment === 'internal_metered';
|
|
383
|
+
if (metered && grant.reservation !== undefined) {
|
|
384
|
+
issue(['reservation'], 'an internally metered grant holds nothing');
|
|
385
|
+
}
|
|
386
|
+
if (!metered && grant.reservation === undefined) {
|
|
387
|
+
issue(['reservation'], 'a charged grant carries the hold it was sized against');
|
|
388
|
+
}
|
|
389
|
+
const byok = grant.customerProviderCredential !== undefined;
|
|
390
|
+
if (byok !== (grant.economics.treatment === 'platform_fee_only')) {
|
|
391
|
+
issue(['customerProviderCredential'], 'a customer provider credential is exactly the platform-fee-only treatment');
|
|
392
|
+
}
|
|
393
|
+
for (const [index, step] of grant.steps.entries()) {
|
|
394
|
+
if (byok && step.limits.maxFundingTier !== undefined) {
|
|
395
|
+
issue(['steps', index, 'limits', 'maxFundingTier'], 'a funding ceiling bounds platform keys; a customer credential is billed to the customer');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (grant.exactRoute !== undefined) {
|
|
399
|
+
const [step] = grant.steps;
|
|
400
|
+
const route = grant.exactRoute;
|
|
401
|
+
const [line, revision] = route.modelReference.split('@');
|
|
402
|
+
if (grant.steps.length !== 1 ||
|
|
403
|
+
step.target.kind !== 'model' ||
|
|
404
|
+
step.target.modelLine !== line ||
|
|
405
|
+
step.target.revision !== revision ||
|
|
406
|
+
byok) {
|
|
407
|
+
issue(['exactRoute'], 'an exact route is one platform route for the one pinned model the grant serves');
|
|
408
|
+
}
|
|
409
|
+
if (step !== undefined &&
|
|
410
|
+
!step.limits.admittedModelLines.some((admitted) => admitted.modelLine === line && admitted.priceVersionId === route.priceVersionId)) {
|
|
411
|
+
issue(['exactRoute', 'priceVersionId'], 'the exact route settles at its admitted price');
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
/**
|
|
416
|
+
* The answer to a catalogue authorization: what the credential may see.
|
|
417
|
+
* Kaana filters `GET /v1/models` and `GET /v1/power-levels` with it.
|
|
418
|
+
*/
|
|
419
|
+
export const catalogueAccessSchema = z
|
|
420
|
+
.object({
|
|
421
|
+
schemaVersion: z.literal(1),
|
|
422
|
+
kind: z.literal('catalogue.read'),
|
|
423
|
+
audience: z.enum(['public', 'platform_internal']),
|
|
424
|
+
/** The power levels the credential's policy allows; empty means every level. */
|
|
425
|
+
allowedLevels: z.array(powerLevelSchema).default([]),
|
|
426
|
+
/** Kaana caches the answer until then, at most 60 s. */
|
|
427
|
+
expiresAt: inferenceTimestampSchema,
|
|
428
|
+
})
|
|
429
|
+
.strict();
|
|
430
|
+
/** The answer to a receipt authorization: the record, amount included. */
|
|
431
|
+
export const receiptAccessSchema = z
|
|
432
|
+
.object({
|
|
433
|
+
schemaVersion: z.literal(1),
|
|
434
|
+
kind: z.literal('receipt.read'),
|
|
435
|
+
record: generationRecordSchema,
|
|
436
|
+
})
|
|
437
|
+
.strict();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifiers only Oxy and Kaana exchange: the billing account, the opaque
|
|
3
|
+
* deployment and provider identities, and the funding tier.
|
|
4
|
+
*
|
|
5
|
+
* Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/identifiers.ts`) by Kaana ADR 0002.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
/**
|
|
9
|
+
* An Oxy account id — the account that owns the workload and is financially
|
|
10
|
+
* responsible for it.
|
|
11
|
+
*
|
|
12
|
+
* Branded, and that brand is load-bearing rather than decorative: it is the
|
|
13
|
+
* type-level half of the rule that a delegated end-user identity can never
|
|
14
|
+
* become the billing identity (see {@link delegatedUserIdSchema} and
|
|
15
|
+
* `billingPrincipalSchema`). A plain `string` — and therefore any id read out
|
|
16
|
+
* of a header, a JWT claim or a request body — is not assignable to it; the
|
|
17
|
+
* only way to obtain one is to parse a value through this schema.
|
|
18
|
+
*/
|
|
19
|
+
export declare const oxyAccountIdSchema: z.core.$ZodBranded<z.ZodString, "OxyAccountId", "out">;
|
|
20
|
+
/**
|
|
21
|
+
* A routing-profile slug, e.g. `auto`, `fast`, `quality`.
|
|
22
|
+
*
|
|
23
|
+
* Deliberately refuses a `/`, so a profile can never be written in the shape of
|
|
24
|
+
* a model id and no caller can be confused about whether they asked for a
|
|
25
|
+
* concrete model or for Oxy to choose one. Modes like `auto`/`fast`/`quality`
|
|
26
|
+
* are profiles or product presets; they are never model objects.
|
|
27
|
+
*/
|
|
28
|
+
export declare const routingProfileSlugSchema: z.ZodString;
|
|
29
|
+
/**
|
|
30
|
+
* The opaque PostgreSQL identity of one routing profile.
|
|
31
|
+
*
|
|
32
|
+
* No trim, case-folding or format heuristic: callers selecting by id must name
|
|
33
|
+
* the exact stored bytes. The database lookup is the authority for existence.
|
|
34
|
+
*/
|
|
35
|
+
export declare const routingProfileIdSchema: z.ZodString;
|
|
36
|
+
/** An inference provider slug, e.g. `openai`, `bedrock`, `oxy-hosted`. */
|
|
37
|
+
export declare const inferenceProviderSlugSchema: z.ZodString;
|
|
38
|
+
/**
|
|
39
|
+
* A deployment/endpoint id. Opaque to customers: which concrete endpoint served
|
|
40
|
+
* a request is the data plane's operational detail, and only the customer-safe
|
|
41
|
+
* subset of it is ever attributed back (see the catalogue's serving-boundary
|
|
42
|
+
* rules).
|
|
43
|
+
*/
|
|
44
|
+
export declare const deploymentIdSchema: z.ZodString;
|
|
45
|
+
/**
|
|
46
|
+
* The economic tier of the provider capacity that funds a request, in the
|
|
47
|
+
* order Kaana spends it: `free` (costs nothing), `credit` (a promotional or
|
|
48
|
+
* trial grant, or a prepaid balance), `paid` (ordinary metered spend).
|
|
49
|
+
*
|
|
50
|
+
* Kaana owns provider keys, their economics and which key serves (Oxy ADR
|
|
51
|
+
* 0038, Kaana ADR 0001); Oxy owns what a customer may spend. The tier is the
|
|
52
|
+
* one economic fact the two exchange: Oxy BOUNDS a route by it
|
|
53
|
+
* (`maxFundingTier`), and Kaana REPORTS the tier that served
|
|
54
|
+
* (`normalizedUsageReportSchema.fundingTier`). No key id, label or price
|
|
55
|
+
* travels with it.
|
|
56
|
+
*/
|
|
57
|
+
export declare const inferenceFundingTierSchema: z.ZodEnum<{
|
|
58
|
+
free: "free";
|
|
59
|
+
credit: "credit";
|
|
60
|
+
paid: "paid";
|
|
61
|
+
}>;
|
|
62
|
+
export type OxyAccountId = z.infer<typeof oxyAccountIdSchema>;
|
|
63
|
+
export type InferenceFundingTier = z.infer<typeof inferenceFundingTierSchema>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifiers only Oxy and Kaana exchange: the billing account, the opaque
|
|
3
|
+
* deployment and provider identities, and the funding tier.
|
|
4
|
+
*
|
|
5
|
+
* Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/identifiers.ts`) by Kaana ADR 0002.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
/**
|
|
9
|
+
* An Oxy account id — the account that owns the workload and is financially
|
|
10
|
+
* responsible for it.
|
|
11
|
+
*
|
|
12
|
+
* Branded, and that brand is load-bearing rather than decorative: it is the
|
|
13
|
+
* type-level half of the rule that a delegated end-user identity can never
|
|
14
|
+
* become the billing identity (see {@link delegatedUserIdSchema} and
|
|
15
|
+
* `billingPrincipalSchema`). A plain `string` — and therefore any id read out
|
|
16
|
+
* of a header, a JWT claim or a request body — is not assignable to it; the
|
|
17
|
+
* only way to obtain one is to parse a value through this schema.
|
|
18
|
+
*/
|
|
19
|
+
export const oxyAccountIdSchema = z.string().min(1).max(64).brand();
|
|
20
|
+
/** One path segment of a canonical model id: a lowercase, URL-safe slug. */
|
|
21
|
+
const SLUG_PATTERN = '[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?';
|
|
22
|
+
/**
|
|
23
|
+
* A routing-profile slug, e.g. `auto`, `fast`, `quality`.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately refuses a `/`, so a profile can never be written in the shape of
|
|
26
|
+
* a model id and no caller can be confused about whether they asked for a
|
|
27
|
+
* concrete model or for Oxy to choose one. Modes like `auto`/`fast`/`quality`
|
|
28
|
+
* are profiles or product presets; they are never model objects.
|
|
29
|
+
*/
|
|
30
|
+
export const routingProfileSlugSchema = z
|
|
31
|
+
.string()
|
|
32
|
+
.max(64)
|
|
33
|
+
.regex(new RegExp(`^${SLUG_PATTERN}$`), 'routing profile must be a lowercase URL-safe slug');
|
|
34
|
+
/**
|
|
35
|
+
* The opaque PostgreSQL identity of one routing profile.
|
|
36
|
+
*
|
|
37
|
+
* No trim, case-folding or format heuristic: callers selecting by id must name
|
|
38
|
+
* the exact stored bytes. The database lookup is the authority for existence.
|
|
39
|
+
*/
|
|
40
|
+
export const routingProfileIdSchema = z.string().min(1).max(128);
|
|
41
|
+
/** An inference provider slug, e.g. `openai`, `bedrock`, `oxy-hosted`. */
|
|
42
|
+
export const inferenceProviderSlugSchema = z
|
|
43
|
+
.string()
|
|
44
|
+
.max(64)
|
|
45
|
+
.regex(new RegExp(`^${SLUG_PATTERN}$`), 'provider must be a lowercase URL-safe slug');
|
|
46
|
+
/**
|
|
47
|
+
* A deployment/endpoint id. Opaque to customers: which concrete endpoint served
|
|
48
|
+
* a request is the data plane's operational detail, and only the customer-safe
|
|
49
|
+
* subset of it is ever attributed back (see the catalogue's serving-boundary
|
|
50
|
+
* rules).
|
|
51
|
+
*/
|
|
52
|
+
export const deploymentIdSchema = z.string().min(1).max(128);
|
|
53
|
+
/**
|
|
54
|
+
* The economic tier of the provider capacity that funds a request, in the
|
|
55
|
+
* order Kaana spends it: `free` (costs nothing), `credit` (a promotional or
|
|
56
|
+
* trial grant, or a prepaid balance), `paid` (ordinary metered spend).
|
|
57
|
+
*
|
|
58
|
+
* Kaana owns provider keys, their economics and which key serves (Oxy ADR
|
|
59
|
+
* 0038, Kaana ADR 0001); Oxy owns what a customer may spend. The tier is the
|
|
60
|
+
* one economic fact the two exchange: Oxy BOUNDS a route by it
|
|
61
|
+
* (`maxFundingTier`), and Kaana REPORTS the tier that served
|
|
62
|
+
* (`normalizedUsageReportSchema.fundingTier`). No key id, label or price
|
|
63
|
+
* travels with it.
|
|
64
|
+
*/
|
|
65
|
+
export const inferenceFundingTierSchema = z.enum(['free', 'credit', 'paid']);
|