pareta 1.3.0 → 2.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
@@ -40,7 +40,9 @@ head-to-head against frontier models on your own ground truth and prices every
40
40
  contender honestly:
41
41
 
42
42
  ```ts
43
- const set = await pa.evals.sets.create({ task: "invoice-extraction", items: [...] });
43
+ const set = await pa.evals.sets.create({
44
+ items: [...], intent: "extract vendor, total and date from each invoice",
45
+ });
44
46
  const run = await pa.evals.runs.create({
45
47
  evalSet: set.id, models: ["auto"], frontier: ["claude-opus-4-7"], wait: true,
46
48
  });
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 = "1.3.0";
177
+ var VERSION = "2.0.0";
178
178
 
179
179
  // src/money.ts
180
180
  var MICRO_PER_CENT = 10000n;
@@ -341,6 +341,11 @@ var EvalSet = class extends BaseModel {
341
341
  get scoringStrategy() {
342
342
  return this.raw.scoring_strategy ?? null;
343
343
  }
344
+ /** 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;
348
+ }
344
349
  };
345
350
  function evalSetFromCreate(raw) {
346
351
  return new EvalSet(raw?.eval_set ?? {});
@@ -348,6 +353,60 @@ function evalSetFromCreate(raw) {
348
353
  function evalSetList(raw) {
349
354
  return (raw?.eval_sets ?? []).map((e) => new EvalSet(e));
350
355
  }
356
+ var ContractProposal = class extends BaseModel {
357
+ get taskId() {
358
+ return this.raw.task_id ?? null;
359
+ }
360
+ get confidence() {
361
+ return this.raw.confidence ?? null;
362
+ }
363
+ get evidence() {
364
+ return this.raw.evidence ?? {};
365
+ }
366
+ get warning() {
367
+ return this.raw.warning ?? null;
368
+ }
369
+ };
370
+ var ProposalResult = class extends BaseModel {
371
+ get proposals() {
372
+ return (this.raw.proposals ?? []).map((p) => new ContractProposal(p));
373
+ }
374
+ get homogeneous() {
375
+ return Boolean(this.raw.homogeneous);
376
+ }
377
+ get split() {
378
+ return this.raw.split ?? null;
379
+ }
380
+ get conflict() {
381
+ return this.raw.conflict ?? null;
382
+ }
383
+ get closestTask() {
384
+ return this.raw.closest_task ?? null;
385
+ }
386
+ get intent() {
387
+ return this.raw.intent ?? null;
388
+ }
389
+ get message() {
390
+ return this.raw.message ?? null;
391
+ }
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. */
398
+ get isClean() {
399
+ const props = this.proposals;
400
+ 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
+ }
402
+ /** The task `create` would auto-bind, or null if the result isn't clean. */
403
+ get boundTask() {
404
+ return this.isClean ? this.proposals[0].taskId : null;
405
+ }
406
+ };
407
+ function proposalResult(raw) {
408
+ return new ProposalResult(raw ?? {});
409
+ }
351
410
  var EvalItemResult = class extends BaseModel {
352
411
  get idx() {
353
412
  return this.raw.idx ?? null;
@@ -641,6 +700,7 @@ var Tasks = class {
641
700
 
642
701
  // src/resources/evals.ts
643
702
  var BASE2 = "/v1/eval-sets";
703
+ var PROPOSE = "/v1/eval-sets/propose-contract";
644
704
  var RUNS = "/v1/eval-runs";
645
705
  var FRONTIER = "/v1/eval/frontier-models";
646
706
  var INLINE_MAX = 5 * 1024 * 1024;
@@ -684,14 +744,59 @@ async function toBlob(file, mimeOverride) {
684
744
  const mime = mimeOverride || "application/octet-stream";
685
745
  return { blob: new Blob([bytes], { type: mime }), filename: "upload", size: bytes.byteLength, mime };
686
746
  }
687
- function itemsJsonl(task, items, name) {
747
+ function requireIntent(intent) {
748
+ const s = (intent ?? "").trim();
749
+ if (!s) {
750
+ 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.'
752
+ );
753
+ }
754
+ return s.slice(0, 500);
755
+ }
756
+ function itemsJsonl(task, items, intent, name) {
688
757
  if (!items || items.length === 0) throw new ParetaError("items is required and must be non-empty");
689
758
  const jsonl = items.map((it) => JSON.stringify(it)).join("\n");
690
759
  return {
691
760
  files: { items: { filename: `items.${task}.jsonl`, content: jsonl, contentType: "application/jsonl" } },
692
- data: { task_id: task, name: name || `sdk eval set (${items.length} items)` }
761
+ data: { task_id: task, intent, name: name || `sdk eval set (${items.length} items)` }
762
+ };
763
+ }
764
+ function proposeMultipart(items, intent) {
765
+ if (!items || items.length === 0) throw new ParetaError("items is required and must be non-empty");
766
+ const jsonl = items.map((it) => JSON.stringify(it)).join("\n");
767
+ return {
768
+ files: { items: { filename: "items.jsonl", content: jsonl, contentType: "application/jsonl" } },
769
+ data: { intent }
693
770
  };
694
771
  }
772
+ function bindError(result) {
773
+ const intent = result.intent ?? "";
774
+ if (result.conflict) {
775
+ const c = result.conflict;
776
+ 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.`
778
+ );
779
+ }
780
+ if (result.split) {
781
+ const s = result.split;
782
+ return new ParetaError(
783
+ `the dataset looks MIXED \u2014 ${s.validated_n}/${s.total_n} items fit '${s.closest_task}', the rest a different shape. Split the set or pass a task.`
784
+ );
785
+ }
786
+ const props = result.proposals;
787
+ if (props.length === 1 && props[0].taskId === "custom-eval") {
788
+ const warn = props[0].warning ? ` (${props[0].warning})` : "";
789
+ 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.`
791
+ );
792
+ }
793
+ const options = props.map((p) => p.taskId).filter(Boolean);
794
+ if (options.length === 0 && result.closestTask) options.push(result.closestTask);
795
+ const hint = options.length ? ` Candidates: ${options.join(", ")}.` : "";
796
+ 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 }).`
798
+ );
799
+ }
695
800
  function mergeCandidates(models, frontierIds) {
696
801
  const cands = [...models ?? [], ...frontierIds ?? []];
697
802
  if (cands.length === 0) throw new ParetaError("models is required (the open candidates to evaluate)");
@@ -712,10 +817,30 @@ var EvalSets = class {
712
817
  this.client = client;
713
818
  }
714
819
  client;
715
- create(params) {
716
- const { files, data } = itemsJsonl(params.task, params.items, params.name);
820
+ /**
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.
826
+ */
827
+ async create(params) {
828
+ const intent = requireIntent(params.intent);
829
+ let task = params.task;
830
+ if (task == null) {
831
+ const proposal = await this.proposeContract(params.items, intent);
832
+ task = proposal.boundTask ?? void 0;
833
+ if (task == null) throw bindError(proposal);
834
+ }
835
+ const { files, data } = itemsJsonl(task, params.items, intent, params.name);
717
836
  return this.client.request("POST", BASE2, { files, data, cast: evalSetFromCreate });
718
837
  }
838
+ /** Internal: the binder call `create` uses when no task is pinned. Public on
839
+ * `Evals` as `proposeContract`; kept here so `sets.create` can reach it. */
840
+ proposeContract(items, intent) {
841
+ const { files, data } = proposeMultipart(items, requireIntent(intent));
842
+ return this.client.request("POST", PROPOSE, { files, data, cast: proposalResult });
843
+ }
719
844
  list() {
720
845
  return this.client.request("GET", BASE2, { cast: evalSetList });
721
846
  }
@@ -783,10 +908,15 @@ var EvalRuns = class {
783
908
  async create(params) {
784
909
  let evalSet = params.evalSet;
785
910
  if (evalSet == null) {
786
- if (!(params.task && params.items)) {
787
- throw new ParetaError("pass evalSet: <id>, or task + items to create one");
911
+ if (!params.items) {
912
+ throw new ParetaError("pass evalSet: <id>, or items (+ intent) to create one");
788
913
  }
789
- evalSet = (await this.sets.create({ task: params.task, items: params.items, name: params.name })).id ?? void 0;
914
+ evalSet = (await this.sets.create({
915
+ items: params.items,
916
+ intent: params.intent,
917
+ task: params.task,
918
+ name: params.name
919
+ })).id ?? void 0;
790
920
  }
791
921
  const frontierIds = await this.frontierIds(params.frontier, evalSet, params.task);
792
922
  const candidateModelIds = mergeCandidates(params.models, frontierIds);
@@ -826,6 +956,15 @@ var Evals = class {
826
956
  this.client = client;
827
957
  }
828
958
  client;
959
+ /**
960
+ * Which grading contract fits your data under your stated `intent`? Stateless
961
+ * 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.
964
+ */
965
+ proposeContract(params) {
966
+ return this.sets.proposeContract(params.items, params.intent);
967
+ }
829
968
  /**
830
969
  * The frontier (vendor) roster you can evaluate against. With `task`, each is
831
970
  * annotated `benchmarked` + the roster is vision-filtered for document tasks.
@@ -917,6 +1056,21 @@ var Audio = class {
917
1056
 
918
1057
  // src/resources/images.ts
919
1058
  var PATH3 = "/v1/images/generations";
1059
+ var EDIT_PATH = "/v1/images/edits";
1060
+ async function toBase642(image) {
1061
+ if (typeof image === "string") {
1062
+ const { readFile } = await import('fs/promises');
1063
+ return bytesToBase64(new Uint8Array(await readFile(image)));
1064
+ }
1065
+ if (typeof image === "object" && image !== null && "base64" in image) {
1066
+ if (!image.base64 || !image.base64.trim()) throw new ParetaError("image.base64 is empty");
1067
+ return image.base64;
1068
+ }
1069
+ if (image instanceof Blob) return bytesToBase64(new Uint8Array(await image.arrayBuffer()));
1070
+ const bytes = image instanceof Uint8Array ? image : new Uint8Array(image);
1071
+ if (bytes.byteLength === 0) throw new ParetaError("image is empty");
1072
+ return bytesToBase64(bytes);
1073
+ }
920
1074
  var Images = class {
921
1075
  constructor(client) {
922
1076
  this.client = client;
@@ -935,6 +1089,19 @@ var Images = class {
935
1089
  cast: (raw) => new ImageGeneration(raw)
936
1090
  });
937
1091
  }
1092
+ /** Edit one reference image with a plain-language instruction (no mask).
1093
+ * `image` is a file path, raw bytes, a Blob, or `{ base64 }` (≤25MB
1094
+ * decoded). The output keeps the reference's aspect ratio. Billed flat
1095
+ * per edit. */
1096
+ async edit(image, prompt, opts = {}) {
1097
+ if (!prompt || !prompt.trim()) throw new ParetaError("prompt is required");
1098
+ const body = { prompt, image: await toBase642(image) };
1099
+ if (opts.seed !== void 0) body.seed = opts.seed;
1100
+ return this.client.request("POST", EDIT_PATH, {
1101
+ body,
1102
+ cast: (raw) => new ImageGeneration(raw)
1103
+ });
1104
+ }
938
1105
  };
939
1106
 
940
1107
  // src/resources/retrieval.ts
@@ -1220,6 +1387,7 @@ exports.ChatCompletion = ChatCompletion;
1220
1387
  exports.ChatCompletionChunk = ChatCompletionChunk;
1221
1388
  exports.Choice = Choice;
1222
1389
  exports.ConflictError = ConflictError;
1390
+ exports.ContractProposal = ContractProposal;
1223
1391
  exports.Embeddings = Embeddings;
1224
1392
  exports.EndpointNotReadyError = EndpointNotReadyError;
1225
1393
  exports.EvalItemResult = EvalItemResult;
@@ -1239,6 +1407,7 @@ exports.NotFoundError = NotFoundError;
1239
1407
  exports.Pareta = Pareta;
1240
1408
  exports.ParetaError = ParetaError;
1241
1409
  exports.PermissionDeniedError = PermissionDeniedError;
1410
+ exports.ProposalResult = ProposalResult;
1242
1411
  exports.RateLimitError = RateLimitError;
1243
1412
  exports.Rerank = Rerank;
1244
1413
  exports.RerankResult = RerankResult;