segmind 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -4
- package/dist/index.cjs +247 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +238 -83
- package/dist/index.d.ts +238 -83
- package/dist/index.js +247 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/core/errors.ts
|
|
2
|
-
var SegmindError = class
|
|
2
|
+
var SegmindError = class extends Error {
|
|
3
3
|
/** HTTP status, when the error came from a response. */
|
|
4
4
|
status;
|
|
5
5
|
/** The server's error text (`error` in the response body), when it sent one. */
|
|
@@ -17,20 +17,21 @@ var SegmindError = class _SegmindError extends Error {
|
|
|
17
17
|
this.body = options.body;
|
|
18
18
|
}
|
|
19
19
|
/** Reads the response body and builds the error. Consumes the body. */
|
|
20
|
-
static
|
|
21
|
-
|
|
22
|
-
let body = text;
|
|
23
|
-
try {
|
|
24
|
-
body = text ? JSON.parse(text) : void 0;
|
|
25
|
-
} catch {
|
|
26
|
-
}
|
|
27
|
-
const detail = errorDetail(body) ?? (typeof body === "string" && body ? body.slice(0, 500) : void 0);
|
|
28
|
-
const message = detail ? `${response.status} ${detail}` : `${response.status} ${response.statusText || "request failed"}`;
|
|
29
|
-
return new _SegmindError(message, { status: response.status, detail, requestId, body });
|
|
20
|
+
static fromResponse(response, requestId) {
|
|
21
|
+
return errorFromResponse(response, requestId);
|
|
30
22
|
}
|
|
31
23
|
};
|
|
32
24
|
var APIConnectionError = class extends SegmindError {
|
|
33
25
|
};
|
|
26
|
+
var InsufficientCredits = class extends SegmindError {
|
|
27
|
+
};
|
|
28
|
+
var RateLimited = class extends SegmindError {
|
|
29
|
+
retryAfterMs;
|
|
30
|
+
constructor(message, options = {}) {
|
|
31
|
+
super(message, options);
|
|
32
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
34
35
|
var InferenceFailed = class extends SegmindError {
|
|
35
36
|
/** The status body that reported the failure. */
|
|
36
37
|
statusBody;
|
|
@@ -51,6 +52,43 @@ var InferenceTimeout = class extends SegmindError {
|
|
|
51
52
|
this.elapsedMs = elapsedMs;
|
|
52
53
|
}
|
|
53
54
|
};
|
|
55
|
+
async function errorFromResponse(response, requestId) {
|
|
56
|
+
const text = await response.text().catch(() => "");
|
|
57
|
+
let body = text;
|
|
58
|
+
try {
|
|
59
|
+
body = text ? JSON.parse(text) : void 0;
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
const detail = errorDetail(body) ?? plainText(body);
|
|
63
|
+
const message = detail ? `${response.status} ${detail}` : `${response.status} ${response.statusText || "request failed"}`;
|
|
64
|
+
const options = { status: response.status, detail, requestId, body };
|
|
65
|
+
if (response.status === 429) {
|
|
66
|
+
return new RateLimited(message, { ...options, retryAfterMs: parseRetryAfter(response.headers.get("retry-after")) });
|
|
67
|
+
}
|
|
68
|
+
if (response.status === 402 || isOutOfCredits(detail)) return new InsufficientCredits(message, options);
|
|
69
|
+
return new SegmindError(message, options);
|
|
70
|
+
}
|
|
71
|
+
function requestFailure(requestId, statusBody, detail) {
|
|
72
|
+
detail ??= typeof statusBody.error === "string" ? statusBody.error : void 0;
|
|
73
|
+
if (isOutOfCredits(detail)) {
|
|
74
|
+
return new InsufficientCredits(`request ${requestId} failed: ${detail}`, { detail, requestId, body: statusBody });
|
|
75
|
+
}
|
|
76
|
+
return new InferenceFailed(requestId, statusBody, detail);
|
|
77
|
+
}
|
|
78
|
+
function parseRetryAfter(value, now = Date.now()) {
|
|
79
|
+
if (!value) return void 0;
|
|
80
|
+
const seconds = Number(value);
|
|
81
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
82
|
+
const date = Date.parse(value);
|
|
83
|
+
return Number.isNaN(date) ? void 0 : Math.max(date - now, 0);
|
|
84
|
+
}
|
|
85
|
+
function isOutOfCredits(detail) {
|
|
86
|
+
return detail?.startsWith("Insufficient credits.") ?? false;
|
|
87
|
+
}
|
|
88
|
+
function plainText(body) {
|
|
89
|
+
if (typeof body !== "string" || !body || body.trimStart().startsWith("<")) return void 0;
|
|
90
|
+
return body.slice(0, 500);
|
|
91
|
+
}
|
|
54
92
|
function errorDetail(body) {
|
|
55
93
|
if (!body || typeof body !== "object") return void 0;
|
|
56
94
|
const record2 = body;
|
|
@@ -168,6 +206,12 @@ function detectRuntime() {
|
|
|
168
206
|
if (g.process?.versions?.node) return "node";
|
|
169
207
|
return "other";
|
|
170
208
|
}
|
|
209
|
+
function initiatorHeaders(options) {
|
|
210
|
+
return {
|
|
211
|
+
...options.initiator ? { "X-Initiator": options.initiator } : {},
|
|
212
|
+
...options.initiatorId ? { "X-Initiator-ID": options.initiatorId } : {}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
171
215
|
function readEnv(name) {
|
|
172
216
|
try {
|
|
173
217
|
const value = g.process?.env?.[name];
|
|
@@ -200,15 +244,15 @@ function toBase64(bytes) {
|
|
|
200
244
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
|
|
201
245
|
var MAX_RETRY_DELAY_MS = 6e4;
|
|
202
246
|
function retryDelay(attempt, retryAfter, now = Date.now()) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (Number.isFinite(seconds) && seconds >= 0) return Math.min(seconds * 1e3, MAX_RETRY_DELAY_MS);
|
|
206
|
-
const date = Date.parse(retryAfter);
|
|
207
|
-
if (!Number.isNaN(date)) return Math.min(Math.max(date - now, 0), MAX_RETRY_DELAY_MS);
|
|
208
|
-
}
|
|
247
|
+
const asked = parseRetryAfter(retryAfter, now);
|
|
248
|
+
if (asked !== void 0) return Math.min(asked, MAX_RETRY_DELAY_MS);
|
|
209
249
|
const base = Math.min(500 * 2 ** attempt, 8e3);
|
|
210
250
|
return base * (0.75 + Math.random() * 0.25);
|
|
211
251
|
}
|
|
252
|
+
function pollDelay(interval, elapsedMs, fallback) {
|
|
253
|
+
const delay = typeof interval === "function" ? interval(elapsedMs) : interval ?? fallback;
|
|
254
|
+
return Number.isFinite(delay) && delay >= 0 ? delay : fallback;
|
|
255
|
+
}
|
|
212
256
|
function sleep(ms, signal) {
|
|
213
257
|
return new Promise((resolve, reject) => {
|
|
214
258
|
if (signal?.aborted) return reject(signal.reason);
|
|
@@ -228,23 +272,35 @@ var HttpClient = class {
|
|
|
228
272
|
this.config = config;
|
|
229
273
|
}
|
|
230
274
|
config;
|
|
275
|
+
/** The hook call in flight, so that requests failing together share one refresh. */
|
|
276
|
+
unauthorized;
|
|
231
277
|
/** The response whatever its status, after retries when `retry` is set. */
|
|
232
278
|
async fetchRaw(req) {
|
|
233
|
-
|
|
279
|
+
let attempt = 0;
|
|
280
|
+
let reauthenticated = false;
|
|
281
|
+
for (; ; ) {
|
|
234
282
|
const canRetry = req.retry === true && attempt < this.config.maxRetries;
|
|
283
|
+
const token = await this.sessionToken(req);
|
|
235
284
|
let response;
|
|
236
285
|
try {
|
|
237
|
-
response = await this.attempt(req);
|
|
286
|
+
response = await this.attempt(req, token);
|
|
238
287
|
} catch (err) {
|
|
239
288
|
if (canRetry && err instanceof APIConnectionError) {
|
|
240
289
|
await sleep(retryDelay(attempt, null), req.signal);
|
|
290
|
+
attempt++;
|
|
241
291
|
continue;
|
|
242
292
|
}
|
|
243
293
|
throw err;
|
|
244
294
|
}
|
|
295
|
+
if (response.status === 401 && !reauthenticated && await this.reauthenticate(req, token)) {
|
|
296
|
+
reauthenticated = true;
|
|
297
|
+
await response.body?.cancel().catch(() => void 0);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
245
300
|
if (canRetry && RETRYABLE_STATUS.has(response.status)) {
|
|
246
301
|
await response.body?.cancel().catch(() => void 0);
|
|
247
302
|
await sleep(retryDelay(attempt, response.headers.get("retry-after")), req.signal);
|
|
303
|
+
attempt++;
|
|
248
304
|
continue;
|
|
249
305
|
}
|
|
250
306
|
return response;
|
|
@@ -260,7 +316,31 @@ var HttpClient = class {
|
|
|
260
316
|
async json(req) {
|
|
261
317
|
return readJson(await this.request(req));
|
|
262
318
|
}
|
|
263
|
-
async
|
|
319
|
+
async sessionToken(req) {
|
|
320
|
+
if (!this.config.tokenProvider || req.bare) return void 0;
|
|
321
|
+
try {
|
|
322
|
+
return await this.config.tokenProvider() ?? void 0;
|
|
323
|
+
} catch (err) {
|
|
324
|
+
throw new SegmindError(`tokenProvider failed: ${describe(err)}`, { cause: err });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/** Tells the app its token was refused. True when it now has a different one to try. */
|
|
328
|
+
async reauthenticate(req, refused) {
|
|
329
|
+
if (!this.config.tokenProvider || req.bare) return false;
|
|
330
|
+
try {
|
|
331
|
+
if (this.config.onUnauthorized) {
|
|
332
|
+
this.unauthorized ??= Promise.resolve(this.config.onUnauthorized()).finally(() => {
|
|
333
|
+
this.unauthorized = void 0;
|
|
334
|
+
});
|
|
335
|
+
await this.unauthorized;
|
|
336
|
+
}
|
|
337
|
+
const next = await this.config.tokenProvider();
|
|
338
|
+
return Boolean(next) && next !== refused;
|
|
339
|
+
} catch {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async attempt(req, token) {
|
|
264
344
|
const { signal } = req;
|
|
265
345
|
if (signal?.aborted) throw signal.reason;
|
|
266
346
|
const timeout = req.timeout ?? this.config.timeout;
|
|
@@ -274,6 +354,7 @@ var HttpClient = class {
|
|
|
274
354
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
275
355
|
const headers = req.bare ? { ...req.headers } : { ...this.config.headers, ...req.headers };
|
|
276
356
|
if (this.config.apiKey && !req.bare) headers["x-api-key"] = this.config.apiKey;
|
|
357
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
277
358
|
if (req.body !== void 0) headers["content-type"] = "application/json";
|
|
278
359
|
const body = req.rawBody ?? (req.body === void 0 ? void 0 : JSON.stringify(req.body));
|
|
279
360
|
try {
|
|
@@ -368,7 +449,12 @@ async function readFileInput(input, options) {
|
|
|
368
449
|
if (detectRuntime() !== "node") {
|
|
369
450
|
throw new SegmindError(`${options.caller}: file paths work in Node only; pass a Blob or bytes`);
|
|
370
451
|
}
|
|
371
|
-
const { readFile } = await import(
|
|
452
|
+
const { readFile } = await import(
|
|
453
|
+
/* webpackIgnore: true */
|
|
454
|
+
/* turbopackIgnore: true */
|
|
455
|
+
/* @vite-ignore */
|
|
456
|
+
"node:fs/promises"
|
|
457
|
+
);
|
|
372
458
|
try {
|
|
373
459
|
bytes = new Uint8Array(await readFile(input));
|
|
374
460
|
} catch (err) {
|
|
@@ -551,12 +637,12 @@ var Job = class {
|
|
|
551
637
|
return await this.read(this.statusUrl, options.signal);
|
|
552
638
|
}
|
|
553
639
|
/**
|
|
554
|
-
* The finished result. Throws `InferenceFailed` when the request failed
|
|
555
|
-
* a `SegmindError` when it has not finished yet (use `wait()` for that).
|
|
640
|
+
* The finished result. Throws `InferenceFailed` when the request failed
|
|
641
|
+
* (`InsufficientCredits` when that was for lack of credits), and a `SegmindError` when it has not finished yet (use `wait()` for that).
|
|
556
642
|
*/
|
|
557
643
|
async result(options = {}) {
|
|
558
644
|
const body = await this.read(this.responseUrl, options.signal);
|
|
559
|
-
if (body.status === "FAILED") throw
|
|
645
|
+
if (body.status === "FAILED") throw requestFailure(this.requestId, body);
|
|
560
646
|
if (body.status !== void 0 && body.status !== "COMPLETED") {
|
|
561
647
|
throw new SegmindError(`request ${this.requestId} is still ${String(body.status)}; use wait()`, {
|
|
562
648
|
requestId: this.requestId,
|
|
@@ -568,15 +654,16 @@ var Job = class {
|
|
|
568
654
|
/** Polls until the request finishes, then returns its result. */
|
|
569
655
|
async wait(options = {}) {
|
|
570
656
|
const timeout = options.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
571
|
-
const interval = options.interval ?? DEFAULT_WAIT_INTERVAL_MS;
|
|
572
657
|
const started = Date.now();
|
|
573
658
|
for (; ; ) {
|
|
574
659
|
const status = await this.status({ signal: options.signal });
|
|
660
|
+
options.onStatus?.(status);
|
|
575
661
|
if (status.status === "COMPLETED") return this.result({ signal: options.signal });
|
|
576
|
-
if (status.status === "FAILED") throw
|
|
662
|
+
if (status.status === "FAILED") throw requestFailure(this.requestId, status);
|
|
577
663
|
const elapsed = Date.now() - started;
|
|
578
664
|
if (elapsed >= timeout) throw new InferenceTimeout(this.requestId, elapsed);
|
|
579
|
-
|
|
665
|
+
const delay = pollDelay(options.interval, elapsed, DEFAULT_WAIT_INTERVAL_MS);
|
|
666
|
+
await sleep(Math.min(delay, timeout - elapsed), options.signal);
|
|
580
667
|
}
|
|
581
668
|
}
|
|
582
669
|
/**
|
|
@@ -608,8 +695,8 @@ var PixelFlows = class {
|
|
|
608
695
|
client;
|
|
609
696
|
/** Runs a workflow and waits for its outputs. */
|
|
610
697
|
async run(slug, inputs = {}, options = {}) {
|
|
611
|
-
const
|
|
612
|
-
return
|
|
698
|
+
const job2 = await this.submit(slug, inputs, options);
|
|
699
|
+
return job2.wait(options);
|
|
613
700
|
}
|
|
614
701
|
/** Starts a workflow and returns a job to wait on. Never retried: a run bills. */
|
|
615
702
|
async submit(slug, inputs = {}, options = {}) {
|
|
@@ -620,6 +707,7 @@ var PixelFlows = class {
|
|
|
620
707
|
method: "POST",
|
|
621
708
|
url: `${this.client.baseURL}${route}${clean}`,
|
|
622
709
|
body: inputs,
|
|
710
|
+
headers: initiatorHeaders(options),
|
|
623
711
|
signal: options.signal
|
|
624
712
|
});
|
|
625
713
|
const requestId = body.request_id;
|
|
@@ -655,20 +743,21 @@ var PixelflowJob = class {
|
|
|
655
743
|
}
|
|
656
744
|
/**
|
|
657
745
|
* Polls until the run finishes. Throws `InferenceFailed` (its `detail` names
|
|
658
|
-
* the failing node) or `InferenceTimeout`; the run may still finish after a
|
|
746
|
+
* the failing node), `InsufficientCredits` or `InferenceTimeout`; the run may still finish after a
|
|
659
747
|
* timeout, so `wait()` can be called again.
|
|
660
748
|
*/
|
|
661
749
|
async wait(options = {}) {
|
|
662
750
|
const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
663
|
-
const interval = options.interval ?? DEFAULT_INTERVAL_MS;
|
|
664
751
|
const started = Date.now();
|
|
665
752
|
for (; ; ) {
|
|
666
753
|
const state = await this.status({ signal: options.signal });
|
|
754
|
+
options.onStatus?.(state);
|
|
667
755
|
if (DONE.has(state.status)) return state;
|
|
668
|
-
if (FAILED.has(state.status)) throw
|
|
756
|
+
if (FAILED.has(state.status)) throw requestFailure(this.requestId, state.raw, failureDetail(state.raw));
|
|
669
757
|
const elapsed = Date.now() - started;
|
|
670
|
-
|
|
671
|
-
|
|
758
|
+
const delay = pollDelay(options.interval, elapsed, DEFAULT_INTERVAL_MS);
|
|
759
|
+
if (elapsed + delay > timeout) throw new InferenceTimeout(this.requestId, elapsed);
|
|
760
|
+
await sleep(delay, options.signal);
|
|
672
761
|
}
|
|
673
762
|
}
|
|
674
763
|
};
|
|
@@ -713,7 +802,7 @@ function failureDetail(body) {
|
|
|
713
802
|
}
|
|
714
803
|
|
|
715
804
|
// src/version.ts
|
|
716
|
-
var VERSION = "0.
|
|
805
|
+
var VERSION = "0.3.0";
|
|
717
806
|
|
|
718
807
|
// src/webhooks.ts
|
|
719
808
|
var Webhooks = class {
|
|
@@ -732,16 +821,26 @@ var Webhooks = class {
|
|
|
732
821
|
signal: options.signal
|
|
733
822
|
});
|
|
734
823
|
}
|
|
735
|
-
/**
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
824
|
+
/**
|
|
825
|
+
* Points an active registration at a new URL. The event types stay as
|
|
826
|
+
* registered: to change them, `delete` the registration and `add` a new one.
|
|
827
|
+
*
|
|
828
|
+
* @param _eventTypes Ignored. Kept so 0.2 calls still compile; the API never
|
|
829
|
+
* changed event types in place.
|
|
830
|
+
*/
|
|
831
|
+
async update(webhookId, webhookUrl, _eventTypes, options = {}) {
|
|
832
|
+
return this.call("PUT", "/update", {
|
|
833
|
+
body: { webhook_id: webhookId, webhook_url: webhookUrl },
|
|
834
|
+
signal: options.signal,
|
|
835
|
+
retry: true
|
|
740
836
|
});
|
|
741
837
|
}
|
|
742
|
-
/**
|
|
838
|
+
/**
|
|
839
|
+
* Deactivates a registration. Not retried: a second attempt after a lost
|
|
840
|
+
* response would report the registration as already deactivated.
|
|
841
|
+
*/
|
|
743
842
|
async delete(webhookId, options = {}) {
|
|
744
|
-
return this.call("
|
|
843
|
+
return this.call("DELETE", "/inactive", { query: { webhook_id: webhookId }, signal: options.signal });
|
|
745
844
|
}
|
|
746
845
|
/** Delivery history for one registration. */
|
|
747
846
|
async logs(webhookId, options = {}) {
|
|
@@ -781,14 +880,17 @@ var Segmind = class {
|
|
|
781
880
|
/** @internal */
|
|
782
881
|
http;
|
|
783
882
|
constructor(options = {}) {
|
|
784
|
-
|
|
883
|
+
if (options.apiKey && options.tokenProvider) {
|
|
884
|
+
throw new SegmindError("Pass either apiKey or tokenProvider, not both.");
|
|
885
|
+
}
|
|
886
|
+
const apiKey = options.apiKey ?? (options.tokenProvider ? void 0 : readEnv("SEGMIND_API_KEY"));
|
|
785
887
|
const runtime = detectRuntime();
|
|
786
888
|
if (apiKey && (runtime === "browser" || runtime === "react-native") && !options.dangerouslyAllowBrowser) {
|
|
787
889
|
throw new SegmindError(
|
|
788
890
|
"An API key in a browser or mobile app exposes it to every user. Call Segmind from your backend instead, or pass dangerouslyAllowBrowser: true if you accept that risk."
|
|
789
891
|
);
|
|
790
892
|
}
|
|
791
|
-
if (!apiKey) {
|
|
893
|
+
if (!apiKey && !options.tokenProvider) {
|
|
792
894
|
throw new SegmindError("Missing API key: pass { apiKey } or set the SEGMIND_API_KEY environment variable.");
|
|
793
895
|
}
|
|
794
896
|
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
@@ -797,11 +899,17 @@ var Segmind = class {
|
|
|
797
899
|
this.spotURL = trimSlash(options.spotURL ?? DEFAULT_SPOT_URL);
|
|
798
900
|
this.consoleURL = trimSlash(options.consoleURL ?? DEFAULT_CONSOLE_URL);
|
|
799
901
|
this.assetsURL = trimSlash(options.assetsURL ?? DEFAULT_ASSETS_URL);
|
|
800
|
-
const headers = {
|
|
902
|
+
const headers = {
|
|
903
|
+
"X-Initiator": "SDK-JS",
|
|
904
|
+
...initiatorHeaders(options),
|
|
905
|
+
...options.defaultHeaders
|
|
906
|
+
};
|
|
801
907
|
const node = nodeVersion();
|
|
802
908
|
if (runtime === "node" && node) headers["User-Agent"] = `segmind-js/${VERSION} node/${node}`;
|
|
803
909
|
this.http = new HttpClient({
|
|
804
910
|
apiKey,
|
|
911
|
+
tokenProvider: options.tokenProvider,
|
|
912
|
+
onUnauthorized: options.onUnauthorized,
|
|
805
913
|
fetch: fetchImpl,
|
|
806
914
|
timeout: options.timeout ?? 6e5,
|
|
807
915
|
maxRetries: options.maxRetries ?? 2,
|
|
@@ -819,8 +927,8 @@ var Segmind = class {
|
|
|
819
927
|
* until it finishes. `params` go to the model unchanged.
|
|
820
928
|
*/
|
|
821
929
|
async run(slug, params = {}, options = {}) {
|
|
822
|
-
const
|
|
823
|
-
return
|
|
930
|
+
const job2 = await this.submit(slug, params, options);
|
|
931
|
+
return job2.wait(options);
|
|
824
932
|
}
|
|
825
933
|
/**
|
|
826
934
|
* One blocking `POST /v1/{slug}`. Returns the fetch `Response`: call
|
|
@@ -832,6 +940,7 @@ var Segmind = class {
|
|
|
832
940
|
method: "POST",
|
|
833
941
|
url: `${this.baseURL}/v1/${path(slug)}`,
|
|
834
942
|
body: params,
|
|
943
|
+
headers: initiatorHeaders(options),
|
|
835
944
|
signal: options.signal,
|
|
836
945
|
timeout: options.timeout
|
|
837
946
|
});
|
|
@@ -844,10 +953,27 @@ var Segmind = class {
|
|
|
844
953
|
async submit(slug, params = {}, options = {}) {
|
|
845
954
|
return this.submitJob(slug, params, options, (body) => body);
|
|
846
955
|
}
|
|
956
|
+
/**
|
|
957
|
+
* A job submitted earlier, from its id alone, for picking one up after a
|
|
958
|
+
* reload or in another process. Makes no request. The result is kept for about
|
|
959
|
+
* an hour, and only for the user who submitted it: anything else reads as a
|
|
960
|
+
* `SegmindError` with status 404.
|
|
961
|
+
*/
|
|
962
|
+
job(requestId) {
|
|
963
|
+
const clean = requestId.trim();
|
|
964
|
+
if (!clean) throw new SegmindError("A request id is required.");
|
|
965
|
+
const url = `${this.baseURL}/v2/requests/${encodeURIComponent(clean)}`;
|
|
966
|
+
return new Job(this.http, {
|
|
967
|
+
request_id: clean,
|
|
968
|
+
status: "UNKNOWN",
|
|
969
|
+
status_url: `${url}/status`,
|
|
970
|
+
response_url: url
|
|
971
|
+
});
|
|
972
|
+
}
|
|
847
973
|
/** Chat with an LLM through the v2 queue. Returns a provider-neutral reply. */
|
|
848
974
|
async chat(slug, params, options = {}) {
|
|
849
|
-
const
|
|
850
|
-
return
|
|
975
|
+
const job2 = await this.submitChat(slug, params, options);
|
|
976
|
+
return job2.wait(options);
|
|
851
977
|
}
|
|
852
978
|
/** Chat with one blocking `POST /v1/{slug}`. */
|
|
853
979
|
async chatSync(slug, params, options = {}) {
|
|
@@ -870,7 +996,7 @@ var Segmind = class {
|
|
|
870
996
|
method: "POST",
|
|
871
997
|
url,
|
|
872
998
|
body: params,
|
|
873
|
-
headers: { "X-Request-ID": requestId },
|
|
999
|
+
headers: { "X-Request-ID": requestId, ...initiatorHeaders(options) },
|
|
874
1000
|
signal: options.signal
|
|
875
1001
|
});
|
|
876
1002
|
} catch (err) {
|
|
@@ -889,7 +1015,7 @@ var Segmind = class {
|
|
|
889
1015
|
body
|
|
890
1016
|
});
|
|
891
1017
|
}
|
|
892
|
-
return new Job(this.http, validateSubmit(body, response.status), transform);
|
|
1018
|
+
return new Job(this.http, onBase(validateSubmit(body, response.status), this.baseURL), transform);
|
|
893
1019
|
}
|
|
894
1020
|
if (canRetry && response.status === 503) {
|
|
895
1021
|
await response.body?.cancel().catch(() => void 0);
|
|
@@ -906,21 +1032,62 @@ var Segmind = class {
|
|
|
906
1032
|
}
|
|
907
1033
|
}
|
|
908
1034
|
};
|
|
1035
|
+
var MODELS_PAGE_LIMIT = 100;
|
|
909
1036
|
var Models = class {
|
|
910
1037
|
constructor(client2) {
|
|
911
1038
|
this.client = client2;
|
|
912
1039
|
}
|
|
913
1040
|
client;
|
|
914
|
-
/**
|
|
915
|
-
async list(options = {}) {
|
|
1041
|
+
/** One page of the catalog. Pass `offset` from the previous page while `has_more` is true. */
|
|
1042
|
+
async list(params = {}, options = {}) {
|
|
916
1043
|
return this.client.http.json({
|
|
917
1044
|
method: "GET",
|
|
918
|
-
url: `${this.client.
|
|
1045
|
+
url: `${this.client.baseURL}/models`,
|
|
1046
|
+
query: modelsQuery(params),
|
|
919
1047
|
signal: options.signal,
|
|
920
1048
|
retry: true
|
|
921
1049
|
});
|
|
922
1050
|
}
|
|
1051
|
+
/** Every model matching `params`, reading page after page. */
|
|
1052
|
+
async listAll(params = {}, options = {}) {
|
|
1053
|
+
const models2 = [];
|
|
1054
|
+
for (let offset = 0; ; ) {
|
|
1055
|
+
const page = await this.list({ ...params, limit: MODELS_PAGE_LIMIT, offset }, options);
|
|
1056
|
+
models2.push(...page.models);
|
|
1057
|
+
offset += page.models.length;
|
|
1058
|
+
if (!page.has_more || !page.models.length || offset >= page.total) return models2;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
923
1061
|
};
|
|
1062
|
+
function modelsQuery(p) {
|
|
1063
|
+
return {
|
|
1064
|
+
q: p.q,
|
|
1065
|
+
sort: p.sort,
|
|
1066
|
+
category: p.category,
|
|
1067
|
+
modality: p.modality,
|
|
1068
|
+
input: joinList(p.input),
|
|
1069
|
+
output: joinList(p.output),
|
|
1070
|
+
provider: p.provider,
|
|
1071
|
+
slugs: joinList(p.slugs),
|
|
1072
|
+
operations: joinList(p.operations),
|
|
1073
|
+
features: joinList(p.features),
|
|
1074
|
+
min_popularity: p.minPopularity,
|
|
1075
|
+
min_category_percentile: p.minCategoryPercentile,
|
|
1076
|
+
min_cost: p.minCost,
|
|
1077
|
+
max_cost: p.maxCost,
|
|
1078
|
+
max_latency: p.maxLatency,
|
|
1079
|
+
is_new: p.isNew,
|
|
1080
|
+
created_after: p.createdAfter,
|
|
1081
|
+
include_coming_soon: p.includeComingSoon,
|
|
1082
|
+
facets: p.facets,
|
|
1083
|
+
limit: p.limit,
|
|
1084
|
+
offset: p.offset
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
function joinList(value) {
|
|
1088
|
+
if (value === void 0) return void 0;
|
|
1089
|
+
return Array.isArray(value) ? value.join(",") : value;
|
|
1090
|
+
}
|
|
924
1091
|
var Accounts = class {
|
|
925
1092
|
constructor(client2) {
|
|
926
1093
|
this.client = client2;
|
|
@@ -935,6 +1102,16 @@ var Accounts = class {
|
|
|
935
1102
|
retry: true
|
|
936
1103
|
});
|
|
937
1104
|
}
|
|
1105
|
+
/** The current credit balance. */
|
|
1106
|
+
async credits(options = {}) {
|
|
1107
|
+
const { "free-credits": freeCredits, ...rest } = await this.client.http.json({
|
|
1108
|
+
method: "GET",
|
|
1109
|
+
url: `${this.client.baseURL}/v1/get-user-credits`,
|
|
1110
|
+
signal: options.signal,
|
|
1111
|
+
retry: true
|
|
1112
|
+
});
|
|
1113
|
+
return { ...rest, credits: Number(rest.credits), freeCredits: Number(freeCredits) };
|
|
1114
|
+
}
|
|
938
1115
|
};
|
|
939
1116
|
function validateSubmit(body, status) {
|
|
940
1117
|
if (!body.request_id || !body.status_url || !body.response_url) {
|
|
@@ -950,6 +1127,14 @@ function path(slug) {
|
|
|
950
1127
|
if (!clean) throw new SegmindError("A model slug is required.");
|
|
951
1128
|
return clean;
|
|
952
1129
|
}
|
|
1130
|
+
function onBase(body, baseURL) {
|
|
1131
|
+
const origin = new URL(baseURL).origin;
|
|
1132
|
+
const pin = (url) => {
|
|
1133
|
+
const target = new URL(url, `${baseURL}/`);
|
|
1134
|
+
return target.origin === origin ? target.href : `${baseURL}${target.pathname}${target.search}`;
|
|
1135
|
+
};
|
|
1136
|
+
return { ...body, status_url: pin(body.status_url), response_url: pin(body.response_url) };
|
|
1137
|
+
}
|
|
953
1138
|
function trimSlash(url) {
|
|
954
1139
|
return url.replace(/\/+$/, "");
|
|
955
1140
|
}
|
|
@@ -975,14 +1160,17 @@ function client() {
|
|
|
975
1160
|
var run = (slug, params, options) => client().run(slug, params, options);
|
|
976
1161
|
var runSync = (slug, params, options) => client().runSync(slug, params, options);
|
|
977
1162
|
var submit = (slug, params, options) => client().submit(slug, params, options);
|
|
1163
|
+
var job = (requestId) => client().job(requestId);
|
|
978
1164
|
var chat = (slug, params, options) => client().chat(slug, params, options);
|
|
979
1165
|
var chatSync = (slug, params, options) => client().chatSync(slug, params, options);
|
|
980
1166
|
var submitChat = (slug, params, options) => client().submitChat(slug, params, options);
|
|
981
1167
|
var models = {
|
|
982
|
-
list: (options) => client().models.list(options)
|
|
1168
|
+
list: (params, options) => client().models.list(params, options),
|
|
1169
|
+
listAll: (params, options) => client().models.listAll(params, options)
|
|
983
1170
|
};
|
|
984
1171
|
var accounts = {
|
|
985
|
-
current: (options) => client().accounts.current(options)
|
|
1172
|
+
current: (options) => client().accounts.current(options),
|
|
1173
|
+
credits: (options) => client().accounts.credits(options)
|
|
986
1174
|
};
|
|
987
1175
|
var files = {
|
|
988
1176
|
upload: ((input, options) => client().files.upload(input, options))
|
|
@@ -1019,10 +1207,12 @@ export {
|
|
|
1019
1207
|
Generations,
|
|
1020
1208
|
InferenceFailed,
|
|
1021
1209
|
InferenceTimeout,
|
|
1210
|
+
InsufficientCredits,
|
|
1022
1211
|
Job,
|
|
1023
1212
|
Models,
|
|
1024
1213
|
PixelFlows,
|
|
1025
1214
|
PixelflowJob,
|
|
1215
|
+
RateLimited,
|
|
1026
1216
|
Segmind,
|
|
1027
1217
|
SegmindError,
|
|
1028
1218
|
VERSION,
|
|
@@ -1033,6 +1223,7 @@ export {
|
|
|
1033
1223
|
files,
|
|
1034
1224
|
generations,
|
|
1035
1225
|
imageUrl,
|
|
1226
|
+
job,
|
|
1036
1227
|
models,
|
|
1037
1228
|
pixelflows,
|
|
1038
1229
|
run,
|