prompt-api-polyfill 1.0.0 → 1.1.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 +201 -0
- package/README.md +27 -7
- package/dist/backends/firebase.js +1808 -0
- package/dist/backends/gemini.js +55 -0
- package/dist/backends/openai.js +199 -0
- package/dist/backends/transformers.js +254 -0
- package/{backends/base.js → dist/chunks/defaults-CNQngzSd.js} +29 -24
- package/dist/prompt-api-polyfill.js +1031 -0
- package/dot_env.json +3 -0
- package/package.json +21 -16
- package/async-iterator-polyfill.js +0 -16
- package/backends/defaults.js +0 -13
- package/backends/firebase.js +0 -49
- package/backends/gemini.js +0 -52
- package/backends/openai.js +0 -337
- package/backends/transformers.js +0 -451
- package/json-schema-converter.js +0 -88
- package/multimodal-converter.js +0 -383
- package/prompt-api-polyfill.js +0 -1467
package/multimodal-converter.js
DELETED
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
export default class MultimodalConverter {
|
|
2
|
-
static async convert(type, value) {
|
|
3
|
-
if (type === 'image') {
|
|
4
|
-
return this.processImage(value);
|
|
5
|
-
}
|
|
6
|
-
if (type === 'audio') {
|
|
7
|
-
return this.processAudio(value);
|
|
8
|
-
}
|
|
9
|
-
throw new DOMException(
|
|
10
|
-
`Unsupported media type: ${type}`,
|
|
11
|
-
'NotSupportedError'
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
static async processImage(source) {
|
|
16
|
-
// Blob
|
|
17
|
-
if (source instanceof Blob) {
|
|
18
|
-
return this.blobToInlineData(source);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// BufferSource (ArrayBuffer/View) -> Sniff or Default
|
|
22
|
-
if (ArrayBuffer.isView(source) || source instanceof ArrayBuffer) {
|
|
23
|
-
const u8 =
|
|
24
|
-
source instanceof ArrayBuffer
|
|
25
|
-
? new Uint8Array(source)
|
|
26
|
-
: new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
|
27
|
-
const buffer = u8.buffer.slice(
|
|
28
|
-
u8.byteOffset,
|
|
29
|
-
u8.byteOffset + u8.byteLength
|
|
30
|
-
);
|
|
31
|
-
const base64 = this.arrayBufferToBase64(buffer);
|
|
32
|
-
const mimeType = this.#sniffImageMimeType(u8);
|
|
33
|
-
if (!mimeType) {
|
|
34
|
-
throw new DOMException('Invalid image data', 'InvalidStateError');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return { inlineData: { data: base64, mimeType } };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ImageBitmapSource (Canvas, Image, VideoFrame, etc.)
|
|
41
|
-
// We draw to a canvas to standardize to PNG
|
|
42
|
-
return this.canvasSourceToInlineData(source);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
static #sniffImageMimeType(u8) {
|
|
46
|
-
const len = u8.length;
|
|
47
|
-
if (len < 4) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// JPEG: FF D8 FF
|
|
52
|
-
if (u8[0] === 0xff && u8[1] === 0xd8 && u8[2] === 0xff) {
|
|
53
|
-
return 'image/jpeg';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// PNG: 89 50 4E 47 0D 0A 1A 0A
|
|
57
|
-
if (
|
|
58
|
-
u8[0] === 0x89 &&
|
|
59
|
-
u8[1] === 0x50 &&
|
|
60
|
-
u8[2] === 0x4e &&
|
|
61
|
-
u8[3] === 0x47 &&
|
|
62
|
-
u8[4] === 0x0d &&
|
|
63
|
-
u8[5] === 0x0a &&
|
|
64
|
-
u8[6] === 0x1a &&
|
|
65
|
-
u8[7] === 0x0a
|
|
66
|
-
) {
|
|
67
|
-
return 'image/png';
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// GIF: GIF87a / GIF89a
|
|
71
|
-
if (u8[0] === 0x47 && u8[1] === 0x49 && u8[2] === 0x46 && u8[3] === 0x38) {
|
|
72
|
-
return 'image/gif';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// WebP: RIFF (offset 0) + WEBP (offset 8)
|
|
76
|
-
if (
|
|
77
|
-
u8[0] === 0x52 &&
|
|
78
|
-
u8[1] === 0x49 &&
|
|
79
|
-
u8[2] === 0x46 &&
|
|
80
|
-
u8[3] === 0x46 &&
|
|
81
|
-
u8[8] === 0x57 &&
|
|
82
|
-
u8[9] === 0x45 &&
|
|
83
|
-
u8[10] === 0x42 &&
|
|
84
|
-
u8[11] === 0x50
|
|
85
|
-
) {
|
|
86
|
-
return 'image/webp';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// BMP: BM
|
|
90
|
-
if (u8[0] === 0x42 && u8[1] === 0x4d) {
|
|
91
|
-
return 'image/bmp';
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ICO: 00 00 01 00
|
|
95
|
-
if (u8[0] === 0x00 && u8[1] === 0x00 && u8[2] === 0x01 && u8[3] === 0x00) {
|
|
96
|
-
return 'image/x-icon';
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// TIFF: II* (LE) / MM* (BE)
|
|
100
|
-
if (
|
|
101
|
-
(u8[0] === 0x49 && u8[1] === 0x49 && u8[2] === 0x2a) ||
|
|
102
|
-
(u8[0] === 0x4d && u8[1] === 0x4d && u8[2] === 0x2a)
|
|
103
|
-
) {
|
|
104
|
-
return 'image/tiff';
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// ISOBMFF (AVIF / HEIC / HEIF)
|
|
108
|
-
// "ftyp" at offset 4
|
|
109
|
-
if (u8[4] === 0x66 && u8[5] === 0x74 && u8[6] === 0x79 && u8[7] === 0x70) {
|
|
110
|
-
const type = String.fromCharCode(u8[8], u8[9], u8[10], u8[11]);
|
|
111
|
-
if (type === 'avif' || type === 'avis') {
|
|
112
|
-
return 'image/avif';
|
|
113
|
-
}
|
|
114
|
-
if (
|
|
115
|
-
type === 'heic' ||
|
|
116
|
-
type === 'heix' ||
|
|
117
|
-
type === 'hevc' ||
|
|
118
|
-
type === 'hevx'
|
|
119
|
-
) {
|
|
120
|
-
return 'image/heic';
|
|
121
|
-
}
|
|
122
|
-
if (type === 'mif1' || type === 'msf1') {
|
|
123
|
-
return 'image/heif';
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// JPEG XL: FF 0A or container bits
|
|
128
|
-
if (u8[0] === 0xff && u8[1] === 0x0a) {
|
|
129
|
-
return 'image/jxl';
|
|
130
|
-
}
|
|
131
|
-
// Container: 00 00 00 0c 4a 58 4c 20 0d 0a 87 0a (JXL )
|
|
132
|
-
if (u8[0] === 0x00 && u8[4] === 0x4a && u8[5] === 0x58 && u8[6] === 0x4c) {
|
|
133
|
-
return 'image/jxl';
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// JPEG 2000
|
|
137
|
-
if (u8[0] === 0x00 && u8[4] === 0x6a && u8[5] === 0x50 && u8[6] === 0x20) {
|
|
138
|
-
return 'image/jp2';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// SVG: Check for <svg or <?xml (heuristics)
|
|
142
|
-
const preview = String.fromCharCode(...u8.slice(0, 100)).toLowerCase();
|
|
143
|
-
if (preview.includes('<svg') || preview.includes('<?xml')) {
|
|
144
|
-
return 'image/svg+xml';
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
static async processAudio(source) {
|
|
151
|
-
// Blob
|
|
152
|
-
if (source instanceof Blob) {
|
|
153
|
-
if (
|
|
154
|
-
source.type &&
|
|
155
|
-
!source.type.startsWith('audio/') &&
|
|
156
|
-
source.type !== 'application/ogg'
|
|
157
|
-
) {
|
|
158
|
-
throw new DOMException('Invalid audio mime type', 'DataError');
|
|
159
|
-
}
|
|
160
|
-
return this.blobToInlineData(source);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// AudioBuffer -> WAV
|
|
164
|
-
if (source instanceof AudioBuffer) {
|
|
165
|
-
const wavBuffer = this.audioBufferToWav(source);
|
|
166
|
-
const base64 = this.arrayBufferToBase64(wavBuffer);
|
|
167
|
-
return { inlineData: { data: base64, mimeType: 'audio/wav' } };
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// BufferSource -> Assume it's already an audio file (mp3/wav)
|
|
171
|
-
const isArrayBuffer =
|
|
172
|
-
source instanceof ArrayBuffer ||
|
|
173
|
-
(source &&
|
|
174
|
-
source.constructor &&
|
|
175
|
-
source.constructor.name === 'ArrayBuffer');
|
|
176
|
-
const isView =
|
|
177
|
-
ArrayBuffer.isView(source) ||
|
|
178
|
-
(source &&
|
|
179
|
-
source.buffer &&
|
|
180
|
-
(source.buffer instanceof ArrayBuffer ||
|
|
181
|
-
source.buffer.constructor.name === 'ArrayBuffer'));
|
|
182
|
-
|
|
183
|
-
if (isArrayBuffer || isView) {
|
|
184
|
-
const buffer = isArrayBuffer ? source : source.buffer;
|
|
185
|
-
return {
|
|
186
|
-
inlineData: {
|
|
187
|
-
data: this.arrayBufferToBase64(buffer),
|
|
188
|
-
mimeType: 'audio/wav', // Fallback assumption
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
throw new DOMException('Unsupported audio source', 'NotSupportedError');
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Low Level Converters
|
|
197
|
-
|
|
198
|
-
static blobToInlineData(blob) {
|
|
199
|
-
return new Promise((resolve, reject) => {
|
|
200
|
-
const reader = new FileReader();
|
|
201
|
-
reader.onloadend = () => {
|
|
202
|
-
if (reader.error) {
|
|
203
|
-
reject(reader.error);
|
|
204
|
-
} else {
|
|
205
|
-
resolve({
|
|
206
|
-
inlineData: {
|
|
207
|
-
data: reader.result.split(',')[1],
|
|
208
|
-
mimeType: blob.type,
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
reader.readAsDataURL(blob);
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
static async canvasSourceToInlineData(source) {
|
|
218
|
-
if (
|
|
219
|
-
typeof HTMLImageElement !== 'undefined' &&
|
|
220
|
-
source instanceof HTMLImageElement &&
|
|
221
|
-
!source.complete
|
|
222
|
-
) {
|
|
223
|
-
await source.decode().catch(() => {});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const getDimension = (name) => {
|
|
227
|
-
const val = source[name];
|
|
228
|
-
if (typeof val === 'object' && val !== null && 'baseVal' in val) {
|
|
229
|
-
return val.baseVal.value;
|
|
230
|
-
}
|
|
231
|
-
return typeof val === 'number' ? val : 0;
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
let w =
|
|
235
|
-
source.displayWidth ||
|
|
236
|
-
source.naturalWidth ||
|
|
237
|
-
source.videoWidth ||
|
|
238
|
-
getDimension('width');
|
|
239
|
-
let h =
|
|
240
|
-
source.displayHeight ||
|
|
241
|
-
source.naturalHeight ||
|
|
242
|
-
source.videoHeight ||
|
|
243
|
-
getDimension('height');
|
|
244
|
-
|
|
245
|
-
// Fallback for SVG elements (like SVGImageElement in DOM)
|
|
246
|
-
if ((!w || !h) && typeof source.getBBox === 'function') {
|
|
247
|
-
try {
|
|
248
|
-
const box = source.getBBox();
|
|
249
|
-
w = w || box.width;
|
|
250
|
-
h = h || box.height;
|
|
251
|
-
} catch (e) {
|
|
252
|
-
// SVG might not be in DOM or not ready
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Last resort fallback for any element in the DOM
|
|
257
|
-
if ((!w || !h) && typeof source.getBoundingClientRect === 'function') {
|
|
258
|
-
try {
|
|
259
|
-
const rect = source.getBoundingClientRect();
|
|
260
|
-
w = w || rect.width;
|
|
261
|
-
h = h || rect.height;
|
|
262
|
-
} catch (e) {}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (!w || !h) {
|
|
266
|
-
throw new DOMException('Invalid image dimensions', 'InvalidStateError');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const canvas = document.createElement('canvas');
|
|
270
|
-
canvas.width = w;
|
|
271
|
-
canvas.height = h;
|
|
272
|
-
|
|
273
|
-
const ctx = canvas.getContext('2d');
|
|
274
|
-
if (typeof ImageData !== 'undefined' && source instanceof ImageData) {
|
|
275
|
-
ctx.putImageData(source, 0, 0);
|
|
276
|
-
} else {
|
|
277
|
-
ctx.drawImage(source, 0, 0);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const dataUrl = canvas.toDataURL('image/png');
|
|
281
|
-
return {
|
|
282
|
-
inlineData: {
|
|
283
|
-
data: dataUrl.split(',')[1],
|
|
284
|
-
mimeType: 'image/png',
|
|
285
|
-
},
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
static arrayBufferToBase64(buffer) {
|
|
290
|
-
let binary = '';
|
|
291
|
-
const bytes = new Uint8Array(buffer);
|
|
292
|
-
const len = bytes.byteLength;
|
|
293
|
-
for (let i = 0; i < len; i++) {
|
|
294
|
-
binary += String.fromCharCode(bytes[i]);
|
|
295
|
-
}
|
|
296
|
-
return window.btoa(binary);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Simple WAV Encoder for AudioBuffer
|
|
300
|
-
static audioBufferToWav(buffer) {
|
|
301
|
-
const numChannels = buffer.numberOfChannels;
|
|
302
|
-
const sampleRate = buffer.sampleRate;
|
|
303
|
-
const format = 1; // PCM
|
|
304
|
-
const bitDepth = 16;
|
|
305
|
-
|
|
306
|
-
let result;
|
|
307
|
-
if (numChannels === 2) {
|
|
308
|
-
result = this.interleave(
|
|
309
|
-
buffer.getChannelData(0),
|
|
310
|
-
buffer.getChannelData(1)
|
|
311
|
-
);
|
|
312
|
-
} else {
|
|
313
|
-
result = buffer.getChannelData(0);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return this.encodeWAV(result, format, sampleRate, numChannels, bitDepth);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
static interleave(inputL, inputR) {
|
|
320
|
-
const length = inputL.length + inputR.length;
|
|
321
|
-
const result = new Float32Array(length);
|
|
322
|
-
let index = 0;
|
|
323
|
-
let inputIndex = 0;
|
|
324
|
-
while (index < length) {
|
|
325
|
-
result[index++] = inputL[inputIndex];
|
|
326
|
-
result[index++] = inputR[inputIndex];
|
|
327
|
-
inputIndex++;
|
|
328
|
-
}
|
|
329
|
-
return result;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
static encodeWAV(samples, format, sampleRate, numChannels, bitDepth) {
|
|
333
|
-
const bytesPerSample = bitDepth / 8;
|
|
334
|
-
const blockAlign = numChannels * bytesPerSample;
|
|
335
|
-
|
|
336
|
-
const buffer = new ArrayBuffer(44 + samples.length * bytesPerSample);
|
|
337
|
-
const view = new DataView(buffer);
|
|
338
|
-
|
|
339
|
-
/* RIFF identifier */
|
|
340
|
-
this.writeString(view, 0, 'RIFF');
|
|
341
|
-
/* RIFF chunk length */
|
|
342
|
-
view.setUint32(4, 36 + samples.length * bytesPerSample, true);
|
|
343
|
-
/* RIFF type */
|
|
344
|
-
this.writeString(view, 8, 'WAVE');
|
|
345
|
-
/* format chunk identifier */
|
|
346
|
-
this.writeString(view, 12, 'fmt ');
|
|
347
|
-
/* format chunk length */
|
|
348
|
-
view.setUint32(16, 16, true);
|
|
349
|
-
/* sample format (raw) */
|
|
350
|
-
view.setUint16(20, format, true);
|
|
351
|
-
/* channel count */
|
|
352
|
-
view.setUint16(22, numChannels, true);
|
|
353
|
-
/* sample rate */
|
|
354
|
-
view.setUint32(24, sampleRate, true);
|
|
355
|
-
/* byte rate (sample rate * block align) */
|
|
356
|
-
view.setUint32(28, sampleRate * blockAlign, true);
|
|
357
|
-
/* block align (channel count * bytes per sample) */
|
|
358
|
-
view.setUint16(32, blockAlign, true);
|
|
359
|
-
/* bits per sample */
|
|
360
|
-
view.setUint16(34, bitDepth, true);
|
|
361
|
-
/* data chunk identifier */
|
|
362
|
-
this.writeString(view, 36, 'data');
|
|
363
|
-
/* data chunk length */
|
|
364
|
-
view.setUint32(40, samples.length * bytesPerSample, true);
|
|
365
|
-
|
|
366
|
-
this.floatTo16BitPCM(view, 44, samples);
|
|
367
|
-
|
|
368
|
-
return buffer;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
static floatTo16BitPCM(output, offset, input) {
|
|
372
|
-
for (let i = 0; i < input.length; i++, offset += 2) {
|
|
373
|
-
const s = Math.max(-1, Math.min(1, input[i]));
|
|
374
|
-
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
static writeString(view, offset, string) {
|
|
379
|
-
for (let i = 0; i < string.length; i++) {
|
|
380
|
-
view.setUint8(offset + i, string.charCodeAt(i));
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|