@oxy.so/contracts 4.2.0 → 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 (34) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/index.js +72 -4
  3. package/dist/cjs/inference/catalogue.js +105 -2
  4. package/dist/cjs/inference/money.js +23 -0
  5. package/dist/cjs/inference/realtime.js +826 -0
  6. package/dist/cjs/inference/request.js +34 -11
  7. package/dist/cjs/inference/streamEvents.js +7 -1
  8. package/dist/cjs/inference/version.js +1 -1
  9. package/dist/cjs/stickers.js +151 -0
  10. package/dist/esm/.tsbuildinfo +1 -1
  11. package/dist/esm/index.js +6 -2
  12. package/dist/esm/inference/catalogue.js +104 -1
  13. package/dist/esm/inference/money.js +23 -0
  14. package/dist/esm/inference/realtime.js +822 -0
  15. package/dist/esm/inference/request.js +34 -11
  16. package/dist/esm/inference/streamEvents.js +7 -1
  17. package/dist/esm/inference/version.js +1 -1
  18. package/dist/esm/stickers.js +148 -0
  19. package/dist/types/.tsbuildinfo +1 -1
  20. package/dist/types/index.d.ts +7 -4
  21. package/dist/types/inference/catalogue.d.ts +299 -10
  22. package/dist/types/inference/entitlement.d.ts +6 -6
  23. package/dist/types/inference/inbox.d.ts +2 -2
  24. package/dist/types/inference/modelDocumentation.d.ts +146 -2
  25. package/dist/types/inference/money.d.ts +28 -8
  26. package/dist/types/inference/priceVersion.d.ts +12 -12
  27. package/dist/types/inference/realtime.d.ts +8012 -0
  28. package/dist/types/inference/request.d.ts +51 -0
  29. package/dist/types/inference/routingPolicy.d.ts +7 -7
  30. package/dist/types/inference/streamEvents.d.ts +28 -16
  31. package/dist/types/inference/usage.d.ts +28 -28
  32. package/dist/types/inference/version.d.ts +1 -1
  33. package/dist/types/stickers.d.ts +1049 -0
  34. package/package.json +1 -1
@@ -25,7 +25,7 @@
25
25
  */
26
26
  import { z } from "zod";
27
27
  import { inferenceAttributionSchema } from "./attribution.js";
28
- import { inferenceModalitySchema, reasoningEffortSchema } from "./catalogue.js";
28
+ import { inferenceApiFormatSchema, inferenceModalitySchema, reasoningEffortSchema, } from "./catalogue.js";
29
29
  import { idempotencyKeySchema, inferenceTimestampSchema } from "./identifiers.js";
30
30
  import { authorizedRouteSchema, routingPolicyReferenceSchema, routingTargetSchema, } from "./routingPolicy.js";
31
31
  /* -------------------------------------------------------------------------- */
@@ -289,16 +289,7 @@ export const inferenceReasoningSchema = z
289
289
  export const clientRequestMetadataSchema = z
290
290
  .object({
291
291
  /** The public dialect the customer called. The response is rendered in it. */
292
- apiFormat: z.enum([
293
- "responses",
294
- "chat_completions",
295
- "embeddings",
296
- "images_generations",
297
- "audio_transcriptions",
298
- "audio_speech",
299
- "rerank",
300
- "batches",
301
- ]),
292
+ apiFormat: inferenceApiFormatSchema,
302
293
  /** The public path, e.g. `/v1/responses`. */
303
294
  endpoint: z.string().min(1).max(256),
304
295
  /** The customer's own correlation id, when they sent one. */
@@ -327,6 +318,25 @@ export const inferenceSpeechParametersSchema = z.object({
327
318
  responseFormat: z.enum(["mp3", "opus", "aac", "flac", "wav", "pcm"]),
328
319
  speed: z.number().min(0.25).max(4).optional(),
329
320
  }).strict();
321
+ /**
322
+ * Spoken output from a CONVERSATIONAL model (contract set 3.2.0) — an audio chat
323
+ * model answering in its own voice, as opposed to `speech`, which reads given
324
+ * text aloud. Presence is the request: a chat request carrying this asks for
325
+ * the answer as audio AND its transcript, which is the only combination the
326
+ * audio chat models produce (OpenAI's `modalities: ["text", "audio"]`).
327
+ *
328
+ * The audio streams as `audio` events and the words as `delta` events on the
329
+ * `output_audio_transcript` channel — never as `output_text`, because a
330
+ * transcript of speech the customer is already hearing is not a second answer.
331
+ * `pcm` is the only format an audio chat model can STREAM; the refinement on the
332
+ * envelope holds that rather than leaving a provider to refuse it mid-flight.
333
+ */
334
+ export const inferenceAudioOutputParametersSchema = z
335
+ .object({
336
+ voice: z.string().min(1).max(64),
337
+ format: z.enum(["wav", "mp3", "flac", "opus", "pcm"]),
338
+ })
339
+ .strict();
330
340
  /**
331
341
  * The canonical internal request Oxy forwards to the data plane.
332
342
  *
@@ -349,6 +359,8 @@ export const inferenceRequestSchema = z
349
359
  /** Absent means the route's own default reasoning behaviour. */
350
360
  reasoning: inferenceReasoningSchema.optional(),
351
361
  speech: inferenceSpeechParametersSchema.optional(),
362
+ /** Spoken output from a conversational model. Contract set 3.2.0. */
363
+ audioOutput: inferenceAudioOutputParametersSchema.optional(),
352
364
  tools: z.array(toolDefinitionSchema).default([]),
353
365
  toolChoice: toolChoiceSchema.optional(),
354
366
  responseFormat: responseFormatSchema.optional(),
@@ -398,6 +410,17 @@ export const inferenceRequestSchema = z
398
410
  if (!isSpeech && request.speech !== undefined) {
399
411
  ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["speech"], message: "speech parameters require the audio_speech API format" });
400
412
  }
413
+ if (request.audioOutput !== undefined) {
414
+ if (request.client.apiFormat !== "chat_completions") {
415
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["audioOutput"], message: "spoken output from a conversational model requires the chat_completions API format" });
416
+ }
417
+ if (request.modality !== "audio" || request.input.format !== "messages") {
418
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["audioOutput"], message: "spoken output requires audio modality and a messages input" });
419
+ }
420
+ if (request.stream && request.audioOutput.format !== "pcm") {
421
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["audioOutput", "format"], message: "streamed spoken output is pcm; the other formats exist only whole" });
422
+ }
423
+ }
401
424
  if (request.toolChoice !== undefined && request.tools.length === 0) {
402
425
  ctx.addIssue({
403
426
  code: z.ZodIssueCode.custom,
@@ -59,7 +59,13 @@ export const inferenceStreamDeltaEventSchema = z.object({
59
59
  sequence: z.number().int().nonnegative().safe(),
60
60
  /** Which output of a multi-output response this chunk belongs to. */
61
61
  outputIndex: z.number().int().nonnegative().safe(),
62
- channel: z.enum(['output_text', 'reasoning', 'refusal']),
62
+ /**
63
+ * `output_audio_transcript` (contract set 3.2.0) carries the words of spoken
64
+ * output requested with `audioOutput`. It is its own channel for the same
65
+ * reason `reasoning` is: a renderer that shows it as `output_text` presents
66
+ * the transcript of what the customer is hearing as a second, written answer.
67
+ */
68
+ channel: z.enum(['output_text', 'reasoning', 'refusal', 'output_audio_transcript']),
63
69
  text: z.string(),
64
70
  });
65
71
  /** The encodings an audio output may carry. */
@@ -99,4 +99,4 @@
99
99
  * change to, say, the catalogue reject every in-flight inference request; the
100
100
  * per-shape `schemaVersion` is what a message is validated against.
101
101
  */
102
- export const INFERENCE_CONTRACT_VERSION = '3.1.0';
102
+ export const INFERENCE_CONTRACT_VERSION = '3.2.0';
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Stickers — the wire shape of Oxy's shared sticker catalogue.
3
+ *
4
+ * One catalogue serves every app: Allo sends a sticker in a chat, Mention draws
5
+ * one in an empty state, and both resolve the same id to the same animation.
6
+ * The API validates its output against these schemas and `@oxy.so/stickers`
7
+ * validates its input against the same ones.
8
+ *
9
+ * A sticker is a Lottie animation plus a static fallback image, both served
10
+ * from Oxy's CDN at content-addressed, immutable URLs. The fallback is what a
11
+ * surface that cannot animate shows — a push notification, a federated copy of
12
+ * a post, a person who asked for reduced motion.
13
+ *
14
+ * Platform-agnostic — zod only. ESM-safe (no `require()`).
15
+ */
16
+ import { z } from 'zod';
17
+ /* -------------------------------------------------------------------------- */
18
+ /* Limits */
19
+ /* -------------------------------------------------------------------------- */
20
+ /**
21
+ * The square canvases a sticker may be authored at. Lottie is vectors, so the
22
+ * canvas is a coordinate space rather than a resolution: a 1024 file draws as
23
+ * sharply in a 128-point bubble as a 512 one.
24
+ */
25
+ export const STICKER_CANVAS_SIZES = [512, 1024];
26
+ /** Size of the static fallback image every sticker gets. */
27
+ export const STICKER_FALLBACK_SIZE = 512;
28
+ /** Longest loop a sticker may have. */
29
+ export const STICKER_MAX_DURATION_MS = 10000;
30
+ /** Highest frame rate a sticker may declare. */
31
+ export const STICKER_MAX_FRAME_RATE = 60;
32
+ /**
33
+ * Largest Lottie JSON accepted on upload, before the API normalizes it.
34
+ * Exported animations carry pretty-printing and full float precision, so the
35
+ * raw file is routinely many times the size of what is stored.
36
+ */
37
+ export const STICKER_MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
38
+ /**
39
+ * Largest animation STORED, after normalization (minified, floats rounded,
40
+ * expressions removed). The CDN compresses JSON in transit, which takes a
41
+ * typical sticker to a few tens of kilobytes on the wire.
42
+ */
43
+ export const STICKER_MAX_ANIMATION_BYTES = 1024 * 1024;
44
+ /** Largest static fallback accepted on upload, when one is supplied. */
45
+ export const STICKER_MAX_FALLBACK_BYTES = 256 * 1024;
46
+ /** How many stickers one pack may hold. */
47
+ export const STICKER_PACK_MAX_STICKERS = 120;
48
+ /** How many packs one person may have installed. */
49
+ export const STICKER_MAX_INSTALLED_PACKS = 200;
50
+ /** How many ids one resolve call may ask about. */
51
+ export const STICKER_RESOLVE_MAX_IDS = 100;
52
+ /* -------------------------------------------------------------------------- */
53
+ /* Primitives */
54
+ /* -------------------------------------------------------------------------- */
55
+ /** Lowercase-hex SHA-256 of a file's bytes. */
56
+ export const stickerSha256Schema = z
57
+ .string()
58
+ .regex(/^[a-f0-9]{64}$/, 'sha256 must be 64 lowercase hex characters');
59
+ /** A pack's public URL segment. */
60
+ export const stickerPackSlugSchema = z
61
+ .string()
62
+ .min(2)
63
+ .max(64)
64
+ .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'slug must be lowercase words joined by hyphens');
65
+ export const stickerIdSchema = z.string().min(1).max(64);
66
+ export const stickerPackStatusSchema = z.enum(['draft', 'published', 'archived']);
67
+ /** One stored file: where to fetch it and the hash its bytes must have. */
68
+ export const stickerFileSchema = z.object({
69
+ url: z.string().url(),
70
+ sha256: stickerSha256Schema,
71
+ mime: z.string(),
72
+ bytes: z.number().int().nonnegative(),
73
+ });
74
+ /* -------------------------------------------------------------------------- */
75
+ /* Catalogue */
76
+ /* -------------------------------------------------------------------------- */
77
+ export const stickerSchema = z.object({
78
+ id: stickerIdSchema,
79
+ packId: z.string(),
80
+ emoji: z.array(z.string()).min(1),
81
+ keywords: z.array(z.string()),
82
+ size: z.number().int().positive(),
83
+ durationMs: z.number().int().positive(),
84
+ /** The Lottie JSON. */
85
+ animation: stickerFileSchema,
86
+ /** A static image of the same sticker. */
87
+ fallback: stickerFileSchema,
88
+ });
89
+ /** What a shop card needs. */
90
+ export const stickerPackSummarySchema = z.object({
91
+ id: z.string(),
92
+ slug: stickerPackSlugSchema,
93
+ title: z.string(),
94
+ description: z.string().nullable(),
95
+ author: z.string().nullable(),
96
+ status: stickerPackStatusSchema,
97
+ publishedAt: z.string().nullable(),
98
+ stickerCount: z.number().int().nonnegative(),
99
+ /** The first sticker, drawn as the pack's cover. Null for an empty draft. */
100
+ cover: stickerSchema.nullable(),
101
+ });
102
+ export const stickerPackSchema = stickerPackSummarySchema.extend({
103
+ stickers: z.array(stickerSchema),
104
+ });
105
+ /** A pack in someone's picker. */
106
+ export const installedStickerPackSchema = stickerPackSchema.extend({
107
+ installedAt: z.string(),
108
+ });
109
+ /* -------------------------------------------------------------------------- */
110
+ /* References */
111
+ /* -------------------------------------------------------------------------- */
112
+ /**
113
+ * How an app stores or sends a sticker: its id, the pack it came from, and the
114
+ * hash of the animation it showed. The hash lets a receiver verify the bytes it
115
+ * fetched are the ones the sender saw — which matters in an end-to-end
116
+ * encrypted chat, where the server relaying the message never sees it.
117
+ */
118
+ export const stickerRefSchema = z.object({
119
+ stickerId: stickerIdSchema,
120
+ packId: z.string().min(1).max(64),
121
+ sha256: stickerSha256Schema,
122
+ });
123
+ /* -------------------------------------------------------------------------- */
124
+ /* Requests */
125
+ /* -------------------------------------------------------------------------- */
126
+ /** POST /stickers/resolve */
127
+ export const resolveStickersRequestSchema = z.object({
128
+ ids: z.array(stickerIdSchema).min(1).max(STICKER_RESOLVE_MAX_IDS),
129
+ });
130
+ /** Unknown ids are absent, not errors: a caller checks what came back. */
131
+ export const resolveStickersResponseSchema = z.object({
132
+ stickers: z.array(stickerSchema),
133
+ });
134
+ /** PATCH /stickers/me/packs/order — the installed pack ids, first to last. */
135
+ export const reorderStickerPacksRequestSchema = z.object({
136
+ packIds: z.array(z.string().min(1).max(64)).max(STICKER_MAX_INSTALLED_PACKS),
137
+ });
138
+ /** POST /stickers/admin/packs */
139
+ export const createStickerPackRequestSchema = z.object({
140
+ slug: stickerPackSlugSchema,
141
+ title: z.string().trim().min(1).max(64),
142
+ description: z.string().trim().max(500).nullish(),
143
+ author: z.string().trim().max(64).nullish(),
144
+ });
145
+ /** PATCH /stickers/admin/packs/:packId */
146
+ export const updateStickerPackRequestSchema = createStickerPackRequestSchema
147
+ .omit({ slug: true })
148
+ .partial();