openpond-sdk 0.12.2 → 0.12.3

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.
Files changed (98) hide show
  1. package/dist/advanced-refiner-evaluations.js +5 -5
  2. package/dist/candidate-evaluations.js +4 -4
  3. package/dist/{chunk-VSRTJDVB.js → chunk-36WJTS5V.js} +3 -3
  4. package/dist/{chunk-O5ARJVT5.js → chunk-77N3QEIL.js} +2 -2
  5. package/dist/{chunk-WNH4DOEC.js → chunk-BFR3VC25.js} +16 -285
  6. package/dist/chunk-BFR3VC25.js.map +7 -0
  7. package/dist/{chunk-DBRRQASP.js → chunk-BSK3VLM3.js} +2 -2
  8. package/dist/{chunk-B7VYE7YQ.js → chunk-CTVKI3BV.js} +2 -2
  9. package/dist/{chunk-7AZDNZP6.js → chunk-CWXM57QR.js} +350 -87
  10. package/dist/chunk-CWXM57QR.js.map +7 -0
  11. package/dist/chunk-FY7NHCEH.js +255 -0
  12. package/dist/chunk-FY7NHCEH.js.map +7 -0
  13. package/dist/{chunk-YSKUXQ2N.js → chunk-G53MA5XX.js} +3 -3
  14. package/dist/{chunk-AV5JV26B.js → chunk-G6Y6CQYV.js} +2 -2
  15. package/dist/{chunk-ENLQCKY6.js → chunk-K5WRVSOL.js} +5 -5
  16. package/dist/{chunk-OEPYLZXK.js → chunk-MLPPSCDQ.js} +2 -2
  17. package/dist/{chunk-7GYXC2AI.js → chunk-QEIT5VXJ.js} +2 -2
  18. package/dist/{chunk-L5PQPAU6.js → chunk-QTJQIHDZ.js} +5 -2
  19. package/dist/{chunk-L5PQPAU6.js.map → chunk-QTJQIHDZ.js.map} +2 -2
  20. package/dist/{chunk-YVFERCUC.js → chunk-TMUAKJWX.js} +71 -2
  21. package/dist/chunk-TMUAKJWX.js.map +7 -0
  22. package/dist/{chunk-D4WTE6ZA.js → chunk-TSMGMA6R.js} +2 -2
  23. package/dist/{chunk-VOL3WES2.js → chunk-UPS52OPZ.js} +10 -165
  24. package/dist/chunk-UPS52OPZ.js.map +7 -0
  25. package/dist/{chunk-MLFKQ7E7.js → chunk-VY5AFE7A.js} +2 -2
  26. package/dist/{chunk-WVISXXIL.js → chunk-XYQD4LDI.js} +2 -2
  27. package/dist/{chunk-DG5CRENU.js → chunk-YUKV7RCD.js} +2 -2
  28. package/dist/{chunk-7WUJBFCH.js → chunk-ZJ3H3VS7.js} +18 -1
  29. package/dist/chunk-ZJ3H3VS7.js.map +7 -0
  30. package/dist/connected-evidence.js +10 -10
  31. package/dist/dataset-workspaces.js +13 -12
  32. package/dist/dataset-workspaces.js.map +1 -1
  33. package/dist/experiment-evaluation-schedules.js +13 -13
  34. package/dist/experiment-improvements.js +5 -5
  35. package/dist/experiments.js +17 -13
  36. package/dist/index.js +19 -19
  37. package/dist/model-starter-catalog.js +5 -5
  38. package/dist/model-starters.js +5 -5
  39. package/dist/model-taskset-runs.js +1 -1
  40. package/dist/post-training.js +4 -4
  41. package/dist/taskset-drafts.js +16 -16
  42. package/dist/taskset-packages.js +14 -14
  43. package/dist/training-bundle.js +161 -25
  44. package/dist/training-bundle.js.map +4 -4
  45. package/dist/training.js +11 -5
  46. package/dist/types/packages/sdk/src/candidate-evaluation-client.d.ts +9 -0
  47. package/dist/types/packages/sdk/src/candidate-evaluation-client.d.ts.map +1 -1
  48. package/dist/types/packages/sdk/src/candidate-evaluations.d.ts +6 -0
  49. package/dist/types/packages/sdk/src/candidate-evaluations.d.ts.map +1 -1
  50. package/dist/types/packages/sdk/src/experiment-client.d.ts +33 -0
  51. package/dist/types/packages/sdk/src/experiment-client.d.ts.map +1 -1
  52. package/dist/types/packages/sdk/src/experiment-contracts.d.ts +12 -0
  53. package/dist/types/packages/sdk/src/experiment-contracts.d.ts.map +1 -1
  54. package/dist/types/packages/sdk/src/experiment-evaluation-schedules.d.ts +21 -0
  55. package/dist/types/packages/sdk/src/experiment-evaluation-schedules.d.ts.map +1 -1
  56. package/dist/types/packages/sdk/src/experiment-run-contracts.d.ts +3 -0
  57. package/dist/types/packages/sdk/src/experiment-run-contracts.d.ts.map +1 -1
  58. package/dist/types/packages/sdk/src/experiment-run-details.d.ts +15 -0
  59. package/dist/types/packages/sdk/src/experiment-run-details.d.ts.map +1 -1
  60. package/dist/types/packages/sdk/src/harness-experiment-policy.d.ts +17 -0
  61. package/dist/types/packages/sdk/src/harness-experiment-policy.d.ts.map +1 -1
  62. package/dist/types/packages/sdk/src/managed-training-preparation.d.ts +3 -1
  63. package/dist/types/packages/sdk/src/managed-training-preparation.d.ts.map +1 -1
  64. package/dist/types/packages/sdk/src/model-taskset-runs-contracts.d.ts +12 -0
  65. package/dist/types/packages/sdk/src/model-taskset-runs-contracts.d.ts.map +1 -1
  66. package/dist/types/packages/sdk/src/model-taskset-runs.d.ts +12 -0
  67. package/dist/types/packages/sdk/src/model-taskset-runs.d.ts.map +1 -1
  68. package/dist/types/packages/sdk/src/post-training.d.ts +15 -0
  69. package/dist/types/packages/sdk/src/post-training.d.ts.map +1 -1
  70. package/dist/types/packages/sdk/src/training-bundle-contracts.d.ts +4 -0
  71. package/dist/types/packages/sdk/src/training-bundle-contracts.d.ts.map +1 -1
  72. package/dist/types/packages/sdk/src/training-bundle.d.ts +4 -0
  73. package/dist/types/packages/sdk/src/training-bundle.d.ts.map +1 -1
  74. package/dist/types/packages/sdk/src/training-qualification-preparation.d.ts +364 -0
  75. package/dist/types/packages/sdk/src/training-qualification-preparation.d.ts.map +1 -0
  76. package/dist/types/packages/sdk/src/training.d.ts +161 -0
  77. package/dist/types/packages/sdk/src/training.d.ts.map +1 -1
  78. package/package.json +1 -1
  79. package/dist/chunk-7AZDNZP6.js.map +0 -7
  80. package/dist/chunk-7WUJBFCH.js.map +0 -7
  81. package/dist/chunk-PGBFI5HC.js +0 -98
  82. package/dist/chunk-PGBFI5HC.js.map +0 -7
  83. package/dist/chunk-VOL3WES2.js.map +0 -7
  84. package/dist/chunk-WNH4DOEC.js.map +0 -7
  85. package/dist/chunk-YVFERCUC.js.map +0 -7
  86. /package/dist/{chunk-VSRTJDVB.js.map → chunk-36WJTS5V.js.map} +0 -0
  87. /package/dist/{chunk-O5ARJVT5.js.map → chunk-77N3QEIL.js.map} +0 -0
  88. /package/dist/{chunk-DBRRQASP.js.map → chunk-BSK3VLM3.js.map} +0 -0
  89. /package/dist/{chunk-B7VYE7YQ.js.map → chunk-CTVKI3BV.js.map} +0 -0
  90. /package/dist/{chunk-YSKUXQ2N.js.map → chunk-G53MA5XX.js.map} +0 -0
  91. /package/dist/{chunk-AV5JV26B.js.map → chunk-G6Y6CQYV.js.map} +0 -0
  92. /package/dist/{chunk-ENLQCKY6.js.map → chunk-K5WRVSOL.js.map} +0 -0
  93. /package/dist/{chunk-OEPYLZXK.js.map → chunk-MLPPSCDQ.js.map} +0 -0
  94. /package/dist/{chunk-7GYXC2AI.js.map → chunk-QEIT5VXJ.js.map} +0 -0
  95. /package/dist/{chunk-D4WTE6ZA.js.map → chunk-TSMGMA6R.js.map} +0 -0
  96. /package/dist/{chunk-MLFKQ7E7.js.map → chunk-VY5AFE7A.js.map} +0 -0
  97. /package/dist/{chunk-WVISXXIL.js.map → chunk-XYQD4LDI.js.map} +0 -0
  98. /package/dist/{chunk-DG5CRENU.js.map → chunk-YUKV7RCD.js.map} +0 -0
@@ -1,32 +1,37 @@
1
1
  import {
2
+ prepareImportedTasksetPackage,
2
3
  projectLearningBatchGraders
3
- } from "./chunk-PGBFI5HC.js";
4
+ } from "./chunk-FY7NHCEH.js";
4
5
  import {
6
+ projectPortableTaskRecord,
5
7
  resolvePortableTasksetRewardExecution
6
8
  } from "./chunk-ROBAX5TN.js";
7
9
  import {
8
10
  TasksetSchema,
9
11
  computeTasksetHash
10
- } from "./chunk-7AZDNZP6.js";
12
+ } from "./chunk-CWXM57QR.js";
11
13
  import {
12
14
  LearningBatchDatasetSourceRefSchema,
13
- MAX_TASKSET_PACKAGE_BYTES
15
+ MAX_TASKSET_PACKAGE_BYTES,
16
+ decodeTasksetPackageFile,
17
+ validateTasksetPackage
14
18
  } from "./chunk-FHCSDXIA.js";
15
19
  import "./chunk-2U54EOXJ.js";
16
20
  import {
17
21
  TRAINING_EVALUATION_SOURCE_PATH,
18
22
  TrainingEvaluationSourceSchema,
19
23
  assertTrainingEvaluationIsolation,
24
+ deterministicTrainingRewardSource,
20
25
  parseAndVerifyTrainingInputArtifactUpload,
21
26
  parseAndVerifyTrainingJobSubmission,
22
27
  trainingEvaluationSourceRef,
23
28
  trainingInputArtifactUploadHash,
24
29
  trainingJobSubmissionHash
25
- } from "./chunk-YVFERCUC.js";
30
+ } from "./chunk-TMUAKJWX.js";
26
31
  import {
27
32
  TrainingHandoffOriginSchema
28
- } from "./chunk-OEPYLZXK.js";
29
- import "./chunk-YSKUXQ2N.js";
33
+ } from "./chunk-MLPPSCDQ.js";
34
+ import "./chunk-G53MA5XX.js";
30
35
  import {
31
36
  HarnessSourceSelectionSchema,
32
37
  createAgentSnapshot,
@@ -35,8 +40,8 @@ import {
35
40
  validateHarnessSourcePackage
36
41
  } from "./chunk-DUKW3R7O.js";
37
42
  import "./chunk-KAE7AMYE.js";
38
- import "./chunk-L5PQPAU6.js";
39
- import "./chunk-7WUJBFCH.js";
43
+ import "./chunk-QTJQIHDZ.js";
44
+ import "./chunk-ZJ3H3VS7.js";
40
45
  import "./chunk-CK3ONRRC.js";
41
46
  import "./chunk-TCHAWJMB.js";
42
47
  import "./chunk-3KXDH6OB.js";
@@ -51,7 +56,7 @@ import {
51
56
  } from "./chunk-COPVBJPL.js";
52
57
 
53
58
  // src/training-bundle.ts
54
- import { compileBoundGraders } from "@openpond/evals/rewards";
59
+ import { compileBoundGraders as compileBoundGraders2 } from "@openpond/evals/rewards";
55
60
  import { verifyLearningTextAsset as verifyLearningTextAsset3 } from "@openpond/evals/learning";
56
61
 
57
62
  // src/training-bundle-contracts.ts
@@ -106,6 +111,7 @@ var TrainingEngineBindingSchema = z.object({
106
111
  }).strict();
107
112
  var HarnessRunManifestContentSchema = z.object({
108
113
  schemaVersion: z.literal("openpond.harnessRunManifest.v1"),
114
+ executionMode: z.literal("qualification").optional(),
109
115
  id: ReleaseIdSchema,
110
116
  harnessRelease: ImmutableReleaseRefSchema,
111
117
  datasetRelease: ImmutableReleaseRefSchema,
@@ -139,6 +145,7 @@ var HarnessRunManifestSchema = HarnessRunManifestContentSchema.extend({
139
145
  }).strict();
140
146
  var ResolvedTrainingBundleContentSchema = z.object({
141
147
  schemaVersion: z.literal("openpond.resolvedTrainingBundle.v1"),
148
+ executionMode: z.literal("qualification").optional(),
142
149
  projection: z.literal("trainer"),
143
150
  harnessRelease: ImmutableReleaseRefSchema,
144
151
  datasetRelease: ImmutableReleaseRefSchema,
@@ -354,6 +361,13 @@ async function prepareManagedTrainingSubmission(raw) {
354
361
  const { project, recipe, taskset, approval } = input;
355
362
  const manifest = HarnessRunManifestSchema.parse(input.manifest);
356
363
  const bundle = ResolvedTrainingBundleManifestSchema.parse(input.bundleManifest);
364
+ const qualification = input.executionMode === "qualification";
365
+ if (manifest.executionMode !== input.executionMode || bundle.executionMode !== input.executionMode) {
366
+ throw new Error("Managed submission execution mode differs from its immutable bundle.");
367
+ }
368
+ if (qualification && (input.evaluationSource || input.resumeFrom || input.rewardSource.kind !== "deterministic")) {
369
+ throw new Error("Qualification requires the original learner, deterministic rewards, and no evaluation source.");
370
+ }
357
371
  const { contentHash: manifestHash, ...manifestContent } = manifest;
358
372
  const { contentHash: bundleHash, ...bundleContent } = bundle;
359
373
  if (contentHash(manifestContent) !== manifestHash || contentHash(bundleContent) !== bundleHash || manifest.resolvedBundleHash !== bundleHash || contentHash(manifest.harnessRelease) !== contentHash(bundle.harnessRelease) || contentHash(manifest.datasetRelease) !== contentHash(bundle.datasetRelease) || contentHash(manifest.evidenceSets) !== contentHash(bundle.evidenceSetRelease ? [bundle.evidenceSetRelease] : [])) {
@@ -372,14 +386,21 @@ async function prepareManagedTrainingSubmission(raw) {
372
386
  if (input.idempotencyKey !== void 0)
373
387
  z2.string().trim().min(16).max(191).parse(input.idempotencyKey);
374
388
  const files = verifyFiles(bundle, input.files);
375
- const evaluationSource = TrainingEvaluationSourceSchema.parse(input.evaluationSource);
389
+ const evaluationSource = qualification ? null : TrainingEvaluationSourceSchema.parse(input.evaluationSource);
376
390
  const evaluationFile = files.find((file) => file.path === TRAINING_EVALUATION_SOURCE_PATH);
377
- if (!evaluationFile || contentHash(JSON.parse(Buffer.from(evaluationFile.content, "base64").toString("utf8"))) !== contentHash(evaluationSource)) {
391
+ if (evaluationSource && (!evaluationFile || contentHash(JSON.parse(Buffer.from(evaluationFile.content, "base64").toString("utf8"))) !== contentHash(evaluationSource))) {
378
392
  throw new Error("The evaluation source differs from the immutable training bundle.");
379
393
  }
380
- const evaluation = await trainingEvaluationSourceRef(evaluationSource);
394
+ if (qualification && (evaluationFile || files.some((file) => file.path === "dataset/train.json") || !files.some((file) => file.path === "dataset/qualification.json") || !files.some((file) => file.path === "qualification-source.json"))) {
395
+ throw new Error("Qualification requires its dedicated dataset and no training/evaluation datasets.");
396
+ }
397
+ if (!qualification && files.some((file) => file.path === "dataset/qualification.json" || file.path === "qualification-source.json")) {
398
+ throw new Error("Qualification datasets cannot be prepared as training.");
399
+ }
400
+ const evaluation = evaluationSource ? await trainingEvaluationSourceRef(evaluationSource) : null;
381
401
  const payload = {
382
402
  schemaVersion: "openpond.managedRlPortableSubmission.v1",
403
+ ...qualification ? { executionMode: "qualification" } : {},
383
404
  ...input.origin ? { origin: TrainingHandoffOriginSchema.parse(input.origin) } : {},
384
405
  sourceRunRef: `openpond:model-run:${manifest.id}`,
385
406
  name: `OpenPond Managed \xB7 ${project.id}`.slice(0, 191),
@@ -393,8 +414,8 @@ async function prepareManagedTrainingSubmission(raw) {
393
414
  modelImprovementQualification: input.modelImprovementQualification ?? null,
394
415
  recipe,
395
416
  resolvedBundle: { manifest: bundle, files },
396
- validationTasks: evaluationSource.tasks,
397
- validationAssets: evaluationSource.assets
417
+ validationTasks: evaluationSource?.tasks ?? [],
418
+ validationAssets: evaluationSource?.assets ?? []
398
419
  };
399
420
  const artifactContent = {
400
421
  schemaVersion: "openpond.trainingInputArtifactUpload.v2",
@@ -424,18 +445,19 @@ async function prepareManagedTrainingSubmission(raw) {
424
445
  taskset,
425
446
  tasksetRelease: { id: taskset.id, contentHash: taskset.contentHash },
426
447
  dataset: bundle.datasetRelease,
427
- evaluation,
448
+ ...evaluation ? { evaluation } : {},
428
449
  evidenceSets: bundle.evidenceSetRelease ? [bundle.evidenceSetRelease] : []
429
450
  },
430
451
  job: {
431
452
  kind: "policy_optimize",
453
+ ...qualification ? { executionMode: "qualification" } : {},
432
454
  baseModel,
433
455
  recipe,
434
456
  rewardSource: input.rewardSource,
435
457
  resumeFrom: input.resumeFrom
436
458
  },
437
459
  requestedCapabilities: [
438
- { id: "managed_rl.policy.grpo", version: "1", required: true },
460
+ { id: qualification ? "managed_rl.policy.qualification" : "managed_rl.policy.grpo", version: "1", required: true },
439
461
  {
440
462
  id: `managed_rl.rollouts.${manifest.runtimeTarget.placement}`,
441
463
  version: "1",
@@ -734,10 +756,114 @@ function asset(input) {
734
756
  };
735
757
  }
736
758
 
759
+ // src/training-qualification-preparation.ts
760
+ import { createRewardBinding, createRewardRelease, compileBoundGraders } from "@openpond/evals/rewards";
761
+ import { LearningTextAssetSchema, sealLearningContent } from "@openpond/evals/learning";
762
+ async function prepareOrdinaryTasksetQualification(input) {
763
+ const source = validateTasksetPackage(input.package);
764
+ if (source.modelResources || source.learningResources || !source.taskset.tasks.length) {
765
+ throw new Error("Qualification requires a nonempty ordinary Taskset package.");
766
+ }
767
+ if (source.taskset.tasks.some((task) => task.privilegedContextRef !== null || task.artifactRefs.some((asset2) => asset2.visibility !== "policy"))) {
768
+ throw new Error("Qualification runtime requires private targets in expectedOutput and policy-only task assets.");
769
+ }
770
+ const rewards = source.taskset.graders.map((grader) => {
771
+ const { id, version, weight: _weight, hardGate: _gate, rewardEligible: _eligible, privileged: _private, taskIds: _tasks, ...implementation } = grader;
772
+ const revision = Number(version);
773
+ if (!Number.isSafeInteger(revision) || revision < 1 || String(revision) !== version || implementation.kind === "model_judge" || implementation.kind === "human") {
774
+ throw new Error("Qualification requires deterministic graders with canonical integer versions.");
775
+ }
776
+ return createRewardRelease({
777
+ schemaVersion: "openpond.rewardRelease.v1",
778
+ id: `qualification-${contentHash({ source: source.contentHash, id }).slice(0, 40)}`,
779
+ revision,
780
+ name: id,
781
+ description: "Exact ordinary package grader for no-update qualification.",
782
+ implementation,
783
+ rawScore: { minimum: 0, maximum: 1 },
784
+ assets: implementation.kind === "custom_verifier" ? [implementation.verifierRef] : []
785
+ });
786
+ });
787
+ const binding = createRewardBinding({
788
+ schemaVersion: "openpond.rewardBinding.v1",
789
+ id: `qualification-${source.contentHash.slice(0, 40)}`,
790
+ revision: 1,
791
+ sources: source.taskset.graders.map((grader, index) => ({
792
+ graderId: grader.id,
793
+ ...grader.taskIds ? { taskIds: grader.taskIds } : {},
794
+ reward: { id: rewards[index].id, revision: rewards[index].revision, contentHash: rewards[index].contentHash },
795
+ role: grader.rewardEligible ? "training" : "evaluation",
796
+ normalization: { kind: "identity" },
797
+ weight: grader.weight,
798
+ required: true,
799
+ hardGate: grader.hardGate,
800
+ privileged: grader.privileged,
801
+ fixtureRefs: []
802
+ })),
803
+ aggregation: "weighted_mean",
804
+ unscorable: "exclude_optional_require_all_required"
805
+ }, rewards);
806
+ if (!binding.sources.some((item) => item.role === "training" && item.weight > 0) || contentHash(compileBoundGraders(binding, rewards)) !== contentHash(source.taskset.graders)) {
807
+ throw new Error("Qualification must preserve an executable positively weighted source grader set.");
808
+ }
809
+ const verifierIds = new Set(rewards.flatMap((reward) => reward.assets.map((asset2) => asset2.id)));
810
+ const verifierAssets = source.files.filter((file) => verifierIds.has(file.asset.id)).map((file) => LearningTextAssetSchema.parse(sealLearningContent({
811
+ schemaVersion: "openpond.learningTextAsset.v1",
812
+ id: file.asset.id,
813
+ revision: 1,
814
+ asset: file.asset,
815
+ text: new TextDecoder("utf-8", { fatal: true }).decode(decodeTasksetPackageFile(file))
816
+ })));
817
+ const { taskset } = prepareImportedTasksetPackage({
818
+ package: source,
819
+ profileId: input.modelProject.id,
820
+ name: source.taskset.id,
821
+ createdAt: input.modelProject.updatedAt
822
+ });
823
+ const taskAssets = new Set(taskset.tasks.flatMap((task) => (task.assets ?? []).map((asset2) => asset2.artifactRef)));
824
+ const tasksetAssetBytes = new Map(source.files.filter((file) => taskAssets.has(file.asset.path)).map((file) => [file.asset.path, decodeTasksetPackageFile(file)]));
825
+ const tasksetRef = { id: source.taskset.id, revision: source.taskset.revision, contentHash: source.taskset.contentHash };
826
+ const rewardExecution = { binding, rewards };
827
+ const bundle = buildTasksetTrainingBundle({
828
+ ...input,
829
+ taskset,
830
+ executionMode: "qualification",
831
+ qualificationSource: source,
832
+ tasksetRelease: { id: tasksetRef.id, contentHash: tasksetRef.contentHash },
833
+ rewardExecution,
834
+ verifierAssets,
835
+ tasksetAssetBytes,
836
+ approval: { approvalHash: input.approval.approvalHash, approvedAt: input.approval.approvedAt, maximumSpendUsd: input.approval.maximumSpendUsd }
837
+ });
838
+ const recipe = input.modelProject.trainingSetup.recipe;
839
+ if (!recipe || recipe.method !== "grpo") throw new Error("Qualification requires a pinned GRPO rollout recipe.");
840
+ const prepared = await prepareManagedTrainingSubmission({
841
+ executionMode: "qualification",
842
+ project: input.modelProject,
843
+ manifest: bundle.manifest,
844
+ bundleManifest: bundle.resolvedBundleManifest,
845
+ taskset: tasksetRef,
846
+ recipe,
847
+ files: [...bundle.assets].map(([path, bytes2]) => ({ ...bundle.resolvedBundleManifest.files.find((file) => file.path === path), encoding: "base64", content: Buffer.from(bytes2).toString("base64") })),
848
+ rewardSource: await deterministicTrainingRewardSource({ graders: source.taskset.graders, rewardExecution }),
849
+ resumeFrom: null,
850
+ approval: { approvalHash: input.approval.approvalHash, maximumSpendUsd: input.approval.maximumSpendUsd, retentionDays: input.approval.retentionDays, region: input.approval.region },
851
+ ...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}
852
+ });
853
+ return { ...prepared, bundle };
854
+ }
855
+
737
856
  // src/training-bundle.ts
738
857
  function buildTasksetTrainingBundle(input) {
739
858
  const { taskset, modelProject, harnessRelease, tasksetRelease } = input;
740
859
  const setup = modelProject.trainingSetup;
860
+ const qualification = input.executionMode === "qualification";
861
+ if (qualification && input.evaluationSource) throw new Error("Qualification does not consume an independent evaluation source.");
862
+ const qualificationSource = qualification ? validateTasksetPackage(input.qualificationSource) : null;
863
+ if (!qualification && input.qualificationSource) throw new Error("Qualification sources cannot be used for training.");
864
+ if (qualificationSource && (qualificationSource.modelResources || qualificationSource.learningResources || contentHash(taskset.tasks.map(projectPortableTaskRecord)) !== contentHash(qualificationSource.taskset.tasks) || releasedRefHash(input.tasksetRelease) !== releasedRefHash(qualificationSource.taskset))) {
865
+ throw new Error("Qualification must preserve its ordinary source release and complete task population.");
866
+ }
741
867
  const releasedHarness = ImmutableReleaseRefSchema.parse(harnessRelease);
742
868
  const releasedTaskset = ImmutableReleaseRefSchema.parse(tasksetRelease);
743
869
  void input.openpondRelease;
@@ -748,7 +874,7 @@ function buildTasksetTrainingBundle(input) {
748
874
  "Taskset authoring state changed after its release was selected."
749
875
  );
750
876
  }
751
- if (!setup.baseModel?.revision || !setup.baseModel.tokenizerRevision || !setup.baseModel.chatTemplateHash || !setup.recipe || !setup.tasksetRef || setup.tasksetRef.id !== taskset.id || setup.tasksetRef.contentHash !== taskset.contentHash) {
877
+ if (!setup.baseModel?.revision || !setup.baseModel.tokenizerRevision || !setup.baseModel.chatTemplateHash || !setup.recipe || !setup.tasksetRef || setup.tasksetRef.id !== (qualification ? tasksetRelease.id : taskset.id) || setup.tasksetRef.contentHash !== (qualification ? tasksetRelease.contentHash : taskset.contentHash)) {
752
878
  throw new Error(
753
879
  "Model Run must bind an exact Taskset, Model revision, tokenizer, chat template, and Recipe."
754
880
  );
@@ -763,6 +889,7 @@ function buildTasksetTrainingBundle(input) {
763
889
  })
764
890
  };
765
891
  const assets = /* @__PURE__ */ new Map();
892
+ if (qualificationSource) addJsonAsset(assets, "qualification-source.json", qualificationSource);
766
893
  if (input.evaluationSource) {
767
894
  const evaluation = TrainingEvaluationSourceSchema.parse(input.evaluationSource);
768
895
  if (contentHash(evaluation.taskset) !== contentHash(setup.evaluationTasksetRef)) {
@@ -770,7 +897,7 @@ function buildTasksetTrainingBundle(input) {
770
897
  }
771
898
  assertTrainingEvaluationIsolation(taskset.tasks.filter((task) => task.split === "train"), evaluation.tasks);
772
899
  assets.set(TRAINING_EVALUATION_SOURCE_PATH, new TextEncoder().encode(canonicalJson(evaluation)));
773
- } else if (setup.evaluationTasksetRef) {
900
+ } else if (!qualification && setup.evaluationTasksetRef) {
774
901
  throw new Error("The prepared Model requires its pinned evaluation source bytes.");
775
902
  }
776
903
  const harnessSource = input.harnessSource ? input.harnessSource.schemaVersion === "openpond.harnessPolicySourcePackage.v1" ? validateHarnessPolicySourcePackage(input.harnessSource, releasedHarness) : validateHarnessSourcePackage(input.harnessSource, releasedHarness) : null;
@@ -794,11 +921,14 @@ function buildTasksetTrainingBundle(input) {
794
921
  sourcePackageHash: harnessSource?.contentHash ?? null
795
922
  }));
796
923
  const rewardExecution = resolvePortableTasksetRewardExecution(taskset, input.rewardExecution);
924
+ if (qualificationSource && (!rewardExecution || contentHash(compileBoundGraders2(rewardExecution.binding, rewardExecution.rewards)) !== contentHash(qualificationSource.taskset.graders) || qualificationSource.taskset.graders.some((grader) => grader.kind === "model_judge" || grader.kind === "human"))) {
925
+ throw new Error("Qualification requires the source package's exact deterministic graders.");
926
+ }
797
927
  if (rewardExecution) {
798
928
  const verifierAssets = input.verifierAssets ?? [];
799
929
  const references = [
800
930
  ...rewardExecution.rewards.flatMap((reward) => reward.assets),
801
- ...compileBoundGraders(rewardExecution.binding, rewardExecution.rewards).flatMap((grader) => grader.kind === "custom_verifier" ? [grader.verifierRef] : grader.kind === "model_judge" ? [grader.rubricRef] : [])
931
+ ...compileBoundGraders2(rewardExecution.binding, rewardExecution.rewards).flatMap((grader) => grader.kind === "custom_verifier" ? [grader.verifierRef] : grader.kind === "model_judge" ? [grader.rubricRef] : [])
802
932
  ];
803
933
  const expected = new Set(references.map((reference) => reference.id));
804
934
  if (new Set(verifierAssets.map((asset2) => asset2.id)).size !== verifierAssets.length || new Set(verifierAssets.map((asset2) => asset2.asset.path)).size !== verifierAssets.length || verifierAssets.some((asset2) => !expected.has(asset2.id))) throw new Error("Unexpected or duplicate private verifier asset.");
@@ -813,7 +943,7 @@ function buildTasksetTrainingBundle(input) {
813
943
  }
814
944
  addJsonAsset(assets, "environment.json", {
815
945
  schemaVersion: "openpond.harnessEnvironment.v1",
816
- environment: taskset.environment,
946
+ environment: qualification ? { ...taskset.environment, metadata: { ...taskset.environment.metadata, policySystemPrompt: taskset.objective } } : taskset.environment,
817
947
  capabilities: taskset.capabilities,
818
948
  policy: taskset.policy,
819
949
  outputContract: taskset.metadata.tasksetOutputContract?.mode === "text" ? null : taskset.metadata.tasksetOutputContract ?? null
@@ -830,11 +960,11 @@ function buildTasksetTrainingBundle(input) {
830
960
  capabilities: taskset.capabilities,
831
961
  connectedAppScopes: taskset.policy.connectedAppScopes
832
962
  });
833
- const trainTasks = taskset.tasks.filter((task) => task.split === "train");
963
+ const trainTasks = qualification ? taskset.tasks : taskset.tasks.filter((task) => task.split === "train");
834
964
  if (trainTasks.length > 0) {
835
- addJsonAsset(assets, "dataset/train.json", {
836
- schemaVersion: "openpond.datasetSplit.v1",
837
- split: "train",
965
+ addJsonAsset(assets, qualification ? "dataset/qualification.json" : "dataset/train.json", {
966
+ schemaVersion: qualification ? "openpond.qualificationDataset.v1" : "openpond.datasetSplit.v1",
967
+ ...qualification ? {} : { split: "train" },
838
968
  tasks: trainTasks
839
969
  });
840
970
  addTasksetAssets({
@@ -858,7 +988,7 @@ function buildTasksetTrainingBundle(input) {
858
988
  taskset: releasedTaskset,
859
989
  sourceRefs: taskset.sourceRefs,
860
990
  datasetArtifact: taskset.datasetArtifact ?? null,
861
- trainTasks
991
+ ...qualification ? { executionMode: "qualification", tasks: trainTasks } : { trainTasks }
862
992
  })
863
993
  };
864
994
  const evidenceSetRelease = approvedSignals.length > 0 ? {
@@ -873,6 +1003,7 @@ function buildTasksetTrainingBundle(input) {
873
1003
  } : null;
874
1004
  const bundleContent = ResolvedTrainingBundleContentSchema.parse({
875
1005
  schemaVersion: "openpond.resolvedTrainingBundle.v1",
1006
+ ...qualification ? { executionMode: "qualification" } : {},
876
1007
  projection: "trainer",
877
1008
  harnessRelease: releasedHarness,
878
1009
  datasetRelease,
@@ -889,6 +1020,7 @@ function buildTasksetTrainingBundle(input) {
889
1020
  });
890
1021
  const manifestContent = HarnessRunManifestContentSchema.parse({
891
1022
  schemaVersion: "openpond.harnessRunManifest.v1",
1023
+ ...qualification ? { executionMode: "qualification" } : {},
892
1024
  id: `manifest_${input.modelRunId}_${input.approval.approvalHash.slice(0, 16)}`,
893
1025
  harnessRelease,
894
1026
  datasetRelease,
@@ -961,6 +1093,9 @@ function addJsonAsset(assets, assetPath, value) {
961
1093
  function bytes(value) {
962
1094
  return Buffer.from(canonicalJson(value), "utf8");
963
1095
  }
1096
+ function releasedRefHash(value) {
1097
+ return contentHash({ id: value.id, contentHash: value.contentHash });
1098
+ }
964
1099
  export {
965
1100
  AdamwOptimizerConfigSchema,
966
1101
  ComputeTargetBindingSchema,
@@ -976,6 +1111,7 @@ export {
976
1111
  createPolicyHarnessContext,
977
1112
  materializeLearningBatchTaskset,
978
1113
  prepareManagedTrainingSubmission,
1114
+ prepareOrdinaryTasksetQualification,
979
1115
  prepareReviewedLearningBatch,
980
1116
  scanAndRedactEvidence,
981
1117
  withAuthoritativeRecipeHashes