segmind 0.1.0 → 0.2.1
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 +45 -1
- package/dist/index.cjs +400 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +266 -8
- package/dist/index.d.ts +266 -8
- package/dist/index.js +400 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The official JavaScript SDK for [Segmind](https://www.segmind.com): image, video, audio and LLM models through one API. It works in Node 22+, and in browsers and React Native through your own backend.
|
|
4
4
|
|
|
5
|
-
> **Pre-release.** The API below is the 0.
|
|
5
|
+
> **Pre-release.** The API below is the 0.2 surface and may change before 1.0.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -90,6 +90,50 @@ const described = await segmind.chat('gpt-5.5', {
|
|
|
90
90
|
|
|
91
91
|
`reply.text` reads the assistant text from OpenAI, Anthropic and Gemini replies alike; `reply.raw` is the provider's own JSON. `chatSync` makes one blocking call, and `submitChat` returns a job.
|
|
92
92
|
|
|
93
|
+
## Files
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const { url } = await segmind.files.upload('portrait.jpg'); // a path (Node), a Blob/File, or bytes
|
|
97
|
+
const result = await segmind.run('some-image-to-image-model', { image: url });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Each file goes straight to storage on a presigned URL, so large videos never travel as base64 JSON. 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
|
+
## Generations
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const page = await segmind.generations.list({ modelName: 'flux-schnell' });
|
|
106
|
+
const history = await segmind.generations.history({ status: 'FAILED', perPage: 50 }); // failures included
|
|
107
|
+
const one = await segmind.generations.get(requestId);
|
|
108
|
+
const latest = await segmind.generations.recent('flux-schnell');
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`history` lists one row per output, each with the request's full cost: de-duplicate on `request_id` before summing `credits_deduction`.
|
|
112
|
+
|
|
113
|
+
## Webhooks
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const hook = await segmind.webhooks.add('https://example.com/hooks/segmind', ['PIXELFLOW']);
|
|
117
|
+
const hooks = await segmind.webhooks.get(); // each with its signing_secret
|
|
118
|
+
await segmind.webhooks.update(hook.id, 'https://example.com/hooks/v2', ['PIXELFLOW']);
|
|
119
|
+
await segmind.webhooks.logs(hook.id);
|
|
120
|
+
await segmind.webhooks.delete(hook.id);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Event types are subscription types: `PIXELFLOW` for PixelFlow runs, plus `WORKFLOW`, `INFERENCE` and `FINETUNE`.
|
|
124
|
+
|
|
125
|
+
## PixelFlow workflows
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const result = await segmind.pixelflows.run('your-workflow-slug', { Text_Prompt: 'An infographic about tea' });
|
|
129
|
+
console.log(result.outputs); // { image_out: 'https://…' }
|
|
130
|
+
|
|
131
|
+
const job = await segmind.pixelflows.submit('your-workflow-slug', { Text_Prompt: '…' });
|
|
132
|
+
const done = await job.wait({ timeout: 20 * 60_000, interval: 5_000 });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The slug is the workflow's API slug from its API tab, and inputs are keyed by each input's keyname. Runs use the current workflow engine; pass `{ engine: 'v1' }` for the legacy one. A failed run throws `InferenceFailed` whose `detail` names the node that failed. A submit is never retried, because a run bills.
|
|
136
|
+
|
|
93
137
|
## Errors
|
|
94
138
|
|
|
95
139
|
| Error | When |
|
package/dist/index.cjs
CHANGED
|
@@ -38,22 +38,31 @@ __export(index_exports, {
|
|
|
38
38
|
DEFAULT_SPOT_URL: () => DEFAULT_SPOT_URL,
|
|
39
39
|
DEFAULT_WAIT_INTERVAL_MS: () => DEFAULT_WAIT_INTERVAL_MS,
|
|
40
40
|
DEFAULT_WAIT_TIMEOUT_MS: () => DEFAULT_WAIT_TIMEOUT_MS,
|
|
41
|
+
Files: () => Files,
|
|
42
|
+
Generations: () => Generations,
|
|
41
43
|
InferenceFailed: () => InferenceFailed,
|
|
42
44
|
InferenceTimeout: () => InferenceTimeout,
|
|
43
45
|
Job: () => Job,
|
|
44
46
|
Models: () => Models,
|
|
47
|
+
PixelFlows: () => PixelFlows,
|
|
48
|
+
PixelflowJob: () => PixelflowJob,
|
|
45
49
|
Segmind: () => Segmind,
|
|
46
50
|
SegmindError: () => SegmindError,
|
|
47
51
|
VERSION: () => VERSION,
|
|
52
|
+
Webhooks: () => Webhooks,
|
|
48
53
|
accounts: () => accounts,
|
|
49
54
|
chat: () => chat,
|
|
50
55
|
chatSync: () => chatSync,
|
|
56
|
+
files: () => files,
|
|
57
|
+
generations: () => generations,
|
|
51
58
|
imageUrl: () => imageUrl,
|
|
52
59
|
models: () => models,
|
|
60
|
+
pixelflows: () => pixelflows,
|
|
53
61
|
run: () => run,
|
|
54
62
|
runSync: () => runSync,
|
|
55
63
|
submit: () => submit,
|
|
56
|
-
submitChat: () => submitChat
|
|
64
|
+
submitChat: () => submitChat,
|
|
65
|
+
webhooks: () => webhooks
|
|
57
66
|
});
|
|
58
67
|
module.exports = __toCommonJS(index_exports);
|
|
59
68
|
|
|
@@ -93,8 +102,8 @@ var APIConnectionError = class extends SegmindError {
|
|
|
93
102
|
var InferenceFailed = class extends SegmindError {
|
|
94
103
|
/** The status body that reported the failure. */
|
|
95
104
|
statusBody;
|
|
96
|
-
constructor(requestId, statusBody) {
|
|
97
|
-
|
|
105
|
+
constructor(requestId, statusBody, detail) {
|
|
106
|
+
detail ??= typeof statusBody.error === "string" ? statusBody.error : void 0;
|
|
98
107
|
super(detail ? `request ${requestId} failed: ${detail}` : `request ${requestId} failed`, {
|
|
99
108
|
detail,
|
|
100
109
|
requestId,
|
|
@@ -331,14 +340,15 @@ var HttpClient = class {
|
|
|
331
340
|
}, timeout);
|
|
332
341
|
const onAbort = () => controller.abort(signal.reason);
|
|
333
342
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
334
|
-
const headers = { ...this.config.headers, ...req.headers };
|
|
335
|
-
if (this.config.apiKey) headers["x-api-key"] = this.config.apiKey;
|
|
343
|
+
const headers = req.bare ? { ...req.headers } : { ...this.config.headers, ...req.headers };
|
|
344
|
+
if (this.config.apiKey && !req.bare) headers["x-api-key"] = this.config.apiKey;
|
|
336
345
|
if (req.body !== void 0) headers["content-type"] = "application/json";
|
|
346
|
+
const body = req.rawBody ?? (req.body === void 0 ? void 0 : JSON.stringify(req.body));
|
|
337
347
|
try {
|
|
338
|
-
return await this.config.fetch(req.url, {
|
|
348
|
+
return await this.config.fetch(withQuery(req.url, req.query), {
|
|
339
349
|
method: req.method,
|
|
340
350
|
headers,
|
|
341
|
-
body
|
|
351
|
+
body,
|
|
342
352
|
signal: controller.signal
|
|
343
353
|
});
|
|
344
354
|
} catch (err) {
|
|
@@ -363,6 +373,15 @@ async function readJson(response) {
|
|
|
363
373
|
});
|
|
364
374
|
}
|
|
365
375
|
}
|
|
376
|
+
function withQuery(url, query) {
|
|
377
|
+
if (!query) return url;
|
|
378
|
+
const params = new URLSearchParams();
|
|
379
|
+
for (const [key, value] of Object.entries(query)) {
|
|
380
|
+
if (value !== void 0) params.set(key, String(value));
|
|
381
|
+
}
|
|
382
|
+
const qs = params.toString();
|
|
383
|
+
return qs ? `${url}${url.includes("?") ? "&" : "?"}${qs}` : url;
|
|
384
|
+
}
|
|
366
385
|
function describe(err) {
|
|
367
386
|
if (err instanceof Error) {
|
|
368
387
|
const cause = err.cause;
|
|
@@ -371,6 +390,182 @@ function describe(err) {
|
|
|
371
390
|
return String(err);
|
|
372
391
|
}
|
|
373
392
|
|
|
393
|
+
// src/core/input.ts
|
|
394
|
+
var MIME_BY_EXTENSION = {
|
|
395
|
+
png: "image/png",
|
|
396
|
+
jpg: "image/jpeg",
|
|
397
|
+
jpeg: "image/jpeg",
|
|
398
|
+
gif: "image/gif",
|
|
399
|
+
webp: "image/webp",
|
|
400
|
+
bmp: "image/bmp",
|
|
401
|
+
svg: "image/svg+xml",
|
|
402
|
+
heic: "image/heic",
|
|
403
|
+
heif: "image/heif",
|
|
404
|
+
tif: "image/tiff",
|
|
405
|
+
tiff: "image/tiff",
|
|
406
|
+
avif: "image/avif",
|
|
407
|
+
mp4: "video/mp4",
|
|
408
|
+
mov: "video/quicktime",
|
|
409
|
+
webm: "video/webm",
|
|
410
|
+
mkv: "video/x-matroska",
|
|
411
|
+
avi: "video/x-msvideo",
|
|
412
|
+
mp3: "audio/mpeg",
|
|
413
|
+
wav: "audio/wav",
|
|
414
|
+
m4a: "audio/mp4",
|
|
415
|
+
ogg: "audio/ogg",
|
|
416
|
+
flac: "audio/flac",
|
|
417
|
+
aac: "audio/aac",
|
|
418
|
+
pdf: "application/pdf",
|
|
419
|
+
txt: "text/plain",
|
|
420
|
+
json: "application/json",
|
|
421
|
+
glb: "model/gltf-binary"
|
|
422
|
+
};
|
|
423
|
+
var EXTENSION_BY_MIME = Object.fromEntries(
|
|
424
|
+
Object.entries(MIME_BY_EXTENSION).map(([ext, mime]) => [mime, ext])
|
|
425
|
+
);
|
|
426
|
+
EXTENSION_BY_MIME["image/jpeg"] = "jpg";
|
|
427
|
+
function mimeFromName(name) {
|
|
428
|
+
const extension = name.includes(".") ? name.split(".").pop().toLowerCase() : "";
|
|
429
|
+
return MIME_BY_EXTENSION[extension];
|
|
430
|
+
}
|
|
431
|
+
async function readFileInput(input, options) {
|
|
432
|
+
let bytes;
|
|
433
|
+
let name = options.filename;
|
|
434
|
+
let mimeType = options.mimeType;
|
|
435
|
+
if (typeof input === "string") {
|
|
436
|
+
if (detectRuntime() !== "node") {
|
|
437
|
+
throw new SegmindError(`${options.caller}: file paths work in Node only; pass a Blob or bytes`);
|
|
438
|
+
}
|
|
439
|
+
const { readFile } = await import("node:fs/promises");
|
|
440
|
+
try {
|
|
441
|
+
bytes = new Uint8Array(await readFile(input));
|
|
442
|
+
} catch (err) {
|
|
443
|
+
throw new SegmindError(`${options.caller}: cannot read ${JSON.stringify(input)}: ${err.message}`, {
|
|
444
|
+
cause: err
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
name ??= input.split(/[\\/]/).pop();
|
|
448
|
+
} else if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
449
|
+
bytes = new Uint8Array(await input.arrayBuffer());
|
|
450
|
+
name ??= input.name || void 0;
|
|
451
|
+
mimeType ??= input.type || void 0;
|
|
452
|
+
} else {
|
|
453
|
+
bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
454
|
+
}
|
|
455
|
+
mimeType ??= name && mimeFromName(name) || sniff(bytes);
|
|
456
|
+
if (!name || !name.includes(".")) {
|
|
457
|
+
const extension = mimeType ? EXTENSION_BY_MIME[mimeType] : void 0;
|
|
458
|
+
if (!extension) {
|
|
459
|
+
throw new SegmindError(`${options.caller}: cannot tell the file type; pass { filename: 'name.ext' } or { mimeType }`);
|
|
460
|
+
}
|
|
461
|
+
name = `${name || "upload"}.${extension}`;
|
|
462
|
+
}
|
|
463
|
+
return { bytes, filename: name, mimeType };
|
|
464
|
+
}
|
|
465
|
+
function sniff(bytes) {
|
|
466
|
+
const at = (offset, signature) => signature.every((b, i) => bytes[offset + i] === b);
|
|
467
|
+
if (at(0, [137, 80, 78, 71])) return "image/png";
|
|
468
|
+
if (at(0, [255, 216, 255])) return "image/jpeg";
|
|
469
|
+
if (at(0, [71, 73, 70, 56])) return "image/gif";
|
|
470
|
+
if (at(0, [82, 73, 70, 70]) && at(8, [87, 69, 66, 80])) return "image/webp";
|
|
471
|
+
if (at(0, [82, 73, 70, 70]) && at(8, [87, 65, 86, 69])) return "audio/wav";
|
|
472
|
+
if (at(4, [102, 116, 121, 112])) return "video/mp4";
|
|
473
|
+
if (at(0, [73, 68, 51]) || at(0, [255, 251])) return "audio/mpeg";
|
|
474
|
+
if (at(0, [37, 80, 68, 70])) return "application/pdf";
|
|
475
|
+
return void 0;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/files.ts
|
|
479
|
+
var Files = class {
|
|
480
|
+
constructor(client2) {
|
|
481
|
+
this.client = client2;
|
|
482
|
+
}
|
|
483
|
+
client;
|
|
484
|
+
async upload(input, options = {}) {
|
|
485
|
+
if (Array.isArray(input)) {
|
|
486
|
+
const uploaded = [];
|
|
487
|
+
for (const file of input) uploaded.push(await this.uploadOne(file, { ...options, filename: void 0 }));
|
|
488
|
+
return uploaded;
|
|
489
|
+
}
|
|
490
|
+
return this.uploadOne(input, options);
|
|
491
|
+
}
|
|
492
|
+
async uploadOne(input, options) {
|
|
493
|
+
const file = await readFileInput(input, { ...options, caller: "files.upload" });
|
|
494
|
+
const { http } = this.client;
|
|
495
|
+
const ticket = await http.json({
|
|
496
|
+
method: "POST",
|
|
497
|
+
url: `${this.client.spotURL}/inference-request/input-presign`,
|
|
498
|
+
body: { filename: file.filename, size: file.bytes.byteLength },
|
|
499
|
+
signal: options.signal,
|
|
500
|
+
retry: true
|
|
501
|
+
});
|
|
502
|
+
if (!ticket.upload_url || !ticket.url) {
|
|
503
|
+
throw new SegmindError("files.upload: the presign response had no upload_url or url", { body: ticket });
|
|
504
|
+
}
|
|
505
|
+
const headers = { ...ticket.required_headers };
|
|
506
|
+
delete headers["Content-Length"];
|
|
507
|
+
delete headers["content-length"];
|
|
508
|
+
await http.request({
|
|
509
|
+
method: "PUT",
|
|
510
|
+
url: ticket.upload_url,
|
|
511
|
+
rawBody: file.bytes,
|
|
512
|
+
headers,
|
|
513
|
+
bare: true,
|
|
514
|
+
signal: options.signal,
|
|
515
|
+
retry: true
|
|
516
|
+
});
|
|
517
|
+
return { url: ticket.url, filename: file.filename, size: file.bytes.byteLength };
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// src/generations.ts
|
|
522
|
+
var Generations = class {
|
|
523
|
+
constructor(client2) {
|
|
524
|
+
this.client = client2;
|
|
525
|
+
}
|
|
526
|
+
client;
|
|
527
|
+
/** Generated outputs, newest first. */
|
|
528
|
+
async list(options = {}) {
|
|
529
|
+
return this.read("/inference-request/generations", options.signal, {
|
|
530
|
+
page: options.page ?? 1,
|
|
531
|
+
model_name: options.modelName,
|
|
532
|
+
start_date: options.startDate,
|
|
533
|
+
end_date: options.endDate,
|
|
534
|
+
user_id: options.userId
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
/** The newest generations for one model. */
|
|
538
|
+
async recent(modelName, options = {}) {
|
|
539
|
+
return this.read("/inference-request/recent-generations", options.signal, { model_name: modelName });
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Requests with their cost, inputs and outcome, failures included. A request
|
|
543
|
+
* with several outputs appears once per output, each carrying its full cost:
|
|
544
|
+
* de-duplicate on `request_id` before summing.
|
|
545
|
+
*/
|
|
546
|
+
async history(options = {}) {
|
|
547
|
+
return this.read("/inference-request/request-history", options.signal, {
|
|
548
|
+
page: options.page ?? 1,
|
|
549
|
+
per_page: options.perPage ?? 20,
|
|
550
|
+
from_date: options.fromDate,
|
|
551
|
+
to_date: options.toDate,
|
|
552
|
+
model_name: options.modelName,
|
|
553
|
+
status: options.status,
|
|
554
|
+
user_id: options.userId,
|
|
555
|
+
user_email: options.userEmail,
|
|
556
|
+
sort_by: options.sortBy,
|
|
557
|
+
sort_order: options.sortOrder
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
/** One of your own requests, of any age. A teammate's request id is not found. */
|
|
561
|
+
async get(requestId, options = {}) {
|
|
562
|
+
return this.read(`/inference-request/request-history/${encodeURIComponent(requestId)}`, options.signal);
|
|
563
|
+
}
|
|
564
|
+
read(path2, signal, query) {
|
|
565
|
+
return this.client.http.json({ method: "GET", url: `${this.client.spotURL}${path2}`, query, signal, retry: true });
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
374
569
|
// src/job.ts
|
|
375
570
|
var DEFAULT_WAIT_TIMEOUT_MS = 6e5;
|
|
376
571
|
var DEFAULT_WAIT_INTERVAL_MS = 1e3;
|
|
@@ -440,8 +635,167 @@ var Job = class {
|
|
|
440
635
|
}
|
|
441
636
|
};
|
|
442
637
|
|
|
638
|
+
// src/pixelflows.ts
|
|
639
|
+
var DONE = /* @__PURE__ */ new Set(["COMPLETED", "SUCCESS", "SUCCEEDED"]);
|
|
640
|
+
var FAILED = /* @__PURE__ */ new Set(["FAILED", "ERROR"]);
|
|
641
|
+
var DEFAULT_TIMEOUT_MS = 6e5;
|
|
642
|
+
var DEFAULT_INTERVAL_MS = 2e3;
|
|
643
|
+
var PixelFlows = class {
|
|
644
|
+
constructor(client2) {
|
|
645
|
+
this.client = client2;
|
|
646
|
+
}
|
|
647
|
+
client;
|
|
648
|
+
/** Runs a workflow and waits for its outputs. */
|
|
649
|
+
async run(slug, inputs = {}, options = {}) {
|
|
650
|
+
const job = await this.submit(slug, inputs, options);
|
|
651
|
+
return job.wait(options);
|
|
652
|
+
}
|
|
653
|
+
/** Starts a workflow and returns a job to wait on. Never retried: a run bills. */
|
|
654
|
+
async submit(slug, inputs = {}, options = {}) {
|
|
655
|
+
const clean = slug.trim().replace(/^\/+/, "");
|
|
656
|
+
if (!clean) throw new SegmindError("pixelflows: a workflow slug is required");
|
|
657
|
+
const route = options.engine === "v1" ? "/workflows/" : "/workflows/v2/";
|
|
658
|
+
const body = await this.client.http.json({
|
|
659
|
+
method: "POST",
|
|
660
|
+
url: `${this.client.baseURL}${route}${clean}`,
|
|
661
|
+
body: inputs,
|
|
662
|
+
signal: options.signal
|
|
663
|
+
});
|
|
664
|
+
const requestId = body.request_id;
|
|
665
|
+
if (typeof requestId !== "string" || !requestId) {
|
|
666
|
+
throw new SegmindError("pixelflows: the submit response had no request_id", { body });
|
|
667
|
+
}
|
|
668
|
+
return new PixelflowJob(this, requestId);
|
|
669
|
+
}
|
|
670
|
+
/** A run's current state, without waiting. */
|
|
671
|
+
async getStatus(requestId, options = {}) {
|
|
672
|
+
const raw = await this.client.http.json({
|
|
673
|
+
method: "GET",
|
|
674
|
+
url: `${this.client.baseURL}/workflows/request/${encodeURIComponent(requestId)}`,
|
|
675
|
+
signal: options.signal,
|
|
676
|
+
retry: true
|
|
677
|
+
});
|
|
678
|
+
return toStatus(raw);
|
|
679
|
+
}
|
|
680
|
+
/** Waits for a run started earlier, by its request id. */
|
|
681
|
+
async poll(requestId, options = {}) {
|
|
682
|
+
return new PixelflowJob(this, requestId).wait(options);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
var PixelflowJob = class {
|
|
686
|
+
constructor(pixelflows2, requestId) {
|
|
687
|
+
this.pixelflows = pixelflows2;
|
|
688
|
+
this.requestId = requestId;
|
|
689
|
+
}
|
|
690
|
+
pixelflows;
|
|
691
|
+
requestId;
|
|
692
|
+
status(options = {}) {
|
|
693
|
+
return this.pixelflows.getStatus(this.requestId, options);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Polls until the run finishes. Throws `InferenceFailed` (its `detail` names
|
|
697
|
+
* the failing node) or `InferenceTimeout`; the run may still finish after a
|
|
698
|
+
* timeout, so `wait()` can be called again.
|
|
699
|
+
*/
|
|
700
|
+
async wait(options = {}) {
|
|
701
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
702
|
+
const interval = options.interval ?? DEFAULT_INTERVAL_MS;
|
|
703
|
+
const started = Date.now();
|
|
704
|
+
for (; ; ) {
|
|
705
|
+
const state = await this.status({ signal: options.signal });
|
|
706
|
+
if (DONE.has(state.status)) return state;
|
|
707
|
+
if (FAILED.has(state.status)) throw new InferenceFailed(this.requestId, state.raw, failureDetail(state.raw));
|
|
708
|
+
const elapsed = Date.now() - started;
|
|
709
|
+
if (elapsed + interval > timeout) throw new InferenceTimeout(this.requestId, elapsed);
|
|
710
|
+
await sleep(interval, options.signal);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
function toStatus(body) {
|
|
715
|
+
const raw = { ...body };
|
|
716
|
+
if (typeof raw.output === "string") {
|
|
717
|
+
try {
|
|
718
|
+
raw.output = JSON.parse(raw.output);
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
const status = String(raw.status ?? "UNKNOWN").toUpperCase();
|
|
723
|
+
return { status, outputs: DONE.has(status) ? flattenOutputs(raw.output) : {}, raw };
|
|
724
|
+
}
|
|
725
|
+
function flattenOutputs(output) {
|
|
726
|
+
const outputs = {};
|
|
727
|
+
if (Array.isArray(output)) {
|
|
728
|
+
for (const item of output) {
|
|
729
|
+
if (item && typeof item === "object" && typeof item.keyname === "string") {
|
|
730
|
+
const { keyname, value } = item;
|
|
731
|
+
outputs[keyname] = unwrap(value);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
} else if (output && typeof output === "object") {
|
|
735
|
+
for (const [key, value] of Object.entries(output)) outputs[key] = unwrap(value);
|
|
736
|
+
}
|
|
737
|
+
return outputs;
|
|
738
|
+
}
|
|
739
|
+
function unwrap(value) {
|
|
740
|
+
if (value && typeof value === "object" && "data" in value) return value.data;
|
|
741
|
+
return value;
|
|
742
|
+
}
|
|
743
|
+
function failureDetail(body) {
|
|
744
|
+
const error = body.error_message ?? body.error;
|
|
745
|
+
if (typeof error === "string") return error;
|
|
746
|
+
if (error && typeof error === "object") {
|
|
747
|
+
const e = error;
|
|
748
|
+
const text = e.details ?? e.error_message ?? e.error;
|
|
749
|
+
if (typeof text === "string") return text;
|
|
750
|
+
}
|
|
751
|
+
return void 0;
|
|
752
|
+
}
|
|
753
|
+
|
|
443
754
|
// src/version.ts
|
|
444
|
-
var VERSION = "0.1
|
|
755
|
+
var VERSION = "0.2.1";
|
|
756
|
+
|
|
757
|
+
// src/webhooks.ts
|
|
758
|
+
var Webhooks = class {
|
|
759
|
+
constructor(client2) {
|
|
760
|
+
this.client = client2;
|
|
761
|
+
}
|
|
762
|
+
client;
|
|
763
|
+
/** Every registration; `[]` when you have none. */
|
|
764
|
+
async get(options = {}) {
|
|
765
|
+
return this.call("GET", "/get", { signal: options.signal, retry: true });
|
|
766
|
+
}
|
|
767
|
+
/** Registers an endpoint for the given event types. A second active registration for the same types is refused (409). */
|
|
768
|
+
async add(webhookUrl, eventTypes, options = {}) {
|
|
769
|
+
return this.call("POST", "/add", {
|
|
770
|
+
body: { webhook_url: webhookUrl, event: { types: requireTypes(eventTypes) } },
|
|
771
|
+
signal: options.signal
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
/** Changes a registration's URL and event types. */
|
|
775
|
+
async update(webhookId, webhookUrl, eventTypes, options = {}) {
|
|
776
|
+
return this.call("POST", "/update", {
|
|
777
|
+
body: { webhook_id: webhookId, webhook_url: webhookUrl, event: { types: requireTypes(eventTypes) } },
|
|
778
|
+
signal: options.signal
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
/** Deactivates a registration. */
|
|
782
|
+
async delete(webhookId, options = {}) {
|
|
783
|
+
return this.call("GET", "/inactive", { query: { webhook_id: webhookId }, signal: options.signal, retry: true });
|
|
784
|
+
}
|
|
785
|
+
/** Delivery history for one registration. */
|
|
786
|
+
async logs(webhookId, options = {}) {
|
|
787
|
+
return this.call("GET", "/dispatch-logs", { query: { webhook_id: webhookId }, signal: options.signal, retry: true });
|
|
788
|
+
}
|
|
789
|
+
call(method, path2, req) {
|
|
790
|
+
return this.client.http.json({ method, url: `${this.client.spotURL}/webhook${path2}`, ...req });
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
function requireTypes(eventTypes) {
|
|
794
|
+
if (!Array.isArray(eventTypes) || eventTypes.length === 0) {
|
|
795
|
+
throw new SegmindError('webhooks: pass at least one event type, e.g. ["PIXELFLOW"]');
|
|
796
|
+
}
|
|
797
|
+
return eventTypes;
|
|
798
|
+
}
|
|
445
799
|
|
|
446
800
|
// src/client.ts
|
|
447
801
|
var DEFAULT_BASE_URL = "https://api.segmind.com";
|
|
@@ -453,6 +807,14 @@ var Segmind = class {
|
|
|
453
807
|
consoleURL;
|
|
454
808
|
models;
|
|
455
809
|
accounts;
|
|
810
|
+
/** Upload files to pass as model inputs. */
|
|
811
|
+
files;
|
|
812
|
+
/** Your generations and request history. */
|
|
813
|
+
generations;
|
|
814
|
+
/** Your webhook registrations. */
|
|
815
|
+
webhooks;
|
|
816
|
+
/** Run published PixelFlow workflows. */
|
|
817
|
+
pixelflows;
|
|
456
818
|
/** @internal */
|
|
457
819
|
http;
|
|
458
820
|
constructor(options = {}) {
|
|
@@ -483,6 +845,10 @@ var Segmind = class {
|
|
|
483
845
|
});
|
|
484
846
|
this.models = new Models(this);
|
|
485
847
|
this.accounts = new Accounts(this);
|
|
848
|
+
this.files = new Files(this);
|
|
849
|
+
this.generations = new Generations(this);
|
|
850
|
+
this.webhooks = new Webhooks(this);
|
|
851
|
+
this.pixelflows = new PixelFlows(this);
|
|
486
852
|
}
|
|
487
853
|
/**
|
|
488
854
|
* Runs a model and returns its result: submits to the v2 queue, then polls
|
|
@@ -628,62 +994,12 @@ function trimBase(url) {
|
|
|
628
994
|
}
|
|
629
995
|
|
|
630
996
|
// src/image-url.ts
|
|
631
|
-
var MIME_BY_EXTENSION = {
|
|
632
|
-
png: "image/png",
|
|
633
|
-
jpg: "image/jpeg",
|
|
634
|
-
jpeg: "image/jpeg",
|
|
635
|
-
gif: "image/gif",
|
|
636
|
-
webp: "image/webp",
|
|
637
|
-
bmp: "image/bmp",
|
|
638
|
-
svg: "image/svg+xml",
|
|
639
|
-
heic: "image/heic",
|
|
640
|
-
heif: "image/heif",
|
|
641
|
-
tif: "image/tiff",
|
|
642
|
-
tiff: "image/tiff",
|
|
643
|
-
avif: "image/avif"
|
|
644
|
-
};
|
|
645
997
|
async function imageUrl(input, options = {}) {
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
async function toDataUri(input, mimeType) {
|
|
649
|
-
if (typeof input === "string") {
|
|
650
|
-
if (/^(https?:|data:)/i.test(input)) return input;
|
|
651
|
-
if (detectRuntime() !== "node") {
|
|
652
|
-
throw new SegmindError(`imageUrl: ${JSON.stringify(input)} is not an http(s) or data: URL (file paths work in Node only)`);
|
|
653
|
-
}
|
|
654
|
-
return fileToDataUri(input, mimeType);
|
|
998
|
+
if (typeof input === "string" && /^(https?:|data:)/i.test(input)) {
|
|
999
|
+
return { type: "image_url", image_url: { url: input } };
|
|
655
1000
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
return dataUri(bytes2, mimeType ?? (input.type || sniff(bytes2)));
|
|
659
|
-
}
|
|
660
|
-
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
661
|
-
return dataUri(bytes, mimeType ?? sniff(bytes));
|
|
662
|
-
}
|
|
663
|
-
async function fileToDataUri(path2, mimeType) {
|
|
664
|
-
const { readFile } = await import("node:fs/promises");
|
|
665
|
-
let bytes;
|
|
666
|
-
try {
|
|
667
|
-
bytes = new Uint8Array(await readFile(path2));
|
|
668
|
-
} catch (err) {
|
|
669
|
-
throw new SegmindError(`imageUrl: cannot read ${JSON.stringify(path2)}: ${err.message}`, { cause: err });
|
|
670
|
-
}
|
|
671
|
-
const extension = path2.split(".").pop()?.toLowerCase() ?? "";
|
|
672
|
-
return dataUri(bytes, mimeType ?? MIME_BY_EXTENSION[extension] ?? sniff(bytes));
|
|
673
|
-
}
|
|
674
|
-
function dataUri(bytes, mimeType) {
|
|
675
|
-
if (!mimeType) {
|
|
676
|
-
throw new SegmindError("imageUrl: cannot tell the image type; pass { mimeType }");
|
|
677
|
-
}
|
|
678
|
-
return `data:${mimeType};base64,${toBase64(bytes)}`;
|
|
679
|
-
}
|
|
680
|
-
function sniff(bytes) {
|
|
681
|
-
const at = (offset, signature) => signature.every((b, i) => bytes[offset + i] === b);
|
|
682
|
-
if (at(0, [137, 80, 78, 71])) return "image/png";
|
|
683
|
-
if (at(0, [255, 216, 255])) return "image/jpeg";
|
|
684
|
-
if (at(0, [71, 73, 70, 56])) return "image/gif";
|
|
685
|
-
if (at(0, [82, 73, 70, 70]) && at(8, [87, 69, 66, 80])) return "image/webp";
|
|
686
|
-
return void 0;
|
|
1001
|
+
const file = await readFileInput(input, { mimeType: options.mimeType, caller: "imageUrl" });
|
|
1002
|
+
return { type: "image_url", image_url: { url: `data:${file.mimeType};base64,${toBase64(file.bytes)}` } };
|
|
687
1003
|
}
|
|
688
1004
|
|
|
689
1005
|
// src/index.ts
|
|
@@ -704,4 +1020,26 @@ var models = {
|
|
|
704
1020
|
var accounts = {
|
|
705
1021
|
current: (options) => client().accounts.current(options)
|
|
706
1022
|
};
|
|
1023
|
+
var files = {
|
|
1024
|
+
upload: ((input, options) => client().files.upload(input, options))
|
|
1025
|
+
};
|
|
1026
|
+
var generations = {
|
|
1027
|
+
list: (options) => client().generations.list(options),
|
|
1028
|
+
recent: (modelName, options) => client().generations.recent(modelName, options),
|
|
1029
|
+
history: (options) => client().generations.history(options),
|
|
1030
|
+
get: (requestId, options) => client().generations.get(requestId, options)
|
|
1031
|
+
};
|
|
1032
|
+
var webhooks = {
|
|
1033
|
+
get: (options) => client().webhooks.get(options),
|
|
1034
|
+
add: (webhookUrl, eventTypes, options) => client().webhooks.add(webhookUrl, eventTypes, options),
|
|
1035
|
+
update: (webhookId, webhookUrl, eventTypes, options) => client().webhooks.update(webhookId, webhookUrl, eventTypes, options),
|
|
1036
|
+
delete: (webhookId, options) => client().webhooks.delete(webhookId, options),
|
|
1037
|
+
logs: (webhookId, options) => client().webhooks.logs(webhookId, options)
|
|
1038
|
+
};
|
|
1039
|
+
var pixelflows = {
|
|
1040
|
+
run: (slug, inputs, options) => client().pixelflows.run(slug, inputs, options),
|
|
1041
|
+
submit: (slug, inputs, options) => client().pixelflows.submit(slug, inputs, options),
|
|
1042
|
+
getStatus: (requestId, options) => client().pixelflows.getStatus(requestId, options),
|
|
1043
|
+
poll: (requestId, options) => client().pixelflows.poll(requestId, options)
|
|
1044
|
+
};
|
|
707
1045
|
//# sourceMappingURL=index.cjs.map
|