batchwork 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,15 +7,29 @@ import {
7
7
  getAdapter,
8
8
  mapWithConcurrency,
9
9
  resolveBatchLimits
10
- } from "./chunk-jvrfwjwq.js";
10
+ } from "./chunk-vy4w8mpb.js";
11
11
  import {
12
12
  __require
13
13
  } from "./chunk-v0bahtg2.js";
14
14
 
15
15
  // src/model.ts
16
16
  var CAPTURE_API_KEY = "batchwork-capture";
17
+ var azureCaptureBaseUrl = (baseURL) => {
18
+ if (!baseURL) {
19
+ return;
20
+ }
21
+ const normalized = baseURL.replace(/\/+$/u, "");
22
+ try {
23
+ if (new URL(normalized).hostname.endsWith(".openai.azure.com")) {
24
+ const root = normalized.replace(/\/v1$/u, "");
25
+ return root.endsWith("/openai") ? root : `${root}/openai`;
26
+ }
27
+ } catch {}
28
+ return normalized;
29
+ };
17
30
  var PACKAGE_BY_PROVIDER = {
18
31
  anthropic: { label: "Anthropic", specifier: "@ai-sdk/anthropic" },
32
+ azure: { label: "Azure OpenAI", specifier: "@ai-sdk/azure" },
19
33
  google: { label: "Google Gemini", specifier: "@ai-sdk/google" },
20
34
  groq: { label: "Groq", specifier: "@ai-sdk/groq" },
21
35
  mistral: { label: "Mistral", specifier: "@ai-sdk/mistral" },
@@ -25,6 +39,7 @@ var PACKAGE_BY_PROVIDER = {
25
39
  };
26
40
  var PROVIDER_BY_FAMILY = {
27
41
  anthropic: "anthropic",
42
+ azure: "azure",
28
43
  google: "google",
29
44
  groq: "groq",
30
45
  mistral: "mistral",
@@ -70,7 +85,7 @@ var resolveModel = (model) => {
70
85
  }
71
86
  const [family, suffix] = splitOnce(model.provider, ".");
72
87
  const provider = PROVIDER_BY_FAMILY[family];
73
- if (provider === "openai") {
88
+ if (provider === "openai" || provider === "azure") {
74
89
  return { kind: openaiKind(suffix), modelId: model.modelId, provider };
75
90
  }
76
91
  if (provider) {
@@ -86,6 +101,9 @@ var importProvider = (provider) => {
86
101
  case "anthropic": {
87
102
  return import("@ai-sdk/anthropic");
88
103
  }
104
+ case "azure": {
105
+ return import("@ai-sdk/azure");
106
+ }
89
107
  case "google": {
90
108
  return import("@ai-sdk/google");
91
109
  }
@@ -139,6 +157,20 @@ var createCaptureModel = async (resolved, credentials, fetchImpl) => {
139
157
  }
140
158
  return provider.chat(resolved.modelId);
141
159
  }
160
+ case "azure": {
161
+ const { createAzure } = await loadProvider("azure");
162
+ const provider = createAzure({
163
+ ...settings,
164
+ baseURL: azureCaptureBaseUrl(credentials.baseURL)
165
+ });
166
+ if (resolved.kind === "responses") {
167
+ return provider.responses(resolved.modelId);
168
+ }
169
+ if (resolved.kind === "completion") {
170
+ return provider.completion(resolved.modelId);
171
+ }
172
+ return provider.chat(resolved.modelId);
173
+ }
142
174
  case "anthropic": {
143
175
  const { createAnthropic } = await loadProvider("anthropic");
144
176
  return createAnthropic(settings).messages(resolved.modelId);
@@ -199,6 +231,38 @@ var createCaptureEmbeddingModel = async (resolved, credentials, fetchImpl) => {
199
231
  }
200
232
  }
201
233
  };
234
+ var IMAGE_EDIT_PROVIDERS = new Set(["openai", "xai"]);
235
+ var unsupportedImageEditProvider = (provider) => new UnsupportedProviderError(provider, `batchwork: provider "${provider}" does not offer batch image editing. Image edits are supported for: openai, xai.`);
236
+ var VIDEO_PROVIDERS = new Set(["xai"]);
237
+ var unsupportedVideoProvider = (provider) => new UnsupportedProviderError(provider, `batchwork: provider "${provider}" does not offer batch video generation. Videos are supported for: xai.`);
238
+ var createCaptureVideoModel = async (resolved, credentials, fetchImpl) => {
239
+ if (resolved.provider !== "xai") {
240
+ throw unsupportedVideoProvider(resolved.provider);
241
+ }
242
+ const { createXai } = await loadProvider("xai");
243
+ return createXai({
244
+ apiKey: credentials.apiKey ?? CAPTURE_API_KEY,
245
+ baseURL: credentials.baseURL,
246
+ fetch: fetchImpl,
247
+ headers: credentials.headers
248
+ }).video(resolved.modelId);
249
+ };
250
+ var TRANSLATION_PROVIDERS = new Set([
251
+ "groq",
252
+ "together"
253
+ ]);
254
+ var unsupportedTranslationProvider = (provider) => new UnsupportedProviderError(provider, `batchwork: provider "${provider}" does not offer batch translation. Translations are supported for: groq, together.`);
255
+ var MODERATION_PROVIDERS = new Set([
256
+ "mistral",
257
+ "openai"
258
+ ]);
259
+ var unsupportedModerationProvider = (provider) => new UnsupportedProviderError(provider, `batchwork: provider "${provider}" does not offer batch moderation. Moderations are supported for: openai, mistral.`);
260
+ var TRANSCRIPTION_PROVIDERS = new Set([
261
+ "groq",
262
+ "mistral",
263
+ "together"
264
+ ]);
265
+ var unsupportedTranscriptionProvider = (provider) => new UnsupportedProviderError(provider, `batchwork: provider "${provider}" does not offer batch transcription. Transcriptions are supported for: groq, mistral, together.`);
202
266
  var IMAGE_PROVIDERS = new Set([
203
267
  "google",
204
268
  "openai",
@@ -232,7 +296,12 @@ var createCaptureImageModel = async (resolved, credentials, fetchImpl) => {
232
296
  };
233
297
 
234
298
  // src/body.ts
235
- import { embed, generateImage, generateText } from "ai";
299
+ import {
300
+ embed,
301
+ experimental_generateVideo,
302
+ generateImage,
303
+ generateText
304
+ } from "ai";
236
305
  var MAX_CAUSE_DEPTH = 10;
237
306
 
238
307
  class CaptureSignalError extends Error {
@@ -395,6 +464,99 @@ var buildEmbeddingBodies = async (resolved, requests, credentials, rawLimits) =>
395
464
  return built;
396
465
  });
397
466
  };
467
+ var MODERATION_ENDPOINT = "/v1/moderations";
468
+ var moderationInput = (request) => {
469
+ const imageUrls = request.imageUrls ?? [];
470
+ if (imageUrls.length === 0) {
471
+ return request.value;
472
+ }
473
+ return [
474
+ ...request.value === undefined ? [] : [{ text: request.value, type: "text" }],
475
+ ...imageUrls.map((url) => ({ image_url: { url }, type: "image_url" }))
476
+ ];
477
+ };
478
+ var moderationBody = (resolved, request, customId) => {
479
+ if (request.value === undefined && !request.imageUrls?.length) {
480
+ throw new BatchworkError(`batchwork: moderation request "${customId}" needs \`value\` or \`imageUrls\`.`);
481
+ }
482
+ const options = request.providerOptions?.[resolved.provider];
483
+ if (resolved.provider === "openai") {
484
+ return {
485
+ input: moderationInput(request),
486
+ model: resolved.modelId,
487
+ ...options
488
+ };
489
+ }
490
+ if (resolved.provider === "mistral") {
491
+ if (request.imageUrls?.length) {
492
+ throw new BatchworkError(`batchwork: moderation request "${customId}" has \`imageUrls\`, but Mistral moderation is text-only.`);
493
+ }
494
+ return { input: request.value, model: resolved.modelId, ...options };
495
+ }
496
+ throw unsupportedModerationProvider(resolved.provider);
497
+ };
498
+ var buildModerationBodies = (resolved, requests, rawLimits) => {
499
+ const limits = resolveBatchLimits(rawLimits);
500
+ if (requests.length > limits.maxRequests) {
501
+ throw new BatchworkError(`batchwork: requests length ${requests.length} exceeds the ${limits.maxRequests} request limit.`);
502
+ }
503
+ return assignCustomIds(requests).map((item) => {
504
+ const body = moderationBody(resolved, item.request, item.customId);
505
+ assertByteLength(`request "${item.customId}"`, JSON.stringify(body), limits.maxRequestBytes);
506
+ return { body, customId: item.customId, endpoint: MODERATION_ENDPOINT };
507
+ });
508
+ };
509
+ var TRANSCRIPTION_ENDPOINT = "/v1/audio/transcriptions";
510
+ var transcriptionBody = (resolved, request) => {
511
+ const options = request.providerOptions?.[resolved.provider];
512
+ if (resolved.provider === "groq") {
513
+ return {
514
+ model: resolved.modelId,
515
+ url: request.audioUrl,
516
+ ...request.language ? { language: request.language } : {},
517
+ ...request.timestampGranularities ? {
518
+ response_format: "verbose_json",
519
+ timestamp_granularities: request.timestampGranularities
520
+ } : {},
521
+ ...options
522
+ };
523
+ }
524
+ if (resolved.provider === "mistral") {
525
+ return {
526
+ file_url: request.audioUrl,
527
+ model: resolved.modelId,
528
+ ...request.language ? { language: request.language } : {},
529
+ ...request.timestampGranularities ? { timestamp_granularities: request.timestampGranularities } : {},
530
+ ...options
531
+ };
532
+ }
533
+ if (resolved.provider === "together") {
534
+ return {
535
+ file: request.audioUrl,
536
+ model: resolved.modelId,
537
+ ...request.language ? { language: request.language } : {},
538
+ ...request.timestampGranularities ? {
539
+ response_format: "verbose_json",
540
+ timestamp_granularities: request.timestampGranularities
541
+ } : {},
542
+ ...options
543
+ };
544
+ }
545
+ throw unsupportedTranscriptionProvider(resolved.provider);
546
+ };
547
+ var buildAudioBodies = (resolved, requests, defaults, endpoint, rawLimits) => {
548
+ const limits = resolveBatchLimits(rawLimits);
549
+ if (requests.length > limits.maxRequests) {
550
+ throw new BatchworkError(`batchwork: requests length ${requests.length} exceeds the ${limits.maxRequests} request limit.`);
551
+ }
552
+ return assignCustomIds(requests).map((item) => {
553
+ const body = transcriptionBody(resolved, mergeDefaults(item.request, defaults));
554
+ assertByteLength(`request "${item.customId}"`, JSON.stringify(body), limits.maxRequestBytes);
555
+ return { body, customId: item.customId, endpoint };
556
+ });
557
+ };
558
+ var buildTranscriptionBodies = (resolved, requests, defaults, rawLimits) => buildAudioBodies(resolved, requests, defaults, TRANSCRIPTION_ENDPOINT, rawLimits);
559
+ var buildTranslationBodies = (resolved, requests, defaults, rawLimits) => buildAudioBodies(resolved, requests, defaults, "/v1/audio/translations", rawLimits);
398
560
  var buildImageBodies = async (resolved, requests, defaults, credentials, rawLimits) => {
399
561
  const limits = resolveBatchLimits(rawLimits);
400
562
  if (requests.length > limits.maxRequests) {
@@ -408,6 +570,87 @@ var buildImageBodies = async (resolved, requests, defaults, credentials, rawLimi
408
570
  return built;
409
571
  });
410
572
  };
573
+ var IMAGE_EDIT_ENDPOINT = "/v1/images/edits";
574
+ var openaiImageRef = (ref) => ("fileId" in ref) ? { file_id: ref.fileId } : { image_url: ref.imageUrl };
575
+ var imageEditBody = (resolved, request, customId) => {
576
+ if (request.images.length === 0) {
577
+ throw new BatchworkError(`batchwork: image-edit request "${customId}" needs at least one entry in \`images\`.`);
578
+ }
579
+ const options = request.providerOptions?.[resolved.provider];
580
+ if (resolved.provider === "openai") {
581
+ return {
582
+ images: request.images.map(openaiImageRef),
583
+ model: resolved.modelId,
584
+ prompt: request.prompt,
585
+ ...request.mask ? { mask: openaiImageRef(request.mask) } : {},
586
+ ...request.n === undefined ? {} : { n: request.n },
587
+ ...request.size ? { size: request.size } : {},
588
+ ...options
589
+ };
590
+ }
591
+ if (resolved.provider === "xai") {
592
+ if (request.mask) {
593
+ throw new BatchworkError(`batchwork: image-edit request "${customId}" has a \`mask\`, but xAI image edits do not support masks.`);
594
+ }
595
+ if (request.size) {
596
+ throw new BatchworkError(`batchwork: image-edit request "${customId}" has \`size\`, but xAI image edits take \`providerOptions.xai.aspect_ratio\` instead.`);
597
+ }
598
+ const urls = request.images.map((ref) => {
599
+ if ("fileId" in ref) {
600
+ throw new BatchworkError(`batchwork: image-edit request "${customId}" uses a \`fileId\` reference, but xAI image edits accept image URLs only.`);
601
+ }
602
+ return ref.imageUrl;
603
+ });
604
+ return {
605
+ model: resolved.modelId,
606
+ prompt: request.prompt,
607
+ ...urls.length === 1 ? { image: { url: urls[0] } } : { images: urls.map((url) => ({ url })) },
608
+ ...request.n === undefined ? {} : { n: request.n },
609
+ ...options
610
+ };
611
+ }
612
+ throw unsupportedImageEditProvider(resolved.provider);
613
+ };
614
+ var buildImageEditBodies = (resolved, requests, defaults, rawLimits) => {
615
+ const limits = resolveBatchLimits(rawLimits);
616
+ if (requests.length > limits.maxRequests) {
617
+ throw new BatchworkError(`batchwork: requests length ${requests.length} exceeds the ${limits.maxRequests} request limit.`);
618
+ }
619
+ return assignCustomIds(requests).map((item) => {
620
+ const body = imageEditBody(resolved, mergeDefaults(item.request, defaults), item.customId);
621
+ assertByteLength(`request "${item.customId}"`, JSON.stringify(body), limits.maxRequestBytes);
622
+ return { body, customId: item.customId, endpoint: IMAGE_EDIT_ENDPOINT };
623
+ });
624
+ };
625
+ var captureVideoOne = async (model, request, customId, maxRequestBytes) => {
626
+ try {
627
+ await experimental_generateVideo({
628
+ aspectRatio: request.aspectRatio,
629
+ duration: request.duration,
630
+ maxRetries: 0,
631
+ model,
632
+ prompt: request.prompt,
633
+ providerOptions: request.providerOptions,
634
+ resolution: request.resolution
635
+ });
636
+ } catch (error) {
637
+ return bodyFromCapture(error, customId, maxRequestBytes);
638
+ }
639
+ throw new BatchworkError("batchwork: the request was not intercepted while building the video body.");
640
+ };
641
+ var buildVideoBodies = async (resolved, requests, defaults, credentials, rawLimits) => {
642
+ const limits = resolveBatchLimits(rawLimits);
643
+ if (requests.length > limits.maxRequests) {
644
+ throw new BatchworkError(`batchwork: requests length ${requests.length} exceeds the ${limits.maxRequests} request limit.`);
645
+ }
646
+ const model = await createCaptureVideoModel(resolved, credentials, captureFetch);
647
+ const items = assignCustomIds(requests);
648
+ return await mapWithConcurrency(items, limits.captureConcurrency, async (item) => {
649
+ const built = await captureVideoOne(model, mergeDefaults(item.request, defaults), item.customId, limits.maxRequestBytes);
650
+ assertByteLength(`request "${item.customId}"`, JSON.stringify(built.body), limits.maxRequestBytes);
651
+ return built;
652
+ });
653
+ };
411
654
 
412
655
  // src/batch.ts
413
656
  var EMPTY_REQUESTS_MESSAGE = "batchwork: `requests` must not be empty.";
@@ -488,10 +731,127 @@ var submitImages = async (options) => {
488
731
  });
489
732
  return new BatchJob(adapter, credentials, snapshot);
490
733
  };
734
+ var submitImageEdits = async (options) => {
735
+ if (options.requests.length === 0) {
736
+ throw new BatchworkError(EMPTY_REQUESTS_MESSAGE);
737
+ }
738
+ const resolved = resolveModel(options.model);
739
+ if (!IMAGE_EDIT_PROVIDERS.has(resolved.provider)) {
740
+ throw unsupportedImageEditProvider(resolved.provider);
741
+ }
742
+ const credentials = pickCredentials(options);
743
+ const limits = resolveBatchLimits(options.limits);
744
+ const adapter = getAdapter(resolved.provider);
745
+ const built = buildImageEditBodies(resolved, options.requests, options.defaults, limits);
746
+ const snapshot = await adapter.submit({
747
+ built,
748
+ credentials,
749
+ endpoint: built[0]?.endpoint ?? "",
750
+ limits,
751
+ metadata: options.metadata,
752
+ modelId: resolved.modelId
753
+ });
754
+ return new BatchJob(adapter, credentials, snapshot);
755
+ };
756
+ var submitModerations = async (options) => {
757
+ if (options.requests.length === 0) {
758
+ throw new BatchworkError(EMPTY_REQUESTS_MESSAGE);
759
+ }
760
+ const resolved = resolveModel(options.model);
761
+ if (!MODERATION_PROVIDERS.has(resolved.provider)) {
762
+ throw unsupportedModerationProvider(resolved.provider);
763
+ }
764
+ const credentials = pickCredentials(options);
765
+ const limits = resolveBatchLimits(options.limits);
766
+ const adapter = getAdapter(resolved.provider);
767
+ const built = buildModerationBodies(resolved, options.requests, limits);
768
+ const snapshot = await adapter.submit({
769
+ built,
770
+ credentials,
771
+ endpoint: built[0]?.endpoint ?? "",
772
+ limits,
773
+ metadata: options.metadata,
774
+ modelId: resolved.modelId
775
+ });
776
+ return new BatchJob(adapter, credentials, snapshot);
777
+ };
778
+ var submitTranscriptions = async (options) => {
779
+ if (options.requests.length === 0) {
780
+ throw new BatchworkError(EMPTY_REQUESTS_MESSAGE);
781
+ }
782
+ const resolved = resolveModel(options.model);
783
+ if (!TRANSCRIPTION_PROVIDERS.has(resolved.provider)) {
784
+ throw unsupportedTranscriptionProvider(resolved.provider);
785
+ }
786
+ const credentials = pickCredentials(options);
787
+ const limits = resolveBatchLimits(options.limits);
788
+ const adapter = getAdapter(resolved.provider);
789
+ const built = buildTranscriptionBodies(resolved, options.requests, options.defaults, limits);
790
+ const snapshot = await adapter.submit({
791
+ built,
792
+ credentials,
793
+ endpoint: built[0]?.endpoint ?? "",
794
+ limits,
795
+ metadata: options.metadata,
796
+ modelId: resolved.modelId
797
+ });
798
+ return new BatchJob(adapter, credentials, snapshot);
799
+ };
800
+ var submitVideos = async (options) => {
801
+ if (options.requests.length === 0) {
802
+ throw new BatchworkError(EMPTY_REQUESTS_MESSAGE);
803
+ }
804
+ const resolved = resolveModel(options.model);
805
+ if (!VIDEO_PROVIDERS.has(resolved.provider)) {
806
+ throw unsupportedVideoProvider(resolved.provider);
807
+ }
808
+ const credentials = pickCredentials(options);
809
+ const limits = resolveBatchLimits(options.limits);
810
+ const adapter = getAdapter(resolved.provider);
811
+ const built = await buildVideoBodies(resolved, options.requests, options.defaults, credentials, limits);
812
+ const snapshot = await adapter.submit({
813
+ built,
814
+ credentials,
815
+ endpoint: built[0]?.endpoint ?? "",
816
+ limits,
817
+ metadata: options.metadata,
818
+ modelId: resolved.modelId
819
+ });
820
+ return new BatchJob(adapter, credentials, snapshot);
821
+ };
822
+ var submitTranslations = async (options) => {
823
+ if (options.requests.length === 0) {
824
+ throw new BatchworkError(EMPTY_REQUESTS_MESSAGE);
825
+ }
826
+ const resolved = resolveModel(options.model);
827
+ if (!TRANSLATION_PROVIDERS.has(resolved.provider)) {
828
+ throw unsupportedTranslationProvider(resolved.provider);
829
+ }
830
+ const credentials = pickCredentials(options);
831
+ const limits = resolveBatchLimits(options.limits);
832
+ const adapter = getAdapter(resolved.provider);
833
+ const built = buildTranslationBodies(resolved, options.requests, options.defaults, limits);
834
+ const snapshot = await adapter.submit({
835
+ built,
836
+ credentials,
837
+ endpoint: built[0]?.endpoint ?? "",
838
+ limits,
839
+ metadata: options.metadata,
840
+ modelId: resolved.modelId
841
+ });
842
+ return new BatchJob(adapter, credentials, snapshot);
843
+ };
491
844
  var batch = Object.assign(submitText, {
492
845
  embeddings: submitEmbeddings,
493
- images: submitImages,
494
- text: submitText
846
+ images: Object.assign(submitImages, {
847
+ create: submitImages,
848
+ edit: submitImageEdits
849
+ }),
850
+ moderations: submitModerations,
851
+ text: submitText,
852
+ transcriptions: submitTranscriptions,
853
+ translations: submitTranslations,
854
+ videos: submitVideos
495
855
  });
496
856
  var batchEmbeddings = submitEmbeddings;
497
857
  var batchImages = submitImages;
@@ -512,5 +872,5 @@ var cancelBatch = async (ref) => {
512
872
 
513
873
  export { resolveModel, batch, batchEmbeddings, batchImages, getBatch, getBatchResults, cancelBatch };
514
874
 
515
- //# debugId=EA06C285349F9BD164756E2164756E21
516
- //# sourceMappingURL=chunk-sw8dg4sm.js.map
875
+ //# debugId=1B451ACF41BE2A3C64756E2164756E21
876
+ //# sourceMappingURL=chunk-2ea62n95.js.map