segmind 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -34,8 +34,8 @@ var APIConnectionError = class extends SegmindError {
34
34
  var InferenceFailed = class extends SegmindError {
35
35
  /** The status body that reported the failure. */
36
36
  statusBody;
37
- constructor(requestId, statusBody) {
38
- const detail = typeof statusBody.error === "string" ? statusBody.error : void 0;
37
+ constructor(requestId, statusBody, detail) {
38
+ detail ??= typeof statusBody.error === "string" ? statusBody.error : void 0;
39
39
  super(detail ? `request ${requestId} failed: ${detail}` : `request ${requestId} failed`, {
40
40
  detail,
41
41
  requestId,
@@ -272,14 +272,15 @@ var HttpClient = class {
272
272
  }, timeout);
273
273
  const onAbort = () => controller.abort(signal.reason);
274
274
  signal?.addEventListener("abort", onAbort, { once: true });
275
- const headers = { ...this.config.headers, ...req.headers };
276
- if (this.config.apiKey) headers["x-api-key"] = this.config.apiKey;
275
+ const headers = req.bare ? { ...req.headers } : { ...this.config.headers, ...req.headers };
276
+ if (this.config.apiKey && !req.bare) headers["x-api-key"] = this.config.apiKey;
277
277
  if (req.body !== void 0) headers["content-type"] = "application/json";
278
+ const body = req.rawBody ?? (req.body === void 0 ? void 0 : JSON.stringify(req.body));
278
279
  try {
279
- return await this.config.fetch(req.url, {
280
+ return await this.config.fetch(withQuery(req.url, req.query), {
280
281
  method: req.method,
281
282
  headers,
282
- body: req.body === void 0 ? void 0 : JSON.stringify(req.body),
283
+ body,
283
284
  signal: controller.signal
284
285
  });
285
286
  } catch (err) {
@@ -304,6 +305,15 @@ async function readJson(response) {
304
305
  });
305
306
  }
306
307
  }
308
+ function withQuery(url, query) {
309
+ if (!query) return url;
310
+ const params = new URLSearchParams();
311
+ for (const [key, value] of Object.entries(query)) {
312
+ if (value !== void 0) params.set(key, String(value));
313
+ }
314
+ const qs = params.toString();
315
+ return qs ? `${url}${url.includes("?") ? "&" : "?"}${qs}` : url;
316
+ }
307
317
  function describe(err) {
308
318
  if (err instanceof Error) {
309
319
  const cause = err.cause;
@@ -312,6 +322,182 @@ function describe(err) {
312
322
  return String(err);
313
323
  }
314
324
 
325
+ // src/core/input.ts
326
+ var MIME_BY_EXTENSION = {
327
+ png: "image/png",
328
+ jpg: "image/jpeg",
329
+ jpeg: "image/jpeg",
330
+ gif: "image/gif",
331
+ webp: "image/webp",
332
+ bmp: "image/bmp",
333
+ svg: "image/svg+xml",
334
+ heic: "image/heic",
335
+ heif: "image/heif",
336
+ tif: "image/tiff",
337
+ tiff: "image/tiff",
338
+ avif: "image/avif",
339
+ mp4: "video/mp4",
340
+ mov: "video/quicktime",
341
+ webm: "video/webm",
342
+ mkv: "video/x-matroska",
343
+ avi: "video/x-msvideo",
344
+ mp3: "audio/mpeg",
345
+ wav: "audio/wav",
346
+ m4a: "audio/mp4",
347
+ ogg: "audio/ogg",
348
+ flac: "audio/flac",
349
+ aac: "audio/aac",
350
+ pdf: "application/pdf",
351
+ txt: "text/plain",
352
+ json: "application/json",
353
+ glb: "model/gltf-binary"
354
+ };
355
+ var EXTENSION_BY_MIME = Object.fromEntries(
356
+ Object.entries(MIME_BY_EXTENSION).map(([ext, mime]) => [mime, ext])
357
+ );
358
+ EXTENSION_BY_MIME["image/jpeg"] = "jpg";
359
+ function mimeFromName(name) {
360
+ const extension = name.includes(".") ? name.split(".").pop().toLowerCase() : "";
361
+ return MIME_BY_EXTENSION[extension];
362
+ }
363
+ async function readFileInput(input, options) {
364
+ let bytes;
365
+ let name = options.filename;
366
+ let mimeType = options.mimeType;
367
+ if (typeof input === "string") {
368
+ if (detectRuntime() !== "node") {
369
+ throw new SegmindError(`${options.caller}: file paths work in Node only; pass a Blob or bytes`);
370
+ }
371
+ const { readFile } = await import("node:fs/promises");
372
+ try {
373
+ bytes = new Uint8Array(await readFile(input));
374
+ } catch (err) {
375
+ throw new SegmindError(`${options.caller}: cannot read ${JSON.stringify(input)}: ${err.message}`, {
376
+ cause: err
377
+ });
378
+ }
379
+ name ??= input.split(/[\\/]/).pop();
380
+ } else if (typeof Blob !== "undefined" && input instanceof Blob) {
381
+ bytes = new Uint8Array(await input.arrayBuffer());
382
+ name ??= input.name || void 0;
383
+ mimeType ??= input.type || void 0;
384
+ } else {
385
+ bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
386
+ }
387
+ mimeType ??= name && mimeFromName(name) || sniff(bytes);
388
+ if (!name || !name.includes(".")) {
389
+ const extension = mimeType ? EXTENSION_BY_MIME[mimeType] : void 0;
390
+ if (!extension) {
391
+ throw new SegmindError(`${options.caller}: cannot tell the file type; pass { filename: 'name.ext' } or { mimeType }`);
392
+ }
393
+ name = `${name || "upload"}.${extension}`;
394
+ }
395
+ return { bytes, filename: name, mimeType };
396
+ }
397
+ function sniff(bytes) {
398
+ const at = (offset, signature) => signature.every((b, i) => bytes[offset + i] === b);
399
+ if (at(0, [137, 80, 78, 71])) return "image/png";
400
+ if (at(0, [255, 216, 255])) return "image/jpeg";
401
+ if (at(0, [71, 73, 70, 56])) return "image/gif";
402
+ if (at(0, [82, 73, 70, 70]) && at(8, [87, 69, 66, 80])) return "image/webp";
403
+ if (at(0, [82, 73, 70, 70]) && at(8, [87, 65, 86, 69])) return "audio/wav";
404
+ if (at(4, [102, 116, 121, 112])) return "video/mp4";
405
+ if (at(0, [73, 68, 51]) || at(0, [255, 251])) return "audio/mpeg";
406
+ if (at(0, [37, 80, 68, 70])) return "application/pdf";
407
+ return void 0;
408
+ }
409
+
410
+ // src/files.ts
411
+ var Files = class {
412
+ constructor(client2) {
413
+ this.client = client2;
414
+ }
415
+ client;
416
+ async upload(input, options = {}) {
417
+ if (Array.isArray(input)) {
418
+ const uploaded = [];
419
+ for (const file of input) uploaded.push(await this.uploadOne(file, { ...options, filename: void 0 }));
420
+ return uploaded;
421
+ }
422
+ return this.uploadOne(input, options);
423
+ }
424
+ async uploadOne(input, options) {
425
+ const file = await readFileInput(input, { ...options, caller: "files.upload" });
426
+ const { http } = this.client;
427
+ const ticket = await http.json({
428
+ method: "POST",
429
+ url: `${this.client.spotURL}/inference-request/input-presign`,
430
+ body: { filename: file.filename, size: file.bytes.byteLength },
431
+ signal: options.signal,
432
+ retry: true
433
+ });
434
+ if (!ticket.upload_url || !ticket.url) {
435
+ throw new SegmindError("files.upload: the presign response had no upload_url or url", { body: ticket });
436
+ }
437
+ const headers = { ...ticket.required_headers };
438
+ delete headers["Content-Length"];
439
+ delete headers["content-length"];
440
+ await http.request({
441
+ method: "PUT",
442
+ url: ticket.upload_url,
443
+ rawBody: file.bytes,
444
+ headers,
445
+ bare: true,
446
+ signal: options.signal,
447
+ retry: true
448
+ });
449
+ return { url: ticket.url, filename: file.filename, size: file.bytes.byteLength };
450
+ }
451
+ };
452
+
453
+ // src/generations.ts
454
+ var Generations = class {
455
+ constructor(client2) {
456
+ this.client = client2;
457
+ }
458
+ client;
459
+ /** Generated outputs, newest first. */
460
+ async list(options = {}) {
461
+ return this.read("/inference-request/generations", options.signal, {
462
+ page: options.page ?? 1,
463
+ model_name: options.modelName,
464
+ start_date: options.startDate,
465
+ end_date: options.endDate,
466
+ user_id: options.userId
467
+ });
468
+ }
469
+ /** The newest generations for one model. */
470
+ async recent(modelName, options = {}) {
471
+ return this.read("/inference-request/recent-generations", options.signal, { model_name: modelName });
472
+ }
473
+ /**
474
+ * Requests with their cost, inputs and outcome, failures included. A request
475
+ * with several outputs appears once per output, each carrying its full cost:
476
+ * de-duplicate on `request_id` before summing.
477
+ */
478
+ async history(options = {}) {
479
+ return this.read("/inference-request/request-history", options.signal, {
480
+ page: options.page ?? 1,
481
+ per_page: options.perPage ?? 20,
482
+ from_date: options.fromDate,
483
+ to_date: options.toDate,
484
+ model_name: options.modelName,
485
+ status: options.status,
486
+ user_id: options.userId,
487
+ user_email: options.userEmail,
488
+ sort_by: options.sortBy,
489
+ sort_order: options.sortOrder
490
+ });
491
+ }
492
+ /** One of your own requests, of any age. A teammate's request id is not found. */
493
+ async get(requestId, options = {}) {
494
+ return this.read(`/inference-request/request-history/${encodeURIComponent(requestId)}`, options.signal);
495
+ }
496
+ read(path2, signal, query) {
497
+ return this.client.http.json({ method: "GET", url: `${this.client.spotURL}${path2}`, query, signal, retry: true });
498
+ }
499
+ };
500
+
315
501
  // src/job.ts
316
502
  var DEFAULT_WAIT_TIMEOUT_MS = 6e5;
317
503
  var DEFAULT_WAIT_INTERVAL_MS = 1e3;
@@ -381,8 +567,167 @@ var Job = class {
381
567
  }
382
568
  };
383
569
 
570
+ // src/pixelflows.ts
571
+ var DONE = /* @__PURE__ */ new Set(["COMPLETED", "SUCCESS", "SUCCEEDED"]);
572
+ var FAILED = /* @__PURE__ */ new Set(["FAILED", "ERROR"]);
573
+ var DEFAULT_TIMEOUT_MS = 6e5;
574
+ var DEFAULT_INTERVAL_MS = 2e3;
575
+ var PixelFlows = class {
576
+ constructor(client2) {
577
+ this.client = client2;
578
+ }
579
+ client;
580
+ /** Runs a workflow and waits for its outputs. */
581
+ async run(slug, inputs = {}, options = {}) {
582
+ const job = await this.submit(slug, inputs, options);
583
+ return job.wait(options);
584
+ }
585
+ /** Starts a workflow and returns a job to wait on. Never retried: a run bills. */
586
+ async submit(slug, inputs = {}, options = {}) {
587
+ const clean = slug.trim().replace(/^\/+/, "");
588
+ if (!clean) throw new SegmindError("pixelflows: a workflow slug is required");
589
+ const route = options.engine === "v1" ? "/workflows/" : "/workflows/v2/";
590
+ const body = await this.client.http.json({
591
+ method: "POST",
592
+ url: `${this.client.baseURL}${route}${clean}`,
593
+ body: inputs,
594
+ signal: options.signal
595
+ });
596
+ const requestId = body.request_id;
597
+ if (typeof requestId !== "string" || !requestId) {
598
+ throw new SegmindError("pixelflows: the submit response had no request_id", { body });
599
+ }
600
+ return new PixelflowJob(this, requestId);
601
+ }
602
+ /** A run's current state, without waiting. */
603
+ async getStatus(requestId, options = {}) {
604
+ const raw = await this.client.http.json({
605
+ method: "GET",
606
+ url: `${this.client.baseURL}/workflows/request/${encodeURIComponent(requestId)}`,
607
+ signal: options.signal,
608
+ retry: true
609
+ });
610
+ return toStatus(raw);
611
+ }
612
+ /** Waits for a run started earlier, by its request id. */
613
+ async poll(requestId, options = {}) {
614
+ return new PixelflowJob(this, requestId).wait(options);
615
+ }
616
+ };
617
+ var PixelflowJob = class {
618
+ constructor(pixelflows2, requestId) {
619
+ this.pixelflows = pixelflows2;
620
+ this.requestId = requestId;
621
+ }
622
+ pixelflows;
623
+ requestId;
624
+ status(options = {}) {
625
+ return this.pixelflows.getStatus(this.requestId, options);
626
+ }
627
+ /**
628
+ * Polls until the run finishes. Throws `InferenceFailed` (its `detail` names
629
+ * the failing node) or `InferenceTimeout`; the run may still finish after a
630
+ * timeout, so `wait()` can be called again.
631
+ */
632
+ async wait(options = {}) {
633
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
634
+ const interval = options.interval ?? DEFAULT_INTERVAL_MS;
635
+ const started = Date.now();
636
+ for (; ; ) {
637
+ const state = await this.status({ signal: options.signal });
638
+ if (DONE.has(state.status)) return state;
639
+ if (FAILED.has(state.status)) throw new InferenceFailed(this.requestId, state.raw, failureDetail(state.raw));
640
+ const elapsed = Date.now() - started;
641
+ if (elapsed + interval > timeout) throw new InferenceTimeout(this.requestId, elapsed);
642
+ await sleep(interval, options.signal);
643
+ }
644
+ }
645
+ };
646
+ function toStatus(body) {
647
+ const raw = { ...body };
648
+ if (typeof raw.output === "string") {
649
+ try {
650
+ raw.output = JSON.parse(raw.output);
651
+ } catch {
652
+ }
653
+ }
654
+ const status = String(raw.status ?? "UNKNOWN").toUpperCase();
655
+ return { status, outputs: DONE.has(status) ? flattenOutputs(raw.output) : {}, raw };
656
+ }
657
+ function flattenOutputs(output) {
658
+ const outputs = {};
659
+ if (Array.isArray(output)) {
660
+ for (const item of output) {
661
+ if (item && typeof item === "object" && typeof item.keyname === "string") {
662
+ const { keyname, value } = item;
663
+ outputs[keyname] = unwrap(value);
664
+ }
665
+ }
666
+ } else if (output && typeof output === "object") {
667
+ for (const [key, value] of Object.entries(output)) outputs[key] = unwrap(value);
668
+ }
669
+ return outputs;
670
+ }
671
+ function unwrap(value) {
672
+ if (value && typeof value === "object" && "data" in value) return value.data;
673
+ return value;
674
+ }
675
+ function failureDetail(body) {
676
+ const error = body.error_message ?? body.error;
677
+ if (typeof error === "string") return error;
678
+ if (error && typeof error === "object") {
679
+ const e = error;
680
+ const text = e.details ?? e.error_message ?? e.error;
681
+ if (typeof text === "string") return text;
682
+ }
683
+ return void 0;
684
+ }
685
+
384
686
  // src/version.ts
385
- var VERSION = "0.1.0";
687
+ var VERSION = "0.2.0";
688
+
689
+ // src/webhooks.ts
690
+ var Webhooks = class {
691
+ constructor(client2) {
692
+ this.client = client2;
693
+ }
694
+ client;
695
+ /** Every registration; `[]` when you have none. */
696
+ async get(options = {}) {
697
+ return this.call("GET", "/get", { signal: options.signal, retry: true });
698
+ }
699
+ /** Registers an endpoint for the given event types. */
700
+ async add(webhookUrl, eventTypes, options = {}) {
701
+ return this.call("POST", "/add", {
702
+ body: { webhook_url: webhookUrl, event: { types: requireTypes(eventTypes) } },
703
+ signal: options.signal
704
+ });
705
+ }
706
+ /** Changes a registration's URL and event types. */
707
+ async update(webhookId, webhookUrl, eventTypes, options = {}) {
708
+ return this.call("POST", "/update", {
709
+ body: { webhook_id: webhookId, webhook_url: webhookUrl, event: { types: requireTypes(eventTypes) } },
710
+ signal: options.signal
711
+ });
712
+ }
713
+ /** Deactivates a registration. */
714
+ async delete(webhookId, options = {}) {
715
+ return this.call("GET", "/inactive", { query: { webhook_id: webhookId }, signal: options.signal, retry: true });
716
+ }
717
+ /** Delivery history for one registration. */
718
+ async logs(webhookId, options = {}) {
719
+ return this.call("GET", "/dispatch-logs", { query: { webhook_id: webhookId }, signal: options.signal, retry: true });
720
+ }
721
+ call(method, path2, req) {
722
+ return this.client.http.json({ method, url: `${this.client.spotURL}/webhook${path2}`, ...req });
723
+ }
724
+ };
725
+ function requireTypes(eventTypes) {
726
+ if (!Array.isArray(eventTypes) || eventTypes.length === 0) {
727
+ throw new SegmindError('webhooks: pass at least one event type, e.g. ["PIXELFLOW_API_RUN"]');
728
+ }
729
+ return eventTypes;
730
+ }
386
731
 
387
732
  // src/client.ts
388
733
  var DEFAULT_BASE_URL = "https://api.segmind.com";
@@ -394,6 +739,14 @@ var Segmind = class {
394
739
  consoleURL;
395
740
  models;
396
741
  accounts;
742
+ /** Upload files to pass as model inputs. */
743
+ files;
744
+ /** Your generations and request history. */
745
+ generations;
746
+ /** Your webhook registrations. */
747
+ webhooks;
748
+ /** Run published PixelFlow workflows. */
749
+ pixelflows;
397
750
  /** @internal */
398
751
  http;
399
752
  constructor(options = {}) {
@@ -424,6 +777,10 @@ var Segmind = class {
424
777
  });
425
778
  this.models = new Models(this);
426
779
  this.accounts = new Accounts(this);
780
+ this.files = new Files(this);
781
+ this.generations = new Generations(this);
782
+ this.webhooks = new Webhooks(this);
783
+ this.pixelflows = new PixelFlows(this);
427
784
  }
428
785
  /**
429
786
  * Runs a model and returns its result: submits to the v2 queue, then polls
@@ -569,62 +926,12 @@ function trimBase(url) {
569
926
  }
570
927
 
571
928
  // src/image-url.ts
572
- var MIME_BY_EXTENSION = {
573
- png: "image/png",
574
- jpg: "image/jpeg",
575
- jpeg: "image/jpeg",
576
- gif: "image/gif",
577
- webp: "image/webp",
578
- bmp: "image/bmp",
579
- svg: "image/svg+xml",
580
- heic: "image/heic",
581
- heif: "image/heif",
582
- tif: "image/tiff",
583
- tiff: "image/tiff",
584
- avif: "image/avif"
585
- };
586
929
  async function imageUrl(input, options = {}) {
587
- return { type: "image_url", image_url: { url: await toDataUri(input, options.mimeType) } };
588
- }
589
- async function toDataUri(input, mimeType) {
590
- if (typeof input === "string") {
591
- if (/^(https?:|data:)/i.test(input)) return input;
592
- if (detectRuntime() !== "node") {
593
- throw new SegmindError(`imageUrl: ${JSON.stringify(input)} is not an http(s) or data: URL (file paths work in Node only)`);
594
- }
595
- return fileToDataUri(input, mimeType);
930
+ if (typeof input === "string" && /^(https?:|data:)/i.test(input)) {
931
+ return { type: "image_url", image_url: { url: input } };
596
932
  }
597
- if (typeof Blob !== "undefined" && input instanceof Blob) {
598
- const bytes2 = new Uint8Array(await input.arrayBuffer());
599
- return dataUri(bytes2, mimeType ?? (input.type || sniff(bytes2)));
600
- }
601
- const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
602
- return dataUri(bytes, mimeType ?? sniff(bytes));
603
- }
604
- async function fileToDataUri(path2, mimeType) {
605
- const { readFile } = await import("node:fs/promises");
606
- let bytes;
607
- try {
608
- bytes = new Uint8Array(await readFile(path2));
609
- } catch (err) {
610
- throw new SegmindError(`imageUrl: cannot read ${JSON.stringify(path2)}: ${err.message}`, { cause: err });
611
- }
612
- const extension = path2.split(".").pop()?.toLowerCase() ?? "";
613
- return dataUri(bytes, mimeType ?? MIME_BY_EXTENSION[extension] ?? sniff(bytes));
614
- }
615
- function dataUri(bytes, mimeType) {
616
- if (!mimeType) {
617
- throw new SegmindError("imageUrl: cannot tell the image type; pass { mimeType }");
618
- }
619
- return `data:${mimeType};base64,${toBase64(bytes)}`;
620
- }
621
- function sniff(bytes) {
622
- const at = (offset, signature) => signature.every((b, i) => bytes[offset + i] === b);
623
- if (at(0, [137, 80, 78, 71])) return "image/png";
624
- if (at(0, [255, 216, 255])) return "image/jpeg";
625
- if (at(0, [71, 73, 70, 56])) return "image/gif";
626
- if (at(0, [82, 73, 70, 70]) && at(8, [87, 69, 66, 80])) return "image/webp";
627
- return void 0;
933
+ const file = await readFileInput(input, { mimeType: options.mimeType, caller: "imageUrl" });
934
+ return { type: "image_url", image_url: { url: `data:${file.mimeType};base64,${toBase64(file.bytes)}` } };
628
935
  }
629
936
 
630
937
  // src/index.ts
@@ -645,6 +952,28 @@ var models = {
645
952
  var accounts = {
646
953
  current: (options) => client().accounts.current(options)
647
954
  };
955
+ var files = {
956
+ upload: ((input, options) => client().files.upload(input, options))
957
+ };
958
+ var generations = {
959
+ list: (options) => client().generations.list(options),
960
+ recent: (modelName, options) => client().generations.recent(modelName, options),
961
+ history: (options) => client().generations.history(options),
962
+ get: (requestId, options) => client().generations.get(requestId, options)
963
+ };
964
+ var webhooks = {
965
+ get: (options) => client().webhooks.get(options),
966
+ add: (webhookUrl, eventTypes, options) => client().webhooks.add(webhookUrl, eventTypes, options),
967
+ update: (webhookId, webhookUrl, eventTypes, options) => client().webhooks.update(webhookId, webhookUrl, eventTypes, options),
968
+ delete: (webhookId, options) => client().webhooks.delete(webhookId, options),
969
+ logs: (webhookId, options) => client().webhooks.logs(webhookId, options)
970
+ };
971
+ var pixelflows = {
972
+ run: (slug, inputs, options) => client().pixelflows.run(slug, inputs, options),
973
+ submit: (slug, inputs, options) => client().pixelflows.submit(slug, inputs, options),
974
+ getStatus: (requestId, options) => client().pixelflows.getStatus(requestId, options),
975
+ poll: (requestId, options) => client().pixelflows.poll(requestId, options)
976
+ };
648
977
  export {
649
978
  APIConnectionError,
650
979
  Accounts,
@@ -654,21 +983,30 @@ export {
654
983
  DEFAULT_SPOT_URL,
655
984
  DEFAULT_WAIT_INTERVAL_MS,
656
985
  DEFAULT_WAIT_TIMEOUT_MS,
986
+ Files,
987
+ Generations,
657
988
  InferenceFailed,
658
989
  InferenceTimeout,
659
990
  Job,
660
991
  Models,
992
+ PixelFlows,
993
+ PixelflowJob,
661
994
  Segmind,
662
995
  SegmindError,
663
996
  VERSION,
997
+ Webhooks,
664
998
  accounts,
665
999
  chat,
666
1000
  chatSync,
1001
+ files,
1002
+ generations,
667
1003
  imageUrl,
668
1004
  models,
1005
+ pixelflows,
669
1006
  run,
670
1007
  runSync,
671
1008
  submit,
672
- submitChat
1009
+ submitChat,
1010
+ webhooks
673
1011
  };
674
1012
  //# sourceMappingURL=index.js.map