segmind 0.2.2 → 0.2.4
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 +13 -3
- package/dist/index.cjs +70 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -5
- package/dist/index.d.ts +31 -5
- package/dist/index.js +70 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,7 +97,17 @@ const { url } = await segmind.files.upload('portrait.jpg'); // a path (Node), a
|
|
|
97
97
|
const result = await segmind.run('some-image-to-image-model', { image: url });
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
Each file goes straight to storage on a presigned URL, so large videos never travel as base64 JSON.
|
|
100
|
+
Each file goes straight to storage on a presigned URL, so large videos never travel as base64 JSON. By default the URLs are public and temporary (kept for at least two days): they are for passing files to models, not for keeping them. Pass an array to upload several, in order. Bytes without a name need `{ filename: 'clip.mp4' }` unless their type can be read from the first bytes.
|
|
101
|
+
|
|
102
|
+
To keep a file, pass `persist: true`. It is saved to your asset library and its URL does not expire, the same as the Python SDK's upload:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const logo = await segmind.files.upload('logo.png', { persist: true });
|
|
106
|
+
logo.url; // https://images.segmind.com/assets/…
|
|
107
|
+
logo.persistent; // true
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Persistent uploads take images, video, audio, PDF and common document types, up to 2 GB each.
|
|
101
111
|
|
|
102
112
|
## Generations
|
|
103
113
|
|
|
@@ -115,12 +125,12 @@ const latest = await segmind.generations.recent('flux-schnell');
|
|
|
115
125
|
```ts
|
|
116
126
|
const hook = await segmind.webhooks.add('https://example.com/hooks/segmind', ['PIXELFLOW']);
|
|
117
127
|
const hooks = await segmind.webhooks.get(); // each with its signing_secret
|
|
118
|
-
await segmind.webhooks.update(hook.id, 'https://example.com/hooks/v2'
|
|
128
|
+
await segmind.webhooks.update(hook.id, 'https://example.com/hooks/v2'); // URL only
|
|
119
129
|
await segmind.webhooks.logs(hook.id);
|
|
120
130
|
await segmind.webhooks.delete(hook.id);
|
|
121
131
|
```
|
|
122
132
|
|
|
123
|
-
Event types are subscription types: `PIXELFLOW` for PixelFlow runs, plus `WORKFLOW`, `INFERENCE` and `FINETUNE`.
|
|
133
|
+
Event types are subscription types: `PIXELFLOW` for PixelFlow runs, plus `WORKFLOW`, `INFERENCE` and `FINETUNE`. `update` changes the URL only; to change the event types, `delete` the registration and `add` a new one. Before 0.2.4, `update` registered a second hook and `delete` deactivated nothing, so check `webhooks.get()` for registrations left behind.
|
|
124
134
|
|
|
125
135
|
## PixelFlow workflows
|
|
126
136
|
|
package/dist/index.cjs
CHANGED
|
@@ -491,12 +491,16 @@ var Files = class {
|
|
|
491
491
|
}
|
|
492
492
|
async uploadOne(input, options) {
|
|
493
493
|
const file = await readFileInput(input, { ...options, caller: "files.upload" });
|
|
494
|
-
const
|
|
495
|
-
|
|
494
|
+
const url = options.persist ? await this.persist(file, options.signal) : await this.stage(file, options.signal);
|
|
495
|
+
return { url, filename: file.filename, size: file.bytes.byteLength, persistent: options.persist === true };
|
|
496
|
+
}
|
|
497
|
+
/** A temporary model input on the inference API's input bucket. */
|
|
498
|
+
async stage(file, signal) {
|
|
499
|
+
const ticket = await this.client.http.json({
|
|
496
500
|
method: "POST",
|
|
497
501
|
url: `${this.client.spotURL}/inference-request/input-presign`,
|
|
498
502
|
body: { filename: file.filename, size: file.bytes.byteLength },
|
|
499
|
-
signal
|
|
503
|
+
signal,
|
|
500
504
|
retry: true
|
|
501
505
|
});
|
|
502
506
|
if (!ticket.upload_url || !ticket.url) {
|
|
@@ -505,16 +509,41 @@ var Files = class {
|
|
|
505
509
|
const headers = { ...ticket.required_headers };
|
|
506
510
|
delete headers["Content-Length"];
|
|
507
511
|
delete headers["content-length"];
|
|
508
|
-
await
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
512
|
+
await this.put(ticket.upload_url, file, headers, signal);
|
|
513
|
+
return ticket.url;
|
|
514
|
+
}
|
|
515
|
+
/** A file in the caller's asset library: presign, PUT, then confirm to register it. */
|
|
516
|
+
async persist(file, signal) {
|
|
517
|
+
const contentType = file.mimeType;
|
|
518
|
+
if (!contentType) {
|
|
519
|
+
throw new SegmindError(`files.upload: cannot tell the type of ${file.filename}; pass { mimeType } to persist it`);
|
|
520
|
+
}
|
|
521
|
+
const { http, assetsURL } = this.client;
|
|
522
|
+
const ticket = await http.json({
|
|
523
|
+
method: "POST",
|
|
524
|
+
url: `${assetsURL}/upload-asset/presign`,
|
|
525
|
+
body: { filename: file.filename, contentType, size: file.bytes.byteLength },
|
|
526
|
+
signal,
|
|
515
527
|
retry: true
|
|
516
528
|
});
|
|
517
|
-
|
|
529
|
+
if (!ticket.presignedUrl || !ticket.s3Key || !ticket.publicUrl) {
|
|
530
|
+
throw new SegmindError("files.upload: the asset presign response had no presignedUrl, s3Key or publicUrl", {
|
|
531
|
+
body: ticket
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
await this.put(ticket.presignedUrl, file, { "Content-Type": contentType }, signal);
|
|
535
|
+
const confirmed = await http.json({
|
|
536
|
+
method: "POST",
|
|
537
|
+
url: `${assetsURL}/upload-asset/confirm`,
|
|
538
|
+
body: { s3Key: ticket.s3Key, publicUrl: ticket.publicUrl, contentType, filename: file.filename },
|
|
539
|
+
signal,
|
|
540
|
+
retry: true
|
|
541
|
+
});
|
|
542
|
+
return confirmed.asset?.url ?? ticket.publicUrl;
|
|
543
|
+
}
|
|
544
|
+
/** A PUT to the same key is idempotent, so it retries. `bare` keeps the API key off the storage host. */
|
|
545
|
+
async put(url, file, headers, signal) {
|
|
546
|
+
await this.client.http.request({ method: "PUT", url, rawBody: file.bytes, headers, bare: true, signal, retry: true });
|
|
518
547
|
}
|
|
519
548
|
};
|
|
520
549
|
|
|
@@ -752,7 +781,7 @@ function failureDetail(body) {
|
|
|
752
781
|
}
|
|
753
782
|
|
|
754
783
|
// src/version.ts
|
|
755
|
-
var VERSION = "0.2.
|
|
784
|
+
var VERSION = "0.2.4";
|
|
756
785
|
|
|
757
786
|
// src/webhooks.ts
|
|
758
787
|
var Webhooks = class {
|
|
@@ -771,16 +800,26 @@ var Webhooks = class {
|
|
|
771
800
|
signal: options.signal
|
|
772
801
|
});
|
|
773
802
|
}
|
|
774
|
-
/**
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
803
|
+
/**
|
|
804
|
+
* Points an active registration at a new URL. The event types stay as
|
|
805
|
+
* registered: to change them, `delete` the registration and `add` a new one.
|
|
806
|
+
*
|
|
807
|
+
* @param _eventTypes Ignored. Kept so 0.2 calls still compile; the API never
|
|
808
|
+
* changed event types in place.
|
|
809
|
+
*/
|
|
810
|
+
async update(webhookId, webhookUrl, _eventTypes, options = {}) {
|
|
811
|
+
return this.call("PUT", "/update", {
|
|
812
|
+
body: { webhook_id: webhookId, webhook_url: webhookUrl },
|
|
813
|
+
signal: options.signal,
|
|
814
|
+
retry: true
|
|
779
815
|
});
|
|
780
816
|
}
|
|
781
|
-
/**
|
|
817
|
+
/**
|
|
818
|
+
* Deactivates a registration. Not retried: a second attempt after a lost
|
|
819
|
+
* response would report the registration as already deactivated.
|
|
820
|
+
*/
|
|
782
821
|
async delete(webhookId, options = {}) {
|
|
783
|
-
return this.call("
|
|
822
|
+
return this.call("DELETE", "/inactive", { query: { webhook_id: webhookId }, signal: options.signal });
|
|
784
823
|
}
|
|
785
824
|
/** Delivery history for one registration. */
|
|
786
825
|
async logs(webhookId, options = {}) {
|
|
@@ -801,10 +840,12 @@ function requireTypes(eventTypes) {
|
|
|
801
840
|
var DEFAULT_BASE_URL = "https://api.segmind.com";
|
|
802
841
|
var DEFAULT_SPOT_URL = "https://api.spotprod.segmind.com";
|
|
803
842
|
var DEFAULT_CONSOLE_URL = "https://cloud.segmind.com/api";
|
|
843
|
+
var DEFAULT_ASSETS_URL = "https://platform-api.segmind.com";
|
|
804
844
|
var Segmind = class {
|
|
805
845
|
baseURL;
|
|
806
846
|
spotURL;
|
|
807
847
|
consoleURL;
|
|
848
|
+
assetsURL;
|
|
808
849
|
models;
|
|
809
850
|
accounts;
|
|
810
851
|
/** Upload files to pass as model inputs. */
|
|
@@ -833,6 +874,7 @@ var Segmind = class {
|
|
|
833
874
|
this.baseURL = trimBase(options.baseURL ?? readEnv("SEGMIND_BASE_URL") ?? DEFAULT_BASE_URL);
|
|
834
875
|
this.spotURL = trimSlash(options.spotURL ?? DEFAULT_SPOT_URL);
|
|
835
876
|
this.consoleURL = trimSlash(options.consoleURL ?? DEFAULT_CONSOLE_URL);
|
|
877
|
+
this.assetsURL = trimSlash(options.assetsURL ?? DEFAULT_ASSETS_URL);
|
|
836
878
|
const headers = { "X-Initiator": "SDK-JS", ...options.defaultHeaders };
|
|
837
879
|
const node = nodeVersion();
|
|
838
880
|
if (runtime === "node" && node) headers["User-Agent"] = `segmind-js/${VERSION} node/${node}`;
|
|
@@ -925,7 +967,7 @@ var Segmind = class {
|
|
|
925
967
|
body
|
|
926
968
|
});
|
|
927
969
|
}
|
|
928
|
-
return new Job(this.http, validateSubmit(body, response.status), transform);
|
|
970
|
+
return new Job(this.http, onBase(validateSubmit(body, response.status), this.baseURL), transform);
|
|
929
971
|
}
|
|
930
972
|
if (canRetry && response.status === 503) {
|
|
931
973
|
await response.body?.cancel().catch(() => void 0);
|
|
@@ -986,6 +1028,14 @@ function path(slug) {
|
|
|
986
1028
|
if (!clean) throw new SegmindError("A model slug is required.");
|
|
987
1029
|
return clean;
|
|
988
1030
|
}
|
|
1031
|
+
function onBase(body, baseURL) {
|
|
1032
|
+
const origin = new URL(baseURL).origin;
|
|
1033
|
+
const pin = (url) => {
|
|
1034
|
+
const target = new URL(url, `${baseURL}/`);
|
|
1035
|
+
return target.origin === origin ? target.href : `${baseURL}${target.pathname}${target.search}`;
|
|
1036
|
+
};
|
|
1037
|
+
return { ...body, status_url: pin(body.status_url), response_url: pin(body.response_url) };
|
|
1038
|
+
}
|
|
989
1039
|
function trimSlash(url) {
|
|
990
1040
|
return url.replace(/\/+$/, "");
|
|
991
1041
|
}
|