pareta 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.mjs ADDED
@@ -0,0 +1,1082 @@
1
+ // src/errors.ts
2
+ var ParetaError = class extends Error {
3
+ constructor(message, options) {
4
+ super(message);
5
+ this.name = "ParetaError";
6
+ if (options && options.cause !== void 0) this.cause = options.cause;
7
+ Object.setPrototypeOf(this, new.target.prototype);
8
+ }
9
+ };
10
+ var APIConnectionError = class extends ParetaError {
11
+ constructor(message = "connection error", options) {
12
+ super(message, options);
13
+ this.name = "APIConnectionError";
14
+ }
15
+ };
16
+ var APITimeoutError = class extends APIConnectionError {
17
+ constructor(message = "request timed out", options) {
18
+ super(message, options);
19
+ this.name = "APITimeoutError";
20
+ }
21
+ };
22
+ var APIStatusError = class extends ParetaError {
23
+ /** The HTTP status code. */
24
+ status;
25
+ /** The server's `detail` (string, or an array of validation items for 422). */
26
+ detail;
27
+ /** Value of the `x-request-id` response header, if present. */
28
+ requestId;
29
+ /** The underlying `Response` (for advanced use). */
30
+ response;
31
+ constructor(message, init) {
32
+ super(message);
33
+ this.name = "APIStatusError";
34
+ this.status = init.status;
35
+ this.detail = init.detail;
36
+ this.requestId = init.requestId ?? null;
37
+ this.response = init.response;
38
+ }
39
+ };
40
+ var BadRequestError = class extends APIStatusError {
41
+ constructor(message, init) {
42
+ super(message, init);
43
+ this.name = "BadRequestError";
44
+ }
45
+ };
46
+ var AuthenticationError = class extends APIStatusError {
47
+ constructor(message, init) {
48
+ super(message, init);
49
+ this.name = "AuthenticationError";
50
+ }
51
+ };
52
+ var PermissionDeniedError = class extends APIStatusError {
53
+ constructor(message, init) {
54
+ super(message, init);
55
+ this.name = "PermissionDeniedError";
56
+ }
57
+ };
58
+ var NotFoundError = class extends APIStatusError {
59
+ constructor(message, init) {
60
+ super(message, init);
61
+ this.name = "NotFoundError";
62
+ }
63
+ };
64
+ var ConflictError = class extends APIStatusError {
65
+ constructor(message, init) {
66
+ super(message, init);
67
+ this.name = "ConflictError";
68
+ }
69
+ };
70
+ var InsufficientCreditsError = class extends APIStatusError {
71
+ constructor(message, init) {
72
+ super(message, init);
73
+ this.name = "InsufficientCreditsError";
74
+ }
75
+ };
76
+ var RateLimitError = class extends APIStatusError {
77
+ constructor(message, init) {
78
+ super(message, init);
79
+ this.name = "RateLimitError";
80
+ }
81
+ };
82
+ var EndpointNotReadyError = class extends APIStatusError {
83
+ constructor(message, init) {
84
+ super(message, init);
85
+ this.name = "EndpointNotReadyError";
86
+ }
87
+ };
88
+ var STATUS_MAP = {
89
+ 400: BadRequestError,
90
+ 401: AuthenticationError,
91
+ 402: InsufficientCreditsError,
92
+ 403: PermissionDeniedError,
93
+ 404: NotFoundError,
94
+ 409: ConflictError,
95
+ 422: BadRequestError,
96
+ 429: RateLimitError,
97
+ 503: EndpointNotReadyError
98
+ };
99
+ function errorFromResponse(status, opts) {
100
+ const { detail, requestId, response } = opts;
101
+ const message = typeof detail === "string" && detail ? detail : `HTTP ${status}`;
102
+ let cls = STATUS_MAP[status];
103
+ if (!cls) cls = status === 429 ? RateLimitError : APIStatusError;
104
+ return new cls(message, { status, detail, requestId, response });
105
+ }
106
+
107
+ // src/streaming.ts
108
+ function normalize(s) {
109
+ return s.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
110
+ }
111
+ async function* iterLines(reader) {
112
+ const decoder = new TextDecoder();
113
+ let buffer = "";
114
+ for (; ; ) {
115
+ const { done, value } = await reader.read();
116
+ if (done) break;
117
+ buffer = normalize(buffer + decoder.decode(value, { stream: true }));
118
+ let idx;
119
+ while ((idx = buffer.indexOf("\n")) >= 0) {
120
+ yield buffer.slice(0, idx);
121
+ buffer = buffer.slice(idx + 1);
122
+ }
123
+ }
124
+ buffer = normalize(buffer + decoder.decode());
125
+ if (buffer.length > 0) {
126
+ for (const line of buffer.split("\n")) yield line;
127
+ }
128
+ }
129
+ async function* parseDataOnly(lines) {
130
+ for await (const rawLine of lines) {
131
+ let line = rawLine.trim();
132
+ if (!line || line.startsWith(":")) continue;
133
+ if (line.startsWith("data:")) line = line.slice("data:".length).trim();
134
+ if (!line || line === "[DONE]") {
135
+ if (line === "[DONE]") return;
136
+ continue;
137
+ }
138
+ try {
139
+ yield JSON.parse(line);
140
+ } catch {
141
+ continue;
142
+ }
143
+ }
144
+ }
145
+ async function* parseNamedEvent(lines) {
146
+ let event = "message";
147
+ for await (const rawLine of lines) {
148
+ const line = rawLine.replace(/\n+$/, "");
149
+ if (!line) {
150
+ event = "message";
151
+ continue;
152
+ }
153
+ if (line.startsWith(":")) continue;
154
+ if (line.startsWith("event:")) {
155
+ event = line.slice("event:".length).trim();
156
+ } else if (line.startsWith("data:")) {
157
+ const raw = line.slice("data:".length).trim();
158
+ if (raw === "[DONE]") return;
159
+ let data;
160
+ try {
161
+ data = JSON.parse(raw);
162
+ } catch {
163
+ data = raw;
164
+ }
165
+ yield { event, data };
166
+ }
167
+ }
168
+ }
169
+ function parseSSE(reader, opts) {
170
+ const lines = iterLines(reader);
171
+ return opts?.events ? parseNamedEvent(lines) : parseDataOnly(lines);
172
+ }
173
+
174
+ // src/version.ts
175
+ var VERSION = "0.1.0";
176
+
177
+ // src/money.ts
178
+ var MICRO_PER_CENT = 10000n;
179
+ function dollarsFlooredToCents(microUsd) {
180
+ const micro = typeof microUsd === "bigint" ? microUsd : BigInt(Math.trunc(Number(microUsd ?? 0)));
181
+ const cents = micro / MICRO_PER_CENT;
182
+ const negative = cents < 0n;
183
+ const abs = negative ? -cents : cents;
184
+ const dollars = abs / 100n;
185
+ const remainder = abs % 100n;
186
+ return `${negative ? "-" : ""}${dollars.toString()}.${remainder.toString().padStart(2, "0")}`;
187
+ }
188
+
189
+ // src/models.ts
190
+ var BaseModel = class {
191
+ /** The raw server JSON, untouched. */
192
+ raw;
193
+ constructor(raw) {
194
+ this.raw = raw ?? {};
195
+ }
196
+ /** The raw server JSON (alias of `.raw`, mirrors Python `.to_dict()`). */
197
+ toDict() {
198
+ return this.raw;
199
+ }
200
+ /** Read an arbitrary key off the raw payload. */
201
+ get(key, fallback) {
202
+ const v = this.raw[key];
203
+ return v === void 0 ? fallback : v;
204
+ }
205
+ };
206
+ var Usage = class extends BaseModel {
207
+ get promptTokens() {
208
+ return this.raw.prompt_tokens ?? null;
209
+ }
210
+ get completionTokens() {
211
+ return this.raw.completion_tokens ?? null;
212
+ }
213
+ get totalTokens() {
214
+ return this.raw.total_tokens ?? null;
215
+ }
216
+ };
217
+ var Message = class extends BaseModel {
218
+ get role() {
219
+ return this.raw.role ?? null;
220
+ }
221
+ get content() {
222
+ return this.raw.content ?? null;
223
+ }
224
+ };
225
+ var Choice = class extends BaseModel {
226
+ get index() {
227
+ return this.raw.index ?? null;
228
+ }
229
+ get finishReason() {
230
+ return this.raw.finish_reason ?? null;
231
+ }
232
+ get message() {
233
+ return new Message(this.raw.message ?? {});
234
+ }
235
+ /** Streaming chunks carry `delta` instead of `message`. */
236
+ get delta() {
237
+ return new Message(this.raw.delta ?? {});
238
+ }
239
+ };
240
+ var ChatCompletion = class extends BaseModel {
241
+ get id() {
242
+ return this.raw.id ?? null;
243
+ }
244
+ get model() {
245
+ return this.raw.model ?? null;
246
+ }
247
+ get created() {
248
+ return this.raw.created ?? null;
249
+ }
250
+ get choices() {
251
+ return (this.raw.choices ?? []).map((c) => new Choice(c));
252
+ }
253
+ get usage() {
254
+ return new Usage(this.raw.usage ?? {});
255
+ }
256
+ };
257
+ var ChatCompletionChunk = class extends ChatCompletion {
258
+ };
259
+ var Model = class extends BaseModel {
260
+ get id() {
261
+ return this.raw.id ?? null;
262
+ }
263
+ get ownedBy() {
264
+ return this.raw.owned_by ?? null;
265
+ }
266
+ get created() {
267
+ return this.raw.created ?? null;
268
+ }
269
+ };
270
+ var ModelList = class extends BaseModel {
271
+ get data() {
272
+ return (this.raw.data ?? []).map((m) => new Model(m));
273
+ }
274
+ get length() {
275
+ return (this.raw.data ?? []).length;
276
+ }
277
+ [Symbol.iterator]() {
278
+ return this.data[Symbol.iterator]();
279
+ }
280
+ };
281
+ var Endpoint = class extends BaseModel {
282
+ get id() {
283
+ return this.raw.id ?? this.raw.name ?? null;
284
+ }
285
+ get name() {
286
+ return this.raw.name ?? null;
287
+ }
288
+ get model() {
289
+ return this.raw.model ?? null;
290
+ }
291
+ get status() {
292
+ return this.raw.status ?? null;
293
+ }
294
+ // Live list sends camelCase `taskName`; the detail record sends `task`.
295
+ get task() {
296
+ return this.raw.taskName ?? this.raw.task ?? null;
297
+ }
298
+ get url() {
299
+ return this.raw.url ?? null;
300
+ }
301
+ get isLive() {
302
+ return this.raw.status === "live";
303
+ }
304
+ };
305
+ function endpointList(raw) {
306
+ return (raw ?? []).map((e) => new Endpoint(e));
307
+ }
308
+ var Task = class extends BaseModel {
309
+ get id() {
310
+ return this.raw.id ?? null;
311
+ }
312
+ get defaultScorer() {
313
+ return this.raw.default_scorer ?? null;
314
+ }
315
+ get hasBlobInput() {
316
+ return Boolean(this.raw.has_blob_input);
317
+ }
318
+ };
319
+ function taskList(raw) {
320
+ return (raw?.tasks ?? []).map((t) => new Task(t));
321
+ }
322
+ var TaskMatchCandidate = class extends BaseModel {
323
+ get taskId() {
324
+ return this.raw.task_id ?? null;
325
+ }
326
+ get score() {
327
+ return this.raw.score ?? null;
328
+ }
329
+ get confidence() {
330
+ return this.raw.confidence ?? null;
331
+ }
332
+ };
333
+ var TaskMatch = class extends BaseModel {
334
+ get query() {
335
+ return this.raw.query ?? null;
336
+ }
337
+ get matched() {
338
+ return Boolean(this.raw.matched);
339
+ }
340
+ get chosen() {
341
+ return this.raw.chosen ? new TaskMatchCandidate(this.raw.chosen) : null;
342
+ }
343
+ get candidates() {
344
+ return (this.raw.candidates ?? []).map((c) => new TaskMatchCandidate(c));
345
+ }
346
+ get ambiguous() {
347
+ return Boolean(this.raw.ambiguous);
348
+ }
349
+ get matcher() {
350
+ return this.raw.matcher ?? null;
351
+ }
352
+ };
353
+ var EvalSet = class extends BaseModel {
354
+ get id() {
355
+ return this.raw.id ?? null;
356
+ }
357
+ get taskId() {
358
+ return this.raw.task_id ?? null;
359
+ }
360
+ get name() {
361
+ return this.raw.name ?? null;
362
+ }
363
+ get itemCount() {
364
+ return this.raw.item_count ?? null;
365
+ }
366
+ get scoringStrategy() {
367
+ return this.raw.scoring_strategy ?? null;
368
+ }
369
+ };
370
+ function evalSetFromCreate(raw) {
371
+ return new EvalSet(raw?.eval_set ?? {});
372
+ }
373
+ function evalSetList(raw) {
374
+ return (raw?.eval_sets ?? []).map((e) => new EvalSet(e));
375
+ }
376
+ var EvalResult = class extends BaseModel {
377
+ get modelId() {
378
+ return this.raw.model_id ?? null;
379
+ }
380
+ get kind() {
381
+ return this.raw.kind ?? null;
382
+ }
383
+ get qualityMean() {
384
+ return this.raw.quality_mean ?? null;
385
+ }
386
+ get qualityCiLow() {
387
+ return this.raw.quality_ci_low ?? null;
388
+ }
389
+ get qualityCiHigh() {
390
+ return this.raw.quality_ci_high ?? null;
391
+ }
392
+ // Raw integer — sub-cent unit rate, NOT floored (see money.ts).
393
+ get meanCostMicroUsd() {
394
+ return this.raw.mean_cost_micro_usd ?? null;
395
+ }
396
+ get nSucceeded() {
397
+ return this.raw.n_succeeded ?? null;
398
+ }
399
+ get errorCount() {
400
+ return this.raw.error_count ?? null;
401
+ }
402
+ };
403
+ var LeaderboardEntry = class extends BaseModel {
404
+ get name() {
405
+ return this.raw.name ?? null;
406
+ }
407
+ get kind() {
408
+ return this.raw.kind ?? null;
409
+ }
410
+ get quality() {
411
+ return this.raw.quality ?? null;
412
+ }
413
+ // Raw integer — sub-cent unit rate, NOT floored (see money.ts).
414
+ get costPerRequestMicroUsd() {
415
+ return this.raw.cost_per_request_micro_usd ?? null;
416
+ }
417
+ get contextK() {
418
+ return this.raw.context_k ?? null;
419
+ }
420
+ get runMode() {
421
+ return this.raw.run_mode ?? null;
422
+ }
423
+ };
424
+ var Leaderboard = class extends BaseModel {
425
+ get taskId() {
426
+ return this.raw.task_id ?? null;
427
+ }
428
+ get metric() {
429
+ return this.raw.metric ?? null;
430
+ }
431
+ get costUnit() {
432
+ return this.raw.cost_unit ?? null;
433
+ }
434
+ get recommended() {
435
+ return this.raw.recommended ?? null;
436
+ }
437
+ get models() {
438
+ return (this.raw.models ?? []).map((m) => new LeaderboardEntry(m));
439
+ }
440
+ get frontier() {
441
+ return this.raw.frontier ? new LeaderboardEntry(this.raw.frontier) : null;
442
+ }
443
+ };
444
+ var FrontierModel = class extends BaseModel {
445
+ get id() {
446
+ return this.raw.id ?? null;
447
+ }
448
+ get vendor() {
449
+ return this.raw.vendor ?? null;
450
+ }
451
+ get vision() {
452
+ return Boolean(this.raw.vision);
453
+ }
454
+ /** Only meaningful when a task was given: it's on that task's leaderboard. */
455
+ get benchmarked() {
456
+ return Boolean(this.raw.benchmarked);
457
+ }
458
+ };
459
+ function leaderboardFrom(raw) {
460
+ return new Leaderboard(raw ?? {});
461
+ }
462
+ function frontierModels(raw) {
463
+ return (raw?.frontier_models ?? []).map((m) => new FrontierModel(m));
464
+ }
465
+ var EvalRun = class extends BaseModel {
466
+ get run() {
467
+ return this.raw.run ?? {};
468
+ }
469
+ get id() {
470
+ return this.run.id ?? null;
471
+ }
472
+ get evalSetId() {
473
+ return this.run.eval_set_id ?? null;
474
+ }
475
+ get status() {
476
+ return this.run.status ?? null;
477
+ }
478
+ get isTerminal() {
479
+ return this.run.status === "completed" || this.run.status === "failed";
480
+ }
481
+ get candidateModels() {
482
+ return [...this.run.candidate_model_ids ?? []];
483
+ }
484
+ get errorDetail() {
485
+ return this.run.error_detail ?? null;
486
+ }
487
+ /** Raw billed micro-USD integer. */
488
+ get costMicroUsd() {
489
+ return Number(this.run.total_cost_micro_usd ?? 0);
490
+ }
491
+ /** Billed total, floored to whole cents, as a fixed-2dp dollar string ("1.23"). */
492
+ get cost() {
493
+ return dollarsFlooredToCents(this.run.total_cost_micro_usd);
494
+ }
495
+ get results() {
496
+ return (this.raw.results ?? []).map((r) => new EvalResult(r));
497
+ }
498
+ };
499
+
500
+ // src/resources/chat.ts
501
+ var PATH = "/v1/chat/completions";
502
+ function buildBody(params) {
503
+ const { model, messages, stream, ...extra } = params;
504
+ if (!model) throw new ParetaError("model is required (an endpoint id from endpoints.deploy)");
505
+ if (!messages || messages.length === 0) {
506
+ throw new ParetaError("messages is required and must be non-empty");
507
+ }
508
+ const body = { model, messages, ...extra };
509
+ if (stream) body.stream = true;
510
+ return body;
511
+ }
512
+ var Completions = class {
513
+ constructor(client) {
514
+ this.client = client;
515
+ }
516
+ client;
517
+ create(params) {
518
+ const body = buildBody(params);
519
+ if (params.stream) {
520
+ return this.client.stream("POST", PATH, {
521
+ body,
522
+ cast: (raw) => new ChatCompletionChunk(raw)
523
+ });
524
+ }
525
+ return this.client.request("POST", PATH, {
526
+ body,
527
+ cast: (raw) => new ChatCompletion(raw)
528
+ });
529
+ }
530
+ };
531
+ var Chat = class {
532
+ completions;
533
+ constructor(client) {
534
+ this.completions = new Completions(client);
535
+ }
536
+ };
537
+
538
+ // src/resources/models.ts
539
+ var PATH2 = "/v1/models";
540
+ var Models = class {
541
+ constructor(client) {
542
+ this.client = client;
543
+ }
544
+ client;
545
+ list() {
546
+ return this.client.request("GET", PATH2, {
547
+ cast: (raw) => new ModelList(raw)
548
+ });
549
+ }
550
+ };
551
+
552
+ // src/resources/endpoints.ts
553
+ var BASE = "/v1/endpoints";
554
+ function buildDeployBody(params) {
555
+ const { task, model, name, wait: _wait, ...extra } = params;
556
+ if (!task) throw new ParetaError("task is required");
557
+ const body = { task, model: model || "recommended", ...extra };
558
+ if (name !== void 0) body.name = name;
559
+ return body;
560
+ }
561
+ function endpointFromComplete(data) {
562
+ const ep = data && typeof data === "object" ? data.endpoint : null;
563
+ return new Endpoint(ep ?? {});
564
+ }
565
+ function deployError(data) {
566
+ const msg = data && typeof data === "object" ? data.message : String(data);
567
+ return new ParetaError(`deploy failed: ${msg || "unknown error"}`);
568
+ }
569
+ var EndpointMetrics = class {
570
+ constructor(client, id) {
571
+ this.client = client;
572
+ this.id = id;
573
+ }
574
+ client;
575
+ id;
576
+ performance(params) {
577
+ return this.client.request("GET", `${BASE}/${this.id}/performance`, { params });
578
+ }
579
+ uptime(params) {
580
+ return this.client.request("GET", `${BASE}/${this.id}/uptime`, { params });
581
+ }
582
+ cost(params) {
583
+ return this.client.request("GET", `${BASE}/${this.id}/cost`, { params });
584
+ }
585
+ quality(params) {
586
+ return this.client.request("GET", `${BASE}/${this.id}/quality`, { params });
587
+ }
588
+ activity(params) {
589
+ return this.client.request("GET", `${BASE}/${this.id}/activity`, { params });
590
+ }
591
+ };
592
+ var Endpoints = class {
593
+ constructor(client) {
594
+ this.client = client;
595
+ }
596
+ client;
597
+ deploy(params) {
598
+ const body = buildDeployBody(params);
599
+ const stream = this.client.stream("POST", BASE, { body, events: true });
600
+ if (!params.wait) return stream;
601
+ return this.waitForDeploy(stream);
602
+ }
603
+ async waitForDeploy(stream) {
604
+ for await (const ev of stream) {
605
+ if (ev.event === "complete") return endpointFromComplete(ev.data);
606
+ if (ev.event === "error") throw deployError(ev.data);
607
+ }
608
+ throw new ParetaError("deploy stream ended without a 'complete' event");
609
+ }
610
+ list() {
611
+ return this.client.request("GET", BASE, { cast: endpointList });
612
+ }
613
+ retrieve(endpointId) {
614
+ return this.client.request("GET", `${BASE}/${endpointId}`, {
615
+ cast: (raw) => new Endpoint(raw)
616
+ });
617
+ }
618
+ start(endpointId) {
619
+ return this.client.request("POST", `${BASE}/${endpointId}/start`);
620
+ }
621
+ stop(endpointId) {
622
+ return this.client.request("POST", `${BASE}/${endpointId}/stop`);
623
+ }
624
+ async delete(endpointId) {
625
+ await this.client.request("DELETE", `${BASE}/${endpointId}`);
626
+ }
627
+ metrics(endpointId) {
628
+ return new EndpointMetrics(this.client, endpointId);
629
+ }
630
+ };
631
+
632
+ // src/resources/tasks.ts
633
+ var BASE2 = "/v1/tasks";
634
+ var Tasks = class {
635
+ constructor(client) {
636
+ this.client = client;
637
+ }
638
+ client;
639
+ list() {
640
+ return this.client.request("GET", BASE2, { cast: taskList });
641
+ }
642
+ retrieve(taskId, opts = {}) {
643
+ const params = opts.examplesN !== void 0 ? { examples_n: opts.examplesN } : void 0;
644
+ return this.client.request("GET", `${BASE2}/${taskId}`, {
645
+ params,
646
+ cast: (raw) => new Task(raw)
647
+ });
648
+ }
649
+ /** Free-text intent → ranked candidate tasks (the Step-0 backend matcher). */
650
+ match(query, opts = {}) {
651
+ if (!query || !query.trim()) throw new ParetaError("query is required");
652
+ return this.client.request("POST", `${BASE2}/match`, {
653
+ body: { query, top_k: opts.topK ?? 5 },
654
+ cast: (raw) => new TaskMatch(raw)
655
+ });
656
+ }
657
+ /** Models ranked by quality/cost for a task (+ recommended + frontier baseline). */
658
+ leaderboard(taskId) {
659
+ return this.client.request("GET", `${BASE2}/${taskId}/leaderboard`, {
660
+ cast: leaderboardFrom
661
+ });
662
+ }
663
+ /** The task's recommended deployable model — what deploy(model:"recommended") resolves to. */
664
+ async recommended(taskId) {
665
+ return (await this.leaderboard(taskId)).recommended;
666
+ }
667
+ };
668
+
669
+ // src/resources/evals.ts
670
+ var BASE3 = "/v1/eval-sets";
671
+ var RUNS = "/v1/eval-runs";
672
+ var FRONTIER = "/v1/eval/frontier-models";
673
+ var INLINE_MAX = 5 * 1024 * 1024;
674
+ var MIME_BY_EXT = {
675
+ pdf: "application/pdf",
676
+ png: "image/png",
677
+ jpg: "image/jpeg",
678
+ jpeg: "image/jpeg",
679
+ gif: "image/gif",
680
+ webp: "image/webp",
681
+ tiff: "image/tiff",
682
+ tif: "image/tiff",
683
+ bmp: "image/bmp",
684
+ txt: "text/plain",
685
+ json: "application/json",
686
+ jsonl: "application/jsonl",
687
+ csv: "text/csv"
688
+ };
689
+ function guessMime(filename, override) {
690
+ if (override) return override;
691
+ const ext = filename.split(".").pop()?.toLowerCase();
692
+ return ext && MIME_BY_EXT[ext] || "application/octet-stream";
693
+ }
694
+ async function toBlob(file, mimeOverride) {
695
+ if (typeof file === "string") {
696
+ const [{ readFile }, { basename }] = await Promise.all([
697
+ import('fs/promises'),
698
+ import('path')
699
+ ]);
700
+ const buf = await readFile(file);
701
+ const filename = basename(file);
702
+ const mime2 = guessMime(filename, mimeOverride);
703
+ return { blob: new Blob([buf], { type: mime2 }), filename, size: buf.byteLength, mime: mime2 };
704
+ }
705
+ if (file instanceof Blob) {
706
+ const mime2 = mimeOverride || file.type || "application/octet-stream";
707
+ const blob = mimeOverride ? new Blob([file], { type: mime2 }) : file;
708
+ return { blob, filename: "upload", size: file.size, mime: mime2 };
709
+ }
710
+ const bytes = file instanceof Uint8Array ? file : new Uint8Array(file);
711
+ const mime = mimeOverride || "application/octet-stream";
712
+ return { blob: new Blob([bytes], { type: mime }), filename: "upload", size: bytes.byteLength, mime };
713
+ }
714
+ function itemsJsonl(task, items, name) {
715
+ if (!items || items.length === 0) throw new ParetaError("items is required and must be non-empty");
716
+ const jsonl = items.map((it) => JSON.stringify(it)).join("\n");
717
+ return {
718
+ files: { items: { filename: `items.${task}.jsonl`, content: jsonl, contentType: "application/jsonl" } },
719
+ data: { task_id: task, name: name || `sdk eval set (${items.length} items)` }
720
+ };
721
+ }
722
+ function mergeCandidates(models, frontierIds) {
723
+ const cands = [...models ?? [], ...frontierIds ?? []];
724
+ if (cands.length === 0) throw new ParetaError("models is required (the open candidates to evaluate)");
725
+ return cands;
726
+ }
727
+ function resolveFrontierFromRoster(frontier, roster) {
728
+ if (frontier === "all") return roster.map((m) => m.id).filter((x) => x != null);
729
+ if (frontier === "benchmarked") {
730
+ return roster.filter((m) => m.benchmarked).map((m) => m.id).filter((x) => x != null);
731
+ }
732
+ throw new ParetaError(
733
+ `unknown frontier keyword '${frontier}' (use 'all'/'benchmarked'/'none' or a list)`
734
+ );
735
+ }
736
+ var sleep = (seconds) => new Promise((r) => setTimeout(r, seconds * 1e3));
737
+ var EvalSets = class {
738
+ constructor(client) {
739
+ this.client = client;
740
+ }
741
+ client;
742
+ create(params) {
743
+ const { files, data } = itemsJsonl(params.task, params.items, params.name);
744
+ return this.client.request("POST", BASE3, { files, data, cast: evalSetFromCreate });
745
+ }
746
+ list() {
747
+ return this.client.request("GET", BASE3, { cast: evalSetList });
748
+ }
749
+ async retrieve(evalSetId) {
750
+ const raw = await this.client.request("GET", `${BASE3}/${evalSetId}`);
751
+ return new EvalSet((raw ?? {}).eval_set ?? {});
752
+ }
753
+ async delete(evalSetId) {
754
+ await this.client.request("DELETE", `${BASE3}/${evalSetId}`);
755
+ }
756
+ /**
757
+ * Attach a binary doc (PDF/image) to one row's blob field. Collapses the
758
+ * 3-call signed-URL flow (or the inline path for small files) into one call.
759
+ * `idx` is the 0-based row, `fieldName` the blob input field.
760
+ */
761
+ async uploadDocument(evalSetId, file, opts) {
762
+ const { blob, filename, size, mime } = await toBlob(file, opts.mime);
763
+ if (size < INLINE_MAX) {
764
+ return this.client.request("POST", `${BASE3}/${evalSetId}/attach-blob`, {
765
+ files: { file: { filename, content: blob, contentType: mime } },
766
+ data: { idx: String(opts.idx), field_name: opts.fieldName, mime }
767
+ });
768
+ }
769
+ const minted = await this.client.request(
770
+ "POST",
771
+ `${BASE3}/${evalSetId}/blob-upload-url`,
772
+ { body: { idx: opts.idx, field_name: opts.fieldName, mime, file_size: size } }
773
+ );
774
+ const put = await globalThis.fetch(minted.upload_url, {
775
+ method: "PUT",
776
+ body: blob,
777
+ headers: { "Content-Type": mime },
778
+ signal: AbortSignal.timeout(6e5)
779
+ });
780
+ if (put.status !== 200 && put.status !== 201) {
781
+ throw new ParetaError(`blob upload PUT failed: ${put.status}`);
782
+ }
783
+ return this.client.request("POST", `${BASE3}/${evalSetId}/blob-upload-complete`, {
784
+ body: { idx: opts.idx, field_name: opts.fieldName, storage_uri: minted.storage_uri, mime }
785
+ });
786
+ }
787
+ };
788
+ var EvalRuns = class {
789
+ constructor(client, sets) {
790
+ this.client = client;
791
+ this.sets = sets;
792
+ }
793
+ client;
794
+ sets;
795
+ async frontierIds(frontier, evalSet, task) {
796
+ if (frontier == null || frontier === "none") return [];
797
+ if (Array.isArray(frontier)) return [...frontier];
798
+ if (typeof frontier !== "string") {
799
+ throw new TypeError("frontier must be null, a list of ids, or 'all'/'benchmarked'/'none'");
800
+ }
801
+ let resolveTask = task;
802
+ if (!resolveTask && evalSet) resolveTask = (await this.sets.retrieve(evalSet)).taskId ?? void 0;
803
+ if (!resolveTask) throw new ParetaError("cannot resolve a frontier keyword without a task");
804
+ const roster = await this.client.request("GET", FRONTIER, {
805
+ params: { task: resolveTask },
806
+ cast: frontierModels
807
+ });
808
+ return resolveFrontierFromRoster(frontier, roster);
809
+ }
810
+ async create(params) {
811
+ let evalSet = params.evalSet;
812
+ if (evalSet == null) {
813
+ if (!(params.task && params.items)) {
814
+ throw new ParetaError("pass evalSet: <id>, or task + items to create one");
815
+ }
816
+ evalSet = (await this.sets.create({ task: params.task, items: params.items, name: params.name })).id ?? void 0;
817
+ }
818
+ const frontierIds = await this.frontierIds(params.frontier, evalSet, params.task);
819
+ const candidateModelIds = mergeCandidates(params.models, frontierIds);
820
+ const started = await this.client.request("POST", RUNS, {
821
+ body: { eval_set_id: evalSet, candidate_model_ids: candidateModelIds }
822
+ });
823
+ if (params.wait) {
824
+ return this.wait(started.run_id, { pollInterval: params.pollInterval, timeout: params.timeout });
825
+ }
826
+ return new EvalRun({ run: { id: started.run_id, status: started.status } });
827
+ }
828
+ retrieve(runId) {
829
+ return this.client.request("GET", `${RUNS}/${runId}`, {
830
+ cast: (raw) => new EvalRun(raw)
831
+ });
832
+ }
833
+ async wait(runId, opts = {}) {
834
+ const pollInterval = opts.pollInterval ?? 3;
835
+ const timeout = opts.timeout ?? 900;
836
+ const deadline = Date.now() + timeout * 1e3;
837
+ for (; ; ) {
838
+ const run = await this.retrieve(runId);
839
+ if (run.isTerminal) return run;
840
+ if (Date.now() >= deadline) {
841
+ throw new ParetaError(`eval run ${runId} did not finish within ${Math.round(timeout)}s`);
842
+ }
843
+ await sleep(pollInterval);
844
+ }
845
+ }
846
+ };
847
+ var Evals = class {
848
+ sets;
849
+ runs;
850
+ constructor(client) {
851
+ this.sets = new EvalSets(client);
852
+ this.runs = new EvalRuns(client, this.sets);
853
+ this.client = client;
854
+ }
855
+ client;
856
+ /**
857
+ * The frontier (vendor) roster you can evaluate against. With `task`, each is
858
+ * annotated `benchmarked` + the roster is vision-filtered for document tasks.
859
+ */
860
+ frontierModels(task) {
861
+ const params = task ? { task } : void 0;
862
+ return this.client.request("GET", FRONTIER, { params, cast: frontierModels });
863
+ }
864
+ };
865
+
866
+ // src/client.ts
867
+ var DEFAULT_BASE_URL = "https://api.pareta.ai";
868
+ var DEFAULT_TIMEOUT_MS = 6e4;
869
+ var DEFAULT_MAX_RETRIES = 2;
870
+ var RETRY_STATUSES = /* @__PURE__ */ new Set([408, 409, 429, 500, 502, 503, 504]);
871
+ function normalizeBaseURL(url) {
872
+ return url.replace(/\/+$/, "");
873
+ }
874
+ var Pareta = class _Pareta {
875
+ apiKey;
876
+ baseURL;
877
+ timeout;
878
+ maxRetries;
879
+ fetchImpl;
880
+ // Resource namespaces (assigned in the constructor; filled in slice by slice).
881
+ chat;
882
+ models;
883
+ endpoints;
884
+ tasks;
885
+ evals;
886
+ constructor(options = {}) {
887
+ if (!options.apiKey) {
888
+ throw new ParetaError(
889
+ "missing API key. Pass apiKey: \u2026 or use Pareta.fromEnv() with PARETA_API_KEY (mint a pareta_sk_ key in the dashboard)."
890
+ );
891
+ }
892
+ this.apiKey = options.apiKey;
893
+ this.baseURL = normalizeBaseURL(options.baseURL || DEFAULT_BASE_URL);
894
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
895
+ this.maxRetries = Math.max(0, Math.trunc(options.maxRetries ?? DEFAULT_MAX_RETRIES));
896
+ const f = options.fetch ?? globalThis.fetch;
897
+ if (typeof f !== "function") {
898
+ throw new ParetaError("no fetch implementation found \u2014 pass fetch: \u2026 (Node < 18 needs a polyfill).");
899
+ }
900
+ this.fetchImpl = options.fetch ? f : f.bind(globalThis);
901
+ this.chat = new Chat(this);
902
+ this.models = new Models(this);
903
+ this.endpoints = new Endpoints(this);
904
+ this.tasks = new Tasks(this);
905
+ this.evals = new Evals(this);
906
+ }
907
+ /** Build from PARETA_API_KEY (+ optional PARETA_BASE_URL); explicit opts win. */
908
+ static fromEnv(options = {}) {
909
+ const env = typeof process !== "undefined" && process.env ? process.env : {};
910
+ return new _Pareta({
911
+ ...options,
912
+ apiKey: options.apiKey || env.PARETA_API_KEY,
913
+ baseURL: options.baseURL || env.PARETA_BASE_URL
914
+ });
915
+ }
916
+ // ── header / url / retry helpers ─────────────────────────────────────
917
+ headers(opts) {
918
+ const h = {
919
+ Authorization: `Bearer ${this.apiKey}`,
920
+ Accept: opts.stream ? "text/event-stream" : "application/json",
921
+ "User-Agent": `pareta-typescript/${VERSION}`
922
+ };
923
+ if (opts.jsonBody) h["Content-Type"] = "application/json";
924
+ return h;
925
+ }
926
+ shouldRetry(status) {
927
+ return RETRY_STATUSES.has(status);
928
+ }
929
+ /** Seconds to wait before retry `attempt` (0-indexed). Overridable in tests. */
930
+ _backoff(attempt, retryAfter) {
931
+ if (retryAfter !== null && retryAfter >= 0) return Math.min(retryAfter, 30);
932
+ return Math.min(0.5 * 2 ** attempt, 8) + Math.random() * 0.25;
933
+ }
934
+ /** Sleep `seconds`. Overridable in tests to skip real waits. */
935
+ _sleep(seconds) {
936
+ return new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
937
+ }
938
+ retryAfterSeconds(res) {
939
+ const val = res.headers.get("retry-after");
940
+ if (!val) return null;
941
+ const n = Number.parseFloat(val);
942
+ return Number.isNaN(n) ? null : n;
943
+ }
944
+ buildURL(path, params) {
945
+ let url = `${this.baseURL}${path}`;
946
+ if (params) {
947
+ const qs = new URLSearchParams();
948
+ for (const [k, v] of Object.entries(params)) {
949
+ if (v !== void 0 && v !== null) qs.append(k, String(v));
950
+ }
951
+ const s = qs.toString();
952
+ if (s) url += `?${s}`;
953
+ }
954
+ return url;
955
+ }
956
+ buildFormData(files, data) {
957
+ const fd = new FormData();
958
+ if (data) for (const [k, v] of Object.entries(data)) fd.append(k, v);
959
+ if (files) {
960
+ for (const [k, f] of Object.entries(files)) {
961
+ const blob = f.content instanceof Blob ? f.content : new Blob([f.content], f.contentType ? { type: f.contentType } : void 0);
962
+ fd.append(k, blob, f.filename);
963
+ }
964
+ }
965
+ return fd;
966
+ }
967
+ wrapFetchError(e) {
968
+ const name = e?.name;
969
+ if (name === "TimeoutError" || name === "AbortError") {
970
+ return new APITimeoutError(void 0, { cause: e });
971
+ }
972
+ const msg = e?.message || "connection error";
973
+ return new APIConnectionError(msg, { cause: e });
974
+ }
975
+ async parseError(res) {
976
+ const requestId = res.headers.get("x-request-id");
977
+ let detail;
978
+ try {
979
+ const body = await res.json();
980
+ detail = body && typeof body === "object" && !Array.isArray(body) ? body.detail : body;
981
+ } catch {
982
+ detail = void 0;
983
+ }
984
+ return errorFromResponse(res.status, { detail, requestId, response: res });
985
+ }
986
+ // ── transport ────────────────────────────────────────────────────────
987
+ async request(method, path, opts = {}) {
988
+ const { body, params, files, data, cast } = opts;
989
+ const url = this.buildURL(path, params);
990
+ const isMultipart = files != null || data != null;
991
+ const headers = this.headers({ jsonBody: body != null && !isMultipart });
992
+ let payload;
993
+ if (isMultipart) {
994
+ payload = this.buildFormData(files, data);
995
+ } else if (body != null) {
996
+ payload = JSON.stringify(body);
997
+ }
998
+ let lastExc;
999
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
1000
+ let res;
1001
+ try {
1002
+ res = await this.fetchImpl(url, {
1003
+ method,
1004
+ headers,
1005
+ body: payload,
1006
+ signal: AbortSignal.timeout(this.timeout)
1007
+ });
1008
+ } catch (e) {
1009
+ lastExc = this.wrapFetchError(e);
1010
+ if (attempt < this.maxRetries) {
1011
+ await this._sleep(this._backoff(attempt, null));
1012
+ continue;
1013
+ }
1014
+ break;
1015
+ }
1016
+ if (res.ok) {
1017
+ const text = await res.text();
1018
+ const raw = text ? JSON.parse(text) : {};
1019
+ return cast ? cast(raw) : raw;
1020
+ }
1021
+ if (attempt < this.maxRetries && this.shouldRetry(res.status)) {
1022
+ await res.text().catch(() => void 0);
1023
+ await this._sleep(this._backoff(attempt, this.retryAfterSeconds(res)));
1024
+ continue;
1025
+ }
1026
+ throw await this.parseError(res);
1027
+ }
1028
+ throw lastExc;
1029
+ }
1030
+ /**
1031
+ * Yield parsed SSE objects. Retries ONLY the initial connect/handshake — once
1032
+ * bytes are flowing a mid-stream drop raises (re-issuing would re-run a
1033
+ * metered generation / re-trigger a deploy). `events:true` → `{event,data}`.
1034
+ */
1035
+ async *stream(method, path, opts = {}) {
1036
+ const { body, params, cast, events } = opts;
1037
+ const url = this.buildURL(path, params);
1038
+ const headers = this.headers({ stream: true, jsonBody: body != null });
1039
+ const payload = body != null ? JSON.stringify(body) : void 0;
1040
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
1041
+ let started = false;
1042
+ try {
1043
+ const res = await this.fetchImpl(url, {
1044
+ method,
1045
+ headers,
1046
+ body: payload,
1047
+ signal: AbortSignal.timeout(this.timeout)
1048
+ });
1049
+ if (!res.ok) {
1050
+ if (attempt < this.maxRetries && this.shouldRetry(res.status)) {
1051
+ await res.text().catch(() => void 0);
1052
+ await this._sleep(this._backoff(attempt, this.retryAfterSeconds(res)));
1053
+ continue;
1054
+ }
1055
+ throw await this.parseError(res);
1056
+ }
1057
+ if (!res.body) throw new APIConnectionError("response had no body to stream");
1058
+ started = true;
1059
+ const reader = res.body.getReader();
1060
+ if (events) {
1061
+ for await (const ev of parseSSE(reader, { events: true })) {
1062
+ yield ev;
1063
+ }
1064
+ } else {
1065
+ for await (const obj of parseSSE(reader)) {
1066
+ yield cast ? cast(obj) : obj;
1067
+ }
1068
+ }
1069
+ return;
1070
+ } catch (e) {
1071
+ if (e instanceof ParetaError && e.status !== void 0) throw e;
1072
+ const wrapped = this.wrapFetchError(e);
1073
+ if (started || attempt >= this.maxRetries) throw wrapped;
1074
+ await this._sleep(this._backoff(attempt, null));
1075
+ }
1076
+ }
1077
+ }
1078
+ };
1079
+
1080
+ export { APIConnectionError, APIStatusError, APITimeoutError, AuthenticationError, BadRequestError, BaseModel, ChatCompletion, ChatCompletionChunk, Choice, ConflictError, Endpoint, EndpointMetrics, EndpointNotReadyError, EvalResult, EvalRun, EvalRuns, EvalSet, EvalSets, FrontierModel, InsufficientCreditsError, Leaderboard, LeaderboardEntry, Message, Model, ModelList, NotFoundError, Pareta, ParetaError, PermissionDeniedError, RateLimitError, Task, TaskMatch, TaskMatchCandidate, Usage, VERSION };
1081
+ //# sourceMappingURL=index.mjs.map
1082
+ //# sourceMappingURL=index.mjs.map