@superlinked/sie-sdk 0.6.16 → 0.6.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +735 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +450 -12
- package/dist/index.d.ts +450 -12
- package/dist/index.js +732 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -85,6 +85,18 @@ var ModelLoadingError = class extends SIEError {
|
|
|
85
85
|
this.model = model;
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
|
+
var ResourceExhaustedError = class extends ServerError {
|
|
89
|
+
/** The model that was requested */
|
|
90
|
+
model;
|
|
91
|
+
/** Number of retry attempts made before giving up */
|
|
92
|
+
retries;
|
|
93
|
+
constructor(message, options) {
|
|
94
|
+
super(message, "RESOURCE_EXHAUSTED", 503);
|
|
95
|
+
this.name = "ResourceExhaustedError";
|
|
96
|
+
this.model = options?.model;
|
|
97
|
+
this.retries = options?.retries ?? 0;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
88
100
|
var SIEStreamError = class extends SIEError {
|
|
89
101
|
/** SIE-native error code (e.g. `context_exceeded`, `cancelled`). */
|
|
90
102
|
code;
|
|
@@ -191,16 +203,24 @@ var HTTP_CLIENT_ERROR_MIN = 400;
|
|
|
191
203
|
var HTTP_CLIENT_ERROR_MAX = 499;
|
|
192
204
|
var HTTP_SERVER_ERROR_MIN = 500;
|
|
193
205
|
var HTTP_SERVER_ERROR_MAX = 599;
|
|
206
|
+
var HTTP_GATEWAY_TIMEOUT = 504;
|
|
194
207
|
var DEFAULT_TIMEOUT = 3e4;
|
|
208
|
+
var DEFAULT_LONG_RUNNING_TIMEOUT = 12e4;
|
|
195
209
|
var DEFAULT_PROVISION_TIMEOUT = 3e5;
|
|
196
210
|
var DEFAULT_RETRY_DELAY = 5e3;
|
|
197
211
|
var DEFAULT_LEASE_RENEWAL_INTERVAL = 6e4;
|
|
212
|
+
var DEFAULT_JOB_WAIT_TIMEOUT = 6e5;
|
|
213
|
+
var DEFAULT_JOB_WAIT_POLL = 2e3;
|
|
198
214
|
var LORA_LOADING_MAX_RETRIES = 10;
|
|
199
215
|
var LORA_LOADING_DEFAULT_DELAY = 1e3;
|
|
200
216
|
var LORA_LOADING_ERROR_CODE = "LORA_LOADING";
|
|
201
217
|
var MODEL_LOADING_DEFAULT_DELAY = 5e3;
|
|
202
218
|
var MODEL_LOADING_ERROR_CODE = "MODEL_LOADING";
|
|
203
219
|
var PROVISIONING_ERROR_CODE = "PROVISIONING";
|
|
220
|
+
var RESOURCE_EXHAUSTED_MAX_RETRIES = 3;
|
|
221
|
+
var RESOURCE_EXHAUSTED_DEFAULT_DELAY = 5e3;
|
|
222
|
+
var RESOURCE_EXHAUSTED_MAX_DELAY = 3e4;
|
|
223
|
+
var RESOURCE_EXHAUSTED_ERROR_CODE = "RESOURCE_EXHAUSTED";
|
|
204
224
|
var SDK_VERSION_HEADER = "X-SIE-SDK-Version";
|
|
205
225
|
var SERVER_VERSION_HEADER = "X-SIE-Server-Version";
|
|
206
226
|
var EXT_TYPE_NUMPY = 78;
|
|
@@ -390,9 +410,19 @@ function unpackMessage(data) {
|
|
|
390
410
|
// src/internal/retry.ts
|
|
391
411
|
var RETRY_JITTER_FRACTION = 0.25;
|
|
392
412
|
function applyRetryJitter(delay) {
|
|
413
|
+
if (delay <= 0) return Math.max(delay, 0);
|
|
393
414
|
const low = delay * (1 - RETRY_JITTER_FRACTION);
|
|
394
415
|
return Math.max(0, low + Math.random() * (delay - low));
|
|
395
416
|
}
|
|
417
|
+
function computeOomBackoff(retryAfter, attempt, baseDelay = RESOURCE_EXHAUSTED_DEFAULT_DELAY, maxDelay = RESOURCE_EXHAUSTED_MAX_DELAY) {
|
|
418
|
+
const safeRetryAfter = retryAfter !== void 0 ? Math.max(retryAfter, 0) : void 0;
|
|
419
|
+
if (safeRetryAfter !== void 0 && attempt === 0) {
|
|
420
|
+
return Math.min(safeRetryAfter, maxDelay);
|
|
421
|
+
}
|
|
422
|
+
const base = safeRetryAfter !== void 0 ? Math.max(baseDelay, safeRetryAfter) : baseDelay;
|
|
423
|
+
const capped = Math.max(0, Math.min(base * 2 ** attempt, maxDelay));
|
|
424
|
+
return applyRetryJitter(capped);
|
|
425
|
+
}
|
|
396
426
|
function getRetryAfter(header) {
|
|
397
427
|
if (!header) return void 0;
|
|
398
428
|
const seconds = Number.parseInt(header, 10);
|
|
@@ -648,8 +678,12 @@ function parseCapacityInfo(data, gpuFilter) {
|
|
|
648
678
|
const parsedWorkers = workers.map((w) => ({
|
|
649
679
|
url: w.url,
|
|
650
680
|
gpu: w.gpu,
|
|
681
|
+
gpuCount: w.gpu_count ?? 0,
|
|
682
|
+
readyGpuSlots: w.ready_gpu_slots ?? w.gpu_count ?? (w.healthy ? 1 : 0),
|
|
651
683
|
healthy: w.healthy,
|
|
652
684
|
queueDepth: w.queue_depth,
|
|
685
|
+
pendingCost: w.pending_cost ?? 0,
|
|
686
|
+
inflightBatches: w.inflight_batches ?? 0,
|
|
653
687
|
loadedModels: w.loaded_models
|
|
654
688
|
}));
|
|
655
689
|
return {
|
|
@@ -667,8 +701,21 @@ function parseCapacityInfo(data, gpuFilter) {
|
|
|
667
701
|
function sleep(ms) {
|
|
668
702
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
669
703
|
}
|
|
704
|
+
function nextOomRetryDelay(opts) {
|
|
705
|
+
const { retryAfter, oomRetries, maxOomRetries, elapsedMs, provisionTimeoutMs, model } = opts;
|
|
706
|
+
const message = `Server resource exhausted after ${oomRetries} retry attempt(s) for model '${model}'`;
|
|
707
|
+
if (oomRetries >= maxOomRetries || elapsedMs >= provisionTimeoutMs) {
|
|
708
|
+
throw new ResourceExhaustedError(message, { model, retries: oomRetries });
|
|
709
|
+
}
|
|
710
|
+
const delay = computeOomBackoff(retryAfter, oomRetries);
|
|
711
|
+
if (delay >= provisionTimeoutMs - elapsedMs) {
|
|
712
|
+
throw new ResourceExhaustedError(message, { model, retries: oomRetries });
|
|
713
|
+
}
|
|
714
|
+
return delay;
|
|
715
|
+
}
|
|
670
716
|
async function withProvisioningRetry(performFetch, opts) {
|
|
671
717
|
const startTime = Date.now();
|
|
718
|
+
let oomRetries = 0;
|
|
672
719
|
while (true) {
|
|
673
720
|
const response = await performFetch();
|
|
674
721
|
await throwIfModelLoadFailed(response, opts.model);
|
|
@@ -704,6 +751,26 @@ async function withProvisioningRetry(performFetch, opts) {
|
|
|
704
751
|
await sleep(Math.min(delay, opts.provisionTimeoutMs - elapsed));
|
|
705
752
|
continue;
|
|
706
753
|
}
|
|
754
|
+
if (errorCode === RESOURCE_EXHAUSTED_ERROR_CODE) {
|
|
755
|
+
const delay = nextOomRetryDelay({
|
|
756
|
+
retryAfter: getRetryAfter2(response),
|
|
757
|
+
oomRetries,
|
|
758
|
+
maxOomRetries: RESOURCE_EXHAUSTED_MAX_RETRIES,
|
|
759
|
+
elapsedMs: Date.now() - startTime,
|
|
760
|
+
provisionTimeoutMs: opts.provisionTimeoutMs,
|
|
761
|
+
model: opts.model
|
|
762
|
+
});
|
|
763
|
+
oomRetries += 1;
|
|
764
|
+
await sleep(delay);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (response.status === HTTP_GATEWAY_TIMEOUT) {
|
|
769
|
+
throw new ServerError(
|
|
770
|
+
"Gateway timed out (504) after the request was published to the queue; a worker may already be generating. Not retried because generation is non-idempotent (retrying could double-bill). Re-issue manually if needed.",
|
|
771
|
+
await getErrorCode(response.clone()),
|
|
772
|
+
HTTP_GATEWAY_TIMEOUT
|
|
773
|
+
);
|
|
707
774
|
}
|
|
708
775
|
if (!response.ok) {
|
|
709
776
|
await handleError(response);
|
|
@@ -715,6 +782,255 @@ async function withProvisioningRetry(performFetch, opts) {
|
|
|
715
782
|
}
|
|
716
783
|
}
|
|
717
784
|
|
|
785
|
+
// src/jobs.ts
|
|
786
|
+
var TERMINAL_JOB_STATES = /* @__PURE__ */ new Set([
|
|
787
|
+
"succeeded",
|
|
788
|
+
"failed",
|
|
789
|
+
"suspended",
|
|
790
|
+
"cancelled"
|
|
791
|
+
]);
|
|
792
|
+
var SINK_RETURN = /* @__PURE__ */ new Set(["return", "default"]);
|
|
793
|
+
var SINK_INPLACE = /* @__PURE__ */ new Set(["inplace", "in_place", "in place"]);
|
|
794
|
+
var INTERNAL_SCHEMES = /* @__PURE__ */ new Set(["upload"]);
|
|
795
|
+
var FIELD_MAP_KEYS = /* @__PURE__ */ new Set(["id_field", "input_field", "carry", "input_type"]);
|
|
796
|
+
var INPUT_TYPES = /* @__PURE__ */ new Set(["text", "document"]);
|
|
797
|
+
function isConnectorUri(value) {
|
|
798
|
+
return value.includes("://");
|
|
799
|
+
}
|
|
800
|
+
function isInternalUri(uri) {
|
|
801
|
+
return INTERNAL_SCHEMES.has(uri.split("://", 1)[0] ?? "");
|
|
802
|
+
}
|
|
803
|
+
function normItem(item, index) {
|
|
804
|
+
if (typeof item === "string") return { text: item };
|
|
805
|
+
if (item !== null && typeof item === "object") return item;
|
|
806
|
+
throw new RequestError(`item ${index} must be a string or an object`, "invalid_request", 400);
|
|
807
|
+
}
|
|
808
|
+
function connectionName(uri) {
|
|
809
|
+
const afterScheme = uri.split("://", 2)[1] ?? "";
|
|
810
|
+
const authority = afterScheme.split(/[/?#]/, 1)[0] ?? "";
|
|
811
|
+
const name = authority.trim();
|
|
812
|
+
if (!name) {
|
|
813
|
+
throw new RequestError(
|
|
814
|
+
`connector URI ${JSON.stringify(uri)} names no connection (expected 'scheme://<connection>/\u2026')`,
|
|
815
|
+
"invalid_request",
|
|
816
|
+
400
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
return name;
|
|
820
|
+
}
|
|
821
|
+
function resolveSource(source, connection) {
|
|
822
|
+
if (Array.isArray(source)) {
|
|
823
|
+
if (source.length === 0) {
|
|
824
|
+
throw new RequestError("inline source has no items", "invalid_request", 400);
|
|
825
|
+
}
|
|
826
|
+
return { items: source.map((item, i) => normItem(item, i)) };
|
|
827
|
+
}
|
|
828
|
+
if (isConnectorUri(source)) {
|
|
829
|
+
if (isInternalUri(source)) {
|
|
830
|
+
return connection ? { src: source, connection } : { src: source };
|
|
831
|
+
}
|
|
832
|
+
return { src: source, connection: connection ?? connectionName(source) };
|
|
833
|
+
}
|
|
834
|
+
if (typeof source === "string" && source.trim()) {
|
|
835
|
+
return { items: [{ text: source }] };
|
|
836
|
+
}
|
|
837
|
+
throw new RequestError(
|
|
838
|
+
"source must be inline items (a list/string) or a connector URI (scheme://<connection>/\u2026)",
|
|
839
|
+
"invalid_request",
|
|
840
|
+
400
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
function resolveSink(sink, sourceConnection, sinkConnection) {
|
|
844
|
+
if (sink === null || sink === void 0 || SINK_RETURN.has(sink.trim().toLowerCase())) {
|
|
845
|
+
return {};
|
|
846
|
+
}
|
|
847
|
+
if (SINK_INPLACE.has(sink.trim().toLowerCase())) {
|
|
848
|
+
return { sink: "inplace" };
|
|
849
|
+
}
|
|
850
|
+
if (isConnectorUri(sink)) {
|
|
851
|
+
const body = { sink };
|
|
852
|
+
if (isInternalUri(sink)) {
|
|
853
|
+
if (sinkConnection != null) body.sink_connection = sinkConnection;
|
|
854
|
+
return body;
|
|
855
|
+
}
|
|
856
|
+
const resolved = sinkConnection ?? connectionName(sink);
|
|
857
|
+
if (sinkConnection != null || resolved !== sourceConnection) {
|
|
858
|
+
body.sink_connection = resolved;
|
|
859
|
+
}
|
|
860
|
+
return body;
|
|
861
|
+
}
|
|
862
|
+
throw new RequestError(
|
|
863
|
+
`sink must be 'return', 'inplace', or a connector URI (got ${JSON.stringify(sink)})`,
|
|
864
|
+
"invalid_request",
|
|
865
|
+
400
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
function resolveFieldMap(fieldMap, outputField) {
|
|
869
|
+
const body = {};
|
|
870
|
+
if (fieldMap != null) {
|
|
871
|
+
const unknown = Object.keys(fieldMap).filter((key) => !FIELD_MAP_KEYS.has(key));
|
|
872
|
+
if (unknown.length > 0) {
|
|
873
|
+
throw new RequestError(
|
|
874
|
+
`unknown field_map key(s) ${JSON.stringify(unknown)} (known: ${[...FIELD_MAP_KEYS].join(", ")})`,
|
|
875
|
+
"invalid_request",
|
|
876
|
+
400
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
if (fieldMap.carry != null && (!Array.isArray(fieldMap.carry) || fieldMap.carry.some((c) => typeof c !== "string" || !c))) {
|
|
880
|
+
throw new RequestError(
|
|
881
|
+
`field_map.carry must be a list of field names (got ${JSON.stringify(fieldMap.carry)})`,
|
|
882
|
+
"invalid_request",
|
|
883
|
+
400
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
if (fieldMap.input_type != null && !INPUT_TYPES.has(fieldMap.input_type)) {
|
|
887
|
+
throw new RequestError(
|
|
888
|
+
`field_map.input_type must be one of ${[...INPUT_TYPES].join(", ")} (got ${JSON.stringify(fieldMap.input_type)})`,
|
|
889
|
+
"invalid_request",
|
|
890
|
+
400
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
const mapped = {};
|
|
894
|
+
if (fieldMap.id_field != null) mapped.id_field = fieldMap.id_field;
|
|
895
|
+
if (fieldMap.input_field != null) mapped.input_field = fieldMap.input_field;
|
|
896
|
+
if (fieldMap.input_type != null) mapped.input_type = fieldMap.input_type;
|
|
897
|
+
if (fieldMap.carry != null && fieldMap.carry.length > 0) mapped.carry = fieldMap.carry;
|
|
898
|
+
if (Object.keys(mapped).length > 0) body.field_map = mapped;
|
|
899
|
+
}
|
|
900
|
+
if (outputField != null) {
|
|
901
|
+
if (typeof outputField !== "string" || !outputField) {
|
|
902
|
+
throw new RequestError(
|
|
903
|
+
`output_field must be a non-empty string (got ${JSON.stringify(outputField)})`,
|
|
904
|
+
"invalid_request",
|
|
905
|
+
400
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
body.output_field = outputField;
|
|
909
|
+
}
|
|
910
|
+
return body;
|
|
911
|
+
}
|
|
912
|
+
function resolveWhen(when) {
|
|
913
|
+
if (when == null || when.trim() === "" || when.trim().toLowerCase() === "now") {
|
|
914
|
+
return {};
|
|
915
|
+
}
|
|
916
|
+
const text = when.trim();
|
|
917
|
+
if (text.toLowerCase().startsWith("schedule:")) {
|
|
918
|
+
return { when: "schedule", schedule: text.slice("schedule:".length).trim() };
|
|
919
|
+
}
|
|
920
|
+
if (text.toLowerCase().startsWith("watch:")) {
|
|
921
|
+
return { when: "watch", watch: text.slice("watch:".length).trim() };
|
|
922
|
+
}
|
|
923
|
+
if (text.toLowerCase() === "schedule") {
|
|
924
|
+
throw new RequestError(
|
|
925
|
+
"schedule trigger needs a cron expr: when='schedule:<cron>'",
|
|
926
|
+
"invalid_request",
|
|
927
|
+
400
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
if (text.split(/\s+/).length === 5) {
|
|
931
|
+
return { when: "schedule", schedule: text };
|
|
932
|
+
}
|
|
933
|
+
throw new RequestError(
|
|
934
|
+
`unrecognized when ${JSON.stringify(when)}: use 'now', 'schedule:<cron>', or 'watch:<source>'`,
|
|
935
|
+
"invalid_request",
|
|
936
|
+
400
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
function buildJobBody(options) {
|
|
940
|
+
const operation = options.operation ?? "encode";
|
|
941
|
+
const body = { operation, model: options.model };
|
|
942
|
+
const sourceFields = resolveSource(options.source, options.connection);
|
|
943
|
+
Object.assign(body, sourceFields);
|
|
944
|
+
Object.assign(
|
|
945
|
+
body,
|
|
946
|
+
resolveSink(
|
|
947
|
+
options.sink,
|
|
948
|
+
sourceFields.connection,
|
|
949
|
+
options.sinkConnection
|
|
950
|
+
)
|
|
951
|
+
);
|
|
952
|
+
const mappingFields = resolveFieldMap(options.fieldMap, options.outputField);
|
|
953
|
+
if (Object.keys(mappingFields).length > 0 && !("src" in body)) {
|
|
954
|
+
throw new RequestError(
|
|
955
|
+
"fieldMap/outputField apply to connector-src jobs; an inline items job maps nothing",
|
|
956
|
+
"invalid_request",
|
|
957
|
+
400
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
Object.assign(body, mappingFields);
|
|
961
|
+
Object.assign(body, resolveWhen(options.when));
|
|
962
|
+
if (options.outputTypes && options.outputTypes.length > 0) {
|
|
963
|
+
body.output_types = options.outputTypes;
|
|
964
|
+
}
|
|
965
|
+
if (options.options && Object.keys(options.options).length > 0) {
|
|
966
|
+
body.options = options.options;
|
|
967
|
+
}
|
|
968
|
+
return body;
|
|
969
|
+
}
|
|
970
|
+
function jobChunks(jobDoc) {
|
|
971
|
+
const raw = jobDoc.output?.chunks ?? [];
|
|
972
|
+
return raw.map((chunk) => ({
|
|
973
|
+
seq: chunk.seq,
|
|
974
|
+
items: chunk.items,
|
|
975
|
+
state: chunk.state,
|
|
976
|
+
ref: chunk.ref,
|
|
977
|
+
units: chunk.units,
|
|
978
|
+
credits: chunk.credits,
|
|
979
|
+
error: chunk.error ?? null
|
|
980
|
+
}));
|
|
981
|
+
}
|
|
982
|
+
function toNumberArrayLike(value) {
|
|
983
|
+
if (value == null) return null;
|
|
984
|
+
if (value instanceof Float32Array) return value;
|
|
985
|
+
if (Array.isArray(value)) return value;
|
|
986
|
+
if (ArrayBuffer.isView(value)) return value;
|
|
987
|
+
return null;
|
|
988
|
+
}
|
|
989
|
+
function denseInfo(dense) {
|
|
990
|
+
if (dense == null) return { dims: null, vector: null };
|
|
991
|
+
if (typeof dense === "object" && !Array.isArray(dense) && !ArrayBuffer.isView(dense)) {
|
|
992
|
+
const rec = dense;
|
|
993
|
+
let raw = null;
|
|
994
|
+
for (const key of ["values", "vector", "dense"]) {
|
|
995
|
+
if (rec[key] != null) {
|
|
996
|
+
raw = rec[key];
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
const vector2 = toNumberArrayLike(raw);
|
|
1001
|
+
let dims = typeof rec.dims === "number" ? rec.dims : null;
|
|
1002
|
+
if (dims == null && vector2 != null) dims = vector2.length;
|
|
1003
|
+
return { dims, vector: vector2 };
|
|
1004
|
+
}
|
|
1005
|
+
const vector = toNumberArrayLike(dense);
|
|
1006
|
+
return { dims: vector != null ? vector.length : null, vector };
|
|
1007
|
+
}
|
|
1008
|
+
function decodeResultItem(result) {
|
|
1009
|
+
const payload = result.result_msgpack;
|
|
1010
|
+
let decoded = null;
|
|
1011
|
+
if (payload instanceof Uint8Array) {
|
|
1012
|
+
try {
|
|
1013
|
+
decoded = unpackMessage(payload);
|
|
1014
|
+
} catch {
|
|
1015
|
+
decoded = null;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
const dense = decoded && typeof decoded === "object" ? decoded.dense : null;
|
|
1019
|
+
const { dims, vector } = denseInfo(dense);
|
|
1020
|
+
return {
|
|
1021
|
+
id: result.id ?? null,
|
|
1022
|
+
success: result.success ?? null,
|
|
1023
|
+
units: result.units ?? null,
|
|
1024
|
+
dims,
|
|
1025
|
+
dense: vector
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
function decodeChunkBytes(raw) {
|
|
1029
|
+
const results = unpackMessage(raw);
|
|
1030
|
+
if (!Array.isArray(results)) return [];
|
|
1031
|
+
return results.map((r) => decodeResultItem(r));
|
|
1032
|
+
}
|
|
1033
|
+
|
|
718
1034
|
// src/sse.ts
|
|
719
1035
|
var SSE_DONE = "[DONE]";
|
|
720
1036
|
var MAX_SSE_BUFFER_CHARS = 8 * 1024 * 1024;
|
|
@@ -854,12 +1170,20 @@ function extractDataPayload(block) {
|
|
|
854
1170
|
}
|
|
855
1171
|
|
|
856
1172
|
// src/version.ts
|
|
857
|
-
var SDK_VERSION = "0.6.
|
|
1173
|
+
var SDK_VERSION = "0.6.18";
|
|
858
1174
|
|
|
859
1175
|
// src/client.ts
|
|
860
1176
|
function sleep2(ms) {
|
|
861
1177
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
862
1178
|
}
|
|
1179
|
+
function resolveUploadFilename(file, filename) {
|
|
1180
|
+
if (filename) return filename;
|
|
1181
|
+
const name = file.name;
|
|
1182
|
+
if (typeof name === "string" && name.length > 0) {
|
|
1183
|
+
return name.split(/[/\\]/).pop() || "upload.jsonl";
|
|
1184
|
+
}
|
|
1185
|
+
return "upload.jsonl";
|
|
1186
|
+
}
|
|
863
1187
|
function abortableSleep(ms, signal) {
|
|
864
1188
|
if (signal.aborted) return Promise.resolve(true);
|
|
865
1189
|
return new Promise((resolve) => {
|
|
@@ -913,6 +1237,16 @@ var SIEClient = class {
|
|
|
913
1237
|
apiKey;
|
|
914
1238
|
defaultWaitForCapacity;
|
|
915
1239
|
provisionTimeout;
|
|
1240
|
+
controlPlaneUrl;
|
|
1241
|
+
org;
|
|
1242
|
+
/** Batch class — `POST/GET /v1/jobs` on the keyed gateway. */
|
|
1243
|
+
jobs;
|
|
1244
|
+
/** Org-scoped connections (connector auth by name) on the control plane. */
|
|
1245
|
+
connections;
|
|
1246
|
+
/** OpenAI-compatible Files API — `POST/GET /v1/files`. */
|
|
1247
|
+
files;
|
|
1248
|
+
/** OpenAI-compatible Batch API — `POST/GET /v1/batches`. */
|
|
1249
|
+
batches;
|
|
916
1250
|
// Pool state: track created pools and their lease renewal scheduling
|
|
917
1251
|
pools = /* @__PURE__ */ new Map();
|
|
918
1252
|
// Version negotiation state
|
|
@@ -930,8 +1264,36 @@ var SIEClient = class {
|
|
|
930
1264
|
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
931
1265
|
this.gpu = options.gpu;
|
|
932
1266
|
this.apiKey = options.apiKey;
|
|
933
|
-
this.defaultWaitForCapacity = options.waitForCapacity ??
|
|
1267
|
+
this.defaultWaitForCapacity = options.waitForCapacity ?? true;
|
|
934
1268
|
this.provisionTimeout = options.provisionTimeout ?? DEFAULT_PROVISION_TIMEOUT;
|
|
1269
|
+
this.controlPlaneUrl = options.controlPlaneUrl?.replace(/\/$/, "");
|
|
1270
|
+
this.org = options.org;
|
|
1271
|
+
this.jobs = {
|
|
1272
|
+
submit: (submitOptions) => this.jobSubmit(submitOptions),
|
|
1273
|
+
get: (jobId) => this.jobGet(jobId),
|
|
1274
|
+
list: () => this.jobList(),
|
|
1275
|
+
cancel: (jobId) => this.jobCancel(jobId),
|
|
1276
|
+
results: (jobId) => this.jobResults(jobId),
|
|
1277
|
+
wait: (jobId, options2) => this.jobWait(jobId, options2)
|
|
1278
|
+
};
|
|
1279
|
+
this.connections = {
|
|
1280
|
+
add: (name, type, secret) => this.connectionAdd(name, type, secret),
|
|
1281
|
+
list: () => this.connectionList(),
|
|
1282
|
+
revoke: (name) => this.connectionRevoke(name)
|
|
1283
|
+
};
|
|
1284
|
+
this.files = {
|
|
1285
|
+
upload: (file, options2) => this.fileUpload(file, options2),
|
|
1286
|
+
create: (options2) => this.fileUpload(options2.file, options2),
|
|
1287
|
+
retrieve: (fileId) => this.fileRetrieve(fileId),
|
|
1288
|
+
content: (fileId) => this.fileContent(fileId),
|
|
1289
|
+
delete: (fileId) => this.fileDelete(fileId)
|
|
1290
|
+
};
|
|
1291
|
+
this.batches = {
|
|
1292
|
+
create: (options2) => this.batchCreate(options2),
|
|
1293
|
+
retrieve: (batchId) => this.batchRetrieve(batchId),
|
|
1294
|
+
list: () => this.batchList(),
|
|
1295
|
+
cancel: (batchId) => this.batchCancel(batchId)
|
|
1296
|
+
};
|
|
935
1297
|
}
|
|
936
1298
|
/**
|
|
937
1299
|
* Get the base URL of the SIE server.
|
|
@@ -1394,8 +1756,10 @@ var SIEClient = class {
|
|
|
1394
1756
|
* generator throws it instead of yielding the chunk.
|
|
1395
1757
|
*
|
|
1396
1758
|
* Retry policy mirrors {@link generate}: only explicit SAFE
|
|
1397
|
-
* pre-execution capacity signals — `503 PROVISIONING
|
|
1398
|
-
* `503 MODEL_LOADING`
|
|
1759
|
+
* pre-execution capacity signals — `503 PROVISIONING`,
|
|
1760
|
+
* `503 MODEL_LOADING` and `503 RESOURCE_EXHAUSTED` (the latter only
|
|
1761
|
+
* under `waitForCapacity`) — are retried while the provision budget
|
|
1762
|
+
* remains; a `504` is post-publish and therefore terminal.
|
|
1399
1763
|
* Once the body opens we never retry (the call is non-idempotent; a
|
|
1400
1764
|
* mid-stream failure must not re-issue generation).
|
|
1401
1765
|
*
|
|
@@ -1417,6 +1781,7 @@ var SIEClient = class {
|
|
|
1417
1781
|
}
|
|
1418
1782
|
try {
|
|
1419
1783
|
const startTime = Date.now();
|
|
1784
|
+
let oomRetries = 0;
|
|
1420
1785
|
let response;
|
|
1421
1786
|
while (true) {
|
|
1422
1787
|
if (signal?.aborted) {
|
|
@@ -1488,6 +1853,34 @@ var SIEClient = class {
|
|
|
1488
1853
|
}
|
|
1489
1854
|
continue;
|
|
1490
1855
|
}
|
|
1856
|
+
if (errorCode === RESOURCE_EXHAUSTED_ERROR_CODE) {
|
|
1857
|
+
if (!waitForCapacity) {
|
|
1858
|
+
throw new ResourceExhaustedError(
|
|
1859
|
+
`Server resource exhausted after ${oomRetries} retry attempt(s) for model '${model}'`,
|
|
1860
|
+
{ model, retries: oomRetries }
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
const delay = nextOomRetryDelay({
|
|
1864
|
+
retryAfter: getRetryAfter2(attemptResponse),
|
|
1865
|
+
oomRetries,
|
|
1866
|
+
maxOomRetries: RESOURCE_EXHAUSTED_MAX_RETRIES,
|
|
1867
|
+
elapsedMs: Date.now() - startTime,
|
|
1868
|
+
provisionTimeoutMs: this.provisionTimeout,
|
|
1869
|
+
model
|
|
1870
|
+
});
|
|
1871
|
+
oomRetries += 1;
|
|
1872
|
+
if (await abortableSleep(delay, controller.signal)) {
|
|
1873
|
+
throw new SIEConnectionError("Stream aborted while provisioning", "other");
|
|
1874
|
+
}
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
if (attemptResponse.status === HTTP_GATEWAY_TIMEOUT) {
|
|
1879
|
+
throw new ServerError(
|
|
1880
|
+
"Gateway timed out (504) after the request was published to the queue; a worker may already be generating. Not retried because generation is non-idempotent (retrying could double-bill).",
|
|
1881
|
+
await getErrorCode(attemptResponse.clone()),
|
|
1882
|
+
HTTP_GATEWAY_TIMEOUT
|
|
1883
|
+
);
|
|
1491
1884
|
}
|
|
1492
1885
|
if (attemptResponse.status !== 200) {
|
|
1493
1886
|
await handleError(attemptResponse);
|
|
@@ -1640,12 +2033,21 @@ var SIEClient = class {
|
|
|
1640
2033
|
* @param gpus - Optional machine profile requirements for pool readiness, e.g., { "l4": 2, "l4-spot": 1 }
|
|
1641
2034
|
* @param gpuCaps - Optional maximum assigned workers per machine profile
|
|
1642
2035
|
* @param queuePool - Optional Helm/NATS queue namespace backing this logical pool. Defaults to "default".
|
|
2036
|
+
* @param options - Optional bundle filter, warm floor, and pinned models
|
|
2037
|
+
* (Python SDK `create_pool` parity)
|
|
1643
2038
|
*
|
|
1644
2039
|
* @example
|
|
1645
2040
|
* ```typescript
|
|
1646
2041
|
* // Create or update a pool with 2 L4 GPUs
|
|
1647
2042
|
* await client.createPool("eval-bench", { l4: 2 });
|
|
1648
2043
|
*
|
|
2044
|
+
* // With a bundle filter, warm floor, and pinned models
|
|
2045
|
+
* await client.createPool("eval-bench", { l4: 2 }, undefined, undefined, {
|
|
2046
|
+
* bundle: "default",
|
|
2047
|
+
* minimumWorkerCount: 1,
|
|
2048
|
+
* pinnedModels: ["bge-m3"],
|
|
2049
|
+
* });
|
|
2050
|
+
*
|
|
1649
2051
|
* // Use the pool for requests
|
|
1650
2052
|
* await client.encode("bge-m3", { text: "Hello" }, { gpu: "eval-bench/l4" });
|
|
1651
2053
|
*
|
|
@@ -1653,8 +2055,11 @@ var SIEClient = class {
|
|
|
1653
2055
|
* await client.deletePool("eval-bench");
|
|
1654
2056
|
* ```
|
|
1655
2057
|
*/
|
|
1656
|
-
async createPool(name, gpus, gpuCaps, queuePool) {
|
|
2058
|
+
async createPool(name, gpus, gpuCaps, queuePool, options = {}) {
|
|
1657
2059
|
const alreadyTracking = this.pools.has(name);
|
|
2060
|
+
if (options.minimumWorkerCount !== void 0 && options.minimumWorkerCount < 0) {
|
|
2061
|
+
throw new RangeError("minimumWorkerCount must be >= 0");
|
|
2062
|
+
}
|
|
1658
2063
|
const requestBody = {
|
|
1659
2064
|
name
|
|
1660
2065
|
};
|
|
@@ -1667,6 +2072,15 @@ var SIEClient = class {
|
|
|
1667
2072
|
if (queuePool) {
|
|
1668
2073
|
requestBody.queue_pool = queuePool;
|
|
1669
2074
|
}
|
|
2075
|
+
if (options.bundle) {
|
|
2076
|
+
requestBody.bundle = options.bundle;
|
|
2077
|
+
}
|
|
2078
|
+
if (options.minimumWorkerCount !== void 0) {
|
|
2079
|
+
requestBody.minimum_worker_count = options.minimumWorkerCount;
|
|
2080
|
+
}
|
|
2081
|
+
if (options.pinnedModels !== void 0) {
|
|
2082
|
+
requestBody.pinned_models = options.pinnedModels;
|
|
2083
|
+
}
|
|
1670
2084
|
const url = `${this.baseUrl}/v1/pools`;
|
|
1671
2085
|
const headers = {
|
|
1672
2086
|
"Content-Type": JSON_CONTENT_TYPE,
|
|
@@ -1974,6 +2388,11 @@ var SIEClient = class {
|
|
|
1974
2388
|
* Retried (capped by `provisionTimeout`):
|
|
1975
2389
|
* - 503 `PROVISIONING` when `waitForCapacity: true`
|
|
1976
2390
|
* - 503 `MODEL_LOADING` / `LORA_LOADING`
|
|
2391
|
+
* - 503 `RESOURCE_EXHAUSTED` regardless of `waitForCapacity` (bounded
|
|
2392
|
+
* exponential backoff, at most `RESOURCE_EXHAUSTED_MAX_RETRIES`)
|
|
2393
|
+
* - 504 gateway timeout when `waitForCapacity: true` — encode/score/
|
|
2394
|
+
* extract are idempotent queue paths, so a post-publish retry is safe
|
|
2395
|
+
* (unlike generate/chat, where a 504 is terminal)
|
|
1977
2396
|
* - `SIEConnectionError` with `kind === "connect"` (issue #95)
|
|
1978
2397
|
*
|
|
1979
2398
|
* `kind === "timeout"` is NOT retried — would extend the user-visible
|
|
@@ -1982,6 +2401,7 @@ var SIEClient = class {
|
|
|
1982
2401
|
async requestWithRetry(path, body, pool, gpu, waitForCapacity, model) {
|
|
1983
2402
|
const startTime = Date.now();
|
|
1984
2403
|
let loraRetries = 0;
|
|
2404
|
+
let oomRetries = 0;
|
|
1985
2405
|
while (true) {
|
|
1986
2406
|
let response;
|
|
1987
2407
|
try {
|
|
@@ -2056,6 +2476,27 @@ var SIEClient = class {
|
|
|
2056
2476
|
await sleep2(actualDelay);
|
|
2057
2477
|
continue;
|
|
2058
2478
|
}
|
|
2479
|
+
if (errorCode === RESOURCE_EXHAUSTED_ERROR_CODE) {
|
|
2480
|
+
const delay = nextOomRetryDelay({
|
|
2481
|
+
retryAfter: getRetryAfter2(response),
|
|
2482
|
+
oomRetries,
|
|
2483
|
+
maxOomRetries: RESOURCE_EXHAUSTED_MAX_RETRIES,
|
|
2484
|
+
elapsedMs: Date.now() - startTime,
|
|
2485
|
+
provisionTimeoutMs: this.provisionTimeout,
|
|
2486
|
+
model
|
|
2487
|
+
});
|
|
2488
|
+
oomRetries += 1;
|
|
2489
|
+
await sleep2(delay);
|
|
2490
|
+
continue;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
if (response.status === HTTP_GATEWAY_TIMEOUT && waitForCapacity) {
|
|
2494
|
+
const elapsed = Date.now() - startTime;
|
|
2495
|
+
if (elapsed < this.provisionTimeout) {
|
|
2496
|
+
const delay = getRetryAfter2(response) ?? MODEL_LOADING_DEFAULT_DELAY;
|
|
2497
|
+
await sleep2(Math.min(delay, this.provisionTimeout - elapsed));
|
|
2498
|
+
continue;
|
|
2499
|
+
}
|
|
2059
2500
|
}
|
|
2060
2501
|
if (!response.ok) {
|
|
2061
2502
|
await handleError(response, gpu);
|
|
@@ -2144,6 +2585,291 @@ var SIEClient = class {
|
|
|
2144
2585
|
clearTimeout(timeoutId);
|
|
2145
2586
|
}
|
|
2146
2587
|
}
|
|
2588
|
+
// ---------------------------------------------------------------------------
|
|
2589
|
+
// Jobs + connections namespaces. Jobs ride the keyed gateway
|
|
2590
|
+
// (`/v1/jobs`); connections ride the control plane (`/internal/orgs/{org}/…`).
|
|
2591
|
+
// ---------------------------------------------------------------------------
|
|
2592
|
+
/** One JSON request over `fetch` (bearer auth reused; absolute or base-relative URL). */
|
|
2593
|
+
async jsonRequest(target, method, body, timeoutMs = this.timeout) {
|
|
2594
|
+
const url = target.startsWith("http") ? target : `${this.baseUrl}${target}`;
|
|
2595
|
+
const headers = {
|
|
2596
|
+
Accept: JSON_CONTENT_TYPE,
|
|
2597
|
+
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
2598
|
+
};
|
|
2599
|
+
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
2600
|
+
const init = { method, headers };
|
|
2601
|
+
if (body !== void 0) {
|
|
2602
|
+
headers["Content-Type"] = JSON_CONTENT_TYPE;
|
|
2603
|
+
init.body = JSON.stringify(body);
|
|
2604
|
+
}
|
|
2605
|
+
const controller = new AbortController();
|
|
2606
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
2607
|
+
init.signal = controller.signal;
|
|
2608
|
+
try {
|
|
2609
|
+
const response = await fetch(url, init);
|
|
2610
|
+
if (!response.ok) {
|
|
2611
|
+
await handleError(response);
|
|
2612
|
+
}
|
|
2613
|
+
this.checkServerVersion(response);
|
|
2614
|
+
const text = await response.text();
|
|
2615
|
+
return text ? JSON.parse(text) : {};
|
|
2616
|
+
} catch (error) {
|
|
2617
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2618
|
+
throw new SIEConnectionError(`Request timeout after ${timeoutMs}ms`, "timeout");
|
|
2619
|
+
}
|
|
2620
|
+
if (error instanceof TypeError) {
|
|
2621
|
+
throw new SIEConnectionError(`Connection failed: ${error.message}`, "connect");
|
|
2622
|
+
}
|
|
2623
|
+
throw error;
|
|
2624
|
+
} finally {
|
|
2625
|
+
clearTimeout(timeoutId);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
async jobSubmit(options) {
|
|
2629
|
+
return this.jsonRequest(
|
|
2630
|
+
"/v1/jobs",
|
|
2631
|
+
"POST",
|
|
2632
|
+
buildJobBody(options),
|
|
2633
|
+
Math.max(this.timeout, DEFAULT_LONG_RUNNING_TIMEOUT)
|
|
2634
|
+
);
|
|
2635
|
+
}
|
|
2636
|
+
async jobGet(jobId) {
|
|
2637
|
+
return this.jsonRequest(`/v1/jobs/${encodeURIComponent(jobId)}`, "GET");
|
|
2638
|
+
}
|
|
2639
|
+
async jobList() {
|
|
2640
|
+
const data = await this.jsonRequest("/v1/jobs", "GET");
|
|
2641
|
+
return Array.isArray(data) ? data : data.data ?? [];
|
|
2642
|
+
}
|
|
2643
|
+
async jobCancel(jobId) {
|
|
2644
|
+
return this.jsonRequest(`/v1/jobs/${encodeURIComponent(jobId)}/cancel`, "POST");
|
|
2645
|
+
}
|
|
2646
|
+
async jobResults(jobId) {
|
|
2647
|
+
const job = await this.jobGet(jobId);
|
|
2648
|
+
const chunks = jobChunks(job);
|
|
2649
|
+
const items = [];
|
|
2650
|
+
for (const chunk of chunks) {
|
|
2651
|
+
if (chunk.state !== "succeeded" || !chunk.ref) continue;
|
|
2652
|
+
const raw = await this.readRef(chunk.ref);
|
|
2653
|
+
items.push(...decodeChunkBytes(raw));
|
|
2654
|
+
}
|
|
2655
|
+
const withDims = items.find((it) => it.dims != null);
|
|
2656
|
+
return {
|
|
2657
|
+
job_id: job.id ?? jobId,
|
|
2658
|
+
state: job.state,
|
|
2659
|
+
total_items: job.total_items,
|
|
2660
|
+
settled_credits: job.settled_credits,
|
|
2661
|
+
chunks,
|
|
2662
|
+
retrieved: items.length,
|
|
2663
|
+
dims: withDims ? withDims.dims : null,
|
|
2664
|
+
items
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
async jobWait(jobId, options) {
|
|
2668
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_JOB_WAIT_TIMEOUT;
|
|
2669
|
+
const pollMs = options?.pollMs ?? DEFAULT_JOB_WAIT_POLL;
|
|
2670
|
+
const deadline = Date.now() + timeoutMs;
|
|
2671
|
+
for (; ; ) {
|
|
2672
|
+
const job = await this.jobGet(jobId);
|
|
2673
|
+
if (job.state && TERMINAL_JOB_STATES.has(job.state)) {
|
|
2674
|
+
return job;
|
|
2675
|
+
}
|
|
2676
|
+
if (Date.now() >= deadline) {
|
|
2677
|
+
throw new RequestError(
|
|
2678
|
+
`job ${jobId} still ${JSON.stringify(job.state)} after ${timeoutMs}ms`,
|
|
2679
|
+
"job_wait_timeout",
|
|
2680
|
+
504
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
await sleep2(pollMs);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
/** Retrieve a chunk's payload-store ref (http(s) URL). */
|
|
2687
|
+
async readRef(ref) {
|
|
2688
|
+
if (!ref.startsWith("http://") && !ref.startsWith("https://")) {
|
|
2689
|
+
throw new RequestError(
|
|
2690
|
+
`cannot retrieve payload-store ref ${JSON.stringify(ref)} (the TS SDK reads http(s) refs)`,
|
|
2691
|
+
"bad_ref",
|
|
2692
|
+
400
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2695
|
+
const controller = new AbortController();
|
|
2696
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2697
|
+
try {
|
|
2698
|
+
const response = await fetch(ref, {
|
|
2699
|
+
headers: { Accept: "application/octet-stream" },
|
|
2700
|
+
signal: controller.signal
|
|
2701
|
+
});
|
|
2702
|
+
if (!response.ok) {
|
|
2703
|
+
await handleError(response);
|
|
2704
|
+
}
|
|
2705
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
2706
|
+
} catch (error) {
|
|
2707
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2708
|
+
throw new SIEConnectionError(`Request timeout after ${this.timeout}ms`, "timeout");
|
|
2709
|
+
}
|
|
2710
|
+
if (error instanceof TypeError) {
|
|
2711
|
+
throw new SIEConnectionError(`Connection failed: ${error.message}`, "connect");
|
|
2712
|
+
}
|
|
2713
|
+
throw error;
|
|
2714
|
+
} finally {
|
|
2715
|
+
clearTimeout(timeoutId);
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
connectionsBase() {
|
|
2719
|
+
if (!this.controlPlaneUrl) {
|
|
2720
|
+
throw new RequestError(
|
|
2721
|
+
"connections require controlPlaneUrl on the client: new SIEClient(url, { controlPlaneUrl, org })",
|
|
2722
|
+
"missing_control_plane_url",
|
|
2723
|
+
400
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
if (!this.org) {
|
|
2727
|
+
throw new RequestError(
|
|
2728
|
+
"connections require org on the client: new SIEClient(url, { controlPlaneUrl, org })",
|
|
2729
|
+
"missing_org",
|
|
2730
|
+
400
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
return `${this.controlPlaneUrl}/internal/orgs/${encodeURIComponent(this.org)}/connections`;
|
|
2734
|
+
}
|
|
2735
|
+
async connectionAdd(name, type, secret) {
|
|
2736
|
+
return this.jsonRequest(this.connectionsBase(), "POST", {
|
|
2737
|
+
type,
|
|
2738
|
+
name,
|
|
2739
|
+
secret
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
async connectionList() {
|
|
2743
|
+
const data = await this.jsonRequest(
|
|
2744
|
+
this.connectionsBase(),
|
|
2745
|
+
"GET"
|
|
2746
|
+
);
|
|
2747
|
+
return Array.isArray(data) ? data : data.connections ?? [];
|
|
2748
|
+
}
|
|
2749
|
+
async connectionRevoke(name) {
|
|
2750
|
+
return this.jsonRequest(
|
|
2751
|
+
`${this.connectionsBase()}/${encodeURIComponent(name)}`,
|
|
2752
|
+
"DELETE"
|
|
2753
|
+
);
|
|
2754
|
+
}
|
|
2755
|
+
// ---------------------------------------------------------------------------
|
|
2756
|
+
// Files + batches namespaces — the OpenAI-compatible file /
|
|
2757
|
+
// batch surface on the keyed gateway. Method names/args mirror `openai.files`
|
|
2758
|
+
// / `openai.batches` so switching an OpenAI-batch caller to the SDK is
|
|
2759
|
+
// mechanical.
|
|
2760
|
+
// ---------------------------------------------------------------------------
|
|
2761
|
+
/** POST a raw body and parse the JSON response (bearer auth reused). */
|
|
2762
|
+
async rawPostJson(path, body, contentType, timeoutMs = this.timeout) {
|
|
2763
|
+
const url = `${this.baseUrl}${path}`;
|
|
2764
|
+
const headers = {
|
|
2765
|
+
Accept: JSON_CONTENT_TYPE,
|
|
2766
|
+
"Content-Type": contentType,
|
|
2767
|
+
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
2768
|
+
};
|
|
2769
|
+
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
2770
|
+
const controller = new AbortController();
|
|
2771
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
2772
|
+
try {
|
|
2773
|
+
const response = await fetch(url, {
|
|
2774
|
+
method: "POST",
|
|
2775
|
+
headers,
|
|
2776
|
+
body,
|
|
2777
|
+
signal: controller.signal
|
|
2778
|
+
});
|
|
2779
|
+
if (!response.ok) {
|
|
2780
|
+
await handleError(response);
|
|
2781
|
+
}
|
|
2782
|
+
this.checkServerVersion(response);
|
|
2783
|
+
const text = await response.text();
|
|
2784
|
+
return text ? JSON.parse(text) : {};
|
|
2785
|
+
} catch (error) {
|
|
2786
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2787
|
+
throw new SIEConnectionError(`Request timeout after ${timeoutMs}ms`, "timeout");
|
|
2788
|
+
}
|
|
2789
|
+
if (error instanceof TypeError) {
|
|
2790
|
+
throw new SIEConnectionError(`Connection failed: ${error.message}`, "connect");
|
|
2791
|
+
}
|
|
2792
|
+
throw error;
|
|
2793
|
+
} finally {
|
|
2794
|
+
clearTimeout(timeoutId);
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
/** GET raw bytes (bearer auth reused); used to download a file's content. */
|
|
2798
|
+
async rawGetBytes(path) {
|
|
2799
|
+
const url = `${this.baseUrl}${path}`;
|
|
2800
|
+
const headers = {
|
|
2801
|
+
Accept: "application/jsonl",
|
|
2802
|
+
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
2803
|
+
};
|
|
2804
|
+
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
2805
|
+
const controller = new AbortController();
|
|
2806
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2807
|
+
try {
|
|
2808
|
+
const response = await fetch(url, { method: "GET", headers, signal: controller.signal });
|
|
2809
|
+
if (!response.ok) {
|
|
2810
|
+
await handleError(response);
|
|
2811
|
+
}
|
|
2812
|
+
this.checkServerVersion(response);
|
|
2813
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
2814
|
+
} catch (error) {
|
|
2815
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2816
|
+
throw new SIEConnectionError(`Request timeout after ${this.timeout}ms`, "timeout");
|
|
2817
|
+
}
|
|
2818
|
+
if (error instanceof TypeError) {
|
|
2819
|
+
throw new SIEConnectionError(`Connection failed: ${error.message}`, "connect");
|
|
2820
|
+
}
|
|
2821
|
+
throw error;
|
|
2822
|
+
} finally {
|
|
2823
|
+
clearTimeout(timeoutId);
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
async fileUpload(file, options) {
|
|
2827
|
+
const purpose = options?.purpose ?? "batch";
|
|
2828
|
+
const filename = resolveUploadFilename(file, options?.filename);
|
|
2829
|
+
const query = new URLSearchParams({ purpose, filename }).toString();
|
|
2830
|
+
const body = file instanceof ArrayBuffer ? new Uint8Array(file) : file;
|
|
2831
|
+
return this.rawPostJson(
|
|
2832
|
+
`/v1/files?${query}`,
|
|
2833
|
+
body,
|
|
2834
|
+
"application/jsonl",
|
|
2835
|
+
Math.max(this.timeout, DEFAULT_LONG_RUNNING_TIMEOUT)
|
|
2836
|
+
);
|
|
2837
|
+
}
|
|
2838
|
+
async fileRetrieve(fileId) {
|
|
2839
|
+
return this.jsonRequest(`/v1/files/${encodeURIComponent(fileId)}`, "GET");
|
|
2840
|
+
}
|
|
2841
|
+
async fileContent(fileId) {
|
|
2842
|
+
return this.rawGetBytes(`/v1/files/${encodeURIComponent(fileId)}/content`);
|
|
2843
|
+
}
|
|
2844
|
+
async fileDelete(fileId) {
|
|
2845
|
+
return this.jsonRequest(`/v1/files/${encodeURIComponent(fileId)}`, "DELETE");
|
|
2846
|
+
}
|
|
2847
|
+
async batchCreate(options) {
|
|
2848
|
+
const body = {
|
|
2849
|
+
input_file_id: options.input_file_id,
|
|
2850
|
+
endpoint: options.endpoint ?? "/v1/embeddings",
|
|
2851
|
+
completion_window: options.completion_window ?? "24h"
|
|
2852
|
+
};
|
|
2853
|
+
if (options.metadata !== void 0) {
|
|
2854
|
+
body.metadata = options.metadata;
|
|
2855
|
+
}
|
|
2856
|
+
return this.jsonRequest(
|
|
2857
|
+
"/v1/batches",
|
|
2858
|
+
"POST",
|
|
2859
|
+
body,
|
|
2860
|
+
Math.max(this.timeout, DEFAULT_LONG_RUNNING_TIMEOUT)
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
async batchRetrieve(batchId) {
|
|
2864
|
+
return this.jsonRequest(`/v1/batches/${encodeURIComponent(batchId)}`, "GET");
|
|
2865
|
+
}
|
|
2866
|
+
async batchList() {
|
|
2867
|
+
const data = await this.jsonRequest("/v1/batches", "GET");
|
|
2868
|
+
return Array.isArray(data) ? data : data.data ?? [];
|
|
2869
|
+
}
|
|
2870
|
+
async batchCancel(batchId) {
|
|
2871
|
+
return this.jsonRequest(`/v1/batches/${encodeURIComponent(batchId)}/cancel`, "POST");
|
|
2872
|
+
}
|
|
2147
2873
|
buildWsUrl(path) {
|
|
2148
2874
|
const url = new URL(this.baseUrl);
|
|
2149
2875
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
@@ -2282,6 +3008,6 @@ function maxsimBatch(queries, documents) {
|
|
|
2282
3008
|
return scores;
|
|
2283
3009
|
}
|
|
2284
3010
|
|
|
2285
|
-
export { InputTooLongError, LoraLoadingError, ModelLoadFailedError, ModelLoadingError, PoolError, ProvisioningError, RequestError, SDK_VERSION, SIEClient, SIEConnectionError, SIEError, SIEStreamError, ServerError, denseEmbedding, detectImageFormat, maxsim, maxsimBatch, maxsimDocuments, multivectorEmbedding, normalizeSparseVector, packMessage, sparseEmbedding, sparseEmbeddingMap, toFloat32Array, toImageBytes, toImageWireFormat, toNumberArray, unpackMessage };
|
|
3011
|
+
export { InputTooLongError, LoraLoadingError, ModelLoadFailedError, ModelLoadingError, PoolError, ProvisioningError, RequestError, ResourceExhaustedError, SDK_VERSION, SIEClient, SIEConnectionError, SIEError, SIEStreamError, ServerError, TERMINAL_JOB_STATES, buildJobBody, connectionName, denseEmbedding, detectImageFormat, maxsim, maxsimBatch, maxsimDocuments, multivectorEmbedding, normalizeSparseVector, packMessage, sparseEmbedding, sparseEmbeddingMap, toFloat32Array, toImageBytes, toImageWireFormat, toNumberArray, unpackMessage };
|
|
2286
3012
|
//# sourceMappingURL=index.js.map
|
|
2287
3013
|
//# sourceMappingURL=index.js.map
|