batchwork 1.4.0 → 1.5.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/dist/body.d.ts +2 -2
- package/dist/body.d.ts.map +1 -1
- package/dist/chunk-6jgvqdpc.js +52 -0
- package/dist/chunk-6jgvqdpc.js.map +11 -0
- package/dist/{chunk-vy4w8mpb.js → chunk-7y95mhvm.js} +119 -151
- package/dist/chunk-7y95mhvm.js.map +27 -0
- package/dist/{chunk-ka9d4t6v.js → chunk-k7n7k3et.js} +66 -152
- package/dist/chunk-k7n7k3et.js.map +13 -0
- package/dist/{chunk-2ea62n95.js → chunk-zss8h417.js} +213 -200
- package/dist/chunk-zss8h417.js.map +12 -0
- package/dist/guards.d.ts +5 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/http.d.ts +3 -2
- package/dist/http.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -28
- package/dist/index.js.map +1 -1
- package/dist/jsonl.d.ts +3 -2
- package/dist/jsonl.d.ts.map +1 -1
- package/dist/limits.d.ts +0 -1
- package/dist/limits.d.ts.map +1 -1
- package/dist/model.d.ts +48 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +14 -20
- package/dist/next/index.js.map +3 -3
- package/dist/postgres/batch-store.d.ts.map +1 -1
- package/dist/postgres/index.js +22 -15
- package/dist/postgres/index.js.map +4 -4
- package/dist/postgres/types.d.ts +1 -1
- package/dist/postgres/types.d.ts.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/azure.d.ts.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/openai-compatible.d.ts +2 -2
- package/dist/providers/openai-compatible.d.ts.map +1 -1
- package/dist/providers/shared.d.ts +8 -8
- package/dist/providers/shared.d.ts.map +1 -1
- package/dist/providers/xai.d.ts.map +1 -1
- package/dist/redis/index.js +40 -6
- package/dist/redis/index.js.map +4 -4
- package/dist/redis/types.d.ts +18 -5
- package/dist/redis/types.d.ts.map +1 -1
- package/dist/server/index.js +13 -14
- package/dist/server/index.js.map +1 -1
- package/dist/server/poller.d.ts +6 -4
- package/dist/server/poller.d.ts.map +1 -1
- package/dist/server/signing.d.ts.map +1 -1
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/util.d.ts +18 -7
- package/dist/util.d.ts.map +1 -1
- package/package.json +20 -17
- package/dist/chunk-2ea62n95.js.map +0 -12
- package/dist/chunk-ka9d4t6v.js.map +0 -13
- package/dist/chunk-v0bahtg2.js +0 -7
- package/dist/chunk-v0bahtg2.js.map +0 -9
- package/dist/chunk-vy4w8mpb.js.map +0 -28
|
@@ -1,12 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBoolean,
|
|
3
|
+
trimTrailingSlashes,
|
|
4
|
+
parseJson,
|
|
5
|
+
asRecord,
|
|
6
|
+
asString,
|
|
7
|
+
asNumber,
|
|
8
|
+
asArray,
|
|
9
|
+
asNumberArray,
|
|
10
|
+
omit,
|
|
11
|
+
toDate
|
|
12
|
+
} from "./chunk-6jgvqdpc.js";
|
|
13
|
+
|
|
1
14
|
// src/errors.ts
|
|
2
|
-
class
|
|
15
|
+
class BatchworkError2 extends Error {
|
|
3
16
|
constructor(message, options) {
|
|
4
17
|
super(message, options);
|
|
5
18
|
this.name = "BatchworkError";
|
|
6
19
|
}
|
|
7
20
|
}
|
|
8
21
|
|
|
9
|
-
class
|
|
22
|
+
class UnsupportedProviderError2 extends BatchworkError2 {
|
|
10
23
|
provider;
|
|
11
24
|
constructor(provider, detail) {
|
|
12
25
|
super(detail ?? `batchwork: provider "${provider}" is not supported yet. Supported providers: openai, azure, anthropic, google, groq, mistral, together, xai.`);
|
|
@@ -15,7 +28,7 @@ class UnsupportedProviderError extends BatchworkError {
|
|
|
15
28
|
}
|
|
16
29
|
}
|
|
17
30
|
|
|
18
|
-
class
|
|
31
|
+
class MissingDependencyError2 extends BatchworkError2 {
|
|
19
32
|
constructor(pkg, provider) {
|
|
20
33
|
super(`batchwork: install \`${pkg}\` to batch ${provider} models (\`npm install ${pkg}\`).`);
|
|
21
34
|
this.name = "MissingDependencyError";
|
|
@@ -31,16 +44,16 @@ var TERMINAL_STATUSES = new Set([
|
|
|
31
44
|
"expired",
|
|
32
45
|
"cancelled"
|
|
33
46
|
]);
|
|
34
|
-
var
|
|
47
|
+
var isTerminalStatus2 = (status) => TERMINAL_STATUSES.has(status);
|
|
35
48
|
var delay = (ms, signal) => new Promise((resolve, reject) => {
|
|
36
49
|
if (signal?.aborted) {
|
|
37
|
-
reject(new
|
|
50
|
+
reject(new BatchworkError2(WAIT_ABORTED_MESSAGE));
|
|
38
51
|
return;
|
|
39
52
|
}
|
|
40
53
|
let timer;
|
|
41
54
|
const onAbort = () => {
|
|
42
55
|
clearTimeout(timer);
|
|
43
|
-
reject(new
|
|
56
|
+
reject(new BatchworkError2(WAIT_ABORTED_MESSAGE));
|
|
44
57
|
};
|
|
45
58
|
timer = setTimeout(() => {
|
|
46
59
|
signal?.removeEventListener("abort", onAbort);
|
|
@@ -49,7 +62,7 @@ var delay = (ms, signal) => new Promise((resolve, reject) => {
|
|
|
49
62
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
50
63
|
});
|
|
51
64
|
|
|
52
|
-
class
|
|
65
|
+
class BatchJob2 {
|
|
53
66
|
provider;
|
|
54
67
|
id;
|
|
55
68
|
#adapter;
|
|
@@ -80,12 +93,12 @@ class BatchJob {
|
|
|
80
93
|
const deadline = options.timeoutMs ? Date.now() + options.timeoutMs : undefined;
|
|
81
94
|
let snapshot = await this.poll();
|
|
82
95
|
options.onPoll?.(snapshot);
|
|
83
|
-
while (!
|
|
96
|
+
while (!isTerminalStatus2(snapshot.status)) {
|
|
84
97
|
if (options.signal?.aborted) {
|
|
85
|
-
throw new
|
|
98
|
+
throw new BatchworkError2(WAIT_ABORTED_MESSAGE);
|
|
86
99
|
}
|
|
87
100
|
if (deadline !== undefined && Date.now() > deadline) {
|
|
88
|
-
throw new
|
|
101
|
+
throw new BatchworkError2(`batchwork: timed out waiting for batch "${this.id}".`);
|
|
89
102
|
}
|
|
90
103
|
await delay(interval, options.signal);
|
|
91
104
|
snapshot = await this.poll();
|
|
@@ -119,7 +132,7 @@ var DEFAULT_LIMITS = {
|
|
|
119
132
|
var encoder = new TextEncoder;
|
|
120
133
|
var positiveInteger = (name, value) => {
|
|
121
134
|
if (!(Number.isInteger(value) && value > 0)) {
|
|
122
|
-
throw new
|
|
135
|
+
throw new BatchworkError2(`batchwork: limits.${name} must be a positive integer.`);
|
|
123
136
|
}
|
|
124
137
|
return value;
|
|
125
138
|
};
|
|
@@ -132,46 +145,29 @@ var resolveBatchLimits = (limits) => ({
|
|
|
132
145
|
var byteLength = (value) => encoder.encode(value).length;
|
|
133
146
|
var assertByteCount = (label, bytes, maxBytes) => {
|
|
134
147
|
if (bytes > maxBytes) {
|
|
135
|
-
throw new
|
|
148
|
+
throw new BatchworkError2(`batchwork: ${label} is ${bytes} bytes, exceeding the ${maxBytes} byte limit.`);
|
|
136
149
|
}
|
|
137
150
|
};
|
|
138
151
|
var assertByteLength = (label, value, maxBytes) => {
|
|
139
152
|
assertByteCount(label, byteLength(value), maxBytes);
|
|
140
153
|
};
|
|
141
|
-
var mapWithConcurrency = async (items, concurrency, mapper) => {
|
|
142
|
-
const results = [];
|
|
143
|
-
results.length = items.length;
|
|
144
|
-
let nextIndex = 0;
|
|
145
|
-
const workerCount = Math.min(concurrency, items.length);
|
|
146
|
-
const runNext = async () => {
|
|
147
|
-
const index = nextIndex;
|
|
148
|
-
nextIndex += 1;
|
|
149
|
-
if (index >= items.length) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
results[index] = await mapper(items[index]);
|
|
153
|
-
await runNext();
|
|
154
|
-
};
|
|
155
|
-
await Promise.all(Array.from({ length: workerCount }, () => runNext()));
|
|
156
|
-
return results;
|
|
157
|
-
};
|
|
158
154
|
|
|
159
155
|
// src/http.ts
|
|
160
156
|
var assertOk = (url, init, response) => {
|
|
161
157
|
if (!response.ok) {
|
|
162
|
-
throw new
|
|
158
|
+
throw new BatchworkError2(`batchwork: ${init.method ?? "GET"} ${url} failed with ${response.status}.`);
|
|
163
159
|
}
|
|
164
160
|
};
|
|
165
161
|
var requestJson = async (url, init) => {
|
|
166
162
|
const response = await fetch(url, init);
|
|
167
163
|
assertOk(url, init, response);
|
|
168
|
-
return await response.
|
|
164
|
+
return parseJson(await response.text());
|
|
169
165
|
};
|
|
170
166
|
var requestStream = async (url, init) => {
|
|
171
167
|
const response = await fetch(url, init);
|
|
172
168
|
assertOk(url, init, response);
|
|
173
169
|
if (!response.body) {
|
|
174
|
-
throw new
|
|
170
|
+
throw new BatchworkError2(`batchwork: ${url} returned an empty body.`);
|
|
175
171
|
}
|
|
176
172
|
return response.body;
|
|
177
173
|
};
|
|
@@ -184,14 +180,14 @@ var NEWLINE_BYTES = byteLength(NEWLINE);
|
|
|
184
180
|
var resolveMaxLineBytes = (options) => {
|
|
185
181
|
const maxLineBytes = options?.maxLineBytes ?? DEFAULT_MAX_JSONL_LINE_BYTES;
|
|
186
182
|
if (!(Number.isInteger(maxLineBytes) && maxLineBytes > 0)) {
|
|
187
|
-
throw new
|
|
183
|
+
throw new BatchworkError2("batchwork: JSONL maxLineBytes must be a positive integer.");
|
|
188
184
|
}
|
|
189
185
|
return maxLineBytes;
|
|
190
186
|
};
|
|
191
187
|
var assertLineSize = (line, lineNumber, maxLineBytes) => {
|
|
192
188
|
const bytes = byteLength(line);
|
|
193
189
|
if (bytes > maxLineBytes) {
|
|
194
|
-
throw new
|
|
190
|
+
throw new BatchworkError2(`batchwork: JSONL line ${lineNumber} is ${bytes} bytes, exceeding the ${maxLineBytes} byte limit.`);
|
|
195
191
|
}
|
|
196
192
|
};
|
|
197
193
|
var parseLine = (line, lineNumber, maxLineBytes) => {
|
|
@@ -201,15 +197,15 @@ var parseLine = (line, lineNumber, maxLineBytes) => {
|
|
|
201
197
|
return;
|
|
202
198
|
}
|
|
203
199
|
try {
|
|
204
|
-
return
|
|
200
|
+
return parseJson(trimmed);
|
|
205
201
|
} catch (error) {
|
|
206
|
-
throw new
|
|
202
|
+
throw new BatchworkError2(`batchwork: invalid JSONL at line ${lineNumber}.`, { cause: error });
|
|
207
203
|
}
|
|
208
204
|
};
|
|
209
205
|
var resolveMaxBytes = (options) => {
|
|
210
206
|
const maxBytes = options?.maxBytes;
|
|
211
207
|
if (maxBytes !== undefined && !(Number.isInteger(maxBytes) && maxBytes > 0)) {
|
|
212
|
-
throw new
|
|
208
|
+
throw new BatchworkError2("batchwork: JSONL maxBytes must be a positive integer.");
|
|
213
209
|
}
|
|
214
210
|
return maxBytes;
|
|
215
211
|
};
|
|
@@ -224,7 +220,7 @@ var encodeJsonl = (items, options) => {
|
|
|
224
220
|
for (const item of items) {
|
|
225
221
|
const line = JSON.stringify(item);
|
|
226
222
|
if (line === undefined) {
|
|
227
|
-
throw new
|
|
223
|
+
throw new BatchworkError2(`batchwork: ${label} contains a value that cannot be JSON encoded.`);
|
|
228
224
|
}
|
|
229
225
|
bytes += byteLength(line) + NEWLINE_BYTES;
|
|
230
226
|
if (maxBytes !== undefined) {
|
|
@@ -264,9 +260,9 @@ async function* streamJsonl(source, options) {
|
|
|
264
260
|
while (newlineIndex !== -1) {
|
|
265
261
|
const line = buffer.slice(0, newlineIndex);
|
|
266
262
|
buffer = buffer.slice(newlineIndex + 1);
|
|
267
|
-
const
|
|
268
|
-
if (
|
|
269
|
-
yield
|
|
263
|
+
const parsed = parseLine(line, lineNumber, maxLineBytes);
|
|
264
|
+
if (parsed !== undefined) {
|
|
265
|
+
yield parsed;
|
|
270
266
|
}
|
|
271
267
|
lineNumber += 1;
|
|
272
268
|
newlineIndex = buffer.indexOf(NEWLINE);
|
|
@@ -294,7 +290,7 @@ var encodeJsonArrayPayload = ({
|
|
|
294
290
|
for (const [index, item] of items.entries()) {
|
|
295
291
|
const encoded = JSON.stringify(item);
|
|
296
292
|
if (encoded === undefined) {
|
|
297
|
-
throw new
|
|
293
|
+
throw new BatchworkError2(`batchwork: ${label} contains a value that cannot be JSON encoded.`);
|
|
298
294
|
}
|
|
299
295
|
bytes += byteLength(encoded);
|
|
300
296
|
if (index > 0) {
|
|
@@ -306,54 +302,18 @@ var encodeJsonArrayPayload = ({
|
|
|
306
302
|
return `${prefix}${encodedItems.join(",")}${suffix}`;
|
|
307
303
|
};
|
|
308
304
|
|
|
309
|
-
// src/util.ts
|
|
310
|
-
var asRecord = (value) => {
|
|
311
|
-
if (typeof value === "object" && value !== null) {
|
|
312
|
-
return value;
|
|
313
|
-
}
|
|
314
|
-
return {};
|
|
315
|
-
};
|
|
316
|
-
var asString = (value) => typeof value === "string" ? value : undefined;
|
|
317
|
-
var asNumber = (value) => typeof value === "number" ? value : undefined;
|
|
318
|
-
var asArray = (value) => Array.isArray(value) ? value : [];
|
|
319
|
-
var asNumberArray = (value) => {
|
|
320
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
const numbers = value.filter((item) => typeof item === "number");
|
|
324
|
-
return numbers.length === value.length ? numbers : undefined;
|
|
325
|
-
};
|
|
326
|
-
var omit = (obj, key) => {
|
|
327
|
-
const result = {};
|
|
328
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
329
|
-
if (k !== key) {
|
|
330
|
-
result[k] = v;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return result;
|
|
334
|
-
};
|
|
335
|
-
var validDate = (date) => Number.isNaN(date.getTime()) ? undefined : date;
|
|
336
|
-
var toDate = (value) => {
|
|
337
|
-
if (typeof value === "string") {
|
|
338
|
-
return validDate(new Date(value));
|
|
339
|
-
}
|
|
340
|
-
if (typeof value === "number") {
|
|
341
|
-
return validDate(new Date(value * 1000));
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
|
|
345
305
|
// src/providers/ids.ts
|
|
346
306
|
var SIMPLE_PROVIDER_ID = /^[A-Za-z0-9_-]+$/u;
|
|
347
307
|
var assertSimpleProviderId = (label, id) => {
|
|
348
308
|
if (!SIMPLE_PROVIDER_ID.test(id)) {
|
|
349
|
-
throw new
|
|
309
|
+
throw new BatchworkError2(`batchwork: invalid ${label}.`);
|
|
350
310
|
}
|
|
351
311
|
return id;
|
|
352
312
|
};
|
|
353
313
|
var assertPrefixedProviderId = (label, id, prefix) => {
|
|
354
314
|
const [actualPrefix, value, ...rest] = id.split("/");
|
|
355
315
|
if (rest.length > 0 || actualPrefix !== prefix || !value || !SIMPLE_PROVIDER_ID.test(value)) {
|
|
356
|
-
throw new
|
|
316
|
+
throw new BatchworkError2(`batchwork: invalid ${label}.`);
|
|
357
317
|
}
|
|
358
318
|
return id;
|
|
359
319
|
};
|
|
@@ -364,7 +324,7 @@ var ANTHROPIC_VERSION = "2023-06-01";
|
|
|
364
324
|
var apiKey = (credentials) => {
|
|
365
325
|
const key = credentials.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
366
326
|
if (!key) {
|
|
367
|
-
throw new
|
|
327
|
+
throw new BatchworkError2("batchwork: missing Anthropic API key. Set ANTHROPIC_API_KEY or pass `apiKey`.");
|
|
368
328
|
}
|
|
369
329
|
return key;
|
|
370
330
|
};
|
|
@@ -376,15 +336,15 @@ var validateResultsUrl = (rawUrl, credentials) => {
|
|
|
376
336
|
resultsUrl = new URL(rawUrl);
|
|
377
337
|
expectedBase = new URL(baseUrl(credentials));
|
|
378
338
|
} catch (error) {
|
|
379
|
-
throw new
|
|
339
|
+
throw new BatchworkError2("batchwork: invalid Anthropic results_url.", {
|
|
380
340
|
cause: error
|
|
381
341
|
});
|
|
382
342
|
}
|
|
383
343
|
if (resultsUrl.origin !== expectedBase.origin) {
|
|
384
|
-
throw new
|
|
344
|
+
throw new BatchworkError2("batchwork: Anthropic results_url must match the configured API origin.");
|
|
385
345
|
}
|
|
386
346
|
if (resultsUrl.username || resultsUrl.password) {
|
|
387
|
-
throw new
|
|
347
|
+
throw new BatchworkError2("batchwork: Anthropic results_url must not include credentials.");
|
|
388
348
|
}
|
|
389
349
|
return resultsUrl.toString();
|
|
390
350
|
};
|
|
@@ -514,7 +474,7 @@ async function* results(id, credentials) {
|
|
|
514
474
|
const snapshot = await retrieve(id, credentials);
|
|
515
475
|
const resultsUrl = asString(asRecord(snapshot.raw).results_url);
|
|
516
476
|
if (!resultsUrl) {
|
|
517
|
-
throw new
|
|
477
|
+
throw new BatchworkError2(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
|
|
518
478
|
}
|
|
519
479
|
const stream = await requestStream(validateResultsUrl(resultsUrl, credentials), {
|
|
520
480
|
headers: headers(credentials),
|
|
@@ -545,7 +505,7 @@ var HTTP_OK_MAX = 300;
|
|
|
545
505
|
var resolveApiKey = (credentials, envVar, label) => {
|
|
546
506
|
const key = credentials.apiKey ?? process.env[envVar];
|
|
547
507
|
if (!key) {
|
|
548
|
-
throw new
|
|
508
|
+
throw new BatchworkError2(`batchwork: missing ${label} API key. Set ${envVar} or pass \`apiKey\`.`);
|
|
549
509
|
}
|
|
550
510
|
return key;
|
|
551
511
|
};
|
|
@@ -622,14 +582,14 @@ var imagesFromBody = (body) => {
|
|
|
622
582
|
return images.length > 0 ? images : undefined;
|
|
623
583
|
};
|
|
624
584
|
var moderationFromBody = (body) => {
|
|
625
|
-
const
|
|
626
|
-
if (
|
|
585
|
+
const results = asArray(asRecord(body).results);
|
|
586
|
+
if (results.length === 0) {
|
|
627
587
|
return;
|
|
628
588
|
}
|
|
629
|
-
const first = asRecord(
|
|
589
|
+
const first = asRecord(results[0]);
|
|
630
590
|
const categories = {};
|
|
631
591
|
for (const [key, value] of Object.entries(asRecord(first.categories))) {
|
|
632
|
-
if (
|
|
592
|
+
if (isBoolean(value)) {
|
|
633
593
|
categories[key] = value;
|
|
634
594
|
}
|
|
635
595
|
}
|
|
@@ -643,7 +603,7 @@ var moderationFromBody = (body) => {
|
|
|
643
603
|
categoryScores[key] = score;
|
|
644
604
|
}
|
|
645
605
|
}
|
|
646
|
-
const flagged =
|
|
606
|
+
const flagged = isBoolean(first.flagged) ? first.flagged : Object.values(categories).some(Boolean);
|
|
647
607
|
return { categories, categoryScores, flagged };
|
|
648
608
|
};
|
|
649
609
|
var usageFromBody = (body) => {
|
|
@@ -703,24 +663,28 @@ var normalizeOpenAIResult = (line) => {
|
|
|
703
663
|
status: "errored"
|
|
704
664
|
};
|
|
705
665
|
};
|
|
706
|
-
var uploadInputFile = async (jsonl,
|
|
666
|
+
var uploadInputFile = async (jsonl, baseUrl, headers, options = {}) => {
|
|
707
667
|
const form = new FormData;
|
|
708
668
|
const purpose = options.purpose === undefined ? "batch" : options.purpose;
|
|
709
669
|
if (purpose !== null) {
|
|
710
670
|
form.append("purpose", purpose);
|
|
711
671
|
}
|
|
712
672
|
form.append("file", new Blob([jsonl], { type: "application/jsonl" }), "batchwork.jsonl");
|
|
713
|
-
const raw = await requestJson(`${
|
|
673
|
+
const raw = await requestJson(`${baseUrl}/files`, {
|
|
714
674
|
body: form,
|
|
715
|
-
headers
|
|
675
|
+
headers,
|
|
716
676
|
method: "POST",
|
|
717
677
|
redirect: "manual"
|
|
718
678
|
});
|
|
719
|
-
|
|
679
|
+
const id = asString(asRecord(raw).id);
|
|
680
|
+
if (!id) {
|
|
681
|
+
throw new BatchworkError2("batchwork: the file upload response carried no file id.");
|
|
682
|
+
}
|
|
683
|
+
return id;
|
|
720
684
|
};
|
|
721
|
-
async function* streamResultFile(fileId,
|
|
722
|
-
const stream = await requestStream(`${
|
|
723
|
-
headers
|
|
685
|
+
async function* streamResultFile(fileId, baseUrl, headers) {
|
|
686
|
+
const stream = await requestStream(`${baseUrl}/files/${fileId}/content`, {
|
|
687
|
+
headers,
|
|
724
688
|
redirect: "manual"
|
|
725
689
|
});
|
|
726
690
|
for await (const line of streamJsonl(stream)) {
|
|
@@ -771,8 +735,8 @@ var normalizeSnapshot2 = (raw, provider) => {
|
|
|
771
735
|
var createOpenAICompatibleAdapter = (config) => {
|
|
772
736
|
const completionWindow = config.completionWindow ?? DEFAULT_COMPLETION_WINDOW;
|
|
773
737
|
const lineFormat = config.lineFormat ?? "method-url";
|
|
774
|
-
const
|
|
775
|
-
throw new
|
|
738
|
+
const baseUrl = (credentials) => config.resolveBaseUrl?.(credentials) ?? credentials.baseURL ?? config.baseUrl ?? (() => {
|
|
739
|
+
throw new BatchworkError2(`batchwork: missing ${config.apiKeyLabel} base URL. Pass \`baseURL\`.`);
|
|
776
740
|
})();
|
|
777
741
|
const authHeaders = (credentials) => ({
|
|
778
742
|
...config.authHeaders ? config.authHeaders(credentials) : {
|
|
@@ -780,7 +744,7 @@ var createOpenAICompatibleAdapter = (config) => {
|
|
|
780
744
|
},
|
|
781
745
|
...credentials.headers
|
|
782
746
|
});
|
|
783
|
-
const
|
|
747
|
+
const submit = async (input) => {
|
|
784
748
|
const limits = resolveBatchLimits(input.limits);
|
|
785
749
|
const endpoint = config.normalizeEndpoint ? config.normalizeEndpoint(input.endpoint) : input.endpoint;
|
|
786
750
|
const batchEndpoint = config.batchEndpoint?.(endpoint) ?? endpoint;
|
|
@@ -800,10 +764,10 @@ var createOpenAICompatibleAdapter = (config) => {
|
|
|
800
764
|
url: endpoint
|
|
801
765
|
};
|
|
802
766
|
}), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
|
|
803
|
-
const
|
|
804
|
-
const url =
|
|
767
|
+
const headers = authHeaders(input.credentials);
|
|
768
|
+
const url = baseUrl(input.credentials);
|
|
805
769
|
const purpose = config.filePurpose ?? "batch";
|
|
806
|
-
const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers
|
|
770
|
+
const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers, jsonl, purpose }) : uploadInputFile(jsonl, url, headers, { purpose }));
|
|
807
771
|
const raw = await requestJson(`${url}/batches`, {
|
|
808
772
|
body: JSON.stringify({
|
|
809
773
|
completion_window: completionWindow,
|
|
@@ -811,46 +775,45 @@ var createOpenAICompatibleAdapter = (config) => {
|
|
|
811
775
|
input_file_id: inputFileId,
|
|
812
776
|
metadata: input.metadata
|
|
813
777
|
}),
|
|
814
|
-
headers: { ...
|
|
778
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
815
779
|
method: "POST"
|
|
816
780
|
});
|
|
817
781
|
return normalizeSnapshot2(raw, config.id);
|
|
818
782
|
};
|
|
819
|
-
const
|
|
783
|
+
const retrieve = async (id, credentials) => {
|
|
820
784
|
const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
|
|
821
|
-
const raw = await requestJson(`${
|
|
785
|
+
const raw = await requestJson(`${baseUrl(credentials)}/batches/${batchId}`, {
|
|
822
786
|
headers: authHeaders(credentials)
|
|
823
787
|
});
|
|
824
788
|
return normalizeSnapshot2(raw, config.id);
|
|
825
789
|
};
|
|
826
|
-
async function*
|
|
827
|
-
const snapshot = await
|
|
790
|
+
async function* results(id, credentials) {
|
|
791
|
+
const snapshot = await retrieve(id, credentials);
|
|
828
792
|
const raw = asRecord(snapshot.raw);
|
|
829
793
|
const outputFileId = asString(raw.output_file_id);
|
|
830
794
|
const errorFileId = asString(raw.error_file_id);
|
|
831
795
|
if (!(outputFileId || errorFileId)) {
|
|
832
|
-
throw new
|
|
796
|
+
throw new BatchworkError2(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
|
|
833
797
|
}
|
|
834
|
-
const
|
|
798
|
+
const headers = authHeaders(credentials);
|
|
835
799
|
if (outputFileId) {
|
|
836
|
-
yield* streamResultFile(assertSimpleProviderId(`${config.id} output file id`, outputFileId),
|
|
800
|
+
yield* streamResultFile(assertSimpleProviderId(`${config.id} output file id`, outputFileId), baseUrl(credentials), headers);
|
|
837
801
|
}
|
|
838
802
|
if (errorFileId) {
|
|
839
|
-
yield* streamResultFile(assertSimpleProviderId(`${config.id} error file id`, errorFileId),
|
|
803
|
+
yield* streamResultFile(assertSimpleProviderId(`${config.id} error file id`, errorFileId), baseUrl(credentials), headers);
|
|
840
804
|
}
|
|
841
805
|
}
|
|
842
|
-
const
|
|
806
|
+
const cancel = async (id, credentials) => {
|
|
843
807
|
const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
|
|
844
|
-
await requestJson(`${
|
|
808
|
+
await requestJson(`${baseUrl(credentials)}/batches/${batchId}/cancel`, {
|
|
845
809
|
headers: authHeaders(credentials),
|
|
846
810
|
method: "POST"
|
|
847
811
|
});
|
|
848
812
|
};
|
|
849
|
-
return { cancel
|
|
813
|
+
return { cancel, id: config.id, results, retrieve, submit };
|
|
850
814
|
};
|
|
851
815
|
|
|
852
816
|
// src/providers/azure.ts
|
|
853
|
-
var withoutTrailingSlash = (value) => value.replace(/\/+$/u, "");
|
|
854
817
|
var isAzureOpenAIUrl = (value) => {
|
|
855
818
|
try {
|
|
856
819
|
return new URL(value).hostname.endsWith(".openai.azure.com");
|
|
@@ -861,9 +824,9 @@ var isAzureOpenAIUrl = (value) => {
|
|
|
861
824
|
var baseUrl2 = (credentials) => {
|
|
862
825
|
const configured = credentials.baseURL ?? (process.env.AZURE_RESOURCE_NAME ? `https://${process.env.AZURE_RESOURCE_NAME}.openai.azure.com/openai` : undefined);
|
|
863
826
|
if (!configured) {
|
|
864
|
-
throw new
|
|
827
|
+
throw new BatchworkError2("batchwork: missing Azure OpenAI resource. Set AZURE_RESOURCE_NAME or pass `baseURL`.");
|
|
865
828
|
}
|
|
866
|
-
const normalized =
|
|
829
|
+
const normalized = trimTrailingSlashes(configured);
|
|
867
830
|
if (!isAzureOpenAIUrl(normalized)) {
|
|
868
831
|
return normalized;
|
|
869
832
|
}
|
|
@@ -872,7 +835,7 @@ var baseUrl2 = (credentials) => {
|
|
|
872
835
|
}
|
|
873
836
|
return normalized.endsWith("/openai") ? `${normalized}/v1` : `${normalized}/openai/v1`;
|
|
874
837
|
};
|
|
875
|
-
var hasCallerAuth = (
|
|
838
|
+
var hasCallerAuth = (headers) => Object.keys(headers ?? {}).some((name) => {
|
|
876
839
|
const normalized = name.toLowerCase();
|
|
877
840
|
return normalized === "api-key" || normalized === "authorization";
|
|
878
841
|
});
|
|
@@ -883,11 +846,11 @@ var authHeaders = (credentials) => {
|
|
|
883
846
|
if (hasCallerAuth(credentials.headers)) {
|
|
884
847
|
return {};
|
|
885
848
|
}
|
|
886
|
-
const
|
|
887
|
-
if (
|
|
888
|
-
return { "api-key":
|
|
849
|
+
const apiKey = process.env.AZURE_API_KEY ?? process.env.AZURE_OPENAI_API_KEY;
|
|
850
|
+
if (apiKey) {
|
|
851
|
+
return { "api-key": apiKey };
|
|
889
852
|
}
|
|
890
|
-
throw new
|
|
853
|
+
throw new BatchworkError2("batchwork: missing Azure OpenAI API key. Set AZURE_API_KEY or pass `apiKey`.");
|
|
891
854
|
};
|
|
892
855
|
var azureAdapter = createOpenAICompatibleAdapter({
|
|
893
856
|
apiKeyEnv: "AZURE_API_KEY",
|
|
@@ -906,7 +869,7 @@ var GOOGLE_BATCH_PREFIX = "batches";
|
|
|
906
869
|
var apiKey2 = (credentials) => {
|
|
907
870
|
const key = credentials.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? process.env.GEMINI_API_KEY;
|
|
908
871
|
if (!key) {
|
|
909
|
-
throw new
|
|
872
|
+
throw new BatchworkError2("batchwork: missing Google Gemini API key. Set GOOGLE_GENERATIVE_AI_API_KEY (or GEMINI_API_KEY) or pass `apiKey`.");
|
|
910
873
|
}
|
|
911
874
|
return key;
|
|
912
875
|
};
|
|
@@ -1093,11 +1056,11 @@ async function* results2(id, credentials) {
|
|
|
1093
1056
|
const dest = asRecord(raw.dest);
|
|
1094
1057
|
const responsesFile = fileNameFrom(response.responsesFile) ?? fileNameFrom(response.responses_file) ?? asString(dest.fileName) ?? asString(dest.file_name);
|
|
1095
1058
|
if (responsesFile) {
|
|
1096
|
-
throw new
|
|
1059
|
+
throw new BatchworkError2(`batchwork: batch "${id}" returned file-mode results, which are not supported yet.`);
|
|
1097
1060
|
}
|
|
1098
1061
|
const items = inlinedResponses(raw);
|
|
1099
1062
|
if (items.length === 0) {
|
|
1100
|
-
throw new
|
|
1063
|
+
throw new BatchworkError2(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
|
|
1101
1064
|
}
|
|
1102
1065
|
for (const item of items) {
|
|
1103
1066
|
yield normalizeResult2(item);
|
|
@@ -1215,15 +1178,15 @@ async function* results3(id, credentials) {
|
|
|
1215
1178
|
const raw = asRecord(snapshot.raw);
|
|
1216
1179
|
const outputFileId = asString(raw.output_file);
|
|
1217
1180
|
const errorFileId = asString(raw.error_file);
|
|
1218
|
-
const
|
|
1181
|
+
const headers = authHeaders2(credentials);
|
|
1219
1182
|
if (outputFileId) {
|
|
1220
|
-
yield* streamResultFile(assertSimpleProviderId("Mistral output file id", outputFileId), baseUrl4(credentials),
|
|
1183
|
+
yield* streamResultFile(assertSimpleProviderId("Mistral output file id", outputFileId), baseUrl4(credentials), headers);
|
|
1221
1184
|
}
|
|
1222
1185
|
if (errorFileId) {
|
|
1223
|
-
yield* streamResultFile(assertSimpleProviderId("Mistral error file id", errorFileId), baseUrl4(credentials),
|
|
1186
|
+
yield* streamResultFile(assertSimpleProviderId("Mistral error file id", errorFileId), baseUrl4(credentials), headers);
|
|
1224
1187
|
}
|
|
1225
1188
|
if (!(outputFileId || errorFileId)) {
|
|
1226
|
-
throw new
|
|
1189
|
+
throw new BatchworkError2(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
|
|
1227
1190
|
}
|
|
1228
1191
|
}
|
|
1229
1192
|
var cancel3 = async (id, credentials) => {
|
|
@@ -1309,16 +1272,16 @@ var validateUploadLocation = (location) => {
|
|
|
1309
1272
|
try {
|
|
1310
1273
|
url = new URL(location);
|
|
1311
1274
|
} catch (error) {
|
|
1312
|
-
throw new
|
|
1275
|
+
throw new BatchworkError2("batchwork: Together upload Location must be a valid URL.", { cause: error });
|
|
1313
1276
|
}
|
|
1314
1277
|
if (url.protocol !== "https:") {
|
|
1315
|
-
throw new
|
|
1278
|
+
throw new BatchworkError2("batchwork: Together upload Location must use https.");
|
|
1316
1279
|
}
|
|
1317
1280
|
if (url.username || url.password) {
|
|
1318
|
-
throw new
|
|
1281
|
+
throw new BatchworkError2("batchwork: Together upload Location must not include credentials.");
|
|
1319
1282
|
}
|
|
1320
1283
|
if (isPrivateHost(url.hostname.toLowerCase())) {
|
|
1321
|
-
throw new
|
|
1284
|
+
throw new BatchworkError2("batchwork: Together upload Location must not target localhost or private networks.");
|
|
1322
1285
|
}
|
|
1323
1286
|
return url.toString();
|
|
1324
1287
|
};
|
|
@@ -1336,7 +1299,7 @@ var uploadTogetherFile = async (args) => {
|
|
|
1336
1299
|
const location = init.headers.get("location");
|
|
1337
1300
|
const fileId = init.headers.get("x-together-file-id");
|
|
1338
1301
|
if (init.status !== HTTP_FOUND || !(location && fileId)) {
|
|
1339
|
-
throw new
|
|
1302
|
+
throw new BatchworkError2(`batchwork: Together upload could not be initiated (${init.status}).`);
|
|
1340
1303
|
}
|
|
1341
1304
|
const uploadLocation = validateUploadLocation(location);
|
|
1342
1305
|
const upload = await fetch(uploadLocation, {
|
|
@@ -1345,7 +1308,7 @@ var uploadTogetherFile = async (args) => {
|
|
|
1345
1308
|
redirect: "manual"
|
|
1346
1309
|
});
|
|
1347
1310
|
if (!upload.ok) {
|
|
1348
|
-
throw new
|
|
1311
|
+
throw new BatchworkError2(`batchwork: Together file upload failed (${upload.status}).`);
|
|
1349
1312
|
}
|
|
1350
1313
|
const safeFileId = assertSimpleProviderId("Together file id", fileId);
|
|
1351
1314
|
await requestJson(`${args.baseUrl}/files/${safeFileId}/preprocess`, {
|
|
@@ -1424,10 +1387,14 @@ var imagesFromXaiCompletion = (completion) => {
|
|
|
1424
1387
|
const data = asString(record.base64) ?? asString(record.b64_json);
|
|
1425
1388
|
const url = asString(record.url);
|
|
1426
1389
|
if (data || url) {
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
}
|
|
1390
|
+
const image = {};
|
|
1391
|
+
if (data) {
|
|
1392
|
+
image.data = data;
|
|
1393
|
+
}
|
|
1394
|
+
if (url) {
|
|
1395
|
+
image.url = url;
|
|
1396
|
+
}
|
|
1397
|
+
images.push(image);
|
|
1431
1398
|
}
|
|
1432
1399
|
}
|
|
1433
1400
|
return images.length > 0 ? images : undefined;
|
|
@@ -1442,11 +1409,12 @@ var videosFromXaiCompletion = (completion) => {
|
|
|
1442
1409
|
const video = asRecord(record.video);
|
|
1443
1410
|
const url = asString(video.url) ?? asString(record.url);
|
|
1444
1411
|
if (url) {
|
|
1412
|
+
const entry = { url };
|
|
1445
1413
|
const duration = asNumber(video.duration) ?? asNumber(record.duration);
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1414
|
+
if (duration !== undefined) {
|
|
1415
|
+
entry.durationSeconds = duration;
|
|
1416
|
+
}
|
|
1417
|
+
videos.push(entry);
|
|
1450
1418
|
}
|
|
1451
1419
|
}
|
|
1452
1420
|
return videos.length > 0 ? videos : undefined;
|
|
@@ -1509,16 +1477,16 @@ var retrieve4 = async (id, credentials) => {
|
|
|
1509
1477
|
};
|
|
1510
1478
|
async function* results4(id, credentials) {
|
|
1511
1479
|
const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
|
|
1512
|
-
const
|
|
1480
|
+
const headers = authHeaders3(credentials);
|
|
1513
1481
|
let token;
|
|
1514
1482
|
do {
|
|
1515
1483
|
const query = new URLSearchParams({ limit: String(RESULTS_PAGE_SIZE) });
|
|
1516
1484
|
if (token) {
|
|
1517
1485
|
query.set("pagination_token", token);
|
|
1518
1486
|
}
|
|
1519
|
-
const raw = await requestJson(`${baseUrl5(credentials)}/batches/${batchId}/results?${query.toString()}`, { headers
|
|
1487
|
+
const raw = await requestJson(`${baseUrl5(credentials)}/batches/${batchId}/results?${query.toString()}`, { headers });
|
|
1520
1488
|
const page = asRecord(raw);
|
|
1521
|
-
for (const item of
|
|
1489
|
+
for (const item of asArray(page.results)) {
|
|
1522
1490
|
yield normalizeResult3(item);
|
|
1523
1491
|
}
|
|
1524
1492
|
token = asString(page.pagination_token);
|
|
@@ -1552,7 +1520,7 @@ var adapters = {
|
|
|
1552
1520
|
};
|
|
1553
1521
|
var getAdapter = (provider) => adapters[provider];
|
|
1554
1522
|
|
|
1555
|
-
export {
|
|
1523
|
+
export { BatchworkError2, UnsupportedProviderError2, MissingDependencyError2, resolveBatchLimits, assertByteLength, isTerminalStatus2, BatchJob2, getAdapter };
|
|
1556
1524
|
|
|
1557
|
-
//# debugId=
|
|
1558
|
-
//# sourceMappingURL=chunk-
|
|
1525
|
+
//# debugId=7B0EC738FEADB97E64756E2164756E21
|
|
1526
|
+
//# sourceMappingURL=chunk-7y95mhvm.js.map
|