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.
@@ -9,7 +9,7 @@ class BatchworkError extends Error {
9
9
  class UnsupportedProviderError extends BatchworkError {
10
10
  provider;
11
11
  constructor(provider, detail) {
12
- super(detail ?? `batchwork: provider "${provider}" is not supported yet. Supported providers: openai, anthropic, google, groq, mistral, together, xai.`);
12
+ super(detail ?? `batchwork: provider "${provider}" is not supported yet. Supported providers: openai, azure, anthropic, google, groq, mistral, together, xai.`);
13
13
  this.name = "UnsupportedProviderError";
14
14
  this.provider = provider;
15
15
  }
@@ -539,6 +539,366 @@ var anthropicAdapter = {
539
539
  submit
540
540
  };
541
541
 
542
+ // src/providers/shared.ts
543
+ var HTTP_OK_MIN = 200;
544
+ var HTTP_OK_MAX = 300;
545
+ var resolveApiKey = (credentials, envVar, label) => {
546
+ const key = credentials.apiKey ?? process.env[envVar];
547
+ if (!key) {
548
+ throw new BatchworkError(`batchwork: missing ${label} API key. Set ${envVar} or pass \`apiKey\`.`);
549
+ }
550
+ return key;
551
+ };
552
+ var textFromBody = (body) => {
553
+ const obj = asRecord(body);
554
+ const choices = asArray(obj.choices);
555
+ if (choices.length > 0) {
556
+ const content = asString(asRecord(asRecord(choices[0]).message).content);
557
+ if (content) {
558
+ return content;
559
+ }
560
+ }
561
+ const directText = asString(obj.output_text) ?? asString(obj.text);
562
+ if (directText !== undefined) {
563
+ return directText;
564
+ }
565
+ const parts = [];
566
+ for (const output of asArray(obj.output)) {
567
+ const item = asRecord(output);
568
+ if (item.type !== "message") {
569
+ continue;
570
+ }
571
+ for (const content of asArray(item.content)) {
572
+ const part = asRecord(content);
573
+ if (part.type !== "output_text") {
574
+ continue;
575
+ }
576
+ const text = asString(part.text);
577
+ if (text !== undefined) {
578
+ parts.push(text);
579
+ }
580
+ }
581
+ }
582
+ if (parts.length > 0) {
583
+ return parts.join("");
584
+ }
585
+ };
586
+ var segmentsFromBody = (body) => {
587
+ const obj = asRecord(body);
588
+ if (asString(obj.text) === undefined) {
589
+ return;
590
+ }
591
+ const segments = [];
592
+ for (const item of asArray(obj.segments)) {
593
+ const segment = asRecord(item);
594
+ const text = asString(segment.text);
595
+ if (text !== undefined) {
596
+ segments.push({
597
+ endSecond: asNumber(segment.end),
598
+ startSecond: asNumber(segment.start),
599
+ text
600
+ });
601
+ }
602
+ }
603
+ return segments.length > 0 ? segments : undefined;
604
+ };
605
+ var embeddingFromBody = (body) => {
606
+ const data = asArray(asRecord(body).data);
607
+ if (data.length === 0) {
608
+ return;
609
+ }
610
+ return asNumberArray(asRecord(data[0]).embedding);
611
+ };
612
+ var imagesFromBody = (body) => {
613
+ const obj = asRecord(body);
614
+ const mediaType = `image/${asString(obj.output_format) ?? "png"}`;
615
+ const images = [];
616
+ for (const item of asArray(obj.data)) {
617
+ const b64 = asString(asRecord(item).b64_json);
618
+ if (b64) {
619
+ images.push({ data: b64, mediaType });
620
+ }
621
+ }
622
+ return images.length > 0 ? images : undefined;
623
+ };
624
+ var moderationFromBody = (body) => {
625
+ const results2 = asArray(asRecord(body).results);
626
+ if (results2.length === 0) {
627
+ return;
628
+ }
629
+ const first = asRecord(results2[0]);
630
+ const categories = {};
631
+ for (const [key, value] of Object.entries(asRecord(first.categories))) {
632
+ if (typeof value === "boolean") {
633
+ categories[key] = value;
634
+ }
635
+ }
636
+ if (Object.keys(categories).length === 0) {
637
+ return;
638
+ }
639
+ const categoryScores = {};
640
+ for (const [key, value] of Object.entries(asRecord(first.category_scores))) {
641
+ const score = asNumber(value);
642
+ if (score !== undefined) {
643
+ categoryScores[key] = score;
644
+ }
645
+ }
646
+ const flagged = typeof first.flagged === "boolean" ? first.flagged : Object.values(categories).some(Boolean);
647
+ return { categories, categoryScores, flagged };
648
+ };
649
+ var usageFromBody = (body) => {
650
+ const usage = asRecord(asRecord(body).usage);
651
+ const inputTokens = asNumber(usage.prompt_tokens) ?? asNumber(usage.input_tokens);
652
+ const outputTokens = asNumber(usage.completion_tokens) ?? asNumber(usage.output_tokens);
653
+ const totalTokens = asNumber(usage.total_tokens);
654
+ if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) {
655
+ return;
656
+ }
657
+ return {
658
+ inputTokens,
659
+ outputTokens,
660
+ totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0)
661
+ };
662
+ };
663
+ var errorFromValue = (value, fallback) => {
664
+ const obj = asRecord(value);
665
+ const nested = asRecord(obj.error);
666
+ const source = nested.message ? nested : obj;
667
+ return {
668
+ code: asNumber(source.code) ?? asString(source.code),
669
+ message: asString(source.message) ?? fallback,
670
+ type: asString(source.type)
671
+ };
672
+ };
673
+ var normalizeOpenAIResult = (line) => {
674
+ const obj = asRecord(line);
675
+ const customId = asString(obj.custom_id) ?? "";
676
+ if (obj.error) {
677
+ return {
678
+ customId,
679
+ error: errorFromValue(obj.error, "Request errored."),
680
+ response: obj.error,
681
+ status: "errored"
682
+ };
683
+ }
684
+ const response = asRecord(obj.response);
685
+ const statusCode = asNumber(response.status_code) ?? 0;
686
+ if (statusCode >= HTTP_OK_MIN && statusCode < HTTP_OK_MAX) {
687
+ return {
688
+ customId,
689
+ embedding: embeddingFromBody(response.body),
690
+ images: imagesFromBody(response.body),
691
+ moderation: moderationFromBody(response.body),
692
+ response: response.body,
693
+ segments: segmentsFromBody(response.body),
694
+ status: "succeeded",
695
+ text: textFromBody(response.body),
696
+ usage: usageFromBody(response.body)
697
+ };
698
+ }
699
+ return {
700
+ customId,
701
+ error: errorFromValue(response.body, `Request failed with status ${statusCode}.`),
702
+ response: response.body,
703
+ status: "errored"
704
+ };
705
+ };
706
+ var uploadInputFile = async (jsonl, baseUrl2, headers2, options = {}) => {
707
+ const form = new FormData;
708
+ const purpose = options.purpose === undefined ? "batch" : options.purpose;
709
+ if (purpose !== null) {
710
+ form.append("purpose", purpose);
711
+ }
712
+ form.append("file", new Blob([jsonl], { type: "application/jsonl" }), "batchwork.jsonl");
713
+ const raw = await requestJson(`${baseUrl2}/files`, {
714
+ body: form,
715
+ headers: headers2,
716
+ method: "POST",
717
+ redirect: "manual"
718
+ });
719
+ return raw.id;
720
+ };
721
+ async function* streamResultFile(fileId, baseUrl2, headers2) {
722
+ const stream = await requestStream(`${baseUrl2}/files/${fileId}/content`, {
723
+ headers: headers2,
724
+ redirect: "manual"
725
+ });
726
+ for await (const line of streamJsonl(stream)) {
727
+ yield normalizeOpenAIResult(line);
728
+ }
729
+ }
730
+
731
+ // src/providers/openai-compatible.ts
732
+ var DEFAULT_COMPLETION_WINDOW = "24h";
733
+ var mapStatus2 = (status) => {
734
+ const normalized = status?.toLowerCase();
735
+ switch (normalized) {
736
+ case "validating":
737
+ case "in_progress":
738
+ case "finalizing":
739
+ case "completed":
740
+ case "failed":
741
+ case "expired":
742
+ case "cancelling":
743
+ case "cancelled": {
744
+ return normalized;
745
+ }
746
+ default: {
747
+ return "in_progress";
748
+ }
749
+ }
750
+ };
751
+ var normalizeSnapshot2 = (raw, provider) => {
752
+ const outer = asRecord(raw);
753
+ const obj = asRecord(outer.job);
754
+ const source = Object.keys(obj).length > 0 ? obj : outer;
755
+ const counts = asRecord(source.request_counts);
756
+ return {
757
+ completedAt: toDate(source.completed_at),
758
+ createdAt: toDate(source.created_at),
759
+ expiresAt: toDate(source.expires_at),
760
+ id: asString(source.id) ?? "",
761
+ provider,
762
+ raw: source,
763
+ requestCounts: {
764
+ completed: asNumber(counts.completed) ?? 0,
765
+ failed: asNumber(counts.failed) ?? 0,
766
+ total: asNumber(counts.total) ?? 0
767
+ },
768
+ status: mapStatus2(asString(source.status))
769
+ };
770
+ };
771
+ var createOpenAICompatibleAdapter = (config) => {
772
+ const completionWindow = config.completionWindow ?? DEFAULT_COMPLETION_WINDOW;
773
+ const lineFormat = config.lineFormat ?? "method-url";
774
+ const baseUrl2 = (credentials) => config.resolveBaseUrl?.(credentials) ?? credentials.baseURL ?? config.baseUrl ?? (() => {
775
+ throw new BatchworkError(`batchwork: missing ${config.apiKeyLabel} base URL. Pass \`baseURL\`.`);
776
+ })();
777
+ const authHeaders = (credentials) => ({
778
+ ...config.authHeaders ? config.authHeaders(credentials) : {
779
+ Authorization: `Bearer ${resolveApiKey(credentials, config.apiKeyEnv, config.apiKeyLabel)}`
780
+ },
781
+ ...credentials.headers
782
+ });
783
+ const submit2 = async (input) => {
784
+ const limits = resolveBatchLimits(input.limits);
785
+ const endpoint = config.normalizeEndpoint ? config.normalizeEndpoint(input.endpoint) : input.endpoint;
786
+ const batchEndpoint = config.batchEndpoint?.(endpoint) ?? endpoint;
787
+ const jsonl = encodeJsonl(input.built.map((item) => {
788
+ const body = omit(item.body, "stream");
789
+ if (lineFormat === "body-only") {
790
+ return {
791
+ body,
792
+ custom_id: item.customId,
793
+ ...config.lineExtras?.(endpoint)
794
+ };
795
+ }
796
+ return {
797
+ body,
798
+ custom_id: item.customId,
799
+ method: "POST",
800
+ url: endpoint
801
+ };
802
+ }), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
803
+ const headers2 = authHeaders(input.credentials);
804
+ const url = baseUrl2(input.credentials);
805
+ const purpose = config.filePurpose ?? "batch";
806
+ const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers: headers2, jsonl, purpose }) : uploadInputFile(jsonl, url, headers2, { purpose }));
807
+ const raw = await requestJson(`${url}/batches`, {
808
+ body: JSON.stringify({
809
+ completion_window: completionWindow,
810
+ endpoint: batchEndpoint,
811
+ input_file_id: inputFileId,
812
+ metadata: input.metadata
813
+ }),
814
+ headers: { ...headers2, "content-type": "application/json" },
815
+ method: "POST"
816
+ });
817
+ return normalizeSnapshot2(raw, config.id);
818
+ };
819
+ const retrieve2 = async (id, credentials) => {
820
+ const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
821
+ const raw = await requestJson(`${baseUrl2(credentials)}/batches/${batchId}`, {
822
+ headers: authHeaders(credentials)
823
+ });
824
+ return normalizeSnapshot2(raw, config.id);
825
+ };
826
+ async function* results2(id, credentials) {
827
+ const snapshot = await retrieve2(id, credentials);
828
+ const raw = asRecord(snapshot.raw);
829
+ const outputFileId = asString(raw.output_file_id);
830
+ const errorFileId = asString(raw.error_file_id);
831
+ if (!(outputFileId || errorFileId)) {
832
+ throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
833
+ }
834
+ const headers2 = authHeaders(credentials);
835
+ if (outputFileId) {
836
+ yield* streamResultFile(assertSimpleProviderId(`${config.id} output file id`, outputFileId), baseUrl2(credentials), headers2);
837
+ }
838
+ if (errorFileId) {
839
+ yield* streamResultFile(assertSimpleProviderId(`${config.id} error file id`, errorFileId), baseUrl2(credentials), headers2);
840
+ }
841
+ }
842
+ const cancel2 = async (id, credentials) => {
843
+ const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
844
+ await requestJson(`${baseUrl2(credentials)}/batches/${batchId}/cancel`, {
845
+ headers: authHeaders(credentials),
846
+ method: "POST"
847
+ });
848
+ };
849
+ return { cancel: cancel2, id: config.id, results: results2, retrieve: retrieve2, submit: submit2 };
850
+ };
851
+
852
+ // src/providers/azure.ts
853
+ var withoutTrailingSlash = (value) => value.replace(/\/+$/u, "");
854
+ var isAzureOpenAIUrl = (value) => {
855
+ try {
856
+ return new URL(value).hostname.endsWith(".openai.azure.com");
857
+ } catch {
858
+ return false;
859
+ }
860
+ };
861
+ var baseUrl2 = (credentials) => {
862
+ const configured = credentials.baseURL ?? (process.env.AZURE_RESOURCE_NAME ? `https://${process.env.AZURE_RESOURCE_NAME}.openai.azure.com/openai` : undefined);
863
+ if (!configured) {
864
+ throw new BatchworkError("batchwork: missing Azure OpenAI resource. Set AZURE_RESOURCE_NAME or pass `baseURL`.");
865
+ }
866
+ const normalized = withoutTrailingSlash(configured);
867
+ if (!isAzureOpenAIUrl(normalized)) {
868
+ return normalized;
869
+ }
870
+ if (normalized.endsWith("/v1")) {
871
+ return normalized;
872
+ }
873
+ return normalized.endsWith("/openai") ? `${normalized}/v1` : `${normalized}/openai/v1`;
874
+ };
875
+ var hasCallerAuth = (headers2) => Object.keys(headers2 ?? {}).some((name) => {
876
+ const normalized = name.toLowerCase();
877
+ return normalized === "api-key" || normalized === "authorization";
878
+ });
879
+ var authHeaders = (credentials) => {
880
+ if (credentials.apiKey) {
881
+ return { "api-key": credentials.apiKey };
882
+ }
883
+ if (hasCallerAuth(credentials.headers)) {
884
+ return {};
885
+ }
886
+ const apiKey2 = process.env.AZURE_API_KEY ?? process.env.AZURE_OPENAI_API_KEY;
887
+ if (apiKey2) {
888
+ return { "api-key": apiKey2 };
889
+ }
890
+ throw new BatchworkError("batchwork: missing Azure OpenAI API key. Set AZURE_API_KEY or pass `apiKey`.");
891
+ };
892
+ var azureAdapter = createOpenAICompatibleAdapter({
893
+ apiKeyEnv: "AZURE_API_KEY",
894
+ apiKeyLabel: "Azure OpenAI",
895
+ authHeaders,
896
+ batchEndpoint: () => "/v1/chat/completions",
897
+ id: "azure",
898
+ normalizeEndpoint: (endpoint) => endpoint.replace(/^\/openai/u, ""),
899
+ resolveBaseUrl: baseUrl2
900
+ });
901
+
542
902
  // src/providers/google.ts
543
903
  var GOOGLE_BASE = "https://generativelanguage.googleapis.com/v1beta";
544
904
  var OPERATION_ID_LABEL = "Google operation id";
@@ -550,7 +910,7 @@ var apiKey2 = (credentials) => {
550
910
  }
551
911
  return key;
552
912
  };
553
- var baseUrl2 = (credentials) => credentials.baseURL ?? GOOGLE_BASE;
913
+ var baseUrl3 = (credentials) => credentials.baseURL ?? GOOGLE_BASE;
554
914
  var headers2 = (credentials) => ({
555
915
  "content-type": "application/json",
556
916
  "x-goog-api-key": apiKey2(credentials),
@@ -596,7 +956,7 @@ var inlinedResponses = (raw) => {
596
956
  ...asArray(nestedDestInline.inlined_responses)
597
957
  ];
598
958
  };
599
- var normalizeSnapshot2 = (raw) => {
959
+ var normalizeSnapshot3 = (raw) => {
600
960
  const obj = asRecord(raw);
601
961
  const items = inlinedResponses(raw);
602
962
  const failed = items.filter((item) => asRecord(item).error).length;
@@ -711,19 +1071,19 @@ var submit2 = async (input) => {
711
1071
  prefix: '{"batch":{"display_name":"batchwork","input_config":{"requests":{"requests":[',
712
1072
  suffix: "]}}}}"
713
1073
  });
714
- const raw = await requestJson(`${baseUrl2(input.credentials)}/models/${input.modelId}:${method}`, {
1074
+ const raw = await requestJson(`${baseUrl3(input.credentials)}/models/${input.modelId}:${method}`, {
715
1075
  body,
716
1076
  headers: headers2(input.credentials),
717
1077
  method: "POST"
718
1078
  });
719
- return normalizeSnapshot2(raw);
1079
+ return normalizeSnapshot3(raw);
720
1080
  };
721
1081
  var retrieve2 = async (id, credentials) => {
722
1082
  const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
723
- const raw = await requestJson(`${baseUrl2(credentials)}/${operationId}`, {
1083
+ const raw = await requestJson(`${baseUrl3(credentials)}/${operationId}`, {
724
1084
  headers: headers2(credentials)
725
1085
  });
726
- return normalizeSnapshot2(raw);
1086
+ return normalizeSnapshot3(raw);
727
1087
  };
728
1088
  var fileNameFrom = (value) => asString(asRecord(value).name) ?? asString(value);
729
1089
  async function* results2(id, credentials) {
@@ -745,7 +1105,7 @@ async function* results2(id, credentials) {
745
1105
  }
746
1106
  var cancel2 = async (id, credentials) => {
747
1107
  const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
748
- await requestJson(`${baseUrl2(credentials)}/${operationId}:cancel`, {
1108
+ await requestJson(`${baseUrl3(credentials)}/${operationId}:cancel`, {
749
1109
  headers: headers2(credentials),
750
1110
  method: "POST"
751
1111
  });
@@ -758,238 +1118,6 @@ var googleAdapter = {
758
1118
  submit: submit2
759
1119
  };
760
1120
 
761
- // src/providers/shared.ts
762
- var HTTP_OK_MIN = 200;
763
- var HTTP_OK_MAX = 300;
764
- var resolveApiKey = (credentials, envVar, label) => {
765
- const key = credentials.apiKey ?? process.env[envVar];
766
- if (!key) {
767
- throw new BatchworkError(`batchwork: missing ${label} API key. Set ${envVar} or pass \`apiKey\`.`);
768
- }
769
- return key;
770
- };
771
- var textFromBody = (body) => {
772
- const obj = asRecord(body);
773
- const choices = asArray(obj.choices);
774
- if (choices.length > 0) {
775
- const content = asString(asRecord(asRecord(choices[0]).message).content);
776
- if (content) {
777
- return content;
778
- }
779
- }
780
- return asString(obj.output_text);
781
- };
782
- var embeddingFromBody = (body) => {
783
- const data = asArray(asRecord(body).data);
784
- if (data.length === 0) {
785
- return;
786
- }
787
- return asNumberArray(asRecord(data[0]).embedding);
788
- };
789
- var imagesFromBody = (body) => {
790
- const obj = asRecord(body);
791
- const mediaType = `image/${asString(obj.output_format) ?? "png"}`;
792
- const images = [];
793
- for (const item of asArray(obj.data)) {
794
- const b64 = asString(asRecord(item).b64_json);
795
- if (b64) {
796
- images.push({ data: b64, mediaType });
797
- }
798
- }
799
- return images.length > 0 ? images : undefined;
800
- };
801
- var usageFromBody = (body) => {
802
- const usage = asRecord(asRecord(body).usage);
803
- const inputTokens = asNumber(usage.prompt_tokens) ?? asNumber(usage.input_tokens);
804
- const outputTokens = asNumber(usage.completion_tokens) ?? asNumber(usage.output_tokens);
805
- const totalTokens = asNumber(usage.total_tokens);
806
- if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) {
807
- return;
808
- }
809
- return {
810
- inputTokens,
811
- outputTokens,
812
- totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0)
813
- };
814
- };
815
- var errorFromValue = (value, fallback) => {
816
- const obj = asRecord(value);
817
- const nested = asRecord(obj.error);
818
- const source = nested.message ? nested : obj;
819
- return {
820
- code: asNumber(source.code) ?? asString(source.code),
821
- message: asString(source.message) ?? fallback,
822
- type: asString(source.type)
823
- };
824
- };
825
- var normalizeOpenAIResult = (line) => {
826
- const obj = asRecord(line);
827
- const customId = asString(obj.custom_id) ?? "";
828
- if (obj.error) {
829
- return {
830
- customId,
831
- error: errorFromValue(obj.error, "Request errored."),
832
- response: obj.error,
833
- status: "errored"
834
- };
835
- }
836
- const response = asRecord(obj.response);
837
- const statusCode = asNumber(response.status_code) ?? 0;
838
- if (statusCode >= HTTP_OK_MIN && statusCode < HTTP_OK_MAX) {
839
- return {
840
- customId,
841
- embedding: embeddingFromBody(response.body),
842
- images: imagesFromBody(response.body),
843
- response: response.body,
844
- status: "succeeded",
845
- text: textFromBody(response.body),
846
- usage: usageFromBody(response.body)
847
- };
848
- }
849
- return {
850
- customId,
851
- error: errorFromValue(response.body, `Request failed with status ${statusCode}.`),
852
- response: response.body,
853
- status: "errored"
854
- };
855
- };
856
- var uploadInputFile = async (jsonl, baseUrl3, headers3, options = {}) => {
857
- const form = new FormData;
858
- const purpose = options.purpose === undefined ? "batch" : options.purpose;
859
- if (purpose !== null) {
860
- form.append("purpose", purpose);
861
- }
862
- form.append("file", new Blob([jsonl], { type: "application/jsonl" }), "batchwork.jsonl");
863
- const raw = await requestJson(`${baseUrl3}/files`, {
864
- body: form,
865
- headers: headers3,
866
- method: "POST",
867
- redirect: "manual"
868
- });
869
- return raw.id;
870
- };
871
- async function* streamResultFile(fileId, baseUrl3, headers3) {
872
- const stream = await requestStream(`${baseUrl3}/files/${fileId}/content`, {
873
- headers: headers3,
874
- redirect: "manual"
875
- });
876
- for await (const line of streamJsonl(stream)) {
877
- yield normalizeOpenAIResult(line);
878
- }
879
- }
880
-
881
- // src/providers/openai-compatible.ts
882
- var DEFAULT_COMPLETION_WINDOW = "24h";
883
- var mapStatus2 = (status) => {
884
- const normalized = status?.toLowerCase();
885
- switch (normalized) {
886
- case "validating":
887
- case "in_progress":
888
- case "finalizing":
889
- case "completed":
890
- case "failed":
891
- case "expired":
892
- case "cancelling":
893
- case "cancelled": {
894
- return normalized;
895
- }
896
- default: {
897
- return "in_progress";
898
- }
899
- }
900
- };
901
- var normalizeSnapshot3 = (raw, provider) => {
902
- const outer = asRecord(raw);
903
- const obj = asRecord(outer.job);
904
- const source = Object.keys(obj).length > 0 ? obj : outer;
905
- const counts = asRecord(source.request_counts);
906
- return {
907
- completedAt: toDate(source.completed_at),
908
- createdAt: toDate(source.created_at),
909
- expiresAt: toDate(source.expires_at),
910
- id: asString(source.id) ?? "",
911
- provider,
912
- raw: source,
913
- requestCounts: {
914
- completed: asNumber(counts.completed) ?? 0,
915
- failed: asNumber(counts.failed) ?? 0,
916
- total: asNumber(counts.total) ?? 0
917
- },
918
- status: mapStatus2(asString(source.status))
919
- };
920
- };
921
- var createOpenAICompatibleAdapter = (config) => {
922
- const completionWindow = config.completionWindow ?? DEFAULT_COMPLETION_WINDOW;
923
- const lineFormat = config.lineFormat ?? "method-url";
924
- const baseUrl3 = (credentials) => credentials.baseURL ?? config.baseUrl;
925
- const authHeaders = (credentials) => ({
926
- Authorization: `Bearer ${resolveApiKey(credentials, config.apiKeyEnv, config.apiKeyLabel)}`,
927
- ...credentials.headers
928
- });
929
- const submit3 = async (input) => {
930
- const limits = resolveBatchLimits(input.limits);
931
- const endpoint = config.normalizeEndpoint ? config.normalizeEndpoint(input.endpoint) : input.endpoint;
932
- const jsonl = encodeJsonl(input.built.map((item) => {
933
- const body = omit(item.body, "stream");
934
- if (lineFormat === "body-only") {
935
- return { body, custom_id: item.customId };
936
- }
937
- return {
938
- body,
939
- custom_id: item.customId,
940
- method: "POST",
941
- url: endpoint
942
- };
943
- }), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
944
- const headers3 = authHeaders(input.credentials);
945
- const url = baseUrl3(input.credentials);
946
- const purpose = config.filePurpose ?? "batch";
947
- const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers: headers3, jsonl, purpose }) : uploadInputFile(jsonl, url, headers3, { purpose }));
948
- const raw = await requestJson(`${url}/batches`, {
949
- body: JSON.stringify({
950
- completion_window: completionWindow,
951
- endpoint,
952
- input_file_id: inputFileId,
953
- metadata: input.metadata
954
- }),
955
- headers: { ...headers3, "content-type": "application/json" },
956
- method: "POST"
957
- });
958
- return normalizeSnapshot3(raw, config.id);
959
- };
960
- const retrieve3 = async (id, credentials) => {
961
- const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
962
- const raw = await requestJson(`${baseUrl3(credentials)}/batches/${batchId}`, {
963
- headers: authHeaders(credentials)
964
- });
965
- return normalizeSnapshot3(raw, config.id);
966
- };
967
- async function* results3(id, credentials) {
968
- const snapshot = await retrieve3(id, credentials);
969
- const raw = asRecord(snapshot.raw);
970
- const outputFileId = asString(raw.output_file_id);
971
- const errorFileId = asString(raw.error_file_id);
972
- if (!(outputFileId || errorFileId)) {
973
- throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
974
- }
975
- const headers3 = authHeaders(credentials);
976
- if (outputFileId) {
977
- yield* streamResultFile(assertSimpleProviderId(`${config.id} output file id`, outputFileId), baseUrl3(credentials), headers3);
978
- }
979
- if (errorFileId) {
980
- yield* streamResultFile(assertSimpleProviderId(`${config.id} error file id`, errorFileId), baseUrl3(credentials), headers3);
981
- }
982
- }
983
- const cancel3 = async (id, credentials) => {
984
- const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
985
- await requestJson(`${baseUrl3(credentials)}/batches/${batchId}/cancel`, {
986
- headers: authHeaders(credentials),
987
- method: "POST"
988
- });
989
- };
990
- return { cancel: cancel3, id: config.id, results: results3, retrieve: retrieve3, submit: submit3 };
991
- };
992
-
993
1121
  // src/providers/groq.ts
994
1122
  var groqAdapter = createOpenAICompatibleAdapter({
995
1123
  apiKeyEnv: "GROQ_API_KEY",
@@ -1004,8 +1132,8 @@ var groqAdapter = createOpenAICompatibleAdapter({
1004
1132
  var MISTRAL_BASE = "https://api.mistral.ai/v1";
1005
1133
  var JOB_ID_LABEL = "Mistral job id";
1006
1134
  var apiKey3 = (credentials) => resolveApiKey(credentials, "MISTRAL_API_KEY", "Mistral");
1007
- var baseUrl3 = (credentials) => credentials.baseURL ?? MISTRAL_BASE;
1008
- var authHeaders = (credentials) => ({
1135
+ var baseUrl4 = (credentials) => credentials.baseURL ?? MISTRAL_BASE;
1136
+ var authHeaders2 = (credentials) => ({
1009
1137
  Authorization: `Bearer ${apiKey3(credentials)}`,
1010
1138
  ...credentials.headers
1011
1139
  });
@@ -1059,8 +1187,8 @@ var submit3 = async (input) => {
1059
1187
  body: omit(omit(item.body, "stream"), "model"),
1060
1188
  custom_id: item.customId
1061
1189
  })), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
1062
- const inputFileId = await uploadInputFile(jsonl, baseUrl3(input.credentials), authHeaders(input.credentials));
1063
- const raw = await requestJson(`${baseUrl3(input.credentials)}/batch/jobs`, {
1190
+ const inputFileId = await uploadInputFile(jsonl, baseUrl4(input.credentials), authHeaders2(input.credentials));
1191
+ const raw = await requestJson(`${baseUrl4(input.credentials)}/batch/jobs`, {
1064
1192
  body: JSON.stringify({
1065
1193
  endpoint: input.endpoint,
1066
1194
  input_files: [inputFileId],
@@ -1068,7 +1196,7 @@ var submit3 = async (input) => {
1068
1196
  model: input.modelId
1069
1197
  }),
1070
1198
  headers: {
1071
- ...authHeaders(input.credentials),
1199
+ ...authHeaders2(input.credentials),
1072
1200
  "content-type": "application/json"
1073
1201
  },
1074
1202
  method: "POST"
@@ -1077,8 +1205,8 @@ var submit3 = async (input) => {
1077
1205
  };
1078
1206
  var retrieve3 = async (id, credentials) => {
1079
1207
  const jobId = assertSimpleProviderId(JOB_ID_LABEL, id);
1080
- const raw = await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}`, {
1081
- headers: authHeaders(credentials)
1208
+ const raw = await requestJson(`${baseUrl4(credentials)}/batch/jobs/${jobId}`, {
1209
+ headers: authHeaders2(credentials)
1082
1210
  });
1083
1211
  return normalizeSnapshot4(raw);
1084
1212
  };
@@ -1087,12 +1215,12 @@ async function* results3(id, credentials) {
1087
1215
  const raw = asRecord(snapshot.raw);
1088
1216
  const outputFileId = asString(raw.output_file);
1089
1217
  const errorFileId = asString(raw.error_file);
1090
- const headers3 = authHeaders(credentials);
1218
+ const headers3 = authHeaders2(credentials);
1091
1219
  if (outputFileId) {
1092
- yield* streamResultFile(assertSimpleProviderId("Mistral output file id", outputFileId), baseUrl3(credentials), headers3);
1220
+ yield* streamResultFile(assertSimpleProviderId("Mistral output file id", outputFileId), baseUrl4(credentials), headers3);
1093
1221
  }
1094
1222
  if (errorFileId) {
1095
- yield* streamResultFile(assertSimpleProviderId("Mistral error file id", errorFileId), baseUrl3(credentials), headers3);
1223
+ yield* streamResultFile(assertSimpleProviderId("Mistral error file id", errorFileId), baseUrl4(credentials), headers3);
1096
1224
  }
1097
1225
  if (!(outputFileId || errorFileId)) {
1098
1226
  throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
@@ -1100,8 +1228,8 @@ async function* results3(id, credentials) {
1100
1228
  }
1101
1229
  var cancel3 = async (id, credentials) => {
1102
1230
  const jobId = assertSimpleProviderId(JOB_ID_LABEL, id);
1103
- await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}/cancel`, {
1104
- headers: authHeaders(credentials),
1231
+ await requestJson(`${baseUrl4(credentials)}/batch/jobs/${jobId}/cancel`, {
1232
+ headers: authHeaders2(credentials),
1105
1233
  method: "POST"
1106
1234
  });
1107
1235
  };
@@ -1232,6 +1360,7 @@ var togetherAdapter = createOpenAICompatibleAdapter({
1232
1360
  baseUrl: "https://api.together.xyz/v1",
1233
1361
  filePurpose: "batch-api",
1234
1362
  id: "together",
1363
+ lineExtras: (endpoint) => endpoint.startsWith("/v1/audio/") ? { method: "FILE" } : undefined,
1235
1364
  lineFormat: "body-only",
1236
1365
  uploadFile: uploadTogetherFile
1237
1366
  });
@@ -1241,8 +1370,8 @@ var XAI_BASE = "https://api.x.ai/v1";
1241
1370
  var BATCH_ID_LABEL = "xAI batch id";
1242
1371
  var RESULTS_PAGE_SIZE = 100;
1243
1372
  var apiKey4 = (credentials) => resolveApiKey(credentials, "XAI_API_KEY", "xAI");
1244
- var baseUrl4 = (credentials) => credentials.baseURL ?? XAI_BASE;
1245
- var authHeaders2 = (credentials) => ({
1373
+ var baseUrl5 = (credentials) => credentials.baseURL ?? XAI_BASE;
1374
+ var authHeaders3 = (credentials) => ({
1246
1375
  Authorization: `Bearer ${apiKey4(credentials)}`,
1247
1376
  ...credentials.headers
1248
1377
  });
@@ -1303,6 +1432,25 @@ var imagesFromXaiCompletion = (completion) => {
1303
1432
  }
1304
1433
  return images.length > 0 ? images : undefined;
1305
1434
  };
1435
+ var videosFromXaiCompletion = (completion) => {
1436
+ const obj = asRecord(completion);
1437
+ const entries = asArray(obj.data);
1438
+ const sources = entries.length > 0 ? entries : [obj];
1439
+ const videos = [];
1440
+ for (const source of sources) {
1441
+ const record = asRecord(source);
1442
+ const video = asRecord(record.video);
1443
+ const url = asString(video.url) ?? asString(record.url);
1444
+ if (url) {
1445
+ const duration = asNumber(video.duration) ?? asNumber(record.duration);
1446
+ videos.push({
1447
+ ...duration === undefined ? {} : { durationSeconds: duration },
1448
+ url
1449
+ });
1450
+ }
1451
+ }
1452
+ return videos.length > 0 ? videos : undefined;
1453
+ };
1306
1454
  var normalizeResult3 = (item) => {
1307
1455
  const obj = asRecord(item);
1308
1456
  const customId = asString(obj.batch_request_id) ?? "";
@@ -1321,10 +1469,12 @@ var normalizeResult3 = (item) => {
1321
1469
  };
1322
1470
  }
1323
1471
  const response = asRecord(batchResult.response);
1472
+ const [opKey] = Object.keys(response);
1324
1473
  const completion = response.chat_get_completion ?? Object.values(response)[0];
1474
+ const isVideo = response.chat_get_completion === undefined && opKey !== undefined && opKey.includes("video");
1325
1475
  return {
1326
1476
  customId,
1327
- images: imagesFromXaiCompletion(completion),
1477
+ ...isVideo ? { videos: videosFromXaiCompletion(completion) } : { images: imagesFromXaiCompletion(completion) },
1328
1478
  response: completion,
1329
1479
  status: "succeeded",
1330
1480
  text: textFromBody(completion),
@@ -1337,13 +1487,13 @@ var submit4 = async (input) => {
1337
1487
  body: omit(item.body, "stream"),
1338
1488
  custom_id: item.customId,
1339
1489
  method: "POST",
1340
- url: input.endpoint
1490
+ url: item.endpoint || input.endpoint
1341
1491
  })), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
1342
- const inputFileId = await uploadInputFile(jsonl, baseUrl4(input.credentials), authHeaders2(input.credentials), { purpose: null });
1343
- const raw = await requestJson(`${baseUrl4(input.credentials)}/batches`, {
1492
+ const inputFileId = await uploadInputFile(jsonl, baseUrl5(input.credentials), authHeaders3(input.credentials), { purpose: null });
1493
+ const raw = await requestJson(`${baseUrl5(input.credentials)}/batches`, {
1344
1494
  body: JSON.stringify({ input_file_id: inputFileId, name: "batchwork" }),
1345
1495
  headers: {
1346
- ...authHeaders2(input.credentials),
1496
+ ...authHeaders3(input.credentials),
1347
1497
  "content-type": "application/json"
1348
1498
  },
1349
1499
  method: "POST"
@@ -1352,21 +1502,21 @@ var submit4 = async (input) => {
1352
1502
  };
1353
1503
  var retrieve4 = async (id, credentials) => {
1354
1504
  const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1355
- const raw = await requestJson(`${baseUrl4(credentials)}/batches/${batchId}`, {
1356
- headers: authHeaders2(credentials)
1505
+ const raw = await requestJson(`${baseUrl5(credentials)}/batches/${batchId}`, {
1506
+ headers: authHeaders3(credentials)
1357
1507
  });
1358
1508
  return normalizeSnapshot5(raw);
1359
1509
  };
1360
1510
  async function* results4(id, credentials) {
1361
1511
  const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1362
- const headers3 = authHeaders2(credentials);
1512
+ const headers3 = authHeaders3(credentials);
1363
1513
  let token;
1364
1514
  do {
1365
1515
  const query = new URLSearchParams({ limit: String(RESULTS_PAGE_SIZE) });
1366
1516
  if (token) {
1367
1517
  query.set("pagination_token", token);
1368
1518
  }
1369
- const raw = await requestJson(`${baseUrl4(credentials)}/batches/${batchId}/results?${query.toString()}`, { headers: headers3 });
1519
+ const raw = await requestJson(`${baseUrl5(credentials)}/batches/${batchId}/results?${query.toString()}`, { headers: headers3 });
1370
1520
  const page = asRecord(raw);
1371
1521
  for (const item of Array.isArray(page.results) ? page.results : []) {
1372
1522
  yield normalizeResult3(item);
@@ -1376,8 +1526,8 @@ async function* results4(id, credentials) {
1376
1526
  }
1377
1527
  var cancel4 = async (id, credentials) => {
1378
1528
  const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1379
- await requestJson(`${baseUrl4(credentials)}/batches/${batchId}:cancel`, {
1380
- headers: authHeaders2(credentials),
1529
+ await requestJson(`${baseUrl5(credentials)}/batches/${batchId}:cancel`, {
1530
+ headers: authHeaders3(credentials),
1381
1531
  method: "POST"
1382
1532
  });
1383
1533
  };
@@ -1392,6 +1542,7 @@ var xaiAdapter = {
1392
1542
  // src/providers/index.ts
1393
1543
  var adapters = {
1394
1544
  anthropic: anthropicAdapter,
1545
+ azure: azureAdapter,
1395
1546
  google: googleAdapter,
1396
1547
  groq: groqAdapter,
1397
1548
  mistral: mistralAdapter,
@@ -1403,5 +1554,5 @@ var getAdapter = (provider) => adapters[provider];
1403
1554
 
1404
1555
  export { BatchworkError, UnsupportedProviderError, MissingDependencyError, resolveBatchLimits, assertByteLength, mapWithConcurrency, isTerminalStatus, BatchJob, getAdapter };
1405
1556
 
1406
- //# debugId=032D6B4F83FC26F964756E2164756E21
1407
- //# sourceMappingURL=chunk-jvrfwjwq.js.map
1557
+ //# debugId=C9B199FFE835E11B64756E2164756E21
1558
+ //# sourceMappingURL=chunk-vy4w8mpb.js.map