@superlinked/sie-sdk 0.1.8
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/LICENSE +201 -0
- package/dist/index.cjs +1406 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +907 -0
- package/dist/index.d.ts +907 -0
- package/dist/index.js +1385 -0
- package/dist/index.js.map +1 -0
- package/dist/scoring.cjs +42 -0
- package/dist/scoring.cjs.map +1 -0
- package/dist/scoring.d.cts +19 -0
- package/dist/scoring.d.ts +19 -0
- package/dist/scoring.js +38 -0
- package/dist/scoring.js.map +1 -0
- package/package.json +79 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1385 @@
|
|
|
1
|
+
import { ExtensionCodec, encode, decode } from '@msgpack/msgpack';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var SIEError = class extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "SIEError";
|
|
8
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var SIEConnectionError = class extends SIEError {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "SIEConnectionError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var RequestError = class extends SIEError {
|
|
18
|
+
/** Error code from the server (e.g., "INVALID_MODEL", "VALIDATION_ERROR") */
|
|
19
|
+
code;
|
|
20
|
+
/** HTTP status code (400-499) */
|
|
21
|
+
statusCode;
|
|
22
|
+
constructor(message, code, statusCode) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "RequestError";
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.statusCode = statusCode;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var ServerError = class extends SIEError {
|
|
30
|
+
/** Error code from the server (e.g., "INTERNAL_ERROR", "LORA_LOADING") */
|
|
31
|
+
code;
|
|
32
|
+
/** HTTP status code (500-599) */
|
|
33
|
+
statusCode;
|
|
34
|
+
constructor(message, code, statusCode) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = "ServerError";
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.statusCode = statusCode;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var ProvisioningError = class extends SIEError {
|
|
42
|
+
/** The GPU type that was requested */
|
|
43
|
+
gpu;
|
|
44
|
+
/** Suggested retry delay in milliseconds (from server Retry-After header) */
|
|
45
|
+
retryAfter;
|
|
46
|
+
constructor(message, gpu, retryAfter) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "ProvisioningError";
|
|
49
|
+
this.gpu = gpu;
|
|
50
|
+
this.retryAfter = retryAfter;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var PoolError = class extends SIEError {
|
|
54
|
+
/** Name of the pool */
|
|
55
|
+
poolName;
|
|
56
|
+
/** Current pool state (if known): "pending", "active", "expired" */
|
|
57
|
+
state;
|
|
58
|
+
constructor(message, poolName, state) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = "PoolError";
|
|
61
|
+
this.poolName = poolName;
|
|
62
|
+
this.state = state;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var LoraLoadingError = class extends SIEError {
|
|
66
|
+
/** The LoRA adapter that was requested */
|
|
67
|
+
lora;
|
|
68
|
+
/** The model the LoRA was requested for */
|
|
69
|
+
model;
|
|
70
|
+
constructor(message, lora, model) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = "LoraLoadingError";
|
|
73
|
+
this.lora = lora;
|
|
74
|
+
this.model = model;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var ModelLoadingError = class extends SIEError {
|
|
78
|
+
/** The model that was requested */
|
|
79
|
+
model;
|
|
80
|
+
constructor(message, model) {
|
|
81
|
+
super(message);
|
|
82
|
+
this.name = "ModelLoadingError";
|
|
83
|
+
this.model = model;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// src/internal/constants.ts
|
|
88
|
+
var MSGPACK_CONTENT_TYPE = "application/msgpack";
|
|
89
|
+
var JSON_CONTENT_TYPE = "application/json";
|
|
90
|
+
var HTTP_ACCEPTED = 202;
|
|
91
|
+
var HTTP_CLIENT_ERROR_MIN = 400;
|
|
92
|
+
var HTTP_CLIENT_ERROR_MAX = 499;
|
|
93
|
+
var HTTP_SERVER_ERROR_MIN = 500;
|
|
94
|
+
var HTTP_SERVER_ERROR_MAX = 599;
|
|
95
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
96
|
+
var DEFAULT_PROVISION_TIMEOUT = 3e5;
|
|
97
|
+
var DEFAULT_RETRY_DELAY = 5e3;
|
|
98
|
+
var DEFAULT_LEASE_RENEWAL_INTERVAL = 6e4;
|
|
99
|
+
var LORA_LOADING_MAX_RETRIES = 10;
|
|
100
|
+
var LORA_LOADING_DEFAULT_DELAY = 1e3;
|
|
101
|
+
var LORA_LOADING_ERROR_CODE = "LORA_LOADING";
|
|
102
|
+
var MODEL_LOADING_DEFAULT_DELAY = 5e3;
|
|
103
|
+
var MODEL_LOADING_ERROR_CODE = "MODEL_LOADING";
|
|
104
|
+
var SDK_VERSION_HEADER = "X-SIE-SDK-Version";
|
|
105
|
+
var SERVER_VERSION_HEADER = "X-SIE-Server-Version";
|
|
106
|
+
|
|
107
|
+
// src/version.ts
|
|
108
|
+
var SDK_VERSION = "0.1.8";
|
|
109
|
+
var EXT_TYPE_NUMPY = 78;
|
|
110
|
+
function parseDtype(dtype) {
|
|
111
|
+
const typeChar = dtype.slice(-2, -1);
|
|
112
|
+
const sizeChar = dtype.slice(-1);
|
|
113
|
+
const size = Number.parseInt(sizeChar, 10);
|
|
114
|
+
switch (`${typeChar}${size}`) {
|
|
115
|
+
case "f4":
|
|
116
|
+
return { size: 4, construct: (buf) => new Float32Array(buf) };
|
|
117
|
+
case "f8":
|
|
118
|
+
return { size: 8, construct: (buf) => new Float64Array(buf) };
|
|
119
|
+
case "f2":
|
|
120
|
+
return {
|
|
121
|
+
size: 2,
|
|
122
|
+
construct: (buf) => {
|
|
123
|
+
const float16 = new Uint16Array(buf);
|
|
124
|
+
const float32 = new Float32Array(float16.length);
|
|
125
|
+
for (let i = 0; i < float16.length; i++) {
|
|
126
|
+
float32[i] = float16ToFloat32(float16[i] ?? 0);
|
|
127
|
+
}
|
|
128
|
+
return float32;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
case "i4":
|
|
132
|
+
return { size: 4, construct: (buf) => new Int32Array(buf) };
|
|
133
|
+
case "i2":
|
|
134
|
+
return { size: 2, construct: (buf) => new Int16Array(buf) };
|
|
135
|
+
case "i1":
|
|
136
|
+
return { size: 1, construct: (buf) => new Int8Array(buf) };
|
|
137
|
+
case "u1":
|
|
138
|
+
return { size: 1, construct: (buf) => new Uint8Array(buf) };
|
|
139
|
+
default:
|
|
140
|
+
throw new Error(`Unsupported numpy dtype: ${dtype}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function float16ToFloat32(h) {
|
|
144
|
+
const sign = h >>> 15 & 1;
|
|
145
|
+
const exp = h >>> 10 & 31;
|
|
146
|
+
const frac = h & 1023;
|
|
147
|
+
if (exp === 0) {
|
|
148
|
+
if (frac === 0) {
|
|
149
|
+
return sign ? -0 : 0;
|
|
150
|
+
}
|
|
151
|
+
const f = frac / 1024;
|
|
152
|
+
return (sign ? -1 : 1) * f * 2 ** -14;
|
|
153
|
+
}
|
|
154
|
+
if (exp === 31) {
|
|
155
|
+
return frac === 0 ? sign ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY : Number.NaN;
|
|
156
|
+
}
|
|
157
|
+
return (sign ? -1 : 1) * (1 + frac / 1024) * 2 ** (exp - 15);
|
|
158
|
+
}
|
|
159
|
+
function decodeNumpyArray(data) {
|
|
160
|
+
let dtypeEnd = 0;
|
|
161
|
+
while (dtypeEnd < data.length && data[dtypeEnd] !== 124) {
|
|
162
|
+
dtypeEnd++;
|
|
163
|
+
}
|
|
164
|
+
const dtypeBytes = data.slice(0, dtypeEnd);
|
|
165
|
+
const dtype = new TextDecoder().decode(dtypeBytes);
|
|
166
|
+
let shapeEnd = dtypeEnd + 1;
|
|
167
|
+
while (shapeEnd < data.length && data[shapeEnd] !== 124) {
|
|
168
|
+
shapeEnd++;
|
|
169
|
+
}
|
|
170
|
+
const shapeBytes = data.slice(dtypeEnd + 1, shapeEnd);
|
|
171
|
+
const shapeStr = new TextDecoder().decode(shapeBytes);
|
|
172
|
+
const shape = shapeStr.length > 0 ? shapeStr.split(",").map((s) => Number.parseInt(s, 10)) : [];
|
|
173
|
+
const arrayData = data.slice(shapeEnd + 1);
|
|
174
|
+
const { size, construct } = parseDtype(dtype);
|
|
175
|
+
const totalElements = shape.length > 0 ? shape.reduce((a, b) => a * b, 1) : arrayData.length / size;
|
|
176
|
+
const buffer = new ArrayBuffer(totalElements * size);
|
|
177
|
+
new Uint8Array(buffer).set(arrayData.slice(0, totalElements * size));
|
|
178
|
+
return construct(buffer);
|
|
179
|
+
}
|
|
180
|
+
function encodeNumpyArray(arr) {
|
|
181
|
+
let dtype;
|
|
182
|
+
if (arr instanceof Float32Array) {
|
|
183
|
+
dtype = "<f4";
|
|
184
|
+
} else if (arr instanceof Int32Array) {
|
|
185
|
+
dtype = "<i4";
|
|
186
|
+
} else {
|
|
187
|
+
throw new Error("Unsupported TypedArray type");
|
|
188
|
+
}
|
|
189
|
+
const dtypeBytes = new TextEncoder().encode(dtype);
|
|
190
|
+
const shapeBytes = new TextEncoder().encode(arr.length.toString());
|
|
191
|
+
const separator = new Uint8Array([124]);
|
|
192
|
+
const dataBytes = new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
193
|
+
const result = new Uint8Array(dtypeBytes.length + 1 + shapeBytes.length + 1 + dataBytes.length);
|
|
194
|
+
let offset = 0;
|
|
195
|
+
result.set(dtypeBytes, offset);
|
|
196
|
+
offset += dtypeBytes.length;
|
|
197
|
+
result.set(separator, offset);
|
|
198
|
+
offset += 1;
|
|
199
|
+
result.set(shapeBytes, offset);
|
|
200
|
+
offset += shapeBytes.length;
|
|
201
|
+
result.set(separator, offset);
|
|
202
|
+
offset += 1;
|
|
203
|
+
result.set(dataBytes, offset);
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
function createExtensionCodec() {
|
|
207
|
+
const codec = new ExtensionCodec();
|
|
208
|
+
codec.register({
|
|
209
|
+
type: EXT_TYPE_NUMPY,
|
|
210
|
+
encode: (value) => {
|
|
211
|
+
if (value instanceof Float32Array || value instanceof Int32Array) {
|
|
212
|
+
return encodeNumpyArray(value);
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
},
|
|
216
|
+
decode: (data) => {
|
|
217
|
+
return decodeNumpyArray(data);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
return codec;
|
|
221
|
+
}
|
|
222
|
+
var extensionCodec = createExtensionCodec();
|
|
223
|
+
function packMessage(data) {
|
|
224
|
+
return encode(data, { extensionCodec });
|
|
225
|
+
}
|
|
226
|
+
function isNumpyArrayMap(obj) {
|
|
227
|
+
if (typeof obj !== "object" || obj === null) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
const map = obj;
|
|
231
|
+
return map.nd === true && typeof map.type === "string" && Array.isArray(map.shape) && map.data instanceof Uint8Array;
|
|
232
|
+
}
|
|
233
|
+
function convertNumpyArrayMap(map) {
|
|
234
|
+
const dtype = map.type;
|
|
235
|
+
const arrayData = map.data;
|
|
236
|
+
const { size, construct } = parseDtype(dtype);
|
|
237
|
+
if (map.shape.length === 2 && map.shape[0] !== void 0 && map.shape[1] !== void 0) {
|
|
238
|
+
const numRows = map.shape[0];
|
|
239
|
+
const numCols = map.shape[1];
|
|
240
|
+
const result = [];
|
|
241
|
+
for (let row = 0; row < numRows; row++) {
|
|
242
|
+
const offset = row * numCols * size;
|
|
243
|
+
const buffer2 = new ArrayBuffer(numCols * size);
|
|
244
|
+
new Uint8Array(buffer2).set(arrayData.slice(offset, offset + numCols * size));
|
|
245
|
+
result.push(construct(buffer2));
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
const totalElements = map.shape.length > 0 ? map.shape.reduce((a, b) => a * b, 1) : arrayData.length / size;
|
|
250
|
+
const buffer = new ArrayBuffer(totalElements * size);
|
|
251
|
+
new Uint8Array(buffer).set(arrayData.slice(0, totalElements * size));
|
|
252
|
+
return construct(buffer);
|
|
253
|
+
}
|
|
254
|
+
function convertNumpyArrays(obj) {
|
|
255
|
+
if (obj === null || obj === void 0) {
|
|
256
|
+
return obj;
|
|
257
|
+
}
|
|
258
|
+
if (isNumpyArrayMap(obj)) {
|
|
259
|
+
return convertNumpyArrayMap(obj);
|
|
260
|
+
}
|
|
261
|
+
if (Array.isArray(obj)) {
|
|
262
|
+
return obj.map((item) => convertNumpyArrays(item));
|
|
263
|
+
}
|
|
264
|
+
if (ArrayBuffer.isView(obj)) {
|
|
265
|
+
return obj;
|
|
266
|
+
}
|
|
267
|
+
if (typeof obj === "object") {
|
|
268
|
+
const result = {};
|
|
269
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
270
|
+
result[key] = convertNumpyArrays(value);
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
return obj;
|
|
275
|
+
}
|
|
276
|
+
function unpackMessage(data) {
|
|
277
|
+
const decoded = decode(data, {
|
|
278
|
+
extensionCodec,
|
|
279
|
+
// Convert byte string keys (Uint8Array) to text strings
|
|
280
|
+
mapKeyConverter: (key) => {
|
|
281
|
+
if (typeof key === "string" || typeof key === "number") {
|
|
282
|
+
return key;
|
|
283
|
+
}
|
|
284
|
+
if (key instanceof Uint8Array) {
|
|
285
|
+
return new TextDecoder().decode(key);
|
|
286
|
+
}
|
|
287
|
+
return JSON.stringify(key);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
return convertNumpyArrays(decoded);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/internal/retry.ts
|
|
294
|
+
function getRetryAfter(header) {
|
|
295
|
+
if (!header) return void 0;
|
|
296
|
+
const seconds = Number.parseInt(header, 10);
|
|
297
|
+
if (!Number.isNaN(seconds) && seconds > 0) {
|
|
298
|
+
return seconds * 1e3;
|
|
299
|
+
}
|
|
300
|
+
const date = new Date(header);
|
|
301
|
+
if (!Number.isNaN(date.getTime())) {
|
|
302
|
+
const delay = date.getTime() - Date.now();
|
|
303
|
+
return delay > 0 ? delay : void 0;
|
|
304
|
+
}
|
|
305
|
+
return void 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/internal/parsing.ts
|
|
309
|
+
function getRetryAfter2(response) {
|
|
310
|
+
const header = response.headers.get("Retry-After");
|
|
311
|
+
return getRetryAfter(header);
|
|
312
|
+
}
|
|
313
|
+
async function getErrorCode(response) {
|
|
314
|
+
try {
|
|
315
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
316
|
+
let data;
|
|
317
|
+
if (contentType.includes(MSGPACK_CONTENT_TYPE)) {
|
|
318
|
+
const buffer = await response.arrayBuffer();
|
|
319
|
+
data = unpackMessage(new Uint8Array(buffer));
|
|
320
|
+
} else {
|
|
321
|
+
data = await response.json();
|
|
322
|
+
}
|
|
323
|
+
if (data.error && typeof data.error === "object") {
|
|
324
|
+
const error = data.error;
|
|
325
|
+
if (typeof error.code === "string") {
|
|
326
|
+
return error.code;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (data.detail && typeof data.detail === "object") {
|
|
330
|
+
const detail = data.detail;
|
|
331
|
+
if (typeof detail.code === "string") {
|
|
332
|
+
return detail.code;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (typeof data.code === "string") {
|
|
336
|
+
return data.code;
|
|
337
|
+
}
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
340
|
+
return void 0;
|
|
341
|
+
}
|
|
342
|
+
async function handleError(response, gpu) {
|
|
343
|
+
const { status } = response;
|
|
344
|
+
let errorBody = {};
|
|
345
|
+
try {
|
|
346
|
+
errorBody = await response.json();
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
const code = errorBody.code ?? "UNKNOWN";
|
|
350
|
+
const message = errorBody.detail ?? response.statusText;
|
|
351
|
+
if (status === HTTP_ACCEPTED) {
|
|
352
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
353
|
+
throw new ProvisioningError(
|
|
354
|
+
message,
|
|
355
|
+
gpu,
|
|
356
|
+
retryAfter ? Number.parseInt(retryAfter, 10) * 1e3 : void 0
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
if (status >= HTTP_CLIENT_ERROR_MIN && status <= HTTP_CLIENT_ERROR_MAX) {
|
|
360
|
+
throw new RequestError(message, code, status);
|
|
361
|
+
}
|
|
362
|
+
if (status >= HTTP_SERVER_ERROR_MIN && status <= HTTP_SERVER_ERROR_MAX) {
|
|
363
|
+
throw new ServerError(message, code, status);
|
|
364
|
+
}
|
|
365
|
+
throw new ServerError(message, code, status);
|
|
366
|
+
}
|
|
367
|
+
function parseEncodeResult(data) {
|
|
368
|
+
const result = {};
|
|
369
|
+
if (data.id !== void 0) {
|
|
370
|
+
result.id = data.id;
|
|
371
|
+
}
|
|
372
|
+
if (data.dense) {
|
|
373
|
+
result.dense = data.dense.values;
|
|
374
|
+
}
|
|
375
|
+
if (data.sparse) {
|
|
376
|
+
result.sparse = {
|
|
377
|
+
indices: data.sparse.indices,
|
|
378
|
+
values: data.sparse.values
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (data.multivector) {
|
|
382
|
+
result.multivector = data.multivector.values;
|
|
383
|
+
}
|
|
384
|
+
if (data.timing) {
|
|
385
|
+
result.timing = {
|
|
386
|
+
totalMs: data.timing.total_ms,
|
|
387
|
+
queueMs: data.timing.queue_ms,
|
|
388
|
+
tokenizationMs: data.timing.tokenization_ms,
|
|
389
|
+
inferenceMs: data.timing.inference_ms
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
function parseEncodeResults(data) {
|
|
395
|
+
return data.map(parseEncodeResult);
|
|
396
|
+
}
|
|
397
|
+
function parseScoreEntry(data) {
|
|
398
|
+
return {
|
|
399
|
+
itemId: data.item_id,
|
|
400
|
+
score: data.score,
|
|
401
|
+
rank: data.rank
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function parseScoreResult(data) {
|
|
405
|
+
const wire = data;
|
|
406
|
+
return {
|
|
407
|
+
model: wire.model,
|
|
408
|
+
queryId: wire.query_id,
|
|
409
|
+
scores: wire.scores.map(parseScoreEntry)
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function parseEntity(data) {
|
|
413
|
+
return {
|
|
414
|
+
text: data.text,
|
|
415
|
+
label: data.label,
|
|
416
|
+
score: data.score,
|
|
417
|
+
start: data.start,
|
|
418
|
+
end: data.end,
|
|
419
|
+
bbox: data.bbox
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function parseExtractResult(data) {
|
|
423
|
+
return {
|
|
424
|
+
id: data.id,
|
|
425
|
+
entities: data.entities.map(parseEntity)
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function parseExtractResults(data) {
|
|
429
|
+
return data.map(parseExtractResult);
|
|
430
|
+
}
|
|
431
|
+
function parseCapacityInfo(data, gpuFilter) {
|
|
432
|
+
const wire = data;
|
|
433
|
+
let workers = wire.workers ?? [];
|
|
434
|
+
if (gpuFilter) {
|
|
435
|
+
const gpuLower = gpuFilter.toLowerCase();
|
|
436
|
+
workers = workers.filter((w) => w.gpu.toLowerCase() === gpuLower);
|
|
437
|
+
}
|
|
438
|
+
const parsedWorkers = workers.map((w) => ({
|
|
439
|
+
url: w.url,
|
|
440
|
+
gpu: w.gpu,
|
|
441
|
+
healthy: w.healthy,
|
|
442
|
+
queueDepth: w.queue_depth,
|
|
443
|
+
loadedModels: w.loaded_models
|
|
444
|
+
}));
|
|
445
|
+
return {
|
|
446
|
+
status: wire.status,
|
|
447
|
+
workerCount: gpuFilter ? parsedWorkers.length : wire.cluster?.worker_count ?? 0,
|
|
448
|
+
gpuCount: wire.cluster?.gpu_count ?? 0,
|
|
449
|
+
modelsLoaded: wire.cluster?.models_loaded ?? 0,
|
|
450
|
+
configuredGpuTypes: wire.configured_gpu_types ?? [],
|
|
451
|
+
liveGpuTypes: wire.live_gpu_types ?? [],
|
|
452
|
+
workers: parsedWorkers
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/client.ts
|
|
457
|
+
function sleep(ms) {
|
|
458
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
459
|
+
}
|
|
460
|
+
function abortableSleep(ms, signal) {
|
|
461
|
+
if (signal.aborted) return Promise.resolve(true);
|
|
462
|
+
return new Promise((resolve) => {
|
|
463
|
+
const onAbort = () => {
|
|
464
|
+
clearTimeout(timeoutId);
|
|
465
|
+
resolve(true);
|
|
466
|
+
};
|
|
467
|
+
const timeoutId = setTimeout(() => {
|
|
468
|
+
signal.removeEventListener("abort", onAbort);
|
|
469
|
+
resolve(false);
|
|
470
|
+
}, ms);
|
|
471
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
var _LEASE_RENEWAL_MAX_RETRIES = 5;
|
|
475
|
+
var SIEClient = class {
|
|
476
|
+
baseUrl;
|
|
477
|
+
timeout;
|
|
478
|
+
gpu;
|
|
479
|
+
apiKey;
|
|
480
|
+
defaultWaitForCapacity;
|
|
481
|
+
provisionTimeout;
|
|
482
|
+
// Pool state: track created pools and their lease renewal scheduling
|
|
483
|
+
pools = /* @__PURE__ */ new Map();
|
|
484
|
+
// Version negotiation state
|
|
485
|
+
versionWarningLogged = false;
|
|
486
|
+
// Note: LoRA and model loading retry counters are now local to each method
|
|
487
|
+
// to avoid interference between concurrent requests
|
|
488
|
+
/**
|
|
489
|
+
* Create a new SIE client.
|
|
490
|
+
*
|
|
491
|
+
* @param baseUrl - Base URL of the SIE server (e.g., "http://localhost:8080")
|
|
492
|
+
* @param options - Client options
|
|
493
|
+
*/
|
|
494
|
+
constructor(baseUrl, options = {}) {
|
|
495
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
496
|
+
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
497
|
+
this.gpu = options.gpu;
|
|
498
|
+
this.apiKey = options.apiKey;
|
|
499
|
+
this.defaultWaitForCapacity = options.waitForCapacity ?? false;
|
|
500
|
+
this.provisionTimeout = options.provisionTimeout ?? DEFAULT_PROVISION_TIMEOUT;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Get the base URL of the SIE server.
|
|
504
|
+
*
|
|
505
|
+
* @returns The normalized base URL (without trailing slash)
|
|
506
|
+
*/
|
|
507
|
+
getBaseUrl() {
|
|
508
|
+
return this.baseUrl;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Encode one or more items.
|
|
512
|
+
*/
|
|
513
|
+
async encode(model, items, options = {}) {
|
|
514
|
+
const isSingleItem = !Array.isArray(items);
|
|
515
|
+
const itemsArray = isSingleItem ? [items] : items;
|
|
516
|
+
const body = {
|
|
517
|
+
items: itemsArray
|
|
518
|
+
};
|
|
519
|
+
const params = {};
|
|
520
|
+
if (options.outputTypes) {
|
|
521
|
+
params.output_types = options.outputTypes;
|
|
522
|
+
}
|
|
523
|
+
if (options.instruction !== void 0) {
|
|
524
|
+
params.instruction = options.instruction;
|
|
525
|
+
}
|
|
526
|
+
if (options.isQuery !== void 0) {
|
|
527
|
+
params.is_query = options.isQuery;
|
|
528
|
+
}
|
|
529
|
+
if (options.outputDtype !== void 0) {
|
|
530
|
+
params.output_dtype = options.outputDtype;
|
|
531
|
+
}
|
|
532
|
+
if (Object.keys(params).length > 0) {
|
|
533
|
+
body.params = params;
|
|
534
|
+
}
|
|
535
|
+
const waitForCapacity = options.waitForCapacity ?? this.defaultWaitForCapacity;
|
|
536
|
+
const { pool, gpu } = this.parseGpuParam(options.gpu);
|
|
537
|
+
const response = await this.requestWithRetry(
|
|
538
|
+
`/v1/encode/${encodeURIComponent(model)}`,
|
|
539
|
+
body,
|
|
540
|
+
pool,
|
|
541
|
+
gpu,
|
|
542
|
+
waitForCapacity,
|
|
543
|
+
model
|
|
544
|
+
);
|
|
545
|
+
const data = unpackMessage(new Uint8Array(await response.arrayBuffer()));
|
|
546
|
+
const results = parseEncodeResults(data.items);
|
|
547
|
+
if (isSingleItem) {
|
|
548
|
+
const first = results[0];
|
|
549
|
+
if (!first) {
|
|
550
|
+
throw new Error("No results returned from encode");
|
|
551
|
+
}
|
|
552
|
+
return first;
|
|
553
|
+
}
|
|
554
|
+
return results;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* List available models.
|
|
558
|
+
*
|
|
559
|
+
* @returns Array of model information
|
|
560
|
+
*/
|
|
561
|
+
async listModels() {
|
|
562
|
+
const response = await this.requestJson("/v1/models", "GET");
|
|
563
|
+
const data = await response.json();
|
|
564
|
+
return data.models.map((m) => ({
|
|
565
|
+
name: m.name,
|
|
566
|
+
loaded: m.loaded,
|
|
567
|
+
inputs: m.inputs,
|
|
568
|
+
outputs: m.outputs,
|
|
569
|
+
dims: m.dims,
|
|
570
|
+
maxSequenceLength: m.max_sequence_length
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Stream real-time status updates from a worker or router.
|
|
575
|
+
*
|
|
576
|
+
* @param mode - "cluster" uses router /ws/cluster-status, "worker" uses /ws/status.
|
|
577
|
+
* "auto" detects the endpoint via /health.
|
|
578
|
+
*/
|
|
579
|
+
async *watch(mode = "auto") {
|
|
580
|
+
const endpoint = mode === "auto" ? await this.detectEndpointType() : mode;
|
|
581
|
+
const path = endpoint === "cluster" ? "/ws/cluster-status" : "/ws/status";
|
|
582
|
+
const wsUrl = this.buildWsUrl(path);
|
|
583
|
+
const ws = this.createWebSocket(wsUrl);
|
|
584
|
+
const queue = [];
|
|
585
|
+
let resolveNext = null;
|
|
586
|
+
let rejectNext = null;
|
|
587
|
+
let closed = false;
|
|
588
|
+
const notify = () => {
|
|
589
|
+
if (resolveNext) {
|
|
590
|
+
resolveNext();
|
|
591
|
+
resolveNext = null;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
const fail = (error) => {
|
|
595
|
+
if (rejectNext) {
|
|
596
|
+
rejectNext(error);
|
|
597
|
+
rejectNext = null;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
const waitForMessage = () => new Promise((resolve, reject) => {
|
|
601
|
+
resolveNext = resolve;
|
|
602
|
+
rejectNext = reject;
|
|
603
|
+
});
|
|
604
|
+
const parseMessage = (data) => {
|
|
605
|
+
if (typeof data === "string") {
|
|
606
|
+
return JSON.parse(data);
|
|
607
|
+
}
|
|
608
|
+
if (data instanceof ArrayBuffer) {
|
|
609
|
+
return JSON.parse(new TextDecoder().decode(new Uint8Array(data)));
|
|
610
|
+
}
|
|
611
|
+
if (data instanceof Uint8Array) {
|
|
612
|
+
return JSON.parse(new TextDecoder().decode(data));
|
|
613
|
+
}
|
|
614
|
+
throw new Error("Unsupported WebSocket message type");
|
|
615
|
+
};
|
|
616
|
+
const openPromise = new Promise((resolve, reject) => {
|
|
617
|
+
ws.addEventListener("open", () => resolve());
|
|
618
|
+
ws.addEventListener("error", (event) => reject(event));
|
|
619
|
+
});
|
|
620
|
+
ws.addEventListener("message", (event) => {
|
|
621
|
+
try {
|
|
622
|
+
queue.push(parseMessage(event.data));
|
|
623
|
+
notify();
|
|
624
|
+
} catch (error) {
|
|
625
|
+
fail(error);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
ws.addEventListener("close", () => {
|
|
629
|
+
closed = true;
|
|
630
|
+
notify();
|
|
631
|
+
});
|
|
632
|
+
try {
|
|
633
|
+
await openPromise;
|
|
634
|
+
while (!closed || queue.length > 0) {
|
|
635
|
+
if (queue.length === 0) {
|
|
636
|
+
await waitForMessage();
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
const next = queue.shift();
|
|
640
|
+
if (next) {
|
|
641
|
+
yield next;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
} finally {
|
|
645
|
+
ws.close();
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Score items against a query using a reranker model.
|
|
650
|
+
*
|
|
651
|
+
* @param model - Model name (e.g., "bge-reranker-v2")
|
|
652
|
+
* @param query - Query item
|
|
653
|
+
* @param items - Items to score against the query
|
|
654
|
+
* @param options - Score options
|
|
655
|
+
* @returns Score result with sorted scores
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```typescript
|
|
659
|
+
* const result = await client.score(
|
|
660
|
+
* "bge-reranker-v2",
|
|
661
|
+
* { text: "What is machine learning?" },
|
|
662
|
+
* [
|
|
663
|
+
* { id: "doc-1", text: "Machine learning is..." },
|
|
664
|
+
* { id: "doc-2", text: "Python is..." },
|
|
665
|
+
* ],
|
|
666
|
+
* );
|
|
667
|
+
*
|
|
668
|
+
* // Scores are sorted by relevance (descending)
|
|
669
|
+
* console.log(result.scores[0].itemId); // most relevant
|
|
670
|
+
* ```
|
|
671
|
+
*/
|
|
672
|
+
async score(model, query, items, options = {}) {
|
|
673
|
+
const body = {
|
|
674
|
+
query,
|
|
675
|
+
items
|
|
676
|
+
};
|
|
677
|
+
if (options.topK !== void 0) {
|
|
678
|
+
body.top_k = options.topK;
|
|
679
|
+
}
|
|
680
|
+
const waitForCapacity = options.waitForCapacity ?? this.defaultWaitForCapacity;
|
|
681
|
+
const { pool, gpu } = this.parseGpuParam(options.gpu);
|
|
682
|
+
const response = await this.requestWithRetry(
|
|
683
|
+
`/v1/score/${encodeURIComponent(model)}`,
|
|
684
|
+
body,
|
|
685
|
+
pool,
|
|
686
|
+
gpu,
|
|
687
|
+
waitForCapacity,
|
|
688
|
+
model
|
|
689
|
+
);
|
|
690
|
+
const data = unpackMessage(new Uint8Array(await response.arrayBuffer()));
|
|
691
|
+
return parseScoreResult(data);
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Extract entities from one or more items.
|
|
695
|
+
*
|
|
696
|
+
* @example
|
|
697
|
+
* ```typescript
|
|
698
|
+
* const result = await client.extract(
|
|
699
|
+
* "gliner-multi-v2.1",
|
|
700
|
+
* { text: "Apple was founded by Steve Jobs." },
|
|
701
|
+
* { labels: ["person", "organization"] },
|
|
702
|
+
* );
|
|
703
|
+
*
|
|
704
|
+
* for (const entity of result.entities) {
|
|
705
|
+
* console.log(`${entity.text} (${entity.label})`);
|
|
706
|
+
* }
|
|
707
|
+
* // Output:
|
|
708
|
+
* // Apple (organization)
|
|
709
|
+
* // Steve Jobs (person)
|
|
710
|
+
* ```
|
|
711
|
+
*/
|
|
712
|
+
async extract(model, items, options) {
|
|
713
|
+
const isSingleItem = !Array.isArray(items);
|
|
714
|
+
const itemsArray = isSingleItem ? [items] : items;
|
|
715
|
+
const body = {
|
|
716
|
+
items: itemsArray
|
|
717
|
+
};
|
|
718
|
+
const params = {
|
|
719
|
+
labels: options.labels
|
|
720
|
+
};
|
|
721
|
+
if (options.threshold !== void 0) {
|
|
722
|
+
params.threshold = options.threshold;
|
|
723
|
+
}
|
|
724
|
+
body.params = params;
|
|
725
|
+
const waitForCapacity = options.waitForCapacity ?? this.defaultWaitForCapacity;
|
|
726
|
+
const { pool, gpu } = this.parseGpuParam(options.gpu);
|
|
727
|
+
const response = await this.requestWithRetry(
|
|
728
|
+
`/v1/extract/${encodeURIComponent(model)}`,
|
|
729
|
+
body,
|
|
730
|
+
pool,
|
|
731
|
+
gpu,
|
|
732
|
+
waitForCapacity,
|
|
733
|
+
model
|
|
734
|
+
);
|
|
735
|
+
const data = unpackMessage(new Uint8Array(await response.arrayBuffer()));
|
|
736
|
+
const results = parseExtractResults(data.items);
|
|
737
|
+
if (isSingleItem) {
|
|
738
|
+
const first = results[0];
|
|
739
|
+
if (!first) {
|
|
740
|
+
throw new Error("No results returned from extract");
|
|
741
|
+
}
|
|
742
|
+
return first;
|
|
743
|
+
}
|
|
744
|
+
return results;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Close the client and cleanup resources.
|
|
748
|
+
*
|
|
749
|
+
* Stops pool lease renewal timers. Note that pools are not deleted
|
|
750
|
+
* automatically - they are garbage collected by the router after inactivity.
|
|
751
|
+
* This allows pool reuse if the client reconnects.
|
|
752
|
+
*/
|
|
753
|
+
async close() {
|
|
754
|
+
for (const [, poolState] of this.pools) {
|
|
755
|
+
if (poolState.timeoutId !== null) {
|
|
756
|
+
clearTimeout(poolState.timeoutId);
|
|
757
|
+
}
|
|
758
|
+
poolState.abortController.abort();
|
|
759
|
+
}
|
|
760
|
+
this.pools.clear();
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Create a resource pool for isolated capacity.
|
|
764
|
+
*
|
|
765
|
+
* Pools provide dedicated worker capacity, isolated from other clients.
|
|
766
|
+
* Workers are assigned to pools and only serve requests from that pool.
|
|
767
|
+
*
|
|
768
|
+
* @param name - Pool name (used in GPU param as "poolName/machineProfile")
|
|
769
|
+
* @param gpus - Machine profile requirements, e.g., { "l4": 2, "l4-spot": 1 }
|
|
770
|
+
*
|
|
771
|
+
* @example
|
|
772
|
+
* ```typescript
|
|
773
|
+
* // Create a pool with 2 L4 GPUs
|
|
774
|
+
* await client.createPool("eval-bench", { l4: 2 });
|
|
775
|
+
*
|
|
776
|
+
* // Use the pool for requests
|
|
777
|
+
* await client.encode("bge-m3", { text: "Hello" }, { gpu: "eval-bench/l4" });
|
|
778
|
+
*
|
|
779
|
+
* // Clean up when done
|
|
780
|
+
* await client.deletePool("eval-bench");
|
|
781
|
+
* ```
|
|
782
|
+
*/
|
|
783
|
+
async createPool(name, gpus) {
|
|
784
|
+
if (this.pools.has(name)) {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const requestBody = { name, gpus };
|
|
788
|
+
const url = `${this.baseUrl}/v1/pools`;
|
|
789
|
+
const headers = {
|
|
790
|
+
"Content-Type": JSON_CONTENT_TYPE,
|
|
791
|
+
Accept: JSON_CONTENT_TYPE,
|
|
792
|
+
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
793
|
+
};
|
|
794
|
+
if (this.apiKey) {
|
|
795
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
796
|
+
}
|
|
797
|
+
const controller = new AbortController();
|
|
798
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
799
|
+
try {
|
|
800
|
+
const response = await fetch(url, {
|
|
801
|
+
method: "POST",
|
|
802
|
+
headers,
|
|
803
|
+
body: JSON.stringify(requestBody),
|
|
804
|
+
signal: controller.signal
|
|
805
|
+
});
|
|
806
|
+
if (response.status >= HTTP_CLIENT_ERROR_MIN) {
|
|
807
|
+
let errorMsg = response.statusText;
|
|
808
|
+
try {
|
|
809
|
+
const data = await response.json();
|
|
810
|
+
errorMsg = data.detail?.message ?? JSON.stringify(data);
|
|
811
|
+
} catch {
|
|
812
|
+
}
|
|
813
|
+
throw new PoolError(`Failed to create pool '${name}': ${errorMsg}`, name);
|
|
814
|
+
}
|
|
815
|
+
const abortController = new AbortController();
|
|
816
|
+
const poolState = {
|
|
817
|
+
timeoutId: null,
|
|
818
|
+
abortController,
|
|
819
|
+
isRenewing: false
|
|
820
|
+
};
|
|
821
|
+
const renewLoop = async () => {
|
|
822
|
+
if (abortController.signal.aborted) return;
|
|
823
|
+
if (poolState.isRenewing) return;
|
|
824
|
+
poolState.isRenewing = true;
|
|
825
|
+
try {
|
|
826
|
+
const renewUrl = `${this.baseUrl}/v1/pools/${encodeURIComponent(name)}/renew`;
|
|
827
|
+
const renewHeaders = {
|
|
828
|
+
Accept: JSON_CONTENT_TYPE
|
|
829
|
+
};
|
|
830
|
+
if (this.apiKey) {
|
|
831
|
+
renewHeaders.Authorization = `Bearer ${this.apiKey}`;
|
|
832
|
+
}
|
|
833
|
+
for (let attempt = 0; attempt < _LEASE_RENEWAL_MAX_RETRIES; attempt++) {
|
|
834
|
+
if (abortController.signal.aborted) return;
|
|
835
|
+
const perAttempt = new AbortController();
|
|
836
|
+
const onPoolAbort = () => perAttempt.abort();
|
|
837
|
+
abortController.signal.addEventListener("abort", onPoolAbort, { once: true });
|
|
838
|
+
const attemptTimeout = setTimeout(() => perAttempt.abort(), this.timeout);
|
|
839
|
+
try {
|
|
840
|
+
const resp = await fetch(renewUrl, {
|
|
841
|
+
method: "POST",
|
|
842
|
+
headers: renewHeaders,
|
|
843
|
+
signal: perAttempt.signal
|
|
844
|
+
});
|
|
845
|
+
if (resp.ok) break;
|
|
846
|
+
} catch (error) {
|
|
847
|
+
if (abortController.signal.aborted) return;
|
|
848
|
+
} finally {
|
|
849
|
+
clearTimeout(attemptTimeout);
|
|
850
|
+
abortController.signal.removeEventListener("abort", onPoolAbort);
|
|
851
|
+
}
|
|
852
|
+
if (attempt < _LEASE_RENEWAL_MAX_RETRIES - 1) {
|
|
853
|
+
const aborted = await abortableSleep(
|
|
854
|
+
Math.min(2 ** attempt * 1e3, 1e4),
|
|
855
|
+
abortController.signal
|
|
856
|
+
);
|
|
857
|
+
if (aborted) return;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
} finally {
|
|
861
|
+
poolState.isRenewing = false;
|
|
862
|
+
}
|
|
863
|
+
if (!abortController.signal.aborted) {
|
|
864
|
+
poolState.timeoutId = setTimeout(renewLoop, DEFAULT_LEASE_RENEWAL_INTERVAL);
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
poolState.timeoutId = setTimeout(renewLoop, DEFAULT_LEASE_RENEWAL_INTERVAL);
|
|
868
|
+
this.pools.set(name, poolState);
|
|
869
|
+
} catch (error) {
|
|
870
|
+
if (error instanceof PoolError) {
|
|
871
|
+
throw error;
|
|
872
|
+
}
|
|
873
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
874
|
+
throw new PoolError(`Timeout creating pool '${name}'`, name);
|
|
875
|
+
}
|
|
876
|
+
throw new PoolError(
|
|
877
|
+
`Failed to create pool '${name}': ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
878
|
+
name
|
|
879
|
+
);
|
|
880
|
+
} finally {
|
|
881
|
+
clearTimeout(timeoutId);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Get information about a pool.
|
|
886
|
+
*
|
|
887
|
+
* @param name - Pool name to query
|
|
888
|
+
* @returns PoolInfo if pool exists, null otherwise
|
|
889
|
+
*
|
|
890
|
+
* @example
|
|
891
|
+
* ```typescript
|
|
892
|
+
* await client.createPool("eval-bench", { l4: 2 });
|
|
893
|
+
* const pool = await client.getPool("eval-bench");
|
|
894
|
+
* console.log(`Pool state: ${pool?.status.state}`);
|
|
895
|
+
* console.log(`Workers: ${pool?.status.assignedWorkers.length}`);
|
|
896
|
+
* ```
|
|
897
|
+
*/
|
|
898
|
+
async getPool(name) {
|
|
899
|
+
try {
|
|
900
|
+
const response = await this.requestJson(`/v1/pools/${encodeURIComponent(name)}`);
|
|
901
|
+
const data = await response.json();
|
|
902
|
+
return {
|
|
903
|
+
name: data.name,
|
|
904
|
+
spec: data.spec,
|
|
905
|
+
status: {
|
|
906
|
+
state: data.status.state,
|
|
907
|
+
assignedWorkers: data.status.assigned_workers,
|
|
908
|
+
createdAt: data.status.created_at,
|
|
909
|
+
lastRenewed: data.status.last_renewed
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
} catch {
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Delete a pool.
|
|
918
|
+
*
|
|
919
|
+
* @param name - Pool name to delete
|
|
920
|
+
* @returns true if pool was deleted, false if pool didn't exist
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* ```typescript
|
|
924
|
+
* // Clean up pool when done
|
|
925
|
+
* const deleted = await client.deletePool("eval-bench");
|
|
926
|
+
* if (deleted) {
|
|
927
|
+
* console.log("Pool deleted successfully");
|
|
928
|
+
* }
|
|
929
|
+
* ```
|
|
930
|
+
*/
|
|
931
|
+
async deletePool(name) {
|
|
932
|
+
const poolState = this.pools.get(name);
|
|
933
|
+
if (poolState) {
|
|
934
|
+
if (poolState.timeoutId !== null) {
|
|
935
|
+
clearTimeout(poolState.timeoutId);
|
|
936
|
+
}
|
|
937
|
+
poolState.abortController.abort();
|
|
938
|
+
this.pools.delete(name);
|
|
939
|
+
}
|
|
940
|
+
try {
|
|
941
|
+
const url = `${this.baseUrl}/v1/pools/${encodeURIComponent(name)}`;
|
|
942
|
+
const headers = {
|
|
943
|
+
Accept: JSON_CONTENT_TYPE
|
|
944
|
+
};
|
|
945
|
+
if (this.apiKey) {
|
|
946
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
947
|
+
}
|
|
948
|
+
const controller = new AbortController();
|
|
949
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
950
|
+
try {
|
|
951
|
+
const response = await fetch(url, {
|
|
952
|
+
method: "DELETE",
|
|
953
|
+
headers,
|
|
954
|
+
signal: controller.signal
|
|
955
|
+
});
|
|
956
|
+
return response.ok || response.status === 404;
|
|
957
|
+
} finally {
|
|
958
|
+
clearTimeout(timeoutId);
|
|
959
|
+
}
|
|
960
|
+
} catch {
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
checkServerVersion(response) {
|
|
965
|
+
if (this.versionWarningLogged) return;
|
|
966
|
+
const serverVersion = response.headers.get(SERVER_VERSION_HEADER);
|
|
967
|
+
if (!serverVersion) return;
|
|
968
|
+
try {
|
|
969
|
+
const sdkParts = SDK_VERSION.split(".").map(Number);
|
|
970
|
+
const serverParts = serverVersion.split(".").map(Number);
|
|
971
|
+
if (sdkParts.length < 2 || serverParts.length < 2) return;
|
|
972
|
+
const sdkMajor = sdkParts[0];
|
|
973
|
+
const sdkMinor = sdkParts[1];
|
|
974
|
+
const serverMajor = serverParts[0];
|
|
975
|
+
const serverMinor = serverParts[1];
|
|
976
|
+
if (sdkMajor === void 0 || sdkMinor === void 0 || serverMajor === void 0 || serverMinor === void 0) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (sdkMajor !== serverMajor || Math.abs(sdkMinor - serverMinor) > 1) {
|
|
980
|
+
console.warn(
|
|
981
|
+
`[SIE SDK] Version skew detected: SDK ${SDK_VERSION}, server ${serverVersion}. Consider upgrading.`
|
|
982
|
+
);
|
|
983
|
+
this.versionWarningLogged = true;
|
|
984
|
+
}
|
|
985
|
+
} catch {
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Parse GPU parameter into pool and GPU components.
|
|
990
|
+
*
|
|
991
|
+
* Supports "pool/gpu" format for pool routing.
|
|
992
|
+
*/
|
|
993
|
+
parseGpuParam(gpu) {
|
|
994
|
+
const effectiveGpu = gpu ?? this.gpu;
|
|
995
|
+
if (!effectiveGpu) {
|
|
996
|
+
return {};
|
|
997
|
+
}
|
|
998
|
+
const parts = effectiveGpu.split("/");
|
|
999
|
+
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
1000
|
+
return { pool: parts[0], gpu: parts[1] };
|
|
1001
|
+
}
|
|
1002
|
+
return { gpu: effectiveGpu };
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Get current cluster capacity information.
|
|
1006
|
+
*
|
|
1007
|
+
* Queries the router's /health endpoint for cluster state. Useful for
|
|
1008
|
+
* checking if specific GPU types are available before sending requests.
|
|
1009
|
+
*
|
|
1010
|
+
* @param gpu - Optional filter to check specific GPU type availability
|
|
1011
|
+
* @returns CapacityInfo with worker count, GPU types, and worker details
|
|
1012
|
+
*
|
|
1013
|
+
* @example
|
|
1014
|
+
* ```typescript
|
|
1015
|
+
* // Check cluster state
|
|
1016
|
+
* const capacity = await client.getCapacity();
|
|
1017
|
+
* console.log(`Workers: ${capacity.workerCount}, GPUs: ${capacity.liveGpuTypes}`);
|
|
1018
|
+
*
|
|
1019
|
+
* // Check if L4 GPUs are available
|
|
1020
|
+
* const l4Capacity = await client.getCapacity("l4");
|
|
1021
|
+
* if (l4Capacity.workerCount > 0) {
|
|
1022
|
+
* console.log("L4 workers available");
|
|
1023
|
+
* }
|
|
1024
|
+
* ```
|
|
1025
|
+
*/
|
|
1026
|
+
async getCapacity(gpu) {
|
|
1027
|
+
const response = await this.requestJson("/health");
|
|
1028
|
+
const data = await response.json();
|
|
1029
|
+
if (data.type !== "router") {
|
|
1030
|
+
throw new RequestError(
|
|
1031
|
+
"getCapacity() requires a router endpoint. This appears to be a worker.",
|
|
1032
|
+
"not_router",
|
|
1033
|
+
400
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
return parseCapacityInfo(data, gpu);
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Wait for GPU capacity to become available.
|
|
1040
|
+
*
|
|
1041
|
+
* Polls the router until workers with the specified GPU type are online.
|
|
1042
|
+
* This is useful for pre-warming the cluster before running benchmarks.
|
|
1043
|
+
*
|
|
1044
|
+
* @param gpu - GPU type to wait for (e.g., "l4", "a100-80gb")
|
|
1045
|
+
* @param options - Wait options
|
|
1046
|
+
* @returns CapacityInfo once capacity is available
|
|
1047
|
+
*
|
|
1048
|
+
* @example
|
|
1049
|
+
* ```typescript
|
|
1050
|
+
* // Wait for L4 capacity before running benchmarks
|
|
1051
|
+
* const capacity = await client.waitForCapacity("l4", { timeout: 300000 });
|
|
1052
|
+
* console.log(`Ready with ${capacity.workerCount} L4 workers`);
|
|
1053
|
+
*
|
|
1054
|
+
* // Wait and pre-load a model
|
|
1055
|
+
* const capacityWithModel = await client.waitForCapacity("l4", { model: "bge-m3" });
|
|
1056
|
+
* ```
|
|
1057
|
+
*/
|
|
1058
|
+
async waitForCapacity(gpu, options = {}) {
|
|
1059
|
+
const timeout = options.timeout ?? this.provisionTimeout;
|
|
1060
|
+
const pollInterval = options.pollInterval ?? 5e3;
|
|
1061
|
+
const startTime = Date.now();
|
|
1062
|
+
if (options.model) {
|
|
1063
|
+
await this.encode(options.model, { text: "warmup" }, { gpu, waitForCapacity: true });
|
|
1064
|
+
return this.getCapacity(gpu);
|
|
1065
|
+
}
|
|
1066
|
+
while (true) {
|
|
1067
|
+
try {
|
|
1068
|
+
const capacity = await this.getCapacity(gpu);
|
|
1069
|
+
if (capacity.workerCount > 0) {
|
|
1070
|
+
return capacity;
|
|
1071
|
+
}
|
|
1072
|
+
} catch {
|
|
1073
|
+
}
|
|
1074
|
+
const elapsed = Date.now() - startTime;
|
|
1075
|
+
if (elapsed >= timeout) {
|
|
1076
|
+
throw new ProvisioningError(
|
|
1077
|
+
`Timeout after ${elapsed}ms waiting for GPU '${gpu}' capacity`,
|
|
1078
|
+
gpu
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
const remaining = timeout - elapsed;
|
|
1082
|
+
const delay = Math.min(pollInterval, remaining);
|
|
1083
|
+
await sleep(delay);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Make a msgpack HTTP request with retry logic for 202 and LoRA loading.
|
|
1088
|
+
*/
|
|
1089
|
+
async requestWithRetry(path, body, pool, gpu, waitForCapacity, model) {
|
|
1090
|
+
const startTime = Date.now();
|
|
1091
|
+
let loraRetries = 0;
|
|
1092
|
+
while (true) {
|
|
1093
|
+
const response = await this.request(path, body, pool, gpu);
|
|
1094
|
+
if (response.status === HTTP_ACCEPTED) {
|
|
1095
|
+
const retryAfter = getRetryAfter2(response);
|
|
1096
|
+
if (!waitForCapacity) {
|
|
1097
|
+
throw new ProvisioningError(
|
|
1098
|
+
`No capacity available for GPU '${gpu}'. Server is provisioning.`,
|
|
1099
|
+
gpu,
|
|
1100
|
+
retryAfter
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
const elapsed = Date.now() - startTime;
|
|
1104
|
+
if (elapsed >= this.provisionTimeout) {
|
|
1105
|
+
throw new ProvisioningError(
|
|
1106
|
+
`Provisioning timeout after ${elapsed}ms waiting for GPU '${gpu}'`,
|
|
1107
|
+
gpu,
|
|
1108
|
+
retryAfter
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
const delay = retryAfter ?? DEFAULT_RETRY_DELAY;
|
|
1112
|
+
const remaining = this.provisionTimeout - elapsed;
|
|
1113
|
+
const actualDelay = Math.min(delay, remaining);
|
|
1114
|
+
await sleep(actualDelay);
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
if (response.status === 503) {
|
|
1118
|
+
const clonedResponse = response.clone();
|
|
1119
|
+
const errorCode = await getErrorCode(clonedResponse);
|
|
1120
|
+
if (errorCode === LORA_LOADING_ERROR_CODE) {
|
|
1121
|
+
loraRetries += 1;
|
|
1122
|
+
if (loraRetries > LORA_LOADING_MAX_RETRIES) {
|
|
1123
|
+
throw new LoraLoadingError(
|
|
1124
|
+
`LoRA loading timeout after ${loraRetries} retries`,
|
|
1125
|
+
void 0,
|
|
1126
|
+
// We don't have lora name at this level
|
|
1127
|
+
model
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
const retryAfter = getRetryAfter2(response);
|
|
1131
|
+
const delay = retryAfter ?? LORA_LOADING_DEFAULT_DELAY;
|
|
1132
|
+
await sleep(delay);
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
if (errorCode === MODEL_LOADING_ERROR_CODE) {
|
|
1136
|
+
const elapsed = Date.now() - startTime;
|
|
1137
|
+
if (elapsed >= this.provisionTimeout) {
|
|
1138
|
+
throw new ModelLoadingError(
|
|
1139
|
+
`Model loading timeout after ${(elapsed / 1e3).toFixed(1)}s for '${model}'`,
|
|
1140
|
+
model
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
const retryAfter = getRetryAfter2(response);
|
|
1144
|
+
const delay = retryAfter ?? MODEL_LOADING_DEFAULT_DELAY;
|
|
1145
|
+
const remaining = this.provisionTimeout - elapsed;
|
|
1146
|
+
const actualDelay = Math.min(delay, remaining);
|
|
1147
|
+
await sleep(actualDelay);
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
if (!response.ok) {
|
|
1152
|
+
await handleError(response, gpu);
|
|
1153
|
+
}
|
|
1154
|
+
this.checkServerVersion(response);
|
|
1155
|
+
return response;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Make a single msgpack HTTP request to the SIE server (no retry logic).
|
|
1160
|
+
*/
|
|
1161
|
+
async request(path, body, pool, gpu) {
|
|
1162
|
+
const url = `${this.baseUrl}${path}`;
|
|
1163
|
+
const headers = {
|
|
1164
|
+
Accept: MSGPACK_CONTENT_TYPE,
|
|
1165
|
+
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
1166
|
+
};
|
|
1167
|
+
if (body !== void 0) {
|
|
1168
|
+
headers["Content-Type"] = MSGPACK_CONTENT_TYPE;
|
|
1169
|
+
}
|
|
1170
|
+
if (pool) {
|
|
1171
|
+
headers["X-SIE-Pool"] = pool;
|
|
1172
|
+
}
|
|
1173
|
+
if (gpu) {
|
|
1174
|
+
headers["X-SIE-MACHINE-PROFILE"] = gpu;
|
|
1175
|
+
}
|
|
1176
|
+
if (this.apiKey) {
|
|
1177
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
1178
|
+
}
|
|
1179
|
+
const controller = new AbortController();
|
|
1180
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1181
|
+
try {
|
|
1182
|
+
const response = await fetch(url, {
|
|
1183
|
+
method: "POST",
|
|
1184
|
+
headers,
|
|
1185
|
+
body: body !== void 0 ? packMessage(body) : void 0,
|
|
1186
|
+
signal: controller.signal
|
|
1187
|
+
});
|
|
1188
|
+
return response;
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1191
|
+
throw new SIEConnectionError(`Request timeout after ${this.timeout}ms`);
|
|
1192
|
+
}
|
|
1193
|
+
if (error instanceof TypeError) {
|
|
1194
|
+
throw new SIEConnectionError(`Connection failed: ${error.message}`);
|
|
1195
|
+
}
|
|
1196
|
+
throw error;
|
|
1197
|
+
} finally {
|
|
1198
|
+
clearTimeout(timeoutId);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Make a JSON HTTP request to the SIE server.
|
|
1203
|
+
* Used for endpoints that return JSON (e.g., /v1/models, /health).
|
|
1204
|
+
*/
|
|
1205
|
+
async requestJson(path, method = "GET") {
|
|
1206
|
+
const url = `${this.baseUrl}${path}`;
|
|
1207
|
+
const headers = {
|
|
1208
|
+
Accept: "application/json",
|
|
1209
|
+
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
1210
|
+
};
|
|
1211
|
+
if (this.apiKey) {
|
|
1212
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
1213
|
+
}
|
|
1214
|
+
const controller = new AbortController();
|
|
1215
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1216
|
+
try {
|
|
1217
|
+
const response = await fetch(url, {
|
|
1218
|
+
method,
|
|
1219
|
+
headers,
|
|
1220
|
+
signal: controller.signal
|
|
1221
|
+
});
|
|
1222
|
+
if (!response.ok) {
|
|
1223
|
+
await handleError(response);
|
|
1224
|
+
}
|
|
1225
|
+
return response;
|
|
1226
|
+
} catch (error) {
|
|
1227
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1228
|
+
throw new SIEConnectionError(`Request timeout after ${this.timeout}ms`);
|
|
1229
|
+
}
|
|
1230
|
+
if (error instanceof TypeError) {
|
|
1231
|
+
throw new SIEConnectionError(`Connection failed: ${error.message}`);
|
|
1232
|
+
}
|
|
1233
|
+
throw error;
|
|
1234
|
+
} finally {
|
|
1235
|
+
clearTimeout(timeoutId);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
buildWsUrl(path) {
|
|
1239
|
+
const url = new URL(this.baseUrl);
|
|
1240
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
1241
|
+
url.pathname = `${url.pathname.replace(/\/$/, "")}${path}`;
|
|
1242
|
+
url.search = "";
|
|
1243
|
+
return url.toString();
|
|
1244
|
+
}
|
|
1245
|
+
createWebSocket(url) {
|
|
1246
|
+
const headers = this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : void 0;
|
|
1247
|
+
try {
|
|
1248
|
+
if (headers) {
|
|
1249
|
+
return new WebSocket(url, [], { headers });
|
|
1250
|
+
}
|
|
1251
|
+
return new WebSocket(url);
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
if (headers) {
|
|
1254
|
+
throw new SIEConnectionError(
|
|
1255
|
+
"WebSocket auth headers are not supported in this environment"
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
throw error;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
async detectEndpointType() {
|
|
1262
|
+
const url = `${this.baseUrl}/health`;
|
|
1263
|
+
const headers = { Accept: "application/json" };
|
|
1264
|
+
if (this.apiKey) {
|
|
1265
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
1266
|
+
}
|
|
1267
|
+
const controller = new AbortController();
|
|
1268
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1269
|
+
try {
|
|
1270
|
+
const response = await fetch(url, {
|
|
1271
|
+
method: "GET",
|
|
1272
|
+
headers,
|
|
1273
|
+
signal: controller.signal
|
|
1274
|
+
});
|
|
1275
|
+
if (!response.ok) {
|
|
1276
|
+
return "worker";
|
|
1277
|
+
}
|
|
1278
|
+
const data = await response.json();
|
|
1279
|
+
return data.type === "router" ? "cluster" : "worker";
|
|
1280
|
+
} catch {
|
|
1281
|
+
return "worker";
|
|
1282
|
+
} finally {
|
|
1283
|
+
clearTimeout(timeoutId);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// src/types.ts
|
|
1289
|
+
function toNumberArray(arr) {
|
|
1290
|
+
return Array.from(arr);
|
|
1291
|
+
}
|
|
1292
|
+
function toFloat32Array(arr) {
|
|
1293
|
+
return new Float32Array(arr);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// src/scoring.ts
|
|
1297
|
+
function maxsim(query, document) {
|
|
1298
|
+
if (query.length === 0 || document.length === 0) {
|
|
1299
|
+
return 0;
|
|
1300
|
+
}
|
|
1301
|
+
let totalScore = 0;
|
|
1302
|
+
for (const queryToken of query) {
|
|
1303
|
+
let maxSim = Number.NEGATIVE_INFINITY;
|
|
1304
|
+
for (const docToken of document) {
|
|
1305
|
+
let sim = 0;
|
|
1306
|
+
for (let i = 0; i < queryToken.length; i++) {
|
|
1307
|
+
sim += (queryToken[i] ?? 0) * (docToken[i] ?? 0);
|
|
1308
|
+
}
|
|
1309
|
+
if (sim > maxSim) {
|
|
1310
|
+
maxSim = sim;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
totalScore += maxSim;
|
|
1314
|
+
}
|
|
1315
|
+
return totalScore;
|
|
1316
|
+
}
|
|
1317
|
+
function maxsimDocuments(query, documents) {
|
|
1318
|
+
return documents.map((doc) => maxsim(query, doc));
|
|
1319
|
+
}
|
|
1320
|
+
function maxsimBatch(queries, documents) {
|
|
1321
|
+
const scores = new Float32Array(queries.length * documents.length);
|
|
1322
|
+
let idx = 0;
|
|
1323
|
+
for (const query of queries) {
|
|
1324
|
+
for (const doc of documents) {
|
|
1325
|
+
scores[idx++] = maxsim(query, doc);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return scores;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// src/images.ts
|
|
1332
|
+
async function toImageBytes(input) {
|
|
1333
|
+
if (input instanceof Uint8Array) {
|
|
1334
|
+
return input;
|
|
1335
|
+
}
|
|
1336
|
+
if (input instanceof ArrayBuffer) {
|
|
1337
|
+
return new Uint8Array(input);
|
|
1338
|
+
}
|
|
1339
|
+
if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
1340
|
+
const buffer = await input.arrayBuffer();
|
|
1341
|
+
return new Uint8Array(buffer);
|
|
1342
|
+
}
|
|
1343
|
+
if (typeof input === "string") {
|
|
1344
|
+
const dataUrlMatch = input.match(/^data:[^;]+;base64,(.+)$/);
|
|
1345
|
+
if (dataUrlMatch?.[1]) {
|
|
1346
|
+
return base64ToBytes(dataUrlMatch[1]);
|
|
1347
|
+
}
|
|
1348
|
+
return base64ToBytes(input);
|
|
1349
|
+
}
|
|
1350
|
+
throw new Error(`Unsupported image input type: ${typeof input}`);
|
|
1351
|
+
}
|
|
1352
|
+
function base64ToBytes(base64) {
|
|
1353
|
+
if (typeof atob === "function") {
|
|
1354
|
+
const binary = atob(base64);
|
|
1355
|
+
const bytes = new Uint8Array(binary.length);
|
|
1356
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1357
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1358
|
+
}
|
|
1359
|
+
return bytes;
|
|
1360
|
+
}
|
|
1361
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
1362
|
+
}
|
|
1363
|
+
async function toImageWireFormat(input, format = "jpeg") {
|
|
1364
|
+
const data = await toImageBytes(input);
|
|
1365
|
+
return { data, format };
|
|
1366
|
+
}
|
|
1367
|
+
function detectImageFormat(bytes) {
|
|
1368
|
+
if (bytes.length < 4) {
|
|
1369
|
+
return "unknown";
|
|
1370
|
+
}
|
|
1371
|
+
if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
|
|
1372
|
+
return "jpeg";
|
|
1373
|
+
}
|
|
1374
|
+
if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
|
|
1375
|
+
return "png";
|
|
1376
|
+
}
|
|
1377
|
+
if (bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes.length >= 12 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) {
|
|
1378
|
+
return "webp";
|
|
1379
|
+
}
|
|
1380
|
+
return "unknown";
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
export { LoraLoadingError, ModelLoadingError, PoolError, ProvisioningError, RequestError, SDK_VERSION, SIEClient, SIEConnectionError, SIEError, ServerError, detectImageFormat, maxsim, maxsimBatch, maxsimDocuments, packMessage, toFloat32Array, toImageBytes, toImageWireFormat, toNumberArray, unpackMessage };
|
|
1384
|
+
//# sourceMappingURL=index.js.map
|
|
1385
|
+
//# sourceMappingURL=index.js.map
|