@skyold/model-protocol 0.1.0-beta.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 (104) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +22 -0
  3. package/README.zh-CN.md +17 -0
  4. package/dist/chat-egress.d.ts +12 -0
  5. package/dist/chat-egress.d.ts.map +1 -0
  6. package/dist/chat-egress.js +144 -0
  7. package/dist/chat-egress.js.map +1 -0
  8. package/dist/chat-ingress.d.ts +39 -0
  9. package/dist/chat-ingress.d.ts.map +1 -0
  10. package/dist/chat-ingress.js +323 -0
  11. package/dist/chat-ingress.js.map +1 -0
  12. package/dist/chat-stream-egress.d.ts +8 -0
  13. package/dist/chat-stream-egress.d.ts.map +1 -0
  14. package/dist/chat-stream-egress.js +129 -0
  15. package/dist/chat-stream-egress.js.map +1 -0
  16. package/dist/contracts.d.ts +61 -0
  17. package/dist/contracts.d.ts.map +1 -0
  18. package/dist/contracts.js +2 -0
  19. package/dist/contracts.js.map +1 -0
  20. package/dist/egress-codec-registry.d.ts +36 -0
  21. package/dist/egress-codec-registry.d.ts.map +1 -0
  22. package/dist/egress-codec-registry.js +125 -0
  23. package/dist/egress-codec-registry.js.map +1 -0
  24. package/dist/index.d.ts +39 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +25 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/ingress-codec-registry.d.ts +26 -0
  29. package/dist/ingress-codec-registry.d.ts.map +1 -0
  30. package/dist/ingress-codec-registry.js +106 -0
  31. package/dist/ingress-codec-registry.js.map +1 -0
  32. package/dist/media-images.d.ts +154 -0
  33. package/dist/media-images.d.ts.map +1 -0
  34. package/dist/media-images.js +721 -0
  35. package/dist/media-images.js.map +1 -0
  36. package/dist/media-video.d.ts +68 -0
  37. package/dist/media-video.d.ts.map +1 -0
  38. package/dist/media-video.js +196 -0
  39. package/dist/media-video.js.map +1 -0
  40. package/dist/native-images.d.ts +68 -0
  41. package/dist/native-images.d.ts.map +1 -0
  42. package/dist/native-images.js +424 -0
  43. package/dist/native-images.js.map +1 -0
  44. package/dist/openai-embeddings-egress.d.ts +4 -0
  45. package/dist/openai-embeddings-egress.d.ts.map +1 -0
  46. package/dist/openai-embeddings-egress.js +21 -0
  47. package/dist/openai-embeddings-egress.js.map +1 -0
  48. package/dist/openai-embeddings.d.ts +66 -0
  49. package/dist/openai-embeddings.d.ts.map +1 -0
  50. package/dist/openai-embeddings.js +442 -0
  51. package/dist/openai-embeddings.js.map +1 -0
  52. package/dist/openai-responses-egress.d.ts +17 -0
  53. package/dist/openai-responses-egress.d.ts.map +1 -0
  54. package/dist/openai-responses-egress.js +99 -0
  55. package/dist/openai-responses-egress.js.map +1 -0
  56. package/dist/openai-responses-stream-egress.d.ts +4 -0
  57. package/dist/openai-responses-stream-egress.d.ts.map +1 -0
  58. package/dist/openai-responses-stream-egress.js +252 -0
  59. package/dist/openai-responses-stream-egress.js.map +1 -0
  60. package/dist/openai-responses.d.ts +15 -0
  61. package/dist/openai-responses.d.ts.map +1 -0
  62. package/dist/openai-responses.js +363 -0
  63. package/dist/openai-responses.js.map +1 -0
  64. package/dist/provider-source.d.ts +20 -0
  65. package/dist/provider-source.d.ts.map +1 -0
  66. package/dist/provider-source.js +41 -0
  67. package/dist/provider-source.js.map +1 -0
  68. package/dist/provider-transport.d.ts +5 -0
  69. package/dist/provider-transport.d.ts.map +1 -0
  70. package/dist/provider-transport.js +114 -0
  71. package/dist/provider-transport.js.map +1 -0
  72. package/dist/runtime-v2-validation.d.ts +16 -0
  73. package/dist/runtime-v2-validation.d.ts.map +1 -0
  74. package/dist/runtime-v2-validation.js +915 -0
  75. package/dist/runtime-v2-validation.js.map +1 -0
  76. package/dist/runtime-v2.d.ts +462 -0
  77. package/dist/runtime-v2.d.ts.map +1 -0
  78. package/dist/runtime-v2.js +56 -0
  79. package/dist/runtime-v2.js.map +1 -0
  80. package/dist/tokenforge-task-projection.d.ts +8 -0
  81. package/dist/tokenforge-task-projection.d.ts.map +1 -0
  82. package/dist/tokenforge-task-projection.js +247 -0
  83. package/dist/tokenforge-task-projection.js.map +1 -0
  84. package/dist/tokenforge-tasks.d.ts +241 -0
  85. package/dist/tokenforge-tasks.d.ts.map +1 -0
  86. package/dist/tokenforge-tasks.js +1132 -0
  87. package/dist/tokenforge-tasks.js.map +1 -0
  88. package/dist/usage-reservation.d.ts +11 -0
  89. package/dist/usage-reservation.d.ts.map +1 -0
  90. package/dist/usage-reservation.js +40 -0
  91. package/dist/usage-reservation.js.map +1 -0
  92. package/dist/validation.d.ts +22 -0
  93. package/dist/validation.d.ts.map +1 -0
  94. package/dist/validation.js +415 -0
  95. package/dist/validation.js.map +1 -0
  96. package/dist/video-ingress.d.ts +31 -0
  97. package/dist/video-ingress.d.ts.map +1 -0
  98. package/dist/video-ingress.js +246 -0
  99. package/dist/video-ingress.js.map +1 -0
  100. package/dist/volc-video-task-codec.d.ts +59 -0
  101. package/dist/volc-video-task-codec.d.ts.map +1 -0
  102. package/dist/volc-video-task-codec.js +123 -0
  103. package/dist/volc-video-task-codec.js.map +1 -0
  104. package/package.json +54 -0
@@ -0,0 +1,721 @@
1
+ import { createHash } from 'node:crypto';
2
+ export const OPENAI_IMAGES_PROTOCOL = 'openai-images/v1';
3
+ export const VOLC_IMAGES_PROTOCOL = 'volc-images/v1';
4
+ export const MAX_IMAGE_REVISED_PROMPT_UTF8_BYTES_V1 = 32 * 1024;
5
+ const MAX_OUTPUT_ITEMS = 10;
6
+ const MAX_OUTPUT_ITEM_BYTES = 25 * 1024 * 1024;
7
+ const MAX_OUTPUT_ITEM_BASE64_CHARS = 4 * Math.ceil(MAX_OUTPUT_ITEM_BYTES / 3);
8
+ const MAX_INLINE_TOTAL_BYTES = 100 * 1024 * 1024;
9
+ const BASE64_VERIFY_CHUNK_CHARS = 64 * 1024;
10
+ const IMAGE_MAGIC_BYTES = 12;
11
+ const IMAGE_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp']);
12
+ const SHA256 = /^[a-f0-9]{64}$/;
13
+ const UTF8_ENCODER = new TextEncoder();
14
+ export function parseImageGenerationCapabilityV1(value) {
15
+ if (!isRecord(value) ||
16
+ !hasExactKeys(value, [
17
+ 'schemaVersion',
18
+ 'supportedPolicies',
19
+ 'responseFormats',
20
+ 'outputFormats',
21
+ 'requestMapping',
22
+ 'qualities',
23
+ 'minCount',
24
+ 'maxCount',
25
+ 'allowedSizes',
26
+ 'maxPromptUtf8Bytes',
27
+ 'certifiedModelRevision',
28
+ ], ['allowedBackgrounds'])) {
29
+ return null;
30
+ }
31
+ const sizes = value['allowedSizes'];
32
+ const responseFormats = value['responseFormats'];
33
+ const outputFormats = value['outputFormats'];
34
+ const requestMapping = parseRequestMapping(value['requestMapping'], responseFormats, outputFormats);
35
+ const allowedBackgrounds = value['allowedBackgrounds'];
36
+ if (value['schemaVersion'] !== 1 ||
37
+ !uniqueArray(value['supportedPolicies'], (item) => item === 'transparent' || item === 'managed') ||
38
+ !uniqueArray(responseFormats, (item) => item === 'url' || item === 'b64_json') ||
39
+ !uniqueArray(outputFormats, (item) => item === 'png' || item === 'jpeg' || item === 'webp') ||
40
+ !uniqueArray(value['qualities'], (item) => item === 'auto' || item === 'low' || item === 'medium' || item === 'high') ||
41
+ (allowedBackgrounds !== undefined &&
42
+ !uniqueArray(allowedBackgrounds, (item) => item === 'auto' || item === 'opaque' || item === 'transparent')) ||
43
+ !positiveInteger(value['minCount']) ||
44
+ !positiveInteger(value['maxCount']) ||
45
+ value['minCount'] > value['maxCount'] ||
46
+ value['maxCount'] > MAX_OUTPUT_ITEMS ||
47
+ !Array.isArray(sizes) ||
48
+ sizes.length === 0 ||
49
+ !sizes.every(isSize) ||
50
+ new Set(sizes.map((size) => `${size.width}x${size.height}`)).size !== sizes.length ||
51
+ !positiveInteger(value['maxPromptUtf8Bytes']) ||
52
+ value['maxPromptUtf8Bytes'] > 32 * 1024 ||
53
+ !nonEmptyString(value['certifiedModelRevision']) ||
54
+ requestMapping === null) {
55
+ return null;
56
+ }
57
+ const parsedAllowedBackgrounds = allowedBackgrounds === undefined
58
+ ? undefined
59
+ : allowedBackgrounds;
60
+ return {
61
+ schemaVersion: 1,
62
+ supportedPolicies: [
63
+ ...value['supportedPolicies'],
64
+ ],
65
+ responseFormats: [...responseFormats],
66
+ outputFormats: [...outputFormats],
67
+ requestMapping,
68
+ ...(parsedAllowedBackgrounds === undefined
69
+ ? {}
70
+ : {
71
+ allowedBackgrounds: [...parsedAllowedBackgrounds],
72
+ }),
73
+ qualities: [...value['qualities']],
74
+ minCount: value['minCount'],
75
+ maxCount: value['maxCount'],
76
+ allowedSizes: sizes.map((size) => ({ width: size.width, height: size.height })),
77
+ maxPromptUtf8Bytes: value['maxPromptUtf8Bytes'],
78
+ certifiedModelRevision: value['certifiedModelRevision'],
79
+ };
80
+ }
81
+ export function parseImageTaskCapabilityDeclarationV1(value) {
82
+ if (!isRecord(value) ||
83
+ !hasExactKeys(value, [
84
+ 'schemaVersion',
85
+ 'taskKind',
86
+ 'protocolFamily',
87
+ 'inputModalities',
88
+ 'outputModalities',
89
+ 'executionModes',
90
+ 'supportsCallback',
91
+ 'supportsCancel',
92
+ 'imageGenerationCapability',
93
+ ]) ||
94
+ value['schemaVersion'] !== 1 ||
95
+ value['taskKind'] !== 'image_generation' ||
96
+ (value['protocolFamily'] !== OPENAI_IMAGES_PROTOCOL &&
97
+ value['protocolFamily'] !== VOLC_IMAGES_PROTOCOL) ||
98
+ !exactSingleton(value['inputModalities'], 'text') ||
99
+ !exactSingleton(value['outputModalities'], 'image') ||
100
+ !exactSingleton(value['executionModes'], 'sync') ||
101
+ value['supportsCallback'] !== false ||
102
+ value['supportsCancel'] !== false) {
103
+ return null;
104
+ }
105
+ const imageGenerationCapability = parseImageGenerationCapabilityV1(value['imageGenerationCapability']);
106
+ if (imageGenerationCapability === null)
107
+ return null;
108
+ return {
109
+ schemaVersion: 1,
110
+ taskKind: 'image_generation',
111
+ protocolFamily: value['protocolFamily'],
112
+ inputModalities: ['text'],
113
+ outputModalities: ['image'],
114
+ executionModes: ['sync'],
115
+ supportsCallback: false,
116
+ supportsCancel: false,
117
+ imageGenerationCapability,
118
+ };
119
+ }
120
+ export function parseImageTaskCapabilityDeclarationV2(value) {
121
+ if (!isRecord(value) ||
122
+ !hasExactKeys(value, [
123
+ 'schemaVersion',
124
+ 'taskKind',
125
+ 'protocolFamily',
126
+ 'operations',
127
+ 'inputModalities',
128
+ 'outputModalities',
129
+ 'executionModes',
130
+ 'supportsCallback',
131
+ 'supportsCancel',
132
+ 'imageGenerationCapability',
133
+ ], ['imageEditingCapability', 'imageUsageReportingCapability']) ||
134
+ value['schemaVersion'] !== 2 ||
135
+ value['taskKind'] !== 'image_generation' ||
136
+ (value['protocolFamily'] !== OPENAI_IMAGES_PROTOCOL &&
137
+ value['protocolFamily'] !== VOLC_IMAGES_PROTOCOL) ||
138
+ !uniqueArray(value['operations'], (item) => item === 'generate' || item === 'edit') ||
139
+ !uniqueArray(value['inputModalities'], (item) => item === 'text' || item === 'image') ||
140
+ !value['inputModalities'].includes('text') ||
141
+ !exactSingleton(value['outputModalities'], 'image') ||
142
+ !exactSingleton(value['executionModes'], 'sync') ||
143
+ value['supportsCallback'] !== false ||
144
+ value['supportsCancel'] !== false) {
145
+ return null;
146
+ }
147
+ const operations = value['operations'];
148
+ const inputModalities = value['inputModalities'];
149
+ const supportsEdit = operations.includes('edit');
150
+ if (supportsEdit !== inputModalities.includes('image'))
151
+ return null;
152
+ const imageGenerationCapability = parseImageGenerationCapabilityV1(value['imageGenerationCapability']);
153
+ if (imageGenerationCapability === null)
154
+ return null;
155
+ const imageEditingCapability = value['imageEditingCapability'] === undefined
156
+ ? undefined
157
+ : parseImageEditingCapabilityV2(value['imageEditingCapability']);
158
+ if (supportsEdit !== (imageEditingCapability !== undefined) ||
159
+ (value['imageEditingCapability'] !== undefined && imageEditingCapability === undefined)) {
160
+ return null;
161
+ }
162
+ const imageUsageReportingCapability = value['imageUsageReportingCapability'] === undefined
163
+ ? undefined
164
+ : parseImageUsageReportingCapabilityV1(value['imageUsageReportingCapability']);
165
+ if (value['imageUsageReportingCapability'] !== undefined &&
166
+ imageUsageReportingCapability === undefined) {
167
+ return null;
168
+ }
169
+ return {
170
+ schemaVersion: 2,
171
+ taskKind: 'image_generation',
172
+ protocolFamily: value['protocolFamily'],
173
+ operations: [...operations],
174
+ inputModalities: [...inputModalities],
175
+ outputModalities: ['image'],
176
+ executionModes: ['sync'],
177
+ supportsCallback: false,
178
+ supportsCancel: false,
179
+ imageGenerationCapability,
180
+ ...(imageEditingCapability === undefined ? {} : { imageEditingCapability }),
181
+ ...(imageUsageReportingCapability === undefined ? {} : { imageUsageReportingCapability }),
182
+ };
183
+ }
184
+ function parseImageUsageReportingCapabilityV1(value) {
185
+ if (!isRecord(value) ||
186
+ !hasExactKeys(value, ['schemaVersion', 'guaranteedUnits']) ||
187
+ value['schemaVersion'] !== 1 ||
188
+ !uniqueArray(value['guaranteedUnits'], (item) => item === 'inputTokens' || item === 'outputTokens' || item === 'totalTokens')) {
189
+ return undefined;
190
+ }
191
+ return {
192
+ schemaVersion: 1,
193
+ guaranteedUnits: [
194
+ ...value['guaranteedUnits'],
195
+ ],
196
+ };
197
+ }
198
+ export function canonicalizeImageTaskCapabilityDeclarationsV1(value) {
199
+ if (!Array.isArray(value))
200
+ return [];
201
+ const parsed = [];
202
+ const keys = new Set();
203
+ for (const item of value) {
204
+ const declaration = parseImageTaskCapabilityDeclarationV1(item);
205
+ if (declaration === null)
206
+ return [];
207
+ const key = `${declaration.taskKind}:${declaration.protocolFamily}`;
208
+ if (keys.has(key))
209
+ return [];
210
+ keys.add(key);
211
+ parsed.push(declaration);
212
+ }
213
+ return parsed;
214
+ }
215
+ export function canonicalizeImageTaskCapabilityDeclarations(value) {
216
+ if (!Array.isArray(value))
217
+ return [];
218
+ const parsed = [];
219
+ const keys = new Set();
220
+ for (const item of value) {
221
+ const declaration = isRecord(item) && item['schemaVersion'] === 1
222
+ ? parseImageTaskCapabilityDeclarationV1(item)
223
+ : parseImageTaskCapabilityDeclarationV2(item);
224
+ if (declaration === null)
225
+ return [];
226
+ const key = `${declaration.taskKind}:${declaration.protocolFamily}`;
227
+ if (keys.has(key))
228
+ return [];
229
+ keys.add(key);
230
+ parsed.push(declaration);
231
+ }
232
+ return parsed;
233
+ }
234
+ export function decodeOpenAiImagesProtocolSnapshotV1(value) {
235
+ if (!isRecord(value) ||
236
+ !hasExactKeys(value, ['protocolFamily', 'created', 'items'], ['background', 'outputFormat', 'quality', 'size', 'usage']) ||
237
+ value['protocolFamily'] !== OPENAI_IMAGES_PROTOCOL ||
238
+ !positiveOrZeroInteger(value['created']) ||
239
+ !Array.isArray(value['items']) ||
240
+ value['items'].length > MAX_OUTPUT_ITEMS) {
241
+ rejectOpenAiImagesSnapshot();
242
+ }
243
+ const items = value['items'].map((item) => {
244
+ if (!isRecord(item) ||
245
+ !hasExactKeys(item, [], ['revisedPrompt']) ||
246
+ (item['revisedPrompt'] !== undefined &&
247
+ (typeof item['revisedPrompt'] !== 'string' ||
248
+ UTF8_ENCODER.encode(item['revisedPrompt']).byteLength >
249
+ MAX_IMAGE_REVISED_PROMPT_UTF8_BYTES_V1))) {
250
+ rejectOpenAiImagesSnapshot();
251
+ }
252
+ return item['revisedPrompt'] === undefined ? {} : { revisedPrompt: item['revisedPrompt'] };
253
+ });
254
+ const background = value['background'];
255
+ const outputFormat = value['outputFormat'];
256
+ const quality = value['quality'];
257
+ const size = value['size'];
258
+ if ((background !== undefined && background !== 'transparent' && background !== 'opaque') ||
259
+ (outputFormat !== undefined &&
260
+ outputFormat !== 'png' &&
261
+ outputFormat !== 'jpeg' &&
262
+ outputFormat !== 'webp') ||
263
+ (quality !== undefined && quality !== 'low' && quality !== 'medium' && quality !== 'high') ||
264
+ (size !== undefined && !imagePixelSize(size))) {
265
+ rejectOpenAiImagesSnapshot();
266
+ }
267
+ const usage = value['usage'] === undefined ? undefined : decodeOpenAiImagesUsageSnapshot(value['usage']);
268
+ return {
269
+ protocolFamily: OPENAI_IMAGES_PROTOCOL,
270
+ created: value['created'],
271
+ items,
272
+ ...(background === undefined ? {} : { background }),
273
+ ...(outputFormat === undefined ? {} : { outputFormat }),
274
+ ...(quality === undefined ? {} : { quality }),
275
+ ...(size === undefined ? {} : { size }),
276
+ ...(usage === undefined ? {} : { usage }),
277
+ };
278
+ }
279
+ export function decodeVolcImagesProtocolSnapshotV1(value) {
280
+ if (!isRecord(value) ||
281
+ !hasExactKeys(value, ['protocolFamily', 'model', 'created', 'items', 'usage']) ||
282
+ value['protocolFamily'] !== VOLC_IMAGES_PROTOCOL ||
283
+ !nonEmptyString(value['model']) ||
284
+ value['model'].length > 256 ||
285
+ !positiveOrZeroInteger(value['created']) ||
286
+ !Array.isArray(value['items']) ||
287
+ value['items'].length === 0 ||
288
+ value['items'].length > MAX_OUTPUT_ITEMS) {
289
+ rejectVolcImagesSnapshot();
290
+ }
291
+ const items = value['items'].map((item) => {
292
+ if (!isRecord(item) || !hasExactKeys(item, ['size']) || !imagePixelSize(item['size'])) {
293
+ rejectVolcImagesSnapshot();
294
+ }
295
+ return { size: item['size'] };
296
+ });
297
+ const usage = decodeVolcImagesUsageSnapshot(value['usage']);
298
+ if (usage.generatedImages !== items.length || usage.totalTokens < usage.outputTokens) {
299
+ rejectVolcImagesSnapshot();
300
+ }
301
+ return {
302
+ protocolFamily: VOLC_IMAGES_PROTOCOL,
303
+ model: value['model'],
304
+ created: value['created'],
305
+ items,
306
+ usage,
307
+ };
308
+ }
309
+ export function decodeImageGenerationProtocolSnapshotV1(value) {
310
+ if (!isRecord(value))
311
+ rejectImageGenerationSnapshot();
312
+ if (value['protocolFamily'] === OPENAI_IMAGES_PROTOCOL) {
313
+ return decodeOpenAiImagesProtocolSnapshotV1(value);
314
+ }
315
+ if (value['protocolFamily'] === VOLC_IMAGES_PROTOCOL) {
316
+ return decodeVolcImagesProtocolSnapshotV1(value);
317
+ }
318
+ return rejectImageGenerationSnapshot();
319
+ }
320
+ function parseRequestMapping(value, responseFormats, outputFormats) {
321
+ if (!isRecord(value) ||
322
+ !hasExactKeys(value, ['responseFormat', 'outputFormat', 'providerOptions']) ||
323
+ !Array.isArray(responseFormats) ||
324
+ !Array.isArray(outputFormats)) {
325
+ return null;
326
+ }
327
+ const responseFormat = parseFormatMapping(value['responseFormat'], responseFormats, (item) => item === 'url' || item === 'b64_json');
328
+ const outputFormat = parseFormatMapping(value['outputFormat'], outputFormats, (item) => item === 'png' || item === 'jpeg' || item === 'webp');
329
+ const providerOptions = value['providerOptions'];
330
+ if (responseFormat === null ||
331
+ outputFormat === null ||
332
+ !isRecord(providerOptions) ||
333
+ !hasExactKeys(providerOptions, ['openaiImages', 'volcImages']) ||
334
+ !closedUniqueArray(providerOptions['openaiImages'], (item) => item === 'background' || item === 'moderation' || item === 'outputCompression') ||
335
+ !closedUniqueArray(providerOptions['volcImages'], (item) => item === 'watermark')) {
336
+ return null;
337
+ }
338
+ return {
339
+ responseFormat,
340
+ outputFormat,
341
+ providerOptions: {
342
+ openaiImages: [
343
+ ...providerOptions['openaiImages'],
344
+ ],
345
+ volcImages: [
346
+ ...providerOptions['volcImages'],
347
+ ],
348
+ },
349
+ };
350
+ }
351
+ function parseFormatMapping(value, supported, isFormat) {
352
+ if (!isRecord(value) || typeof value['mode'] !== 'string')
353
+ return null;
354
+ if (value['mode'] === 'parameter' &&
355
+ hasExactKeys(value, ['mode', 'defaultValue']) &&
356
+ isFormat(value['defaultValue']) &&
357
+ supported.includes(value['defaultValue'])) {
358
+ return { mode: 'parameter', defaultValue: value['defaultValue'] };
359
+ }
360
+ if (value['mode'] === 'fixed' &&
361
+ hasExactKeys(value, ['mode', 'value']) &&
362
+ isFormat(value['value']) &&
363
+ supported.length === 1 &&
364
+ supported[0] === value['value']) {
365
+ return { mode: 'fixed', value: value['value'] };
366
+ }
367
+ return null;
368
+ }
369
+ function decodeOpenAiImagesUsageSnapshot(value) {
370
+ if (!isRecord(value) ||
371
+ !hasExactKeys(value, ['inputTokens', 'inputTokenDetails', 'outputTokens', 'totalTokens'], ['outputTokenDetails']) ||
372
+ !positiveOrZeroInteger(value['inputTokens']) ||
373
+ !positiveOrZeroInteger(value['outputTokens']) ||
374
+ !positiveOrZeroInteger(value['totalTokens'])) {
375
+ rejectOpenAiImagesSnapshot();
376
+ }
377
+ const inputTokenDetails = decodeImageTokenDetails(value['inputTokenDetails']);
378
+ const outputTokenDetails = value['outputTokenDetails'] === undefined
379
+ ? undefined
380
+ : decodeImageTokenDetails(value['outputTokenDetails']);
381
+ if (safeIntegerSum(inputTokenDetails.imageTokens, inputTokenDetails.textTokens) !==
382
+ value['inputTokens'] ||
383
+ (outputTokenDetails !== undefined &&
384
+ safeIntegerSum(outputTokenDetails.imageTokens, outputTokenDetails.textTokens) !==
385
+ value['outputTokens']) ||
386
+ safeIntegerSum(value['inputTokens'], value['outputTokens']) !== value['totalTokens']) {
387
+ rejectOpenAiImagesSnapshot();
388
+ }
389
+ return {
390
+ inputTokens: value['inputTokens'],
391
+ inputTokenDetails,
392
+ outputTokens: value['outputTokens'],
393
+ totalTokens: value['totalTokens'],
394
+ ...(outputTokenDetails === undefined ? {} : { outputTokenDetails }),
395
+ };
396
+ }
397
+ function decodeImageTokenDetails(value) {
398
+ if (!isRecord(value) ||
399
+ !hasExactKeys(value, ['imageTokens', 'textTokens']) ||
400
+ !positiveOrZeroInteger(value['imageTokens']) ||
401
+ !positiveOrZeroInteger(value['textTokens'])) {
402
+ rejectOpenAiImagesSnapshot();
403
+ }
404
+ return { imageTokens: value['imageTokens'], textTokens: value['textTokens'] };
405
+ }
406
+ function rejectOpenAiImagesSnapshot() {
407
+ throw new Error('INVALID_OPENAI_IMAGES_PROTOCOL_SNAPSHOT');
408
+ }
409
+ function decodeVolcImagesUsageSnapshot(value) {
410
+ if (!isRecord(value) ||
411
+ !hasExactKeys(value, ['generatedImages', 'outputTokens', 'totalTokens']) ||
412
+ !positiveOrZeroInteger(value['generatedImages']) ||
413
+ !positiveOrZeroInteger(value['outputTokens']) ||
414
+ !positiveOrZeroInteger(value['totalTokens'])) {
415
+ rejectVolcImagesSnapshot();
416
+ }
417
+ return {
418
+ generatedImages: value['generatedImages'],
419
+ outputTokens: value['outputTokens'],
420
+ totalTokens: value['totalTokens'],
421
+ };
422
+ }
423
+ function imagePixelSize(value) {
424
+ if (typeof value !== 'string')
425
+ return false;
426
+ const match = /^([1-9]\d{1,3})x([1-9]\d{1,3})$/.exec(value);
427
+ if (match === null)
428
+ return false;
429
+ const width = Number(match[1]);
430
+ const height = Number(match[2]);
431
+ return width >= 64 && width <= 8192 && height >= 64 && height <= 8192;
432
+ }
433
+ function safeIntegerSum(left, right) {
434
+ const sum = left + right;
435
+ return Number.isSafeInteger(sum) ? sum : null;
436
+ }
437
+ function rejectVolcImagesSnapshot() {
438
+ throw new Error('INVALID_VOLC_IMAGES_PROTOCOL_SNAPSHOT');
439
+ }
440
+ function rejectImageGenerationSnapshot() {
441
+ throw new Error('INVALID_IMAGE_GENERATION_PROTOCOL_SNAPSHOT');
442
+ }
443
+ export function decodeMediaTaskResultV2(value) {
444
+ if (!isRecord(value) ||
445
+ !hasExactKeys(value, ['type', 'items']) ||
446
+ value['type'] !== 'media' ||
447
+ !Array.isArray(value['items']) ||
448
+ value['items'].length > MAX_OUTPUT_ITEMS) {
449
+ rejectMediaResult();
450
+ }
451
+ const declaredInlineTotal = value['items'].reduce((total, item) => {
452
+ if (!isRecord(item) || item['kind'] !== 'inline' || !positiveOrZeroInteger(item['sizeBytes'])) {
453
+ return total;
454
+ }
455
+ return total + item['sizeBytes'];
456
+ }, 0);
457
+ if (declaredInlineTotal > MAX_INLINE_TOTAL_BYTES)
458
+ rejectMediaResult();
459
+ let inlineBytes = 0;
460
+ return {
461
+ type: 'media',
462
+ items: value['items'].map((item) => {
463
+ const output = decodeMediaTaskOutput(item);
464
+ if (output.kind === 'inline') {
465
+ inlineBytes += output.sizeBytes;
466
+ if (inlineBytes > MAX_INLINE_TOTAL_BYTES)
467
+ rejectMediaResult();
468
+ }
469
+ return output;
470
+ }),
471
+ };
472
+ }
473
+ export function summarizeMediaTaskOutput(output) {
474
+ if (output.kind === 'reference')
475
+ return output;
476
+ const { base64: _base64, ...summary } = output;
477
+ return summary;
478
+ }
479
+ function inspectInlineImageBase64V1(value, reject) {
480
+ if (typeof value !== 'string' ||
481
+ value.length > MAX_OUTPUT_ITEM_BASE64_CHARS ||
482
+ !isCanonicalBase64(value)) {
483
+ reject();
484
+ }
485
+ const sizeBytes = decodedBase64Length(value);
486
+ if (sizeBytes > MAX_OUTPUT_ITEM_BYTES)
487
+ reject();
488
+ return { base64: value, sizeBytes };
489
+ }
490
+ export function materializeInlineImageBase64ValueV1(value, expectedFormat) {
491
+ const inspected = inspectInlineImageBase64V1(value, rejectInlineImageBase64);
492
+ const { detected, sha256 } = verifyInlineImageBase64(inspected, rejectInlineImageBase64);
493
+ if (detected?.format !== expectedFormat)
494
+ rejectInlineImageBase64();
495
+ return {
496
+ kind: 'inline',
497
+ mimeType: detected.mimeType,
498
+ base64: inspected.base64,
499
+ sizeBytes: inspected.sizeBytes,
500
+ sha256,
501
+ };
502
+ }
503
+ function decodeMediaTaskOutput(value) {
504
+ if (!isRecord(value) || typeof value['kind'] !== 'string')
505
+ rejectMediaResult();
506
+ if (value['kind'] === 'reference') {
507
+ if (!hasExactKeys(value, ['kind', 'reference']))
508
+ rejectMediaResult();
509
+ return { kind: 'reference', reference: decodeReference(value['reference']) };
510
+ }
511
+ if (value['kind'] !== 'inline' ||
512
+ !hasExactKeys(value, ['kind', 'mimeType', 'base64', 'sizeBytes', 'sha256']))
513
+ rejectMediaResult();
514
+ const { mimeType, base64, sizeBytes, sha256 } = value;
515
+ if (typeof base64 !== 'string' || base64.length > MAX_OUTPUT_ITEM_BASE64_CHARS)
516
+ rejectMediaResult();
517
+ if (typeof mimeType !== 'string' ||
518
+ !IMAGE_MIME_TYPES.has(mimeType) ||
519
+ !positiveOrZeroInteger(sizeBytes) ||
520
+ sizeBytes > MAX_OUTPUT_ITEM_BYTES ||
521
+ typeof sha256 !== 'string' ||
522
+ !SHA256.test(sha256))
523
+ rejectMediaResult();
524
+ const inspected = inspectInlineImageBase64V1(base64, rejectMediaResult);
525
+ if (inspected.sizeBytes !== sizeBytes)
526
+ rejectMediaResult();
527
+ const verified = verifyInlineImageBase64(inspected, rejectMediaResult);
528
+ if (verified.sha256 !== sha256 || verified.detected?.mimeType !== mimeType)
529
+ rejectMediaResult();
530
+ return {
531
+ kind: 'inline',
532
+ mimeType,
533
+ base64,
534
+ sizeBytes,
535
+ sha256,
536
+ };
537
+ }
538
+ function verifyInlineImageBase64(inspected, reject) {
539
+ const hash = createHash('sha256');
540
+ const magic = new Uint8Array(Math.min(IMAGE_MAGIC_BYTES, inspected.sizeBytes));
541
+ let magicOffset = 0;
542
+ let decodedBytes = 0;
543
+ for (let offset = 0; offset < inspected.base64.length; offset += BASE64_VERIFY_CHUNK_CHARS) {
544
+ const chunk = Buffer.from(inspected.base64.slice(offset, offset + BASE64_VERIFY_CHUNK_CHARS), 'base64');
545
+ decodedBytes += chunk.byteLength;
546
+ if (decodedBytes > inspected.sizeBytes)
547
+ reject();
548
+ hash.update(chunk);
549
+ if (magicOffset < magic.byteLength) {
550
+ const copied = Math.min(chunk.byteLength, magic.byteLength - magicOffset);
551
+ magic.set(chunk.subarray(0, copied), magicOffset);
552
+ magicOffset += copied;
553
+ }
554
+ }
555
+ if (decodedBytes !== inspected.sizeBytes)
556
+ reject();
557
+ return { detected: detectImageFormat(magic), sha256: hash.digest('hex') };
558
+ }
559
+ function decodeReference(value) {
560
+ if (!isRecord(value) || typeof value['kind'] !== 'string')
561
+ rejectMediaResult();
562
+ if (value['kind'] === 'url' &&
563
+ hasExactKeys(value, ['kind', 'url', 'mimeType'], ['sizeBytes', 'expiresAt']) &&
564
+ nonEmptyString(value['url']) &&
565
+ imageMimeType(value['mimeType']) &&
566
+ (value['sizeBytes'] === undefined ||
567
+ (positiveOrZeroInteger(value['sizeBytes']) && value['sizeBytes'] <= MAX_OUTPUT_ITEM_BYTES)) &&
568
+ (value['expiresAt'] === undefined || nonEmptyString(value['expiresAt']))) {
569
+ return value;
570
+ }
571
+ if (value['kind'] === 'object' &&
572
+ hasExactKeys(value, ['kind', 'objectKey', 'mimeType', 'sizeBytes', 'sha256', 'expiresAt']) &&
573
+ nonEmptyString(value['objectKey']) &&
574
+ imageMimeType(value['mimeType']) &&
575
+ positiveOrZeroInteger(value['sizeBytes']) &&
576
+ value['sizeBytes'] <= MAX_OUTPUT_ITEM_BYTES &&
577
+ typeof value['sha256'] === 'string' &&
578
+ SHA256.test(value['sha256']) &&
579
+ nonEmptyString(value['expiresAt'])) {
580
+ return value;
581
+ }
582
+ return rejectMediaResult();
583
+ }
584
+ function decodedBase64Length(value) {
585
+ const padding = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0;
586
+ return (value.length / 4) * 3 - padding;
587
+ }
588
+ function isCanonicalBase64(value) {
589
+ if (value.length % 4 !== 0)
590
+ return false;
591
+ const padding = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0;
592
+ for (let index = 0; index < value.length - padding; index += 1) {
593
+ const code = value.charCodeAt(index);
594
+ if (!((code >= 65 && code <= 90) ||
595
+ (code >= 97 && code <= 122) ||
596
+ (code >= 48 && code <= 57) ||
597
+ code === 43 ||
598
+ code === 47)) {
599
+ return false;
600
+ }
601
+ }
602
+ for (let index = value.length - padding; index < value.length; index += 1) {
603
+ if (value[index] !== '=')
604
+ return false;
605
+ }
606
+ if (padding > 0) {
607
+ const tail = base64Sextet(value.charCodeAt(value.length - padding - 1));
608
+ if (tail < 0 || (padding === 2 ? (tail & 0b1111) !== 0 : (tail & 0b11) !== 0)) {
609
+ return false;
610
+ }
611
+ }
612
+ return true;
613
+ }
614
+ function base64Sextet(code) {
615
+ if (code >= 65 && code <= 90)
616
+ return code - 65;
617
+ if (code >= 97 && code <= 122)
618
+ return code - 97 + 26;
619
+ if (code >= 48 && code <= 57)
620
+ return code - 48 + 52;
621
+ if (code === 43)
622
+ return 62;
623
+ if (code === 47)
624
+ return 63;
625
+ return -1;
626
+ }
627
+ function parseImageEditingCapabilityV2(value) {
628
+ if (!isRecord(value) ||
629
+ !hasExactKeys(value, ['maxInputImages', 'supportsMask', 'inputMimeTypes']) ||
630
+ !positiveInteger(value['maxInputImages']) ||
631
+ value['maxInputImages'] > MAX_OUTPUT_ITEMS ||
632
+ typeof value['supportsMask'] !== 'boolean' ||
633
+ !uniqueArray(value['inputMimeTypes'], imageMimeType)) {
634
+ return undefined;
635
+ }
636
+ return {
637
+ maxInputImages: value['maxInputImages'],
638
+ supportsMask: value['supportsMask'],
639
+ inputMimeTypes: [...value['inputMimeTypes']],
640
+ };
641
+ }
642
+ function isSize(value) {
643
+ return (isRecord(value) &&
644
+ hasExactKeys(value, ['width', 'height']) &&
645
+ positiveInteger(value['width']) &&
646
+ positiveInteger(value['height']) &&
647
+ value['width'] >= 256 &&
648
+ value['width'] <= 4096 &&
649
+ value['height'] >= 256 &&
650
+ value['height'] <= 4096);
651
+ }
652
+ function uniqueArray(value, predicate) {
653
+ return (Array.isArray(value) &&
654
+ value.length > 0 &&
655
+ value.every(predicate) &&
656
+ new Set(value).size === value.length);
657
+ }
658
+ function closedUniqueArray(value, predicate) {
659
+ return Array.isArray(value) && value.every(predicate) && new Set(value).size === value.length;
660
+ }
661
+ function hasExactKeys(value, required, optional = []) {
662
+ const allowed = new Set([...required, ...optional]);
663
+ return (Object.keys(value).every((key) => allowed.has(key)) &&
664
+ required.every((key) => Object.prototype.hasOwnProperty.call(value, key)));
665
+ }
666
+ function isRecord(value) {
667
+ return (value !== null &&
668
+ typeof value === 'object' &&
669
+ !Array.isArray(value) &&
670
+ (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null));
671
+ }
672
+ function exactSingleton(value, expected) {
673
+ return Array.isArray(value) && value.length === 1 && value[0] === expected;
674
+ }
675
+ function positiveInteger(value) {
676
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
677
+ }
678
+ function positiveOrZeroInteger(value) {
679
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
680
+ }
681
+ function nonEmptyString(value) {
682
+ return typeof value === 'string' && Boolean(value.trim());
683
+ }
684
+ function imageMimeType(value) {
685
+ return typeof value === 'string' && IMAGE_MIME_TYPES.has(value);
686
+ }
687
+ function detectImageFormat(bytes) {
688
+ if (bytes.length >= 8 &&
689
+ bytes[0] === 0x89 &&
690
+ bytes[1] === 0x50 &&
691
+ bytes[2] === 0x4e &&
692
+ bytes[3] === 0x47 &&
693
+ bytes[4] === 0x0d &&
694
+ bytes[5] === 0x0a &&
695
+ bytes[6] === 0x1a &&
696
+ bytes[7] === 0x0a) {
697
+ return { format: 'png', mimeType: 'image/png' };
698
+ }
699
+ if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
700
+ return { format: 'jpeg', mimeType: 'image/jpeg' };
701
+ }
702
+ if (bytes.length >= 12 &&
703
+ bytes[0] === 0x52 &&
704
+ bytes[1] === 0x49 &&
705
+ bytes[2] === 0x46 &&
706
+ bytes[3] === 0x46 &&
707
+ bytes[8] === 0x57 &&
708
+ bytes[9] === 0x45 &&
709
+ bytes[10] === 0x42 &&
710
+ bytes[11] === 0x50) {
711
+ return { format: 'webp', mimeType: 'image/webp' };
712
+ }
713
+ return null;
714
+ }
715
+ function rejectInlineImageBase64() {
716
+ throw new Error('INVALID_INLINE_IMAGE_BASE64');
717
+ }
718
+ function rejectMediaResult() {
719
+ throw new Error('INVALID_MEDIA_TASK_RESULT');
720
+ }
721
+ //# sourceMappingURL=media-images.js.map