pareta 2.0.0 → 3.0.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/README.md CHANGED
@@ -41,7 +41,7 @@ contender honestly:
41
41
 
42
42
  ```ts
43
43
  const set = await pa.evals.sets.create({
44
- items: [...], intent: "extract vendor, total and date from each invoice",
44
+ items: [...], prompt: "extract vendor, total and date from each invoice",
45
45
  });
46
46
  const run = await pa.evals.runs.create({
47
47
  evalSet: set.id, models: ["auto"], frontier: ["claude-opus-4-7"], wait: true,
package/dist/index.cjs CHANGED
@@ -174,7 +174,7 @@ function parseSSE(reader, opts) {
174
174
  }
175
175
 
176
176
  // src/version.ts
177
- var VERSION = "2.0.0";
177
+ var VERSION = "3.0.0";
178
178
 
179
179
  // src/money.ts
180
180
  var MICRO_PER_CENT = 10000n;
@@ -255,6 +255,36 @@ var ChatCompletion = class extends BaseModel {
255
255
  get usage() {
256
256
  return new Usage(this.raw.usage ?? {});
257
257
  }
258
+ /** #164 receipt, held OFF `.raw` so `toDict()` stays the raw server JSON. */
259
+ _cost;
260
+ /** #164: attach the per-call receipt from the response headers (billed +
261
+ * the frontier counterfactual, micro-USD ints). Chainable. */
262
+ withCost(headers) {
263
+ const int = (name) => {
264
+ const v = headers?.get(name);
265
+ if (v == null) return null;
266
+ const n = parseInt(v, 10);
267
+ return Number.isNaN(n) ? null : n;
268
+ };
269
+ this._cost = {
270
+ billed: int("X-Pareta-Billed"),
271
+ frontier: int("X-Pareta-Frontier-Would-Have-Cost")
272
+ };
273
+ return this;
274
+ }
275
+ /** What Pareta charged, micro-USD (0 on an idempotent replay); null if absent. */
276
+ get billedMicroUsd() {
277
+ return this._cost?.billed ?? null;
278
+ }
279
+ /** What one list-priced frontier call would have cost, micro-USD. */
280
+ get frontierWouldHaveCostMicroUsd() {
281
+ return this._cost?.frontier ?? null;
282
+ }
283
+ /** frontier / billed — e.g. 17 = 17× cheaper; null if either is missing or billed is 0. */
284
+ get savingsFactor() {
285
+ const b = this.billedMicroUsd, f = this.frontierWouldHaveCostMicroUsd;
286
+ return b && f && b > 0 ? Math.round(f / b * 10) / 10 : null;
287
+ }
258
288
  };
259
289
  var ChatCompletionChunk = class extends ChatCompletion {
260
290
  };
@@ -342,9 +372,9 @@ var EvalSet = class extends BaseModel {
342
372
  return this.raw.scoring_strategy ?? null;
343
373
  }
344
374
  /** The one-sentence success criterion this set was created with (CB1:
345
- * DATA + INTENT). Required at create. */
346
- get intent() {
347
- return this.raw.intent ?? null;
375
+ * DATA + PROMPT). Required at create. */
376
+ get prompt() {
377
+ return this.raw.prompt ?? null;
348
378
  }
349
379
  };
350
380
  function evalSetFromCreate(raw) {
@@ -383,23 +413,24 @@ var ProposalResult = class extends BaseModel {
383
413
  get closestTask() {
384
414
  return this.raw.closest_task ?? null;
385
415
  }
386
- get intent() {
387
- return this.raw.intent ?? null;
416
+ get prompt() {
417
+ return this.raw.prompt ?? null;
388
418
  }
389
419
  get message() {
390
420
  return this.raw.message ?? null;
391
421
  }
392
- /** True when the binder matched exactly ONE homogeneous SPECIFIC contract at
393
- * high/medium confidence and no conflict/split — the case `create` auto-binds
394
- * without a human confirming. The `custom-eval` universal FLOOR is EXCLUDED:
395
- * the binder offers it only when no specific contract fits, and per the
396
- * precision ladder the floor is a CHOICE — the user opts in with
397
- * `task: "custom-eval"`, never a silent auto-bind. */
422
+ /** True when exactly ONE homogeneous SPECIFIC task matched at high/medium
423
+ * confidence with no conflict/split — the case a task-less `create`
424
+ * proceeds with, no human confirming. `custom-eval` (a judge panel grading
425
+ * each answer against your prompt) is EXCLUDED: it is offered only when
426
+ * nothing specific fits, and using it is the user's CHOICE — opt in with
427
+ * `task: "custom-eval"`, never silently. */
398
428
  get isClean() {
399
429
  const props = this.proposals;
400
430
  return this.homogeneous && this.conflict == null && this.split == null && props.length === 1 && (props[0].confidence === "high" || props[0].confidence === "medium") && Boolean(props[0].taskId) && props[0].taskId !== "custom-eval";
401
431
  }
402
- /** The task `create` would auto-bind, or null if the result isn't clean. */
432
+ /** How a task-less `create` would score this set, or null if the result
433
+ * isn't clean (pass `task` yourself). */
403
434
  get boundTask() {
404
435
  return this.isClean ? this.proposals[0].taskId : null;
405
436
  }
@@ -644,10 +675,14 @@ var Completions = class {
644
675
  cast: (raw) => new ChatCompletionChunk(raw)
645
676
  });
646
677
  }
678
+ let headers;
647
679
  return this.client.request("POST", PATH, {
648
680
  body,
649
- cast: (raw) => new ChatCompletion(raw)
650
- });
681
+ cast: (raw) => new ChatCompletion(raw),
682
+ onHeaders: (h) => {
683
+ headers = h;
684
+ }
685
+ }).then((completion) => completion.withCost(headers));
651
686
  }
652
687
  };
653
688
  var Chat = class {
@@ -744,37 +779,37 @@ async function toBlob(file, mimeOverride) {
744
779
  const mime = mimeOverride || "application/octet-stream";
745
780
  return { blob: new Blob([bytes], { type: mime }), filename: "upload", size: bytes.byteLength, mime };
746
781
  }
747
- function requireIntent(intent) {
748
- const s = (intent ?? "").trim();
782
+ function requirePrompt(prompt) {
783
+ const s = (prompt ?? "").trim();
749
784
  if (!s) {
750
785
  throw new ParetaError(
751
- 'intent is required: one sentence describing what the model should do with each item (e.g. "extract vendor, total and date from each invoice"). Pass intent to evals.create / proposeContract.'
786
+ 'prompt is required: one sentence describing what the model should do with each item (e.g. "extract vendor, total and date from each invoice"). Pass prompt to evals.create / proposeContract.'
752
787
  );
753
788
  }
754
789
  return s.slice(0, 500);
755
790
  }
756
- function itemsJsonl(task, items, intent, name) {
791
+ function itemsJsonl(task, items, prompt, name) {
757
792
  if (!items || items.length === 0) throw new ParetaError("items is required and must be non-empty");
758
793
  const jsonl = items.map((it) => JSON.stringify(it)).join("\n");
759
794
  return {
760
795
  files: { items: { filename: `items.${task}.jsonl`, content: jsonl, contentType: "application/jsonl" } },
761
- data: { task_id: task, intent, name: name || `sdk eval set (${items.length} items)` }
796
+ data: { task_id: task, prompt, name: name || `sdk eval set (${items.length} items)` }
762
797
  };
763
798
  }
764
- function proposeMultipart(items, intent) {
799
+ function proposeMultipart(items, prompt) {
765
800
  if (!items || items.length === 0) throw new ParetaError("items is required and must be non-empty");
766
801
  const jsonl = items.map((it) => JSON.stringify(it)).join("\n");
767
802
  return {
768
803
  files: { items: { filename: "items.jsonl", content: jsonl, contentType: "application/jsonl" } },
769
- data: { intent }
804
+ data: { prompt }
770
805
  };
771
806
  }
772
807
  function bindError(result) {
773
- const intent = result.intent ?? "";
808
+ const prompt = result.prompt ?? "";
774
809
  if (result.conflict) {
775
810
  const c = result.conflict;
776
811
  return new ParetaError(
777
- `intent '${intent}' describes a different job than the data's shape supports (reads as '${c.intended_task}': ${c.reasoning}). Pass a task to pin a contract, or revise the intent.`
812
+ `prompt '${prompt}' describes a different job than the data's shape supports (reads as '${c.intended_task}': ${c.reasoning}). Pass a task to pin the task, or revise the prompt.`
778
813
  );
779
814
  }
780
815
  if (result.split) {
@@ -787,14 +822,14 @@ function bindError(result) {
787
822
  if (props.length === 1 && props[0].taskId === "custom-eval") {
788
823
  const warn = props[0].warning ? ` (${props[0].warning})` : "";
789
824
  return new ParetaError(
790
- `no specific grading contract fits this data for intent '${intent}'. The custom-eval universal floor is available \u2014 it grades by judged win-rate vs the frontier anchor.${warn} Pass task: "custom-eval" to use it, or revise the data/intent for a specific contract.`
825
+ `no ready-made scorer fits this data for prompt '${prompt}'. A judge panel can grade each answer against what you asked for (win rate vs gpt-5.5) \u2014 pass task: "custom-eval" to use it.${warn} Or revise the data/prompt so a specific task fits.`
791
826
  );
792
827
  }
793
828
  const options = props.map((p) => p.taskId).filter(Boolean);
794
829
  if (options.length === 0 && result.closestTask) options.push(result.closestTask);
795
830
  const hint = options.length ? ` Candidates: ${options.join(", ")}.` : "";
796
831
  return new ParetaError(
797
- `could not confidently bind a grading contract for intent '${intent}'.${hint} Pass a task to pin one, or inspect evals.proposeContract({ items, intent }).`
832
+ `couldn't work out how to score this for prompt '${prompt}'.${hint} Pass a task to choose yourself, or inspect evals.proposeContract({ items, prompt }).`
798
833
  );
799
834
  }
800
835
  function mergeCandidates(models, frontierIds) {
@@ -818,27 +853,27 @@ var EvalSets = class {
818
853
  }
819
854
  client;
820
855
  /**
821
- * Persist an eval set from your rows. `intent` is REQUIRED (v2): one sentence
822
- * on what the model should do with each item. `task` is OPTIONAL — omit it and
823
- * the binder resolves your intent + the data's shape to a grading contract
824
- * (auto-binds a clean single match; throws with the proposals otherwise). Pass
825
- * `task` to pin one explicitly.
856
+ * Persist an eval set from your rows. `prompt` is REQUIRED (v3): one sentence
857
+ * on what the model should do with each item. `task` is OPTIONAL — omit it
858
+ * and Pareta works out how to score the results from your prompt + the
859
+ * data's shape (a clean single match is used; anything ambiguous throws with
860
+ * the proposals). Pass `task` to pin one explicitly.
826
861
  */
827
862
  async create(params) {
828
- const intent = requireIntent(params.intent);
863
+ const prompt = requirePrompt(params.prompt);
829
864
  let task = params.task;
830
865
  if (task == null) {
831
- const proposal = await this.proposeContract(params.items, intent);
866
+ const proposal = await this.proposeContract(params.items, prompt);
832
867
  task = proposal.boundTask ?? void 0;
833
868
  if (task == null) throw bindError(proposal);
834
869
  }
835
- const { files, data } = itemsJsonl(task, params.items, intent, params.name);
870
+ const { files, data } = itemsJsonl(task, params.items, prompt, params.name);
836
871
  return this.client.request("POST", BASE2, { files, data, cast: evalSetFromCreate });
837
872
  }
838
- /** Internal: the binder call `create` uses when no task is pinned. Public on
873
+ /** Internal: the propose call `create` uses when no task is pinned. Public on
839
874
  * `Evals` as `proposeContract`; kept here so `sets.create` can reach it. */
840
- proposeContract(items, intent) {
841
- const { files, data } = proposeMultipart(items, requireIntent(intent));
875
+ proposeContract(items, prompt) {
876
+ const { files, data } = proposeMultipart(items, requirePrompt(prompt));
842
877
  return this.client.request("POST", PROPOSE, { files, data, cast: proposalResult });
843
878
  }
844
879
  list() {
@@ -909,11 +944,11 @@ var EvalRuns = class {
909
944
  let evalSet = params.evalSet;
910
945
  if (evalSet == null) {
911
946
  if (!params.items) {
912
- throw new ParetaError("pass evalSet: <id>, or items (+ intent) to create one");
947
+ throw new ParetaError("pass evalSet: <id>, or items (+ prompt) to create one");
913
948
  }
914
949
  evalSet = (await this.sets.create({
915
950
  items: params.items,
916
- intent: params.intent,
951
+ prompt: params.prompt,
917
952
  task: params.task,
918
953
  name: params.name
919
954
  })).id ?? void 0;
@@ -957,13 +992,14 @@ var Evals = class {
957
992
  }
958
993
  client;
959
994
  /**
960
- * Which grading contract fits your data under your stated `intent`? Stateless
995
+ * How would your data be scored under your stated `prompt`? Stateless
961
996
  * discovery — nothing is persisted. Returns a ProposalResult (ranked
962
- * proposals, the auto-bind decision, conflict/split reporting). `sets.create`
963
- * calls this under the hood; use it directly to preview the binding first.
997
+ * proposals, the task a task-less create would use, conflict/split
998
+ * reporting). `sets.create` calls this under the hood; use it directly to
999
+ * preview the scoring first.
964
1000
  */
965
1001
  proposeContract(params) {
966
- return this.sets.proposeContract(params.items, params.intent);
1002
+ return this.sets.proposeContract(params.items, params.prompt);
967
1003
  }
968
1004
  /**
969
1005
  * The frontier (vendor) roster you can evaluate against. With `task`, each is
@@ -1310,6 +1346,7 @@ var Pareta = class _Pareta {
1310
1346
  break;
1311
1347
  }
1312
1348
  if (res.ok) {
1349
+ if (opts.onHeaders) opts.onHeaders(res.headers);
1313
1350
  const text = await res.text();
1314
1351
  const raw = text ? JSON.parse(text) : {};
1315
1352
  return cast ? cast(raw) : raw;