segmind 0.0.1 → 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/LICENSE +201 -0
- package/README.md +160 -3
- package/dist/index.cjs +1045 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +614 -0
- package/dist/index.d.ts +614 -0
- package/dist/index.js +1012 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -6
- package/index.js +0 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,1012 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var SegmindError = class _SegmindError extends Error {
|
|
3
|
+
/** HTTP status, when the error came from a response. */
|
|
4
|
+
status;
|
|
5
|
+
/** The server's error text (`error` in the response body), when it sent one. */
|
|
6
|
+
detail;
|
|
7
|
+
/** The v2 request id the error belongs to, when there is one. */
|
|
8
|
+
requestId;
|
|
9
|
+
/** The parsed response body, or its text when it was not JSON. */
|
|
10
|
+
body;
|
|
11
|
+
constructor(message, options = {}) {
|
|
12
|
+
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
13
|
+
this.name = new.target.name;
|
|
14
|
+
this.status = options.status;
|
|
15
|
+
this.detail = options.detail;
|
|
16
|
+
this.requestId = options.requestId;
|
|
17
|
+
this.body = options.body;
|
|
18
|
+
}
|
|
19
|
+
/** Reads the response body and builds the error. Consumes the body. */
|
|
20
|
+
static async fromResponse(response, requestId) {
|
|
21
|
+
const text = await response.text().catch(() => "");
|
|
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 });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var APIConnectionError = class extends SegmindError {
|
|
33
|
+
};
|
|
34
|
+
var InferenceFailed = class extends SegmindError {
|
|
35
|
+
/** The status body that reported the failure. */
|
|
36
|
+
statusBody;
|
|
37
|
+
constructor(requestId, statusBody, detail) {
|
|
38
|
+
detail ??= typeof statusBody.error === "string" ? statusBody.error : void 0;
|
|
39
|
+
super(detail ? `request ${requestId} failed: ${detail}` : `request ${requestId} failed`, {
|
|
40
|
+
detail,
|
|
41
|
+
requestId,
|
|
42
|
+
body: statusBody
|
|
43
|
+
});
|
|
44
|
+
this.statusBody = statusBody;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var InferenceTimeout = class extends SegmindError {
|
|
48
|
+
elapsedMs;
|
|
49
|
+
constructor(requestId, elapsedMs) {
|
|
50
|
+
super(`request ${requestId} did not finish within ${Math.round(elapsedMs)} ms`, { requestId });
|
|
51
|
+
this.elapsedMs = elapsedMs;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
function errorDetail(body) {
|
|
55
|
+
if (!body || typeof body !== "object") return void 0;
|
|
56
|
+
const record2 = body;
|
|
57
|
+
for (const key of ["error", "message", "detail"]) {
|
|
58
|
+
const value = record2[key];
|
|
59
|
+
if (typeof value === "string" && value) return value;
|
|
60
|
+
if (value && typeof value === "object" && typeof value.message === "string") {
|
|
61
|
+
return value.message;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/chat.ts
|
|
68
|
+
var ChatResponse = class {
|
|
69
|
+
constructor(raw) {
|
|
70
|
+
this.raw = raw;
|
|
71
|
+
}
|
|
72
|
+
raw;
|
|
73
|
+
/** The assistant's text. Throws when the body holds none (inspect `raw`). */
|
|
74
|
+
get text() {
|
|
75
|
+
const text = extractText(this.raw);
|
|
76
|
+
if (text === void 0) {
|
|
77
|
+
throw new SegmindError(
|
|
78
|
+
`could not find assistant text in the response (top-level keys: ${Object.keys(this.raw).sort().join(", ")}); inspect ChatResponse.raw`,
|
|
79
|
+
{ body: this.raw }
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return text;
|
|
83
|
+
}
|
|
84
|
+
/** `text` parsed as JSON, for JSON mode and structured outputs. */
|
|
85
|
+
json() {
|
|
86
|
+
const text = this.text;
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(text);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
throw new SegmindError(`ChatResponse.text is not valid JSON: ${err.message}`, {
|
|
91
|
+
body: text,
|
|
92
|
+
cause: err
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** The provider's token usage (`usage` or Gemini's `usageMetadata`). */
|
|
97
|
+
get usage() {
|
|
98
|
+
return (record(this.raw.usage) ?? record(this.raw.usageMetadata)) || void 0;
|
|
99
|
+
}
|
|
100
|
+
/** Why generation stopped (`finish_reason`, `stop_reason` or `finishReason`). */
|
|
101
|
+
get finishReason() {
|
|
102
|
+
if (Array.isArray(this.raw.choices)) return str(record(this.raw.choices[0])?.finish_reason);
|
|
103
|
+
if ("stop_reason" in this.raw) return str(this.raw.stop_reason);
|
|
104
|
+
if (Array.isArray(this.raw.candidates)) return str(record(this.raw.candidates[0])?.finishReason);
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
/** Tool calls in the OpenAI shape, when the model made any. */
|
|
108
|
+
get toolCalls() {
|
|
109
|
+
if (!Array.isArray(this.raw.choices)) return [];
|
|
110
|
+
const calls = record(record(this.raw.choices[0])?.message)?.tool_calls;
|
|
111
|
+
return Array.isArray(calls) ? calls : [];
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function chatBody(params) {
|
|
115
|
+
if (params.messages === void 0 && params.prompt === void 0) {
|
|
116
|
+
throw new SegmindError('chat: pass messages: [...] or prompt: "..."');
|
|
117
|
+
}
|
|
118
|
+
if (params.stream === true) {
|
|
119
|
+
throw new SegmindError("chat: streaming is not supported yet; omit stream or set it to false");
|
|
120
|
+
}
|
|
121
|
+
return params;
|
|
122
|
+
}
|
|
123
|
+
function chatFromRunResult(body) {
|
|
124
|
+
const output = body.output;
|
|
125
|
+
if (typeof output === "string") {
|
|
126
|
+
try {
|
|
127
|
+
const parsed = JSON.parse(output);
|
|
128
|
+
if (record(parsed)) return new ChatResponse(parsed);
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
return new ChatResponse({ choices: [{ message: { content: output } }] });
|
|
132
|
+
}
|
|
133
|
+
if (record(output)) return new ChatResponse(output);
|
|
134
|
+
return new ChatResponse(body);
|
|
135
|
+
}
|
|
136
|
+
function extractText(raw) {
|
|
137
|
+
if ("choices" in raw) {
|
|
138
|
+
const content = record(record(Array.isArray(raw.choices) ? raw.choices[0] : void 0)?.message)?.content;
|
|
139
|
+
if (typeof content === "string") return content;
|
|
140
|
+
if (Array.isArray(content)) {
|
|
141
|
+
return content.map(record).filter((part) => part && (part.type === void 0 || part.type === "text")).map((part) => str(part.text) ?? "").join("");
|
|
142
|
+
}
|
|
143
|
+
return void 0;
|
|
144
|
+
}
|
|
145
|
+
if (Array.isArray(raw.content)) {
|
|
146
|
+
return raw.content.map(record).filter((part) => part?.type === "text").map((part) => str(part.text) ?? "").join("");
|
|
147
|
+
}
|
|
148
|
+
if ("candidates" in raw) {
|
|
149
|
+
const candidate = record(Array.isArray(raw.candidates) ? raw.candidates[0] : void 0);
|
|
150
|
+
const parts = record(candidate?.content)?.parts;
|
|
151
|
+
if (!Array.isArray(parts)) return candidate ? "" : void 0;
|
|
152
|
+
return parts.map((part) => str(record(part)?.text) ?? "").join("");
|
|
153
|
+
}
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
156
|
+
function record(value) {
|
|
157
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
158
|
+
}
|
|
159
|
+
function str(value) {
|
|
160
|
+
return typeof value === "string" ? value : void 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/core/env.ts
|
|
164
|
+
var g = globalThis;
|
|
165
|
+
function detectRuntime() {
|
|
166
|
+
if (g.navigator?.product === "ReactNative") return "react-native";
|
|
167
|
+
if (typeof g.window !== "undefined" && typeof g.document !== "undefined") return "browser";
|
|
168
|
+
if (g.process?.versions?.node) return "node";
|
|
169
|
+
return "other";
|
|
170
|
+
}
|
|
171
|
+
function readEnv(name) {
|
|
172
|
+
try {
|
|
173
|
+
const value = g.process?.env?.[name];
|
|
174
|
+
return value?.trim() ? value.trim() : void 0;
|
|
175
|
+
} catch {
|
|
176
|
+
return void 0;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function nodeVersion() {
|
|
180
|
+
return g.process?.versions?.node ? g.process.version : void 0;
|
|
181
|
+
}
|
|
182
|
+
function randomRequestId() {
|
|
183
|
+
const uuid = g.crypto?.randomUUID?.();
|
|
184
|
+
if (uuid) return uuid;
|
|
185
|
+
let id = "";
|
|
186
|
+
for (let i = 0; i < 32; i++) id += Math.floor(Math.random() * 16).toString(16);
|
|
187
|
+
return id;
|
|
188
|
+
}
|
|
189
|
+
function toBase64(bytes) {
|
|
190
|
+
if (g.Buffer) return g.Buffer.from(bytes).toString("base64");
|
|
191
|
+
let binary = "";
|
|
192
|
+
const chunk = 32768;
|
|
193
|
+
for (let i = 0; i < bytes.length; i += chunk) {
|
|
194
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
|
195
|
+
}
|
|
196
|
+
return btoa(binary);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/core/http.ts
|
|
200
|
+
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
|
|
201
|
+
var MAX_RETRY_DELAY_MS = 6e4;
|
|
202
|
+
function retryDelay(attempt, retryAfter, now = Date.now()) {
|
|
203
|
+
if (retryAfter) {
|
|
204
|
+
const seconds = Number(retryAfter);
|
|
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
|
+
}
|
|
209
|
+
const base = Math.min(500 * 2 ** attempt, 8e3);
|
|
210
|
+
return base * (0.75 + Math.random() * 0.25);
|
|
211
|
+
}
|
|
212
|
+
function sleep(ms, signal) {
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
if (signal?.aborted) return reject(signal.reason);
|
|
215
|
+
const timer = setTimeout(() => {
|
|
216
|
+
signal?.removeEventListener("abort", onAbort);
|
|
217
|
+
resolve();
|
|
218
|
+
}, ms);
|
|
219
|
+
const onAbort = () => {
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
reject(signal.reason);
|
|
222
|
+
};
|
|
223
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
var HttpClient = class {
|
|
227
|
+
constructor(config) {
|
|
228
|
+
this.config = config;
|
|
229
|
+
}
|
|
230
|
+
config;
|
|
231
|
+
/** The response whatever its status, after retries when `retry` is set. */
|
|
232
|
+
async fetchRaw(req) {
|
|
233
|
+
for (let attempt = 0; ; attempt++) {
|
|
234
|
+
const canRetry = req.retry === true && attempt < this.config.maxRetries;
|
|
235
|
+
let response;
|
|
236
|
+
try {
|
|
237
|
+
response = await this.attempt(req);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
if (canRetry && err instanceof APIConnectionError) {
|
|
240
|
+
await sleep(retryDelay(attempt, null), req.signal);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
245
|
+
if (canRetry && RETRYABLE_STATUS.has(response.status)) {
|
|
246
|
+
await response.body?.cancel().catch(() => void 0);
|
|
247
|
+
await sleep(retryDelay(attempt, response.headers.get("retry-after")), req.signal);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
return response;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/** The response, or a `SegmindError` for any status outside 2xx. */
|
|
254
|
+
async request(req) {
|
|
255
|
+
const response = await this.fetchRaw(req);
|
|
256
|
+
if (!response.ok) throw await SegmindError.fromResponse(response);
|
|
257
|
+
return response;
|
|
258
|
+
}
|
|
259
|
+
/** The parsed JSON body of a 2xx response. */
|
|
260
|
+
async json(req) {
|
|
261
|
+
return readJson(await this.request(req));
|
|
262
|
+
}
|
|
263
|
+
async attempt(req) {
|
|
264
|
+
const { signal } = req;
|
|
265
|
+
if (signal?.aborted) throw signal.reason;
|
|
266
|
+
const timeout = req.timeout ?? this.config.timeout;
|
|
267
|
+
const controller = new AbortController();
|
|
268
|
+
let timedOut = false;
|
|
269
|
+
const timer = setTimeout(() => {
|
|
270
|
+
timedOut = true;
|
|
271
|
+
controller.abort();
|
|
272
|
+
}, timeout);
|
|
273
|
+
const onAbort = () => controller.abort(signal.reason);
|
|
274
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
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
|
+
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));
|
|
279
|
+
try {
|
|
280
|
+
return await this.config.fetch(withQuery(req.url, req.query), {
|
|
281
|
+
method: req.method,
|
|
282
|
+
headers,
|
|
283
|
+
body,
|
|
284
|
+
signal: controller.signal
|
|
285
|
+
});
|
|
286
|
+
} catch (err) {
|
|
287
|
+
if (signal?.aborted) throw signal.reason ?? err;
|
|
288
|
+
const reason = timedOut ? `request timed out after ${timeout} ms` : `connection error: ${describe(err)}`;
|
|
289
|
+
throw new APIConnectionError(`${req.method} ${req.url}: ${reason}`, { cause: err });
|
|
290
|
+
} finally {
|
|
291
|
+
clearTimeout(timer);
|
|
292
|
+
signal?.removeEventListener("abort", onAbort);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
async function readJson(response) {
|
|
297
|
+
const text = await response.text();
|
|
298
|
+
try {
|
|
299
|
+
return JSON.parse(text);
|
|
300
|
+
} catch (err) {
|
|
301
|
+
throw new SegmindError(`${response.status} response was not JSON`, {
|
|
302
|
+
status: response.status,
|
|
303
|
+
body: text.slice(0, 500),
|
|
304
|
+
cause: err
|
|
305
|
+
});
|
|
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
|
+
}
|
|
317
|
+
function describe(err) {
|
|
318
|
+
if (err instanceof Error) {
|
|
319
|
+
const cause = err.cause;
|
|
320
|
+
return cause instanceof Error ? `${err.message} (${cause.message})` : err.message;
|
|
321
|
+
}
|
|
322
|
+
return String(err);
|
|
323
|
+
}
|
|
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
|
+
|
|
501
|
+
// src/job.ts
|
|
502
|
+
var DEFAULT_WAIT_TIMEOUT_MS = 6e5;
|
|
503
|
+
var DEFAULT_WAIT_INTERVAL_MS = 1e3;
|
|
504
|
+
var TERMINAL = /* @__PURE__ */ new Set(["COMPLETED", "FAILED"]);
|
|
505
|
+
var Job = class {
|
|
506
|
+
constructor(http, submitResponse, transform = (body) => body) {
|
|
507
|
+
this.http = http;
|
|
508
|
+
this.submitResponse = submitResponse;
|
|
509
|
+
this.transform = transform;
|
|
510
|
+
this.requestId = submitResponse.request_id;
|
|
511
|
+
this.statusUrl = submitResponse.status_url;
|
|
512
|
+
this.responseUrl = submitResponse.response_url;
|
|
513
|
+
}
|
|
514
|
+
http;
|
|
515
|
+
submitResponse;
|
|
516
|
+
transform;
|
|
517
|
+
requestId;
|
|
518
|
+
statusUrl;
|
|
519
|
+
responseUrl;
|
|
520
|
+
/** The current state, without waiting. */
|
|
521
|
+
async status(options = {}) {
|
|
522
|
+
return await this.read(this.statusUrl, options.signal);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* The finished result. Throws `InferenceFailed` when the request failed, and
|
|
526
|
+
* a `SegmindError` when it has not finished yet (use `wait()` for that).
|
|
527
|
+
*/
|
|
528
|
+
async result(options = {}) {
|
|
529
|
+
const body = await this.read(this.responseUrl, options.signal);
|
|
530
|
+
if (body.status === "FAILED") throw new InferenceFailed(this.requestId, body);
|
|
531
|
+
if (body.status !== void 0 && body.status !== "COMPLETED") {
|
|
532
|
+
throw new SegmindError(`request ${this.requestId} is still ${String(body.status)}; use wait()`, {
|
|
533
|
+
requestId: this.requestId,
|
|
534
|
+
body
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
return this.transform(body);
|
|
538
|
+
}
|
|
539
|
+
/** Polls until the request finishes, then returns its result. */
|
|
540
|
+
async wait(options = {}) {
|
|
541
|
+
const timeout = options.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
542
|
+
const interval = options.interval ?? DEFAULT_WAIT_INTERVAL_MS;
|
|
543
|
+
const started = Date.now();
|
|
544
|
+
for (; ; ) {
|
|
545
|
+
const status = await this.status({ signal: options.signal });
|
|
546
|
+
if (status.status === "COMPLETED") return this.result({ signal: options.signal });
|
|
547
|
+
if (status.status === "FAILED") throw new InferenceFailed(this.requestId, status);
|
|
548
|
+
const elapsed = Date.now() - started;
|
|
549
|
+
if (elapsed >= timeout) throw new InferenceTimeout(this.requestId, elapsed);
|
|
550
|
+
await sleep(Math.min(interval, timeout - elapsed), options.signal);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* The gateway serves a FAILED request's body with HTTP 422 on both the status
|
|
555
|
+
* and result URLs. A body that names a terminal state is the answer whatever
|
|
556
|
+
* its status code; anything else outside 2xx is a real error.
|
|
557
|
+
*/
|
|
558
|
+
async read(url, signal) {
|
|
559
|
+
const response = await this.http.fetchRaw({ method: "GET", url, signal, retry: true });
|
|
560
|
+
if (response.ok) return readJson(response);
|
|
561
|
+
const error = await SegmindError.fromResponse(response, this.requestId);
|
|
562
|
+
const body = error.body;
|
|
563
|
+
if (body && typeof body === "object" && TERMINAL.has(String(body.status))) {
|
|
564
|
+
return body;
|
|
565
|
+
}
|
|
566
|
+
throw error;
|
|
567
|
+
}
|
|
568
|
+
};
|
|
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
|
+
|
|
686
|
+
// src/version.ts
|
|
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
|
+
}
|
|
731
|
+
|
|
732
|
+
// src/client.ts
|
|
733
|
+
var DEFAULT_BASE_URL = "https://api.segmind.com";
|
|
734
|
+
var DEFAULT_SPOT_URL = "https://api.spotprod.segmind.com";
|
|
735
|
+
var DEFAULT_CONSOLE_URL = "https://cloud.segmind.com/api";
|
|
736
|
+
var Segmind = class {
|
|
737
|
+
baseURL;
|
|
738
|
+
spotURL;
|
|
739
|
+
consoleURL;
|
|
740
|
+
models;
|
|
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;
|
|
750
|
+
/** @internal */
|
|
751
|
+
http;
|
|
752
|
+
constructor(options = {}) {
|
|
753
|
+
const apiKey = options.apiKey ?? readEnv("SEGMIND_API_KEY");
|
|
754
|
+
const runtime = detectRuntime();
|
|
755
|
+
if (apiKey && (runtime === "browser" || runtime === "react-native") && !options.dangerouslyAllowBrowser) {
|
|
756
|
+
throw new SegmindError(
|
|
757
|
+
"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."
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
if (!apiKey) {
|
|
761
|
+
throw new SegmindError("Missing API key: pass { apiKey } or set the SEGMIND_API_KEY environment variable.");
|
|
762
|
+
}
|
|
763
|
+
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
764
|
+
if (!fetchImpl) throw new SegmindError("No fetch implementation available: pass { fetch }.");
|
|
765
|
+
this.baseURL = trimBase(options.baseURL ?? readEnv("SEGMIND_BASE_URL") ?? DEFAULT_BASE_URL);
|
|
766
|
+
this.spotURL = trimSlash(options.spotURL ?? DEFAULT_SPOT_URL);
|
|
767
|
+
this.consoleURL = trimSlash(options.consoleURL ?? DEFAULT_CONSOLE_URL);
|
|
768
|
+
const headers = { "X-Initiator": "SDK-JS", ...options.defaultHeaders };
|
|
769
|
+
const node = nodeVersion();
|
|
770
|
+
if (runtime === "node" && node) headers["User-Agent"] = `segmind-js/${VERSION} node/${node}`;
|
|
771
|
+
this.http = new HttpClient({
|
|
772
|
+
apiKey,
|
|
773
|
+
fetch: fetchImpl,
|
|
774
|
+
timeout: options.timeout ?? 6e5,
|
|
775
|
+
maxRetries: options.maxRetries ?? 2,
|
|
776
|
+
headers
|
|
777
|
+
});
|
|
778
|
+
this.models = new Models(this);
|
|
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);
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Runs a model and returns its result: submits to the v2 queue, then polls
|
|
787
|
+
* until it finishes. `params` go to the model unchanged.
|
|
788
|
+
*/
|
|
789
|
+
async run(slug, params = {}, options = {}) {
|
|
790
|
+
const job = await this.submit(slug, params, options);
|
|
791
|
+
return job.wait(options);
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* One blocking `POST /v1/{slug}`. Returns the fetch `Response`: call
|
|
795
|
+
* `.arrayBuffer()` for media or `.json()` for JSON models. Never retried,
|
|
796
|
+
* because the call bills.
|
|
797
|
+
*/
|
|
798
|
+
async runSync(slug, params = {}, options = {}) {
|
|
799
|
+
return this.http.request({
|
|
800
|
+
method: "POST",
|
|
801
|
+
url: `${this.baseURL}/v1/${path(slug)}`,
|
|
802
|
+
body: params,
|
|
803
|
+
signal: options.signal,
|
|
804
|
+
timeout: options.timeout
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Submits to the v2 queue and returns a `Job` to wait on. Safe to retry: the
|
|
809
|
+
* submit carries an `X-Request-ID`, and the gateway answers a repeat with
|
|
810
|
+
* the existing request rather than a second one.
|
|
811
|
+
*/
|
|
812
|
+
async submit(slug, params = {}, options = {}) {
|
|
813
|
+
return this.submitJob(slug, params, options, (body) => body);
|
|
814
|
+
}
|
|
815
|
+
/** Chat with an LLM through the v2 queue. Returns a provider-neutral reply. */
|
|
816
|
+
async chat(slug, params, options = {}) {
|
|
817
|
+
const job = await this.submitChat(slug, params, options);
|
|
818
|
+
return job.wait(options);
|
|
819
|
+
}
|
|
820
|
+
/** Chat with one blocking `POST /v1/{slug}`. */
|
|
821
|
+
async chatSync(slug, params, options = {}) {
|
|
822
|
+
const response = await this.runSync(slug, chatBody(params), options);
|
|
823
|
+
return new ChatResponse(await readJson(response));
|
|
824
|
+
}
|
|
825
|
+
/** Submits a chat to the v2 queue and returns a `Job` whose result is a `ChatResponse`. */
|
|
826
|
+
async submitChat(slug, params, options = {}) {
|
|
827
|
+
return this.submitJob(slug, chatBody(params), options, chatFromRunResult);
|
|
828
|
+
}
|
|
829
|
+
async submitJob(slug, params, options, transform) {
|
|
830
|
+
const url = `${this.baseURL}/v2/${path(slug)}`;
|
|
831
|
+
const maxRetries = this.http.config.maxRetries;
|
|
832
|
+
let requestId = options.requestId ?? randomRequestId();
|
|
833
|
+
for (let attempt = 0; ; attempt++) {
|
|
834
|
+
const canRetry = attempt < maxRetries;
|
|
835
|
+
let response;
|
|
836
|
+
try {
|
|
837
|
+
response = await this.http.fetchRaw({
|
|
838
|
+
method: "POST",
|
|
839
|
+
url,
|
|
840
|
+
body: params,
|
|
841
|
+
headers: { "X-Request-ID": requestId },
|
|
842
|
+
signal: options.signal
|
|
843
|
+
});
|
|
844
|
+
} catch (err) {
|
|
845
|
+
if (canRetry && err instanceof APIConnectionError) {
|
|
846
|
+
await sleep(retryDelay(attempt, null), options.signal);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
throw err;
|
|
850
|
+
}
|
|
851
|
+
if (response.ok || response.status === 409) {
|
|
852
|
+
const body = await readJson(response);
|
|
853
|
+
if (response.status === 409 && !body.status_url) {
|
|
854
|
+
throw new SegmindError(`409 ${String(body.error ?? "conflict")}`, {
|
|
855
|
+
status: 409,
|
|
856
|
+
requestId,
|
|
857
|
+
body
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
return new Job(this.http, validateSubmit(body, response.status), transform);
|
|
861
|
+
}
|
|
862
|
+
if (canRetry && response.status === 503) {
|
|
863
|
+
await response.body?.cancel().catch(() => void 0);
|
|
864
|
+
requestId = randomRequestId();
|
|
865
|
+
await sleep(retryDelay(attempt, response.headers.get("retry-after")), options.signal);
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
if (canRetry && (response.status === 429 || response.status === 502 || response.status === 504)) {
|
|
869
|
+
await response.body?.cancel().catch(() => void 0);
|
|
870
|
+
await sleep(retryDelay(attempt, response.headers.get("retry-after")), options.signal);
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
throw await SegmindError.fromResponse(response, requestId);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
var Models = class {
|
|
878
|
+
constructor(client2) {
|
|
879
|
+
this.client = client2;
|
|
880
|
+
}
|
|
881
|
+
client;
|
|
882
|
+
/** Every model in the catalog, under the key `Inference Model Information List`. */
|
|
883
|
+
async list(options = {}) {
|
|
884
|
+
return this.client.http.json({
|
|
885
|
+
method: "GET",
|
|
886
|
+
url: `${this.client.spotURL}/inference-model-information/list`,
|
|
887
|
+
signal: options.signal,
|
|
888
|
+
retry: true
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
var Accounts = class {
|
|
893
|
+
constructor(client2) {
|
|
894
|
+
this.client = client2;
|
|
895
|
+
}
|
|
896
|
+
client;
|
|
897
|
+
/** Account details for the API key. */
|
|
898
|
+
async current(options = {}) {
|
|
899
|
+
return this.client.http.json({
|
|
900
|
+
method: "GET",
|
|
901
|
+
url: `${this.client.consoleURL}/auth/authenticate`,
|
|
902
|
+
signal: options.signal,
|
|
903
|
+
retry: true
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
function validateSubmit(body, status) {
|
|
908
|
+
if (!body.request_id || !body.status_url || !body.response_url) {
|
|
909
|
+
throw new SegmindError(
|
|
910
|
+
`v2 submit returned ${status} without request_id, status_url and response_url (keys: ${Object.keys(body).sort().join(", ")})`,
|
|
911
|
+
{ status, body }
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
return body;
|
|
915
|
+
}
|
|
916
|
+
function path(slug) {
|
|
917
|
+
const clean = slug.trim().replace(/^\/+/, "");
|
|
918
|
+
if (!clean) throw new SegmindError("A model slug is required.");
|
|
919
|
+
return clean;
|
|
920
|
+
}
|
|
921
|
+
function trimSlash(url) {
|
|
922
|
+
return url.replace(/\/+$/, "");
|
|
923
|
+
}
|
|
924
|
+
function trimBase(url) {
|
|
925
|
+
return trimSlash(url).replace(/\/v\d+$/, "");
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/image-url.ts
|
|
929
|
+
async function imageUrl(input, options = {}) {
|
|
930
|
+
if (typeof input === "string" && /^(https?:|data:)/i.test(input)) {
|
|
931
|
+
return { type: "image_url", image_url: { url: input } };
|
|
932
|
+
}
|
|
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)}` } };
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// src/index.ts
|
|
938
|
+
var shared;
|
|
939
|
+
function client() {
|
|
940
|
+
shared ??= new Segmind();
|
|
941
|
+
return shared;
|
|
942
|
+
}
|
|
943
|
+
var run = (slug, params, options) => client().run(slug, params, options);
|
|
944
|
+
var runSync = (slug, params, options) => client().runSync(slug, params, options);
|
|
945
|
+
var submit = (slug, params, options) => client().submit(slug, params, options);
|
|
946
|
+
var chat = (slug, params, options) => client().chat(slug, params, options);
|
|
947
|
+
var chatSync = (slug, params, options) => client().chatSync(slug, params, options);
|
|
948
|
+
var submitChat = (slug, params, options) => client().submitChat(slug, params, options);
|
|
949
|
+
var models = {
|
|
950
|
+
list: (options) => client().models.list(options)
|
|
951
|
+
};
|
|
952
|
+
var accounts = {
|
|
953
|
+
current: (options) => client().accounts.current(options)
|
|
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
|
+
};
|
|
977
|
+
export {
|
|
978
|
+
APIConnectionError,
|
|
979
|
+
Accounts,
|
|
980
|
+
ChatResponse,
|
|
981
|
+
DEFAULT_BASE_URL,
|
|
982
|
+
DEFAULT_CONSOLE_URL,
|
|
983
|
+
DEFAULT_SPOT_URL,
|
|
984
|
+
DEFAULT_WAIT_INTERVAL_MS,
|
|
985
|
+
DEFAULT_WAIT_TIMEOUT_MS,
|
|
986
|
+
Files,
|
|
987
|
+
Generations,
|
|
988
|
+
InferenceFailed,
|
|
989
|
+
InferenceTimeout,
|
|
990
|
+
Job,
|
|
991
|
+
Models,
|
|
992
|
+
PixelFlows,
|
|
993
|
+
PixelflowJob,
|
|
994
|
+
Segmind,
|
|
995
|
+
SegmindError,
|
|
996
|
+
VERSION,
|
|
997
|
+
Webhooks,
|
|
998
|
+
accounts,
|
|
999
|
+
chat,
|
|
1000
|
+
chatSync,
|
|
1001
|
+
files,
|
|
1002
|
+
generations,
|
|
1003
|
+
imageUrl,
|
|
1004
|
+
models,
|
|
1005
|
+
pixelflows,
|
|
1006
|
+
run,
|
|
1007
|
+
runSync,
|
|
1008
|
+
submit,
|
|
1009
|
+
submitChat,
|
|
1010
|
+
webhooks
|
|
1011
|
+
};
|
|
1012
|
+
//# sourceMappingURL=index.js.map
|