segmind 0.0.1 → 0.1.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.cjs ADDED
@@ -0,0 +1,707 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ APIConnectionError: () => APIConnectionError,
34
+ Accounts: () => Accounts,
35
+ ChatResponse: () => ChatResponse,
36
+ DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
37
+ DEFAULT_CONSOLE_URL: () => DEFAULT_CONSOLE_URL,
38
+ DEFAULT_SPOT_URL: () => DEFAULT_SPOT_URL,
39
+ DEFAULT_WAIT_INTERVAL_MS: () => DEFAULT_WAIT_INTERVAL_MS,
40
+ DEFAULT_WAIT_TIMEOUT_MS: () => DEFAULT_WAIT_TIMEOUT_MS,
41
+ InferenceFailed: () => InferenceFailed,
42
+ InferenceTimeout: () => InferenceTimeout,
43
+ Job: () => Job,
44
+ Models: () => Models,
45
+ Segmind: () => Segmind,
46
+ SegmindError: () => SegmindError,
47
+ VERSION: () => VERSION,
48
+ accounts: () => accounts,
49
+ chat: () => chat,
50
+ chatSync: () => chatSync,
51
+ imageUrl: () => imageUrl,
52
+ models: () => models,
53
+ run: () => run,
54
+ runSync: () => runSync,
55
+ submit: () => submit,
56
+ submitChat: () => submitChat
57
+ });
58
+ module.exports = __toCommonJS(index_exports);
59
+
60
+ // src/core/errors.ts
61
+ var SegmindError = class _SegmindError extends Error {
62
+ /** HTTP status, when the error came from a response. */
63
+ status;
64
+ /** The server's error text (`error` in the response body), when it sent one. */
65
+ detail;
66
+ /** The v2 request id the error belongs to, when there is one. */
67
+ requestId;
68
+ /** The parsed response body, or its text when it was not JSON. */
69
+ body;
70
+ constructor(message, options = {}) {
71
+ super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
72
+ this.name = new.target.name;
73
+ this.status = options.status;
74
+ this.detail = options.detail;
75
+ this.requestId = options.requestId;
76
+ this.body = options.body;
77
+ }
78
+ /** Reads the response body and builds the error. Consumes the body. */
79
+ static async fromResponse(response, requestId) {
80
+ const text = await response.text().catch(() => "");
81
+ let body = text;
82
+ try {
83
+ body = text ? JSON.parse(text) : void 0;
84
+ } catch {
85
+ }
86
+ const detail = errorDetail(body) ?? (typeof body === "string" && body ? body.slice(0, 500) : void 0);
87
+ const message = detail ? `${response.status} ${detail}` : `${response.status} ${response.statusText || "request failed"}`;
88
+ return new _SegmindError(message, { status: response.status, detail, requestId, body });
89
+ }
90
+ };
91
+ var APIConnectionError = class extends SegmindError {
92
+ };
93
+ var InferenceFailed = class extends SegmindError {
94
+ /** The status body that reported the failure. */
95
+ statusBody;
96
+ constructor(requestId, statusBody) {
97
+ const detail = typeof statusBody.error === "string" ? statusBody.error : void 0;
98
+ super(detail ? `request ${requestId} failed: ${detail}` : `request ${requestId} failed`, {
99
+ detail,
100
+ requestId,
101
+ body: statusBody
102
+ });
103
+ this.statusBody = statusBody;
104
+ }
105
+ };
106
+ var InferenceTimeout = class extends SegmindError {
107
+ elapsedMs;
108
+ constructor(requestId, elapsedMs) {
109
+ super(`request ${requestId} did not finish within ${Math.round(elapsedMs)} ms`, { requestId });
110
+ this.elapsedMs = elapsedMs;
111
+ }
112
+ };
113
+ function errorDetail(body) {
114
+ if (!body || typeof body !== "object") return void 0;
115
+ const record2 = body;
116
+ for (const key of ["error", "message", "detail"]) {
117
+ const value = record2[key];
118
+ if (typeof value === "string" && value) return value;
119
+ if (value && typeof value === "object" && typeof value.message === "string") {
120
+ return value.message;
121
+ }
122
+ }
123
+ return void 0;
124
+ }
125
+
126
+ // src/chat.ts
127
+ var ChatResponse = class {
128
+ constructor(raw) {
129
+ this.raw = raw;
130
+ }
131
+ raw;
132
+ /** The assistant's text. Throws when the body holds none (inspect `raw`). */
133
+ get text() {
134
+ const text = extractText(this.raw);
135
+ if (text === void 0) {
136
+ throw new SegmindError(
137
+ `could not find assistant text in the response (top-level keys: ${Object.keys(this.raw).sort().join(", ")}); inspect ChatResponse.raw`,
138
+ { body: this.raw }
139
+ );
140
+ }
141
+ return text;
142
+ }
143
+ /** `text` parsed as JSON, for JSON mode and structured outputs. */
144
+ json() {
145
+ const text = this.text;
146
+ try {
147
+ return JSON.parse(text);
148
+ } catch (err) {
149
+ throw new SegmindError(`ChatResponse.text is not valid JSON: ${err.message}`, {
150
+ body: text,
151
+ cause: err
152
+ });
153
+ }
154
+ }
155
+ /** The provider's token usage (`usage` or Gemini's `usageMetadata`). */
156
+ get usage() {
157
+ return (record(this.raw.usage) ?? record(this.raw.usageMetadata)) || void 0;
158
+ }
159
+ /** Why generation stopped (`finish_reason`, `stop_reason` or `finishReason`). */
160
+ get finishReason() {
161
+ if (Array.isArray(this.raw.choices)) return str(record(this.raw.choices[0])?.finish_reason);
162
+ if ("stop_reason" in this.raw) return str(this.raw.stop_reason);
163
+ if (Array.isArray(this.raw.candidates)) return str(record(this.raw.candidates[0])?.finishReason);
164
+ return void 0;
165
+ }
166
+ /** Tool calls in the OpenAI shape, when the model made any. */
167
+ get toolCalls() {
168
+ if (!Array.isArray(this.raw.choices)) return [];
169
+ const calls = record(record(this.raw.choices[0])?.message)?.tool_calls;
170
+ return Array.isArray(calls) ? calls : [];
171
+ }
172
+ };
173
+ function chatBody(params) {
174
+ if (params.messages === void 0 && params.prompt === void 0) {
175
+ throw new SegmindError('chat: pass messages: [...] or prompt: "..."');
176
+ }
177
+ if (params.stream === true) {
178
+ throw new SegmindError("chat: streaming is not supported yet; omit stream or set it to false");
179
+ }
180
+ return params;
181
+ }
182
+ function chatFromRunResult(body) {
183
+ const output = body.output;
184
+ if (typeof output === "string") {
185
+ try {
186
+ const parsed = JSON.parse(output);
187
+ if (record(parsed)) return new ChatResponse(parsed);
188
+ } catch {
189
+ }
190
+ return new ChatResponse({ choices: [{ message: { content: output } }] });
191
+ }
192
+ if (record(output)) return new ChatResponse(output);
193
+ return new ChatResponse(body);
194
+ }
195
+ function extractText(raw) {
196
+ if ("choices" in raw) {
197
+ const content = record(record(Array.isArray(raw.choices) ? raw.choices[0] : void 0)?.message)?.content;
198
+ if (typeof content === "string") return content;
199
+ if (Array.isArray(content)) {
200
+ return content.map(record).filter((part) => part && (part.type === void 0 || part.type === "text")).map((part) => str(part.text) ?? "").join("");
201
+ }
202
+ return void 0;
203
+ }
204
+ if (Array.isArray(raw.content)) {
205
+ return raw.content.map(record).filter((part) => part?.type === "text").map((part) => str(part.text) ?? "").join("");
206
+ }
207
+ if ("candidates" in raw) {
208
+ const candidate = record(Array.isArray(raw.candidates) ? raw.candidates[0] : void 0);
209
+ const parts = record(candidate?.content)?.parts;
210
+ if (!Array.isArray(parts)) return candidate ? "" : void 0;
211
+ return parts.map((part) => str(record(part)?.text) ?? "").join("");
212
+ }
213
+ return void 0;
214
+ }
215
+ function record(value) {
216
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
217
+ }
218
+ function str(value) {
219
+ return typeof value === "string" ? value : void 0;
220
+ }
221
+
222
+ // src/core/env.ts
223
+ var g = globalThis;
224
+ function detectRuntime() {
225
+ if (g.navigator?.product === "ReactNative") return "react-native";
226
+ if (typeof g.window !== "undefined" && typeof g.document !== "undefined") return "browser";
227
+ if (g.process?.versions?.node) return "node";
228
+ return "other";
229
+ }
230
+ function readEnv(name) {
231
+ try {
232
+ const value = g.process?.env?.[name];
233
+ return value?.trim() ? value.trim() : void 0;
234
+ } catch {
235
+ return void 0;
236
+ }
237
+ }
238
+ function nodeVersion() {
239
+ return g.process?.versions?.node ? g.process.version : void 0;
240
+ }
241
+ function randomRequestId() {
242
+ const uuid = g.crypto?.randomUUID?.();
243
+ if (uuid) return uuid;
244
+ let id = "";
245
+ for (let i = 0; i < 32; i++) id += Math.floor(Math.random() * 16).toString(16);
246
+ return id;
247
+ }
248
+ function toBase64(bytes) {
249
+ if (g.Buffer) return g.Buffer.from(bytes).toString("base64");
250
+ let binary = "";
251
+ const chunk = 32768;
252
+ for (let i = 0; i < bytes.length; i += chunk) {
253
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
254
+ }
255
+ return btoa(binary);
256
+ }
257
+
258
+ // src/core/http.ts
259
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
260
+ var MAX_RETRY_DELAY_MS = 6e4;
261
+ function retryDelay(attempt, retryAfter, now = Date.now()) {
262
+ if (retryAfter) {
263
+ const seconds = Number(retryAfter);
264
+ if (Number.isFinite(seconds) && seconds >= 0) return Math.min(seconds * 1e3, MAX_RETRY_DELAY_MS);
265
+ const date = Date.parse(retryAfter);
266
+ if (!Number.isNaN(date)) return Math.min(Math.max(date - now, 0), MAX_RETRY_DELAY_MS);
267
+ }
268
+ const base = Math.min(500 * 2 ** attempt, 8e3);
269
+ return base * (0.75 + Math.random() * 0.25);
270
+ }
271
+ function sleep(ms, signal) {
272
+ return new Promise((resolve, reject) => {
273
+ if (signal?.aborted) return reject(signal.reason);
274
+ const timer = setTimeout(() => {
275
+ signal?.removeEventListener("abort", onAbort);
276
+ resolve();
277
+ }, ms);
278
+ const onAbort = () => {
279
+ clearTimeout(timer);
280
+ reject(signal.reason);
281
+ };
282
+ signal?.addEventListener("abort", onAbort, { once: true });
283
+ });
284
+ }
285
+ var HttpClient = class {
286
+ constructor(config) {
287
+ this.config = config;
288
+ }
289
+ config;
290
+ /** The response whatever its status, after retries when `retry` is set. */
291
+ async fetchRaw(req) {
292
+ for (let attempt = 0; ; attempt++) {
293
+ const canRetry = req.retry === true && attempt < this.config.maxRetries;
294
+ let response;
295
+ try {
296
+ response = await this.attempt(req);
297
+ } catch (err) {
298
+ if (canRetry && err instanceof APIConnectionError) {
299
+ await sleep(retryDelay(attempt, null), req.signal);
300
+ continue;
301
+ }
302
+ throw err;
303
+ }
304
+ if (canRetry && RETRYABLE_STATUS.has(response.status)) {
305
+ await response.body?.cancel().catch(() => void 0);
306
+ await sleep(retryDelay(attempt, response.headers.get("retry-after")), req.signal);
307
+ continue;
308
+ }
309
+ return response;
310
+ }
311
+ }
312
+ /** The response, or a `SegmindError` for any status outside 2xx. */
313
+ async request(req) {
314
+ const response = await this.fetchRaw(req);
315
+ if (!response.ok) throw await SegmindError.fromResponse(response);
316
+ return response;
317
+ }
318
+ /** The parsed JSON body of a 2xx response. */
319
+ async json(req) {
320
+ return readJson(await this.request(req));
321
+ }
322
+ async attempt(req) {
323
+ const { signal } = req;
324
+ if (signal?.aborted) throw signal.reason;
325
+ const timeout = req.timeout ?? this.config.timeout;
326
+ const controller = new AbortController();
327
+ let timedOut = false;
328
+ const timer = setTimeout(() => {
329
+ timedOut = true;
330
+ controller.abort();
331
+ }, timeout);
332
+ const onAbort = () => controller.abort(signal.reason);
333
+ 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;
336
+ if (req.body !== void 0) headers["content-type"] = "application/json";
337
+ try {
338
+ return await this.config.fetch(req.url, {
339
+ method: req.method,
340
+ headers,
341
+ body: req.body === void 0 ? void 0 : JSON.stringify(req.body),
342
+ signal: controller.signal
343
+ });
344
+ } catch (err) {
345
+ if (signal?.aborted) throw signal.reason ?? err;
346
+ const reason = timedOut ? `request timed out after ${timeout} ms` : `connection error: ${describe(err)}`;
347
+ throw new APIConnectionError(`${req.method} ${req.url}: ${reason}`, { cause: err });
348
+ } finally {
349
+ clearTimeout(timer);
350
+ signal?.removeEventListener("abort", onAbort);
351
+ }
352
+ }
353
+ };
354
+ async function readJson(response) {
355
+ const text = await response.text();
356
+ try {
357
+ return JSON.parse(text);
358
+ } catch (err) {
359
+ throw new SegmindError(`${response.status} response was not JSON`, {
360
+ status: response.status,
361
+ body: text.slice(0, 500),
362
+ cause: err
363
+ });
364
+ }
365
+ }
366
+ function describe(err) {
367
+ if (err instanceof Error) {
368
+ const cause = err.cause;
369
+ return cause instanceof Error ? `${err.message} (${cause.message})` : err.message;
370
+ }
371
+ return String(err);
372
+ }
373
+
374
+ // src/job.ts
375
+ var DEFAULT_WAIT_TIMEOUT_MS = 6e5;
376
+ var DEFAULT_WAIT_INTERVAL_MS = 1e3;
377
+ var TERMINAL = /* @__PURE__ */ new Set(["COMPLETED", "FAILED"]);
378
+ var Job = class {
379
+ constructor(http, submitResponse, transform = (body) => body) {
380
+ this.http = http;
381
+ this.submitResponse = submitResponse;
382
+ this.transform = transform;
383
+ this.requestId = submitResponse.request_id;
384
+ this.statusUrl = submitResponse.status_url;
385
+ this.responseUrl = submitResponse.response_url;
386
+ }
387
+ http;
388
+ submitResponse;
389
+ transform;
390
+ requestId;
391
+ statusUrl;
392
+ responseUrl;
393
+ /** The current state, without waiting. */
394
+ async status(options = {}) {
395
+ return await this.read(this.statusUrl, options.signal);
396
+ }
397
+ /**
398
+ * The finished result. Throws `InferenceFailed` when the request failed, and
399
+ * a `SegmindError` when it has not finished yet (use `wait()` for that).
400
+ */
401
+ async result(options = {}) {
402
+ const body = await this.read(this.responseUrl, options.signal);
403
+ if (body.status === "FAILED") throw new InferenceFailed(this.requestId, body);
404
+ if (body.status !== void 0 && body.status !== "COMPLETED") {
405
+ throw new SegmindError(`request ${this.requestId} is still ${String(body.status)}; use wait()`, {
406
+ requestId: this.requestId,
407
+ body
408
+ });
409
+ }
410
+ return this.transform(body);
411
+ }
412
+ /** Polls until the request finishes, then returns its result. */
413
+ async wait(options = {}) {
414
+ const timeout = options.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
415
+ const interval = options.interval ?? DEFAULT_WAIT_INTERVAL_MS;
416
+ const started = Date.now();
417
+ for (; ; ) {
418
+ const status = await this.status({ signal: options.signal });
419
+ if (status.status === "COMPLETED") return this.result({ signal: options.signal });
420
+ if (status.status === "FAILED") throw new InferenceFailed(this.requestId, status);
421
+ const elapsed = Date.now() - started;
422
+ if (elapsed >= timeout) throw new InferenceTimeout(this.requestId, elapsed);
423
+ await sleep(Math.min(interval, timeout - elapsed), options.signal);
424
+ }
425
+ }
426
+ /**
427
+ * The gateway serves a FAILED request's body with HTTP 422 on both the status
428
+ * and result URLs. A body that names a terminal state is the answer whatever
429
+ * its status code; anything else outside 2xx is a real error.
430
+ */
431
+ async read(url, signal) {
432
+ const response = await this.http.fetchRaw({ method: "GET", url, signal, retry: true });
433
+ if (response.ok) return readJson(response);
434
+ const error = await SegmindError.fromResponse(response, this.requestId);
435
+ const body = error.body;
436
+ if (body && typeof body === "object" && TERMINAL.has(String(body.status))) {
437
+ return body;
438
+ }
439
+ throw error;
440
+ }
441
+ };
442
+
443
+ // src/version.ts
444
+ var VERSION = "0.1.0";
445
+
446
+ // src/client.ts
447
+ var DEFAULT_BASE_URL = "https://api.segmind.com";
448
+ var DEFAULT_SPOT_URL = "https://api.spotprod.segmind.com";
449
+ var DEFAULT_CONSOLE_URL = "https://cloud.segmind.com/api";
450
+ var Segmind = class {
451
+ baseURL;
452
+ spotURL;
453
+ consoleURL;
454
+ models;
455
+ accounts;
456
+ /** @internal */
457
+ http;
458
+ constructor(options = {}) {
459
+ const apiKey = options.apiKey ?? readEnv("SEGMIND_API_KEY");
460
+ const runtime = detectRuntime();
461
+ if (apiKey && (runtime === "browser" || runtime === "react-native") && !options.dangerouslyAllowBrowser) {
462
+ throw new SegmindError(
463
+ "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."
464
+ );
465
+ }
466
+ if (!apiKey) {
467
+ throw new SegmindError("Missing API key: pass { apiKey } or set the SEGMIND_API_KEY environment variable.");
468
+ }
469
+ const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
470
+ if (!fetchImpl) throw new SegmindError("No fetch implementation available: pass { fetch }.");
471
+ this.baseURL = trimBase(options.baseURL ?? readEnv("SEGMIND_BASE_URL") ?? DEFAULT_BASE_URL);
472
+ this.spotURL = trimSlash(options.spotURL ?? DEFAULT_SPOT_URL);
473
+ this.consoleURL = trimSlash(options.consoleURL ?? DEFAULT_CONSOLE_URL);
474
+ const headers = { "X-Initiator": "SDK-JS", ...options.defaultHeaders };
475
+ const node = nodeVersion();
476
+ if (runtime === "node" && node) headers["User-Agent"] = `segmind-js/${VERSION} node/${node}`;
477
+ this.http = new HttpClient({
478
+ apiKey,
479
+ fetch: fetchImpl,
480
+ timeout: options.timeout ?? 6e5,
481
+ maxRetries: options.maxRetries ?? 2,
482
+ headers
483
+ });
484
+ this.models = new Models(this);
485
+ this.accounts = new Accounts(this);
486
+ }
487
+ /**
488
+ * Runs a model and returns its result: submits to the v2 queue, then polls
489
+ * until it finishes. `params` go to the model unchanged.
490
+ */
491
+ async run(slug, params = {}, options = {}) {
492
+ const job = await this.submit(slug, params, options);
493
+ return job.wait(options);
494
+ }
495
+ /**
496
+ * One blocking `POST /v1/{slug}`. Returns the fetch `Response`: call
497
+ * `.arrayBuffer()` for media or `.json()` for JSON models. Never retried,
498
+ * because the call bills.
499
+ */
500
+ async runSync(slug, params = {}, options = {}) {
501
+ return this.http.request({
502
+ method: "POST",
503
+ url: `${this.baseURL}/v1/${path(slug)}`,
504
+ body: params,
505
+ signal: options.signal,
506
+ timeout: options.timeout
507
+ });
508
+ }
509
+ /**
510
+ * Submits to the v2 queue and returns a `Job` to wait on. Safe to retry: the
511
+ * submit carries an `X-Request-ID`, and the gateway answers a repeat with
512
+ * the existing request rather than a second one.
513
+ */
514
+ async submit(slug, params = {}, options = {}) {
515
+ return this.submitJob(slug, params, options, (body) => body);
516
+ }
517
+ /** Chat with an LLM through the v2 queue. Returns a provider-neutral reply. */
518
+ async chat(slug, params, options = {}) {
519
+ const job = await this.submitChat(slug, params, options);
520
+ return job.wait(options);
521
+ }
522
+ /** Chat with one blocking `POST /v1/{slug}`. */
523
+ async chatSync(slug, params, options = {}) {
524
+ const response = await this.runSync(slug, chatBody(params), options);
525
+ return new ChatResponse(await readJson(response));
526
+ }
527
+ /** Submits a chat to the v2 queue and returns a `Job` whose result is a `ChatResponse`. */
528
+ async submitChat(slug, params, options = {}) {
529
+ return this.submitJob(slug, chatBody(params), options, chatFromRunResult);
530
+ }
531
+ async submitJob(slug, params, options, transform) {
532
+ const url = `${this.baseURL}/v2/${path(slug)}`;
533
+ const maxRetries = this.http.config.maxRetries;
534
+ let requestId = options.requestId ?? randomRequestId();
535
+ for (let attempt = 0; ; attempt++) {
536
+ const canRetry = attempt < maxRetries;
537
+ let response;
538
+ try {
539
+ response = await this.http.fetchRaw({
540
+ method: "POST",
541
+ url,
542
+ body: params,
543
+ headers: { "X-Request-ID": requestId },
544
+ signal: options.signal
545
+ });
546
+ } catch (err) {
547
+ if (canRetry && err instanceof APIConnectionError) {
548
+ await sleep(retryDelay(attempt, null), options.signal);
549
+ continue;
550
+ }
551
+ throw err;
552
+ }
553
+ if (response.ok || response.status === 409) {
554
+ const body = await readJson(response);
555
+ if (response.status === 409 && !body.status_url) {
556
+ throw new SegmindError(`409 ${String(body.error ?? "conflict")}`, {
557
+ status: 409,
558
+ requestId,
559
+ body
560
+ });
561
+ }
562
+ return new Job(this.http, validateSubmit(body, response.status), transform);
563
+ }
564
+ if (canRetry && response.status === 503) {
565
+ await response.body?.cancel().catch(() => void 0);
566
+ requestId = randomRequestId();
567
+ await sleep(retryDelay(attempt, response.headers.get("retry-after")), options.signal);
568
+ continue;
569
+ }
570
+ if (canRetry && (response.status === 429 || response.status === 502 || response.status === 504)) {
571
+ await response.body?.cancel().catch(() => void 0);
572
+ await sleep(retryDelay(attempt, response.headers.get("retry-after")), options.signal);
573
+ continue;
574
+ }
575
+ throw await SegmindError.fromResponse(response, requestId);
576
+ }
577
+ }
578
+ };
579
+ var Models = class {
580
+ constructor(client2) {
581
+ this.client = client2;
582
+ }
583
+ client;
584
+ /** Every model in the catalog, under the key `Inference Model Information List`. */
585
+ async list(options = {}) {
586
+ return this.client.http.json({
587
+ method: "GET",
588
+ url: `${this.client.spotURL}/inference-model-information/list`,
589
+ signal: options.signal,
590
+ retry: true
591
+ });
592
+ }
593
+ };
594
+ var Accounts = class {
595
+ constructor(client2) {
596
+ this.client = client2;
597
+ }
598
+ client;
599
+ /** Account details for the API key. */
600
+ async current(options = {}) {
601
+ return this.client.http.json({
602
+ method: "GET",
603
+ url: `${this.client.consoleURL}/auth/authenticate`,
604
+ signal: options.signal,
605
+ retry: true
606
+ });
607
+ }
608
+ };
609
+ function validateSubmit(body, status) {
610
+ if (!body.request_id || !body.status_url || !body.response_url) {
611
+ throw new SegmindError(
612
+ `v2 submit returned ${status} without request_id, status_url and response_url (keys: ${Object.keys(body).sort().join(", ")})`,
613
+ { status, body }
614
+ );
615
+ }
616
+ return body;
617
+ }
618
+ function path(slug) {
619
+ const clean = slug.trim().replace(/^\/+/, "");
620
+ if (!clean) throw new SegmindError("A model slug is required.");
621
+ return clean;
622
+ }
623
+ function trimSlash(url) {
624
+ return url.replace(/\/+$/, "");
625
+ }
626
+ function trimBase(url) {
627
+ return trimSlash(url).replace(/\/v\d+$/, "");
628
+ }
629
+
630
+ // 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
+ async function imageUrl(input, options = {}) {
646
+ return { type: "image_url", image_url: { url: await toDataUri(input, options.mimeType) } };
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);
655
+ }
656
+ if (typeof Blob !== "undefined" && input instanceof Blob) {
657
+ const bytes2 = new Uint8Array(await input.arrayBuffer());
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;
687
+ }
688
+
689
+ // src/index.ts
690
+ var shared;
691
+ function client() {
692
+ shared ??= new Segmind();
693
+ return shared;
694
+ }
695
+ var run = (slug, params, options) => client().run(slug, params, options);
696
+ var runSync = (slug, params, options) => client().runSync(slug, params, options);
697
+ var submit = (slug, params, options) => client().submit(slug, params, options);
698
+ var chat = (slug, params, options) => client().chat(slug, params, options);
699
+ var chatSync = (slug, params, options) => client().chatSync(slug, params, options);
700
+ var submitChat = (slug, params, options) => client().submitChat(slug, params, options);
701
+ var models = {
702
+ list: (options) => client().models.list(options)
703
+ };
704
+ var accounts = {
705
+ current: (options) => client().accounts.current(options)
706
+ };
707
+ //# sourceMappingURL=index.cjs.map