batchwork 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +90 -6
  2. package/dist/batch.d.ts +179 -9
  3. package/dist/batch.d.ts.map +1 -1
  4. package/dist/body.d.ts +30 -1
  5. package/dist/body.d.ts.map +1 -1
  6. package/dist/{chunk-3yqs6x71.js → chunk-9cwfwzm4.js} +40 -10
  7. package/dist/{chunk-3yqs6x71.js.map → chunk-9cwfwzm4.js.map} +3 -3
  8. package/dist/{chunk-h2he3d16.js → chunk-gwa0dkhj.js} +139 -26
  9. package/dist/chunk-gwa0dkhj.js.map +27 -0
  10. package/dist/{chunk-bjkbtj1q.js → chunk-qqz5h9v6.js} +337 -14
  11. package/dist/chunk-qqz5h9v6.js.map +12 -0
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -2
  15. package/dist/job.d.ts.map +1 -1
  16. package/dist/model.d.ts +50 -3
  17. package/dist/model.d.ts.map +1 -1
  18. package/dist/next/index.d.ts +4 -3
  19. package/dist/next/index.d.ts.map +1 -1
  20. package/dist/next/index.js +11 -15
  21. package/dist/next/index.js.map +3 -3
  22. package/dist/providers/anthropic.d.ts.map +1 -1
  23. package/dist/providers/google.d.ts.map +1 -1
  24. package/dist/providers/mistral.d.ts.map +1 -1
  25. package/dist/providers/openai-compatible.d.ts +5 -0
  26. package/dist/providers/openai-compatible.d.ts.map +1 -1
  27. package/dist/providers/shared.d.ts +16 -1
  28. package/dist/providers/shared.d.ts.map +1 -1
  29. package/dist/providers/together.d.ts.map +1 -1
  30. package/dist/providers/xai.d.ts.map +1 -1
  31. package/dist/server/index.js +2 -2
  32. package/dist/server/poller.d.ts.map +1 -1
  33. package/dist/types.d.ts +209 -4
  34. package/dist/types.d.ts.map +1 -1
  35. package/package.json +14 -15
  36. package/dist/chunk-bjkbtj1q.js.map +0 -12
  37. package/dist/chunk-h2he3d16.js.map +0 -27
@@ -23,6 +23,7 @@ class MissingDependencyError extends BatchworkError {
23
23
  }
24
24
 
25
25
  // src/job.ts
26
+ var WAIT_ABORTED_MESSAGE = "batchwork: wait aborted.";
26
27
  var DEFAULT_POLL_INTERVAL_MS = 15000;
27
28
  var TERMINAL_STATUSES = new Set([
28
29
  "completed",
@@ -33,13 +34,13 @@ var TERMINAL_STATUSES = new Set([
33
34
  var isTerminalStatus = (status) => TERMINAL_STATUSES.has(status);
34
35
  var delay = (ms, signal) => new Promise((resolve, reject) => {
35
36
  if (signal?.aborted) {
36
- reject(new BatchworkError("batchwork: wait aborted."));
37
+ reject(new BatchworkError(WAIT_ABORTED_MESSAGE));
37
38
  return;
38
39
  }
39
40
  let timer;
40
41
  const onAbort = () => {
41
42
  clearTimeout(timer);
42
- reject(new BatchworkError("batchwork: wait aborted."));
43
+ reject(new BatchworkError(WAIT_ABORTED_MESSAGE));
43
44
  };
44
45
  timer = setTimeout(() => {
45
46
  signal?.removeEventListener("abort", onAbort);
@@ -81,7 +82,7 @@ class BatchJob {
81
82
  options.onPoll?.(snapshot);
82
83
  while (!isTerminalStatus(snapshot.status)) {
83
84
  if (options.signal?.aborted) {
84
- throw new BatchworkError("batchwork: wait aborted.");
85
+ throw new BatchworkError(WAIT_ABORTED_MESSAGE);
85
86
  }
86
87
  if (deadline !== undefined && Date.now() > deadline) {
87
88
  throw new BatchworkError(`batchwork: timed out waiting for batch "${this.id}".`);
@@ -429,7 +430,13 @@ var normalizeSnapshot = (raw) => {
429
430
  };
430
431
  };
431
432
  var textFromMessage = (message) => {
432
- const text = asArray(asRecord(message).content).map((block) => asRecord(block)).filter((block) => block.type === "text").map((block) => asString(block.text) ?? "").join("");
433
+ let text = "";
434
+ for (const item of asArray(asRecord(message).content)) {
435
+ const block = asRecord(item);
436
+ if (block.type === "text") {
437
+ text += asString(block.text) ?? "";
438
+ }
439
+ }
433
440
  return text.length > 0 ? text : undefined;
434
441
  };
435
442
  var usageFromMessage = (message) => {
@@ -534,6 +541,7 @@ var anthropicAdapter = {
534
541
 
535
542
  // src/providers/google.ts
536
543
  var GOOGLE_BASE = "https://generativelanguage.googleapis.com/v1beta";
544
+ var OPERATION_ID_LABEL = "Google operation id";
537
545
  var GOOGLE_BATCH_PREFIX = "batches";
538
546
  var apiKey2 = (credentials) => {
539
547
  const key = credentials.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? process.env.GEMINI_API_KEY;
@@ -594,7 +602,7 @@ var normalizeSnapshot2 = (raw) => {
594
602
  const failed = items.filter((item) => asRecord(item).error).length;
595
603
  const id = asString(obj.name) ?? "";
596
604
  return {
597
- id: id ? assertPrefixedProviderId("Google operation id", id, GOOGLE_BATCH_PREFIX) : "",
605
+ id: id ? assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX) : "",
598
606
  provider: "google",
599
607
  raw,
600
608
  requestCounts: {
@@ -711,18 +719,19 @@ var submit2 = async (input) => {
711
719
  return normalizeSnapshot2(raw);
712
720
  };
713
721
  var retrieve2 = async (id, credentials) => {
714
- const operationId = assertPrefixedProviderId("Google operation id", id, GOOGLE_BATCH_PREFIX);
722
+ const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
715
723
  const raw = await requestJson(`${baseUrl2(credentials)}/${operationId}`, {
716
724
  headers: headers2(credentials)
717
725
  });
718
726
  return normalizeSnapshot2(raw);
719
727
  };
728
+ var fileNameFrom = (value) => asString(asRecord(value).name) ?? asString(value);
720
729
  async function* results2(id, credentials) {
721
730
  const snapshot = await retrieve2(id, credentials);
722
731
  const raw = asRecord(snapshot.raw);
723
732
  const response = asRecord(raw.response);
724
733
  const dest = asRecord(raw.dest);
725
- const responsesFile = asString(asRecord(response.responsesFile).name) ?? asString(response.responsesFile) ?? asString(asRecord(response.responses_file).name) ?? asString(response.responses_file) ?? asString(dest.fileName) ?? asString(dest.file_name);
734
+ const responsesFile = fileNameFrom(response.responsesFile) ?? fileNameFrom(response.responses_file) ?? asString(dest.fileName) ?? asString(dest.file_name);
726
735
  if (responsesFile) {
727
736
  throw new BatchworkError(`batchwork: batch "${id}" returned file-mode results, which are not supported yet.`);
728
737
  }
@@ -735,7 +744,7 @@ async function* results2(id, credentials) {
735
744
  }
736
745
  }
737
746
  var cancel2 = async (id, credentials) => {
738
- const operationId = assertPrefixedProviderId("Google operation id", id, GOOGLE_BATCH_PREFIX);
747
+ const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
739
748
  await requestJson(`${baseUrl2(credentials)}/${operationId}:cancel`, {
740
749
  headers: headers2(credentials),
741
750
  method: "POST"
@@ -768,7 +777,26 @@ var textFromBody = (body) => {
768
777
  return content;
769
778
  }
770
779
  }
771
- return asString(obj.output_text);
780
+ return asString(obj.output_text) ?? asString(obj.text);
781
+ };
782
+ var segmentsFromBody = (body) => {
783
+ const obj = asRecord(body);
784
+ if (asString(obj.text) === undefined) {
785
+ return;
786
+ }
787
+ const segments = [];
788
+ for (const item of asArray(obj.segments)) {
789
+ const segment = asRecord(item);
790
+ const text = asString(segment.text);
791
+ if (text !== undefined) {
792
+ segments.push({
793
+ endSecond: asNumber(segment.end),
794
+ startSecond: asNumber(segment.start),
795
+ text
796
+ });
797
+ }
798
+ }
799
+ return segments.length > 0 ? segments : undefined;
772
800
  };
773
801
  var embeddingFromBody = (body) => {
774
802
  const data = asArray(asRecord(body).data);
@@ -789,6 +817,31 @@ var imagesFromBody = (body) => {
789
817
  }
790
818
  return images.length > 0 ? images : undefined;
791
819
  };
820
+ var moderationFromBody = (body) => {
821
+ const results3 = asArray(asRecord(body).results);
822
+ if (results3.length === 0) {
823
+ return;
824
+ }
825
+ const first = asRecord(results3[0]);
826
+ const categories = {};
827
+ for (const [key, value] of Object.entries(asRecord(first.categories))) {
828
+ if (typeof value === "boolean") {
829
+ categories[key] = value;
830
+ }
831
+ }
832
+ if (Object.keys(categories).length === 0) {
833
+ return;
834
+ }
835
+ const categoryScores = {};
836
+ for (const [key, value] of Object.entries(asRecord(first.category_scores))) {
837
+ const score = asNumber(value);
838
+ if (score !== undefined) {
839
+ categoryScores[key] = score;
840
+ }
841
+ }
842
+ const flagged = typeof first.flagged === "boolean" ? first.flagged : Object.values(categories).some(Boolean);
843
+ return { categories, categoryScores, flagged };
844
+ };
792
845
  var usageFromBody = (body) => {
793
846
  const usage = asRecord(asRecord(body).usage);
794
847
  const inputTokens = asNumber(usage.prompt_tokens) ?? asNumber(usage.input_tokens);
@@ -831,7 +884,9 @@ var normalizeOpenAIResult = (line) => {
831
884
  customId,
832
885
  embedding: embeddingFromBody(response.body),
833
886
  images: imagesFromBody(response.body),
887
+ moderation: moderationFromBody(response.body),
834
888
  response: response.body,
889
+ segments: segmentsFromBody(response.body),
835
890
  status: "succeeded",
836
891
  text: textFromBody(response.body),
837
892
  usage: usageFromBody(response.body)
@@ -923,7 +978,11 @@ var createOpenAICompatibleAdapter = (config) => {
923
978
  const jsonl = encodeJsonl(input.built.map((item) => {
924
979
  const body = omit(item.body, "stream");
925
980
  if (lineFormat === "body-only") {
926
- return { body, custom_id: item.customId };
981
+ return {
982
+ body,
983
+ custom_id: item.customId,
984
+ ...config.lineExtras?.(endpoint)
985
+ };
927
986
  }
928
987
  return {
929
988
  body,
@@ -993,6 +1052,7 @@ var groqAdapter = createOpenAICompatibleAdapter({
993
1052
 
994
1053
  // src/providers/mistral.ts
995
1054
  var MISTRAL_BASE = "https://api.mistral.ai/v1";
1055
+ var JOB_ID_LABEL = "Mistral job id";
996
1056
  var apiKey3 = (credentials) => resolveApiKey(credentials, "MISTRAL_API_KEY", "Mistral");
997
1057
  var baseUrl3 = (credentials) => credentials.baseURL ?? MISTRAL_BASE;
998
1058
  var authHeaders = (credentials) => ({
@@ -1032,7 +1092,7 @@ var normalizeSnapshot4 = (raw) => {
1032
1092
  return {
1033
1093
  completedAt: toDate(obj.completed_at),
1034
1094
  createdAt: toDate(obj.created_at),
1035
- id: id ? assertSimpleProviderId("Mistral job id", id) : "",
1095
+ id: id ? assertSimpleProviderId(JOB_ID_LABEL, id) : "",
1036
1096
  provider: "mistral",
1037
1097
  raw,
1038
1098
  requestCounts: {
@@ -1066,7 +1126,7 @@ var submit3 = async (input) => {
1066
1126
  return normalizeSnapshot4(raw);
1067
1127
  };
1068
1128
  var retrieve3 = async (id, credentials) => {
1069
- const jobId = assertSimpleProviderId("Mistral job id", id);
1129
+ const jobId = assertSimpleProviderId(JOB_ID_LABEL, id);
1070
1130
  const raw = await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}`, {
1071
1131
  headers: authHeaders(credentials)
1072
1132
  });
@@ -1089,7 +1149,7 @@ async function* results3(id, credentials) {
1089
1149
  }
1090
1150
  }
1091
1151
  var cancel3 = async (id, credentials) => {
1092
- const jobId = assertSimpleProviderId("Mistral job id", id);
1152
+ const jobId = assertSimpleProviderId(JOB_ID_LABEL, id);
1093
1153
  await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}/cancel`, {
1094
1154
  headers: authHeaders(credentials),
1095
1155
  method: "POST"
@@ -1125,14 +1185,46 @@ var parseIpv4 = (host) => {
1125
1185
  };
1126
1186
  var isPrivateIpv4 = (parts) => {
1127
1187
  const [a = 0, b = 0] = parts;
1128
- return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a >= 224;
1188
+ if (a === 0 || a === 10 || a === 127 || a >= 224) {
1189
+ return true;
1190
+ }
1191
+ switch (a) {
1192
+ case 100: {
1193
+ return b >= 64 && b <= 127;
1194
+ }
1195
+ case 169: {
1196
+ return b === 254;
1197
+ }
1198
+ case 172: {
1199
+ return b >= 16 && b <= 31;
1200
+ }
1201
+ case 192: {
1202
+ return b === 168;
1203
+ }
1204
+ case 198: {
1205
+ return b === 18 || b === 19;
1206
+ }
1207
+ default: {
1208
+ return false;
1209
+ }
1210
+ }
1129
1211
  };
1130
1212
  var isPrivateIpv6 = (host) => {
1131
1213
  const normalized = host.replace(/^\[/u, "").replace(/\]$/u, "").toLowerCase();
1132
1214
  if (!normalized.includes(":")) {
1133
1215
  return false;
1134
1216
  }
1135
- return normalized === "::" || normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80:");
1217
+ if (normalized === "::" || normalized === "::1") {
1218
+ return true;
1219
+ }
1220
+ return normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80:");
1221
+ };
1222
+ var isPrivateHost = (host) => {
1223
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
1224
+ return true;
1225
+ }
1226
+ const ipv4 = parseIpv4(host);
1227
+ return (ipv4 ? isPrivateIpv4(ipv4) : false) || isPrivateIpv6(host);
1136
1228
  };
1137
1229
  var validateUploadLocation = (location) => {
1138
1230
  let url;
@@ -1147,9 +1239,7 @@ var validateUploadLocation = (location) => {
1147
1239
  if (url.username || url.password) {
1148
1240
  throw new BatchworkError("batchwork: Together upload Location must not include credentials.");
1149
1241
  }
1150
- const host = url.hostname.toLowerCase();
1151
- const ipv4 = parseIpv4(host);
1152
- if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || ipv4 && isPrivateIpv4(ipv4) || isPrivateIpv6(host)) {
1242
+ if (isPrivateHost(url.hostname.toLowerCase())) {
1153
1243
  throw new BatchworkError("batchwork: Together upload Location must not target localhost or private networks.");
1154
1244
  }
1155
1245
  return url.toString();
@@ -1192,12 +1282,14 @@ var togetherAdapter = createOpenAICompatibleAdapter({
1192
1282
  baseUrl: "https://api.together.xyz/v1",
1193
1283
  filePurpose: "batch-api",
1194
1284
  id: "together",
1285
+ lineExtras: (endpoint) => endpoint.startsWith("/v1/audio/") ? { method: "FILE" } : undefined,
1195
1286
  lineFormat: "body-only",
1196
1287
  uploadFile: uploadTogetherFile
1197
1288
  });
1198
1289
 
1199
1290
  // src/providers/xai.ts
1200
1291
  var XAI_BASE = "https://api.x.ai/v1";
1292
+ var BATCH_ID_LABEL = "xAI batch id";
1201
1293
  var RESULTS_PAGE_SIZE = 100;
1202
1294
  var apiKey4 = (credentials) => resolveApiKey(credentials, "XAI_API_KEY", "xAI");
1203
1295
  var baseUrl4 = (credentials) => credentials.baseURL ?? XAI_BASE;
@@ -1231,7 +1323,7 @@ var normalizeSnapshot5 = (raw) => {
1231
1323
  completedAt: toDate(obj.finish_time ?? obj.completed_at),
1232
1324
  createdAt: toDate(obj.create_time),
1233
1325
  expiresAt: toDate(obj.expire_time ?? obj.expires_at),
1234
- id: id ? assertSimpleProviderId("xAI batch id", id) : "",
1326
+ id: id ? assertSimpleProviderId(BATCH_ID_LABEL, id) : "",
1235
1327
  provider: "xai",
1236
1328
  raw,
1237
1329
  requestCounts: {
@@ -1262,6 +1354,25 @@ var imagesFromXaiCompletion = (completion) => {
1262
1354
  }
1263
1355
  return images.length > 0 ? images : undefined;
1264
1356
  };
1357
+ var videosFromXaiCompletion = (completion) => {
1358
+ const obj = asRecord(completion);
1359
+ const entries = asArray(obj.data);
1360
+ const sources = entries.length > 0 ? entries : [obj];
1361
+ const videos = [];
1362
+ for (const source of sources) {
1363
+ const record = asRecord(source);
1364
+ const video = asRecord(record.video);
1365
+ const url = asString(video.url) ?? asString(record.url);
1366
+ if (url) {
1367
+ const duration = asNumber(video.duration) ?? asNumber(record.duration);
1368
+ videos.push({
1369
+ ...duration === undefined ? {} : { durationSeconds: duration },
1370
+ url
1371
+ });
1372
+ }
1373
+ }
1374
+ return videos.length > 0 ? videos : undefined;
1375
+ };
1265
1376
  var normalizeResult3 = (item) => {
1266
1377
  const obj = asRecord(item);
1267
1378
  const customId = asString(obj.batch_request_id) ?? "";
@@ -1280,10 +1391,12 @@ var normalizeResult3 = (item) => {
1280
1391
  };
1281
1392
  }
1282
1393
  const response = asRecord(batchResult.response);
1394
+ const [opKey] = Object.keys(response);
1283
1395
  const completion = response.chat_get_completion ?? Object.values(response)[0];
1396
+ const isVideo = response.chat_get_completion === undefined && opKey !== undefined && opKey.includes("video");
1284
1397
  return {
1285
1398
  customId,
1286
- images: imagesFromXaiCompletion(completion),
1399
+ ...isVideo ? { videos: videosFromXaiCompletion(completion) } : { images: imagesFromXaiCompletion(completion) },
1287
1400
  response: completion,
1288
1401
  status: "succeeded",
1289
1402
  text: textFromBody(completion),
@@ -1296,7 +1409,7 @@ var submit4 = async (input) => {
1296
1409
  body: omit(item.body, "stream"),
1297
1410
  custom_id: item.customId,
1298
1411
  method: "POST",
1299
- url: input.endpoint
1412
+ url: item.endpoint || input.endpoint
1300
1413
  })), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
1301
1414
  const inputFileId = await uploadInputFile(jsonl, baseUrl4(input.credentials), authHeaders2(input.credentials), { purpose: null });
1302
1415
  const raw = await requestJson(`${baseUrl4(input.credentials)}/batches`, {
@@ -1310,14 +1423,14 @@ var submit4 = async (input) => {
1310
1423
  return normalizeSnapshot5(raw);
1311
1424
  };
1312
1425
  var retrieve4 = async (id, credentials) => {
1313
- const batchId = assertSimpleProviderId("xAI batch id", id);
1426
+ const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1314
1427
  const raw = await requestJson(`${baseUrl4(credentials)}/batches/${batchId}`, {
1315
1428
  headers: authHeaders2(credentials)
1316
1429
  });
1317
1430
  return normalizeSnapshot5(raw);
1318
1431
  };
1319
1432
  async function* results4(id, credentials) {
1320
- const batchId = assertSimpleProviderId("xAI batch id", id);
1433
+ const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1321
1434
  const headers3 = authHeaders2(credentials);
1322
1435
  let token;
1323
1436
  do {
@@ -1334,7 +1447,7 @@ async function* results4(id, credentials) {
1334
1447
  } while (token);
1335
1448
  }
1336
1449
  var cancel4 = async (id, credentials) => {
1337
- const batchId = assertSimpleProviderId("xAI batch id", id);
1450
+ const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1338
1451
  await requestJson(`${baseUrl4(credentials)}/batches/${batchId}:cancel`, {
1339
1452
  headers: authHeaders2(credentials),
1340
1453
  method: "POST"
@@ -1362,5 +1475,5 @@ var getAdapter = (provider) => adapters[provider];
1362
1475
 
1363
1476
  export { BatchworkError, UnsupportedProviderError, MissingDependencyError, resolveBatchLimits, assertByteLength, mapWithConcurrency, isTerminalStatus, BatchJob, getAdapter };
1364
1477
 
1365
- //# debugId=308C7FBEE03E10E564756E2164756E21
1366
- //# sourceMappingURL=chunk-h2he3d16.js.map
1478
+ //# debugId=4C939B96519322C164756E2164756E21
1479
+ //# sourceMappingURL=chunk-gwa0dkhj.js.map