batchwork 1.3.0 → 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,225 +539,6 @@ var anthropicAdapter = {
539
539
  submit
540
540
  };
541
541
 
542
- // src/providers/google.ts
543
- var GOOGLE_BASE = "https://generativelanguage.googleapis.com/v1beta";
544
- var OPERATION_ID_LABEL = "Google operation id";
545
- var GOOGLE_BATCH_PREFIX = "batches";
546
- var apiKey2 = (credentials) => {
547
- const key = credentials.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? process.env.GEMINI_API_KEY;
548
- if (!key) {
549
- throw new BatchworkError("batchwork: missing Google Gemini API key. Set GOOGLE_GENERATIVE_AI_API_KEY (or GEMINI_API_KEY) or pass `apiKey`.");
550
- }
551
- return key;
552
- };
553
- var baseUrl2 = (credentials) => credentials.baseURL ?? GOOGLE_BASE;
554
- var headers2 = (credentials) => ({
555
- "content-type": "application/json",
556
- "x-goog-api-key": apiKey2(credentials),
557
- ...credentials.headers
558
- });
559
- var mapState = (state, done) => {
560
- if (state) {
561
- if (state.endsWith("SUCCEEDED")) {
562
- return "completed";
563
- }
564
- if (state.endsWith("FAILED")) {
565
- return "failed";
566
- }
567
- if (state.endsWith("CANCELLED")) {
568
- return "cancelled";
569
- }
570
- if (state.endsWith("EXPIRED")) {
571
- return "expired";
572
- }
573
- if (state.endsWith("PENDING")) {
574
- return "validating";
575
- }
576
- if (state.endsWith("RUNNING")) {
577
- return "in_progress";
578
- }
579
- }
580
- return done ? "completed" : "in_progress";
581
- };
582
- var inlinedResponses = (raw) => {
583
- const obj = asRecord(raw);
584
- const response = asRecord(obj.response);
585
- const dest = asRecord(obj.dest);
586
- const responseInline = response.inlinedResponses ?? response.inlined_responses;
587
- const destInline = dest.inlinedResponses ?? dest.inlined_responses;
588
- const nestedResponseInline = asRecord(responseInline);
589
- const nestedDestInline = asRecord(destInline);
590
- return [
591
- ...asArray(responseInline),
592
- ...asArray(nestedResponseInline.inlinedResponses),
593
- ...asArray(nestedResponseInline.inlined_responses),
594
- ...asArray(destInline),
595
- ...asArray(nestedDestInline.inlinedResponses),
596
- ...asArray(nestedDestInline.inlined_responses)
597
- ];
598
- };
599
- var normalizeSnapshot2 = (raw) => {
600
- const obj = asRecord(raw);
601
- const items = inlinedResponses(raw);
602
- const failed = items.filter((item) => asRecord(item).error).length;
603
- const id = asString(obj.name) ?? "";
604
- return {
605
- id: id ? assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX) : "",
606
- provider: "google",
607
- raw,
608
- requestCounts: {
609
- completed: items.length - failed,
610
- failed,
611
- total: items.length
612
- },
613
- status: mapState(asString(obj.state) ?? asString(asRecord(obj.state).name) ?? asString(asRecord(obj.metadata).state), obj.done === true)
614
- };
615
- };
616
- var textFromResponse = (response) => {
617
- const candidate = asRecord(asArray(asRecord(response).candidates)[0]);
618
- const text = asArray(asRecord(candidate.content).parts).map((part) => asString(asRecord(part).text) ?? "").join("");
619
- return text.length > 0 ? text : undefined;
620
- };
621
- var embeddingFromResponse = (response) => asNumberArray(asRecord(asRecord(response).embedding).values);
622
- var imagesFromResponse = (response) => {
623
- const candidate = asRecord(asArray(asRecord(response).candidates)[0]);
624
- const images = [];
625
- for (const part of asArray(asRecord(candidate.content).parts)) {
626
- const partObj = asRecord(part);
627
- const inline = asRecord(partObj.inlineData ?? partObj.inline_data);
628
- const data = asString(inline.data);
629
- const mediaType = asString(inline.mimeType) ?? asString(inline.mime_type);
630
- if (data && mediaType?.startsWith("image/")) {
631
- images.push({ data, mediaType });
632
- }
633
- }
634
- return images.length > 0 ? images : undefined;
635
- };
636
- var usageFromResponse = (response) => {
637
- const usage = asRecord(asRecord(response).usageMetadata);
638
- const inputTokens = asNumber(usage.promptTokenCount);
639
- const outputTokens = asNumber(usage.candidatesTokenCount);
640
- const totalTokens = asNumber(usage.totalTokenCount);
641
- if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) {
642
- return;
643
- }
644
- return {
645
- inputTokens,
646
- outputTokens,
647
- totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0)
648
- };
649
- };
650
- var normalizeResult2 = (item) => {
651
- const obj = asRecord(item);
652
- const customId = asString(asRecord(obj.metadata).key) ?? asString(obj.key) ?? asString(obj.custom_id) ?? "";
653
- if (obj.error) {
654
- const error = asRecord(obj.error);
655
- return {
656
- customId,
657
- error: {
658
- code: asNumber(error.code) ?? asString(error.code),
659
- message: asString(error.message) ?? "Request errored.",
660
- type: asString(error.status)
661
- },
662
- response: obj.error,
663
- status: "errored"
664
- };
665
- }
666
- return {
667
- customId,
668
- embedding: embeddingFromResponse(obj.response),
669
- images: imagesFromResponse(obj.response),
670
- response: obj.response,
671
- status: "succeeded",
672
- text: textFromResponse(obj.response),
673
- usage: usageFromResponse(obj.response)
674
- };
675
- };
676
- var EMBED_CONFIG_KEYS = new Set([
677
- "outputDimensionality",
678
- "taskType",
679
- "title"
680
- ]);
681
- var toEmbedRequest = (body) => {
682
- const request = {};
683
- const config = {};
684
- for (const [key, value] of Object.entries(body)) {
685
- if (EMBED_CONFIG_KEYS.has(key)) {
686
- config[key] = value;
687
- } else {
688
- request[key] = value;
689
- }
690
- }
691
- if (Object.keys(config).length > 0) {
692
- request.embedContentConfig = config;
693
- }
694
- return request;
695
- };
696
- var submit2 = async (input) => {
697
- const limits = resolveBatchLimits(input.limits);
698
- const isEmbedding = input.endpoint.toLowerCase().includes("embedcontent");
699
- const method = isEmbedding ? "asyncBatchEmbedContent" : "batchGenerateContent";
700
- const requests = input.built.map((item) => {
701
- const payload = omit(item.body, "stream");
702
- return {
703
- metadata: { key: item.customId },
704
- request: isEmbedding ? toEmbedRequest(payload) : payload
705
- };
706
- });
707
- const body = encodeJsonArrayPayload({
708
- items: requests,
709
- label: "batch upload payload",
710
- maxBytes: limits.maxUploadBytes,
711
- prefix: '{"batch":{"display_name":"batchwork","input_config":{"requests":{"requests":[',
712
- suffix: "]}}}}"
713
- });
714
- const raw = await requestJson(`${baseUrl2(input.credentials)}/models/${input.modelId}:${method}`, {
715
- body,
716
- headers: headers2(input.credentials),
717
- method: "POST"
718
- });
719
- return normalizeSnapshot2(raw);
720
- };
721
- var retrieve2 = async (id, credentials) => {
722
- const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
723
- const raw = await requestJson(`${baseUrl2(credentials)}/${operationId}`, {
724
- headers: headers2(credentials)
725
- });
726
- return normalizeSnapshot2(raw);
727
- };
728
- var fileNameFrom = (value) => asString(asRecord(value).name) ?? asString(value);
729
- async function* results2(id, credentials) {
730
- const snapshot = await retrieve2(id, credentials);
731
- const raw = asRecord(snapshot.raw);
732
- const response = asRecord(raw.response);
733
- const dest = asRecord(raw.dest);
734
- const responsesFile = fileNameFrom(response.responsesFile) ?? fileNameFrom(response.responses_file) ?? asString(dest.fileName) ?? asString(dest.file_name);
735
- if (responsesFile) {
736
- throw new BatchworkError(`batchwork: batch "${id}" returned file-mode results, which are not supported yet.`);
737
- }
738
- const items = inlinedResponses(raw);
739
- if (items.length === 0) {
740
- throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
741
- }
742
- for (const item of items) {
743
- yield normalizeResult2(item);
744
- }
745
- }
746
- var cancel2 = async (id, credentials) => {
747
- const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
748
- await requestJson(`${baseUrl2(credentials)}/${operationId}:cancel`, {
749
- headers: headers2(credentials),
750
- method: "POST"
751
- });
752
- };
753
- var googleAdapter = {
754
- cancel: cancel2,
755
- id: "google",
756
- results: results2,
757
- retrieve: retrieve2,
758
- submit: submit2
759
- };
760
-
761
542
  // src/providers/shared.ts
762
543
  var HTTP_OK_MIN = 200;
763
544
  var HTTP_OK_MAX = 300;
@@ -777,7 +558,30 @@ var textFromBody = (body) => {
777
558
  return content;
778
559
  }
779
560
  }
780
- return asString(obj.output_text) ?? asString(obj.text);
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
+ }
781
585
  };
782
586
  var segmentsFromBody = (body) => {
783
587
  const obj = asRecord(body);
@@ -818,11 +622,11 @@ var imagesFromBody = (body) => {
818
622
  return images.length > 0 ? images : undefined;
819
623
  };
820
624
  var moderationFromBody = (body) => {
821
- const results3 = asArray(asRecord(body).results);
822
- if (results3.length === 0) {
625
+ const results2 = asArray(asRecord(body).results);
626
+ if (results2.length === 0) {
823
627
  return;
824
628
  }
825
- const first = asRecord(results3[0]);
629
+ const first = asRecord(results2[0]);
826
630
  const categories = {};
827
631
  for (const [key, value] of Object.entries(asRecord(first.categories))) {
828
632
  if (typeof value === "boolean") {
@@ -899,24 +703,24 @@ var normalizeOpenAIResult = (line) => {
899
703
  status: "errored"
900
704
  };
901
705
  };
902
- var uploadInputFile = async (jsonl, baseUrl3, headers3, options = {}) => {
706
+ var uploadInputFile = async (jsonl, baseUrl2, headers2, options = {}) => {
903
707
  const form = new FormData;
904
708
  const purpose = options.purpose === undefined ? "batch" : options.purpose;
905
709
  if (purpose !== null) {
906
710
  form.append("purpose", purpose);
907
711
  }
908
712
  form.append("file", new Blob([jsonl], { type: "application/jsonl" }), "batchwork.jsonl");
909
- const raw = await requestJson(`${baseUrl3}/files`, {
713
+ const raw = await requestJson(`${baseUrl2}/files`, {
910
714
  body: form,
911
- headers: headers3,
715
+ headers: headers2,
912
716
  method: "POST",
913
717
  redirect: "manual"
914
718
  });
915
719
  return raw.id;
916
720
  };
917
- async function* streamResultFile(fileId, baseUrl3, headers3) {
918
- const stream = await requestStream(`${baseUrl3}/files/${fileId}/content`, {
919
- headers: headers3,
721
+ async function* streamResultFile(fileId, baseUrl2, headers2) {
722
+ const stream = await requestStream(`${baseUrl2}/files/${fileId}/content`, {
723
+ headers: headers2,
920
724
  redirect: "manual"
921
725
  });
922
726
  for await (const line of streamJsonl(stream)) {
@@ -944,7 +748,7 @@ var mapStatus2 = (status) => {
944
748
  }
945
749
  }
946
750
  };
947
- var normalizeSnapshot3 = (raw, provider) => {
751
+ var normalizeSnapshot2 = (raw, provider) => {
948
752
  const outer = asRecord(raw);
949
753
  const obj = asRecord(outer.job);
950
754
  const source = Object.keys(obj).length > 0 ? obj : outer;
@@ -967,14 +771,19 @@ var normalizeSnapshot3 = (raw, provider) => {
967
771
  var createOpenAICompatibleAdapter = (config) => {
968
772
  const completionWindow = config.completionWindow ?? DEFAULT_COMPLETION_WINDOW;
969
773
  const lineFormat = config.lineFormat ?? "method-url";
970
- const baseUrl3 = (credentials) => credentials.baseURL ?? config.baseUrl;
774
+ const baseUrl2 = (credentials) => config.resolveBaseUrl?.(credentials) ?? credentials.baseURL ?? config.baseUrl ?? (() => {
775
+ throw new BatchworkError(`batchwork: missing ${config.apiKeyLabel} base URL. Pass \`baseURL\`.`);
776
+ })();
971
777
  const authHeaders = (credentials) => ({
972
- Authorization: `Bearer ${resolveApiKey(credentials, config.apiKeyEnv, config.apiKeyLabel)}`,
778
+ ...config.authHeaders ? config.authHeaders(credentials) : {
779
+ Authorization: `Bearer ${resolveApiKey(credentials, config.apiKeyEnv, config.apiKeyLabel)}`
780
+ },
973
781
  ...credentials.headers
974
782
  });
975
- const submit3 = async (input) => {
783
+ const submit2 = async (input) => {
976
784
  const limits = resolveBatchLimits(input.limits);
977
785
  const endpoint = config.normalizeEndpoint ? config.normalizeEndpoint(input.endpoint) : input.endpoint;
786
+ const batchEndpoint = config.batchEndpoint?.(endpoint) ?? endpoint;
978
787
  const jsonl = encodeJsonl(input.built.map((item) => {
979
788
  const body = omit(item.body, "stream");
980
789
  if (lineFormat === "body-only") {
@@ -991,53 +800,322 @@ var createOpenAICompatibleAdapter = (config) => {
991
800
  url: endpoint
992
801
  };
993
802
  }), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
994
- const headers3 = authHeaders(input.credentials);
995
- const url = baseUrl3(input.credentials);
803
+ const headers2 = authHeaders(input.credentials);
804
+ const url = baseUrl2(input.credentials);
996
805
  const purpose = config.filePurpose ?? "batch";
997
- const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers: headers3, jsonl, purpose }) : uploadInputFile(jsonl, url, headers3, { purpose }));
806
+ const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers: headers2, jsonl, purpose }) : uploadInputFile(jsonl, url, headers2, { purpose }));
998
807
  const raw = await requestJson(`${url}/batches`, {
999
808
  body: JSON.stringify({
1000
809
  completion_window: completionWindow,
1001
- endpoint,
810
+ endpoint: batchEndpoint,
1002
811
  input_file_id: inputFileId,
1003
812
  metadata: input.metadata
1004
813
  }),
1005
- headers: { ...headers3, "content-type": "application/json" },
814
+ headers: { ...headers2, "content-type": "application/json" },
1006
815
  method: "POST"
1007
816
  });
1008
- return normalizeSnapshot3(raw, config.id);
817
+ return normalizeSnapshot2(raw, config.id);
1009
818
  };
1010
- const retrieve3 = async (id, credentials) => {
819
+ const retrieve2 = async (id, credentials) => {
1011
820
  const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
1012
- const raw = await requestJson(`${baseUrl3(credentials)}/batches/${batchId}`, {
821
+ const raw = await requestJson(`${baseUrl2(credentials)}/batches/${batchId}`, {
1013
822
  headers: authHeaders(credentials)
1014
823
  });
1015
- return normalizeSnapshot3(raw, config.id);
824
+ return normalizeSnapshot2(raw, config.id);
1016
825
  };
1017
- async function* results3(id, credentials) {
1018
- const snapshot = await retrieve3(id, credentials);
826
+ async function* results2(id, credentials) {
827
+ const snapshot = await retrieve2(id, credentials);
1019
828
  const raw = asRecord(snapshot.raw);
1020
829
  const outputFileId = asString(raw.output_file_id);
1021
830
  const errorFileId = asString(raw.error_file_id);
1022
831
  if (!(outputFileId || errorFileId)) {
1023
832
  throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
1024
833
  }
1025
- const headers3 = authHeaders(credentials);
834
+ const headers2 = authHeaders(credentials);
1026
835
  if (outputFileId) {
1027
- yield* streamResultFile(assertSimpleProviderId(`${config.id} output file id`, outputFileId), baseUrl3(credentials), headers3);
836
+ yield* streamResultFile(assertSimpleProviderId(`${config.id} output file id`, outputFileId), baseUrl2(credentials), headers2);
1028
837
  }
1029
838
  if (errorFileId) {
1030
- yield* streamResultFile(assertSimpleProviderId(`${config.id} error file id`, errorFileId), baseUrl3(credentials), headers3);
839
+ yield* streamResultFile(assertSimpleProviderId(`${config.id} error file id`, errorFileId), baseUrl2(credentials), headers2);
1031
840
  }
1032
841
  }
1033
- const cancel3 = async (id, credentials) => {
842
+ const cancel2 = async (id, credentials) => {
1034
843
  const batchId = assertSimpleProviderId(`${config.id} batch id`, id);
1035
- await requestJson(`${baseUrl3(credentials)}/batches/${batchId}/cancel`, {
844
+ await requestJson(`${baseUrl2(credentials)}/batches/${batchId}/cancel`, {
1036
845
  headers: authHeaders(credentials),
1037
846
  method: "POST"
1038
847
  });
1039
848
  };
1040
- return { cancel: cancel3, id: config.id, results: results3, retrieve: retrieve3, submit: submit3 };
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
+
902
+ // src/providers/google.ts
903
+ var GOOGLE_BASE = "https://generativelanguage.googleapis.com/v1beta";
904
+ var OPERATION_ID_LABEL = "Google operation id";
905
+ var GOOGLE_BATCH_PREFIX = "batches";
906
+ var apiKey2 = (credentials) => {
907
+ const key = credentials.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? process.env.GEMINI_API_KEY;
908
+ if (!key) {
909
+ throw new BatchworkError("batchwork: missing Google Gemini API key. Set GOOGLE_GENERATIVE_AI_API_KEY (or GEMINI_API_KEY) or pass `apiKey`.");
910
+ }
911
+ return key;
912
+ };
913
+ var baseUrl3 = (credentials) => credentials.baseURL ?? GOOGLE_BASE;
914
+ var headers2 = (credentials) => ({
915
+ "content-type": "application/json",
916
+ "x-goog-api-key": apiKey2(credentials),
917
+ ...credentials.headers
918
+ });
919
+ var mapState = (state, done) => {
920
+ if (state) {
921
+ if (state.endsWith("SUCCEEDED")) {
922
+ return "completed";
923
+ }
924
+ if (state.endsWith("FAILED")) {
925
+ return "failed";
926
+ }
927
+ if (state.endsWith("CANCELLED")) {
928
+ return "cancelled";
929
+ }
930
+ if (state.endsWith("EXPIRED")) {
931
+ return "expired";
932
+ }
933
+ if (state.endsWith("PENDING")) {
934
+ return "validating";
935
+ }
936
+ if (state.endsWith("RUNNING")) {
937
+ return "in_progress";
938
+ }
939
+ }
940
+ return done ? "completed" : "in_progress";
941
+ };
942
+ var inlinedResponses = (raw) => {
943
+ const obj = asRecord(raw);
944
+ const response = asRecord(obj.response);
945
+ const dest = asRecord(obj.dest);
946
+ const responseInline = response.inlinedResponses ?? response.inlined_responses;
947
+ const destInline = dest.inlinedResponses ?? dest.inlined_responses;
948
+ const nestedResponseInline = asRecord(responseInline);
949
+ const nestedDestInline = asRecord(destInline);
950
+ return [
951
+ ...asArray(responseInline),
952
+ ...asArray(nestedResponseInline.inlinedResponses),
953
+ ...asArray(nestedResponseInline.inlined_responses),
954
+ ...asArray(destInline),
955
+ ...asArray(nestedDestInline.inlinedResponses),
956
+ ...asArray(nestedDestInline.inlined_responses)
957
+ ];
958
+ };
959
+ var normalizeSnapshot3 = (raw) => {
960
+ const obj = asRecord(raw);
961
+ const items = inlinedResponses(raw);
962
+ const failed = items.filter((item) => asRecord(item).error).length;
963
+ const id = asString(obj.name) ?? "";
964
+ return {
965
+ id: id ? assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX) : "",
966
+ provider: "google",
967
+ raw,
968
+ requestCounts: {
969
+ completed: items.length - failed,
970
+ failed,
971
+ total: items.length
972
+ },
973
+ status: mapState(asString(obj.state) ?? asString(asRecord(obj.state).name) ?? asString(asRecord(obj.metadata).state), obj.done === true)
974
+ };
975
+ };
976
+ var textFromResponse = (response) => {
977
+ const candidate = asRecord(asArray(asRecord(response).candidates)[0]);
978
+ const text = asArray(asRecord(candidate.content).parts).map((part) => asString(asRecord(part).text) ?? "").join("");
979
+ return text.length > 0 ? text : undefined;
980
+ };
981
+ var embeddingFromResponse = (response) => asNumberArray(asRecord(asRecord(response).embedding).values);
982
+ var imagesFromResponse = (response) => {
983
+ const candidate = asRecord(asArray(asRecord(response).candidates)[0]);
984
+ const images = [];
985
+ for (const part of asArray(asRecord(candidate.content).parts)) {
986
+ const partObj = asRecord(part);
987
+ const inline = asRecord(partObj.inlineData ?? partObj.inline_data);
988
+ const data = asString(inline.data);
989
+ const mediaType = asString(inline.mimeType) ?? asString(inline.mime_type);
990
+ if (data && mediaType?.startsWith("image/")) {
991
+ images.push({ data, mediaType });
992
+ }
993
+ }
994
+ return images.length > 0 ? images : undefined;
995
+ };
996
+ var usageFromResponse = (response) => {
997
+ const usage = asRecord(asRecord(response).usageMetadata);
998
+ const inputTokens = asNumber(usage.promptTokenCount);
999
+ const outputTokens = asNumber(usage.candidatesTokenCount);
1000
+ const totalTokens = asNumber(usage.totalTokenCount);
1001
+ if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) {
1002
+ return;
1003
+ }
1004
+ return {
1005
+ inputTokens,
1006
+ outputTokens,
1007
+ totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0)
1008
+ };
1009
+ };
1010
+ var normalizeResult2 = (item) => {
1011
+ const obj = asRecord(item);
1012
+ const customId = asString(asRecord(obj.metadata).key) ?? asString(obj.key) ?? asString(obj.custom_id) ?? "";
1013
+ if (obj.error) {
1014
+ const error = asRecord(obj.error);
1015
+ return {
1016
+ customId,
1017
+ error: {
1018
+ code: asNumber(error.code) ?? asString(error.code),
1019
+ message: asString(error.message) ?? "Request errored.",
1020
+ type: asString(error.status)
1021
+ },
1022
+ response: obj.error,
1023
+ status: "errored"
1024
+ };
1025
+ }
1026
+ return {
1027
+ customId,
1028
+ embedding: embeddingFromResponse(obj.response),
1029
+ images: imagesFromResponse(obj.response),
1030
+ response: obj.response,
1031
+ status: "succeeded",
1032
+ text: textFromResponse(obj.response),
1033
+ usage: usageFromResponse(obj.response)
1034
+ };
1035
+ };
1036
+ var EMBED_CONFIG_KEYS = new Set([
1037
+ "outputDimensionality",
1038
+ "taskType",
1039
+ "title"
1040
+ ]);
1041
+ var toEmbedRequest = (body) => {
1042
+ const request = {};
1043
+ const config = {};
1044
+ for (const [key, value] of Object.entries(body)) {
1045
+ if (EMBED_CONFIG_KEYS.has(key)) {
1046
+ config[key] = value;
1047
+ } else {
1048
+ request[key] = value;
1049
+ }
1050
+ }
1051
+ if (Object.keys(config).length > 0) {
1052
+ request.embedContentConfig = config;
1053
+ }
1054
+ return request;
1055
+ };
1056
+ var submit2 = async (input) => {
1057
+ const limits = resolveBatchLimits(input.limits);
1058
+ const isEmbedding = input.endpoint.toLowerCase().includes("embedcontent");
1059
+ const method = isEmbedding ? "asyncBatchEmbedContent" : "batchGenerateContent";
1060
+ const requests = input.built.map((item) => {
1061
+ const payload = omit(item.body, "stream");
1062
+ return {
1063
+ metadata: { key: item.customId },
1064
+ request: isEmbedding ? toEmbedRequest(payload) : payload
1065
+ };
1066
+ });
1067
+ const body = encodeJsonArrayPayload({
1068
+ items: requests,
1069
+ label: "batch upload payload",
1070
+ maxBytes: limits.maxUploadBytes,
1071
+ prefix: '{"batch":{"display_name":"batchwork","input_config":{"requests":{"requests":[',
1072
+ suffix: "]}}}}"
1073
+ });
1074
+ const raw = await requestJson(`${baseUrl3(input.credentials)}/models/${input.modelId}:${method}`, {
1075
+ body,
1076
+ headers: headers2(input.credentials),
1077
+ method: "POST"
1078
+ });
1079
+ return normalizeSnapshot3(raw);
1080
+ };
1081
+ var retrieve2 = async (id, credentials) => {
1082
+ const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
1083
+ const raw = await requestJson(`${baseUrl3(credentials)}/${operationId}`, {
1084
+ headers: headers2(credentials)
1085
+ });
1086
+ return normalizeSnapshot3(raw);
1087
+ };
1088
+ var fileNameFrom = (value) => asString(asRecord(value).name) ?? asString(value);
1089
+ async function* results2(id, credentials) {
1090
+ const snapshot = await retrieve2(id, credentials);
1091
+ const raw = asRecord(snapshot.raw);
1092
+ const response = asRecord(raw.response);
1093
+ const dest = asRecord(raw.dest);
1094
+ const responsesFile = fileNameFrom(response.responsesFile) ?? fileNameFrom(response.responses_file) ?? asString(dest.fileName) ?? asString(dest.file_name);
1095
+ if (responsesFile) {
1096
+ throw new BatchworkError(`batchwork: batch "${id}" returned file-mode results, which are not supported yet.`);
1097
+ }
1098
+ const items = inlinedResponses(raw);
1099
+ if (items.length === 0) {
1100
+ throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
1101
+ }
1102
+ for (const item of items) {
1103
+ yield normalizeResult2(item);
1104
+ }
1105
+ }
1106
+ var cancel2 = async (id, credentials) => {
1107
+ const operationId = assertPrefixedProviderId(OPERATION_ID_LABEL, id, GOOGLE_BATCH_PREFIX);
1108
+ await requestJson(`${baseUrl3(credentials)}/${operationId}:cancel`, {
1109
+ headers: headers2(credentials),
1110
+ method: "POST"
1111
+ });
1112
+ };
1113
+ var googleAdapter = {
1114
+ cancel: cancel2,
1115
+ id: "google",
1116
+ results: results2,
1117
+ retrieve: retrieve2,
1118
+ submit: submit2
1041
1119
  };
1042
1120
 
1043
1121
  // src/providers/groq.ts
@@ -1054,8 +1132,8 @@ var groqAdapter = createOpenAICompatibleAdapter({
1054
1132
  var MISTRAL_BASE = "https://api.mistral.ai/v1";
1055
1133
  var JOB_ID_LABEL = "Mistral job id";
1056
1134
  var apiKey3 = (credentials) => resolveApiKey(credentials, "MISTRAL_API_KEY", "Mistral");
1057
- var baseUrl3 = (credentials) => credentials.baseURL ?? MISTRAL_BASE;
1058
- var authHeaders = (credentials) => ({
1135
+ var baseUrl4 = (credentials) => credentials.baseURL ?? MISTRAL_BASE;
1136
+ var authHeaders2 = (credentials) => ({
1059
1137
  Authorization: `Bearer ${apiKey3(credentials)}`,
1060
1138
  ...credentials.headers
1061
1139
  });
@@ -1109,8 +1187,8 @@ var submit3 = async (input) => {
1109
1187
  body: omit(omit(item.body, "stream"), "model"),
1110
1188
  custom_id: item.customId
1111
1189
  })), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
1112
- const inputFileId = await uploadInputFile(jsonl, baseUrl3(input.credentials), authHeaders(input.credentials));
1113
- 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`, {
1114
1192
  body: JSON.stringify({
1115
1193
  endpoint: input.endpoint,
1116
1194
  input_files: [inputFileId],
@@ -1118,7 +1196,7 @@ var submit3 = async (input) => {
1118
1196
  model: input.modelId
1119
1197
  }),
1120
1198
  headers: {
1121
- ...authHeaders(input.credentials),
1199
+ ...authHeaders2(input.credentials),
1122
1200
  "content-type": "application/json"
1123
1201
  },
1124
1202
  method: "POST"
@@ -1127,8 +1205,8 @@ var submit3 = async (input) => {
1127
1205
  };
1128
1206
  var retrieve3 = async (id, credentials) => {
1129
1207
  const jobId = assertSimpleProviderId(JOB_ID_LABEL, id);
1130
- const raw = await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}`, {
1131
- headers: authHeaders(credentials)
1208
+ const raw = await requestJson(`${baseUrl4(credentials)}/batch/jobs/${jobId}`, {
1209
+ headers: authHeaders2(credentials)
1132
1210
  });
1133
1211
  return normalizeSnapshot4(raw);
1134
1212
  };
@@ -1137,12 +1215,12 @@ async function* results3(id, credentials) {
1137
1215
  const raw = asRecord(snapshot.raw);
1138
1216
  const outputFileId = asString(raw.output_file);
1139
1217
  const errorFileId = asString(raw.error_file);
1140
- const headers3 = authHeaders(credentials);
1218
+ const headers3 = authHeaders2(credentials);
1141
1219
  if (outputFileId) {
1142
- yield* streamResultFile(assertSimpleProviderId("Mistral output file id", outputFileId), baseUrl3(credentials), headers3);
1220
+ yield* streamResultFile(assertSimpleProviderId("Mistral output file id", outputFileId), baseUrl4(credentials), headers3);
1143
1221
  }
1144
1222
  if (errorFileId) {
1145
- yield* streamResultFile(assertSimpleProviderId("Mistral error file id", errorFileId), baseUrl3(credentials), headers3);
1223
+ yield* streamResultFile(assertSimpleProviderId("Mistral error file id", errorFileId), baseUrl4(credentials), headers3);
1146
1224
  }
1147
1225
  if (!(outputFileId || errorFileId)) {
1148
1226
  throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`);
@@ -1150,8 +1228,8 @@ async function* results3(id, credentials) {
1150
1228
  }
1151
1229
  var cancel3 = async (id, credentials) => {
1152
1230
  const jobId = assertSimpleProviderId(JOB_ID_LABEL, id);
1153
- await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}/cancel`, {
1154
- headers: authHeaders(credentials),
1231
+ await requestJson(`${baseUrl4(credentials)}/batch/jobs/${jobId}/cancel`, {
1232
+ headers: authHeaders2(credentials),
1155
1233
  method: "POST"
1156
1234
  });
1157
1235
  };
@@ -1292,8 +1370,8 @@ var XAI_BASE = "https://api.x.ai/v1";
1292
1370
  var BATCH_ID_LABEL = "xAI batch id";
1293
1371
  var RESULTS_PAGE_SIZE = 100;
1294
1372
  var apiKey4 = (credentials) => resolveApiKey(credentials, "XAI_API_KEY", "xAI");
1295
- var baseUrl4 = (credentials) => credentials.baseURL ?? XAI_BASE;
1296
- var authHeaders2 = (credentials) => ({
1373
+ var baseUrl5 = (credentials) => credentials.baseURL ?? XAI_BASE;
1374
+ var authHeaders3 = (credentials) => ({
1297
1375
  Authorization: `Bearer ${apiKey4(credentials)}`,
1298
1376
  ...credentials.headers
1299
1377
  });
@@ -1411,11 +1489,11 @@ var submit4 = async (input) => {
1411
1489
  method: "POST",
1412
1490
  url: item.endpoint || input.endpoint
1413
1491
  })), { label: "batch upload JSONL", maxBytes: limits.maxUploadBytes });
1414
- const inputFileId = await uploadInputFile(jsonl, baseUrl4(input.credentials), authHeaders2(input.credentials), { purpose: null });
1415
- 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`, {
1416
1494
  body: JSON.stringify({ input_file_id: inputFileId, name: "batchwork" }),
1417
1495
  headers: {
1418
- ...authHeaders2(input.credentials),
1496
+ ...authHeaders3(input.credentials),
1419
1497
  "content-type": "application/json"
1420
1498
  },
1421
1499
  method: "POST"
@@ -1424,21 +1502,21 @@ var submit4 = async (input) => {
1424
1502
  };
1425
1503
  var retrieve4 = async (id, credentials) => {
1426
1504
  const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1427
- const raw = await requestJson(`${baseUrl4(credentials)}/batches/${batchId}`, {
1428
- headers: authHeaders2(credentials)
1505
+ const raw = await requestJson(`${baseUrl5(credentials)}/batches/${batchId}`, {
1506
+ headers: authHeaders3(credentials)
1429
1507
  });
1430
1508
  return normalizeSnapshot5(raw);
1431
1509
  };
1432
1510
  async function* results4(id, credentials) {
1433
1511
  const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1434
- const headers3 = authHeaders2(credentials);
1512
+ const headers3 = authHeaders3(credentials);
1435
1513
  let token;
1436
1514
  do {
1437
1515
  const query = new URLSearchParams({ limit: String(RESULTS_PAGE_SIZE) });
1438
1516
  if (token) {
1439
1517
  query.set("pagination_token", token);
1440
1518
  }
1441
- 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 });
1442
1520
  const page = asRecord(raw);
1443
1521
  for (const item of Array.isArray(page.results) ? page.results : []) {
1444
1522
  yield normalizeResult3(item);
@@ -1448,8 +1526,8 @@ async function* results4(id, credentials) {
1448
1526
  }
1449
1527
  var cancel4 = async (id, credentials) => {
1450
1528
  const batchId = assertSimpleProviderId(BATCH_ID_LABEL, id);
1451
- await requestJson(`${baseUrl4(credentials)}/batches/${batchId}:cancel`, {
1452
- headers: authHeaders2(credentials),
1529
+ await requestJson(`${baseUrl5(credentials)}/batches/${batchId}:cancel`, {
1530
+ headers: authHeaders3(credentials),
1453
1531
  method: "POST"
1454
1532
  });
1455
1533
  };
@@ -1464,6 +1542,7 @@ var xaiAdapter = {
1464
1542
  // src/providers/index.ts
1465
1543
  var adapters = {
1466
1544
  anthropic: anthropicAdapter,
1545
+ azure: azureAdapter,
1467
1546
  google: googleAdapter,
1468
1547
  groq: groqAdapter,
1469
1548
  mistral: mistralAdapter,
@@ -1475,5 +1554,5 @@ var getAdapter = (provider) => adapters[provider];
1475
1554
 
1476
1555
  export { BatchworkError, UnsupportedProviderError, MissingDependencyError, resolveBatchLimits, assertByteLength, mapWithConcurrency, isTerminalStatus, BatchJob, getAdapter };
1477
1556
 
1478
- //# debugId=4C939B96519322C164756E2164756E21
1479
- //# sourceMappingURL=chunk-gwa0dkhj.js.map
1557
+ //# debugId=C9B199FFE835E11B64756E2164756E21
1558
+ //# sourceMappingURL=chunk-vy4w8mpb.js.map