@openpond/evals 0.10.6 → 0.10.8

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 (49) hide show
  1. package/LEARNING.md +18 -0
  2. package/dist/learning/authoring-service.js +11 -1
  3. package/dist/learning/authoring.js +1 -0
  4. package/dist/learning/contracts.js +36 -1
  5. package/dist/learning/index.js +2 -0
  6. package/dist/learning/iteration-candidate-decision.js +12 -7
  7. package/dist/learning/iteration-reservation-contracts.js +2 -1
  8. package/dist/learning/iteration-reservation-service.js +5 -1
  9. package/dist/learning/iteration-training-parent.js +47 -0
  10. package/dist/learning/policy-inspection.js +15 -3
  11. package/dist/learning/rating-reward-fixture.js +19 -0
  12. package/dist/learning/service.js +7 -2
  13. package/dist/learning/transport.js +1 -1
  14. package/dist/types/learning/authoring-service.d.ts +11 -0
  15. package/dist/types/learning/authoring-service.d.ts.map +1 -1
  16. package/dist/types/learning/authoring.d.ts +55 -0
  17. package/dist/types/learning/authoring.d.ts.map +1 -1
  18. package/dist/types/learning/contracts.d.ts +75 -0
  19. package/dist/types/learning/contracts.d.ts.map +1 -1
  20. package/dist/types/learning/index.d.ts +2 -0
  21. package/dist/types/learning/index.d.ts.map +1 -1
  22. package/dist/types/learning/iteration-candidate-decision.d.ts +16 -0
  23. package/dist/types/learning/iteration-candidate-decision.d.ts.map +1 -1
  24. package/dist/types/learning/iteration-reservation-contracts.d.ts +14 -0
  25. package/dist/types/learning/iteration-reservation-contracts.d.ts.map +1 -1
  26. package/dist/types/learning/iteration-reservation-service.d.ts.map +1 -1
  27. package/dist/types/learning/iteration-training-parent.d.ts +37 -0
  28. package/dist/types/learning/iteration-training-parent.d.ts.map +1 -0
  29. package/dist/types/learning/operations.d.ts +22 -0
  30. package/dist/types/learning/operations.d.ts.map +1 -1
  31. package/dist/types/learning/policy-inspection.d.ts +34 -0
  32. package/dist/types/learning/policy-inspection.d.ts.map +1 -1
  33. package/dist/types/learning/rating-reward-fixture.d.ts +8 -0
  34. package/dist/types/learning/rating-reward-fixture.d.ts.map +1 -0
  35. package/dist/types/learning/repository.d.ts +50 -0
  36. package/dist/types/learning/repository.d.ts.map +1 -1
  37. package/dist/types/learning/service.d.ts +34 -0
  38. package/dist/types/learning/service.d.ts.map +1 -1
  39. package/dist/types/learning/transport.d.ts +64 -0
  40. package/dist/types/learning/transport.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/schemas/learning/v1/chain.schema.json +75 -2
  43. package/schemas/learning/v1/command-request.schema.json +63 -20
  44. package/schemas/learning/v1/draft.schema.json +31 -0
  45. package/schemas/learning/v1/feedback-submission.schema.json +55 -37
  46. package/schemas/learning/v1/feedback.schema.json +94 -38
  47. package/schemas/learning/v1/iteration.schema.json +47 -0
  48. package/schemas/learning/v1/policy-inspection.schema.json +127 -33
  49. package/schemas/learning/v1/read-request.schema.json +7 -0
package/LEARNING.md CHANGED
@@ -79,6 +79,24 @@ for untrusted schemas and deliberately rejects unsupported capabilities.
79
79
 
80
80
  ## Host responsibilities
81
81
 
82
+ ### Accepted checkpoints between iterations
83
+
84
+ Candidate decisions are reconciled with their verified `decidedAt`, immutable
85
+ decision reference, execution, evaluation and terminal receipt. The chain records
86
+ the latest accepted candidate. New reservations capture its exact accepted
87
+ iteration revision in `trainingParentSelection`; `trainingParent` becomes that
88
+ candidate's artifact reference. Before the first acceptance, the configured base
89
+ parent is used. A rejected candidate is removed from future parent selection;
90
+ already reserved iterations keep their captured parent and original review.
91
+
92
+ `inspectPolicy` returns the resolved next `trainingParent`, or a blocker when its
93
+ accepted evidence or underlying model/method is incompatible. Execution owners
94
+ must resolve the captured iteration revision, verify the team/Model-scoped
95
+ terminal checkpoint and materialize it through their existing continuation
96
+ adapter. The hosted adapter uses fresh optimizer state. Acceptance does not
97
+ activate serving. Candidate decisions lacking verified timing must be reconciled
98
+ from their retained provider receipts before another reservation can use them.
99
+
82
100
  ### Reward fixture checks
83
101
 
84
102
  Reward drafts may contain up to 50 named fixtures. JSON and numeric editor fields
@@ -2,7 +2,7 @@ import { assertBoundedTaskJson } from "../task-schema.js";
2
2
  import { AuthoringDraftSchema } from "./authoring.js";
3
3
  import { LearningDomainError } from "./errors.js";
4
4
  import { learningRef, sameLearningRef, sealLearningContent } from "./contracts.js";
5
- import { requireLearningRelease } from "./repository.js";
5
+ import { requireLearningRelease, requireLearningResource } from "./repository.js";
6
6
  export async function saveAuthoringDraft(tx, input, now) {
7
7
  // A 100-record page remains below the common 16 MiB response limit.
8
8
  assertBoundedTaskJson(input.draft, 131_072);
@@ -14,6 +14,16 @@ export async function saveAuthoringDraft(tx, input, now) {
14
14
  throw new LearningDomainError("authoring_draft_base_mismatch", 422);
15
15
  await requireLearningRelease(tx, input.draft.targetKind, input.draft.baseRelease);
16
16
  }
17
+ if (input.draft.targetKind === "reward")
18
+ for (const fixture of input.draft.fields.fixtures ?? []) {
19
+ if (!fixture.sourceLabel)
20
+ continue;
21
+ const { evidence, feedback } = fixture.sourceLabel;
22
+ await requireLearningRelease(tx, "evidence", evidence);
23
+ const label = await requireLearningResource(tx, "feedback", feedback.id, feedback.revision);
24
+ if (!label.evidence || !sameLearningRef(label.evidence, evidence))
25
+ throw new LearningDomainError("reward_fixture_label_mismatch", 422);
26
+ }
17
27
  const draft = AuthoringDraftSchema.parse(sealLearningContent({ ...input.draft, schemaVersion: "openpond.authoringDraft.v1", revision: input.expectedRevision + 1, status: "draft", publishedRelease: null, createdAt: previous?.createdAt ?? now, updatedAt: now }));
18
28
  await tx.put("draft", draft, input.expectedRevision, { parentId: draft.targetKind, status: draft.status });
19
29
  return { kind: "draft", id: draft.id, revision: draft.revision };
@@ -10,6 +10,7 @@ export const RewardFixtureAuthoringFieldsSchema = z.object({
10
10
  infrastructureError: z.string().max(20_000),
11
11
  expectedStatus: z.enum(["scored", "pending", "unavailable", "failed"]),
12
12
  minimumScore: z.string(), maximumScore: z.string(), expectedPassed: z.enum(["true", "false", "any"]),
13
+ sourceLabel: z.object({ evidence: LearningRevisionRefSchema, feedback: z.object({ id: ReleaseIdSchema, revision: z.number().int().positive() }).strict() }).strict().optional(),
13
14
  }).strict();
14
15
  export const RewardAuthoringFieldsSchema = z.object({
15
16
  name: z.string().max(500), description: z.string().max(10_000),
@@ -105,6 +105,20 @@ export const TaskEvidenceContentSchema = z.object({
105
105
  receivedAt: ReleaseTimestampSchema,
106
106
  }).strict();
107
107
  export const TaskEvidenceSchema = TaskEvidenceContentSchema.extend({ contentHash: ReleaseHashSchema }).strict();
108
+ /** A retained judgment of this exact attempt, separate from task admission. */
109
+ export const TaskRatingSchema = z.object({
110
+ schemaVersion: z.literal("openpond.taskRating.v1"),
111
+ criteria: z.string().trim().min(1).max(20_000),
112
+ scale: z.object({ minimum: z.number().finite(), maximum: z.number().finite() }).strict(),
113
+ score: z.number().finite(),
114
+ evidence: z.string().max(20_000),
115
+ explanation: z.string().max(20_000),
116
+ }).strict().superRefine((rating, context) => {
117
+ if (rating.scale.minimum >= rating.scale.maximum)
118
+ context.addIssue({ code: "custom", path: ["scale"], message: "The score scale must have an increasing range." });
119
+ if (rating.score < rating.scale.minimum || rating.score > rating.scale.maximum)
120
+ context.addIssue({ code: "custom", path: ["score"], message: "The score must be within its declared scale." });
121
+ });
108
122
  export const TaskFeedbackSubmissionSchema = z.object({
109
123
  schemaVersion: z.literal("openpond.taskFeedback.v1"),
110
124
  sourceId: ReleaseIdSchema,
@@ -116,7 +130,18 @@ export const TaskFeedbackSubmissionSchema = z.object({
116
130
  kind: z.enum(["outcome", "target_correction", "ground_truth_correction", "input_correction", "family_resolution"]),
117
131
  value: LearningJsonObjectSchema,
118
132
  note: z.string().max(20_000),
119
- }).strict();
133
+ }).strict().superRefine((submission, context) => {
134
+ if (submission.value.schemaVersion !== "openpond.taskRating.v1")
135
+ return;
136
+ if (submission.expectedEvidenceHash === null)
137
+ context.addIssue({ code: "custom", path: ["expectedEvidenceHash"], message: "A rating must identify the exact evidence revision." });
138
+ if (submission.kind !== "outcome")
139
+ context.addIssue({ code: "custom", path: ["kind"], message: "Ratings must be submitted as outcomes." });
140
+ const rating = TaskRatingSchema.safeParse(submission.value);
141
+ if (!rating.success)
142
+ for (const issue of rating.error.issues)
143
+ context.addIssue({ code: "custom", path: ["value", ...issue.path], message: issue.message });
144
+ });
120
145
  export const TaskFeedbackSchema = z.object({
121
146
  schemaVersion: z.literal("openpond.taskFeedbackRecord.v1"),
122
147
  id: ReleaseIdSchema,
@@ -125,6 +150,7 @@ export const TaskFeedbackSchema = z.object({
125
150
  evidence: LearningRevisionRefSchema.nullable(),
126
151
  createdAt: ReleaseTimestampSchema,
127
152
  revision: z.number().int().positive(),
153
+ submittedBy: z.object({ id: ReleaseIdSchema, role: z.enum(["editor", "reviewer", "source"]), sourceId: ReleaseIdSchema.nullable() }).strict().nullable().default(null),
128
154
  review: z.object({ actorId: ReleaseIdSchema, decision: LearningRevisionRefSchema.nullable(), note: z.string().max(20_000), resolvedAt: ReleaseTimestampSchema }).strict().nullable().default(null),
129
155
  }).strict();
130
156
  export const TaskAdmissionDecisionContentSchema = z.object({
@@ -217,6 +243,13 @@ export const LearningPolicyContentSchema = z.object({
217
243
  context.addIssue({ code: "custom", path: ["limits"], message: "Daily spend must permit the iteration budget." });
218
244
  });
219
245
  export const LearningPolicySchema = LearningPolicyContentSchema.safeExtend({ contentHash: ReleaseHashSchema }).strict();
246
+ export const LearningAcceptedParentSchema = z.object({
247
+ iterationId: ReleaseIdSchema,
248
+ iterationRevision: z.number().int().positive(),
249
+ candidate: ImmutableReleaseRefSchema,
250
+ decision: LearningRevisionRefSchema,
251
+ decidedAt: ReleaseTimestampSchema,
252
+ }).strict();
220
253
  export const LearningIterationStatusSchema = z.enum(["waiting_for_data", "waiting_for_review", "ready", "dispatching", "training", "evaluating", "candidate_ready", "accepted", "rejected", "paused", "failed", "cancelling", "cancelled", "completed_without_candidate"]);
221
254
  export const LearningIterationSchema = z.object({
222
255
  schemaVersion: z.literal("openpond.learningIteration.v1"),
@@ -228,12 +261,14 @@ export const LearningIterationSchema = z.object({
228
261
  batch: LearningRevisionRefSchema.nullable(),
229
262
  sourceWatermarks: z.record(ReleaseIdSchema, z.number().int().nonnegative()),
230
263
  trainingParent: ImmutableReleaseRefSchema,
264
+ trainingParentSelection: LearningAcceptedParentSchema.nullable().optional(),
231
265
  teacher: ImmutableReleaseRefSchema.nullable(),
232
266
  upstreamEvent: ImmutableReleaseRefSchema.nullable(),
233
267
  trainingJob: ImmutableReleaseRefSchema.nullable(),
234
268
  evaluationJob: ImmutableReleaseRefSchema.nullable(),
235
269
  candidateVersion: ImmutableReleaseRefSchema.nullable(),
236
270
  candidateDecision: LearningRevisionRefSchema.nullable().default(null),
271
+ candidateDecisionAt: ReleaseTimestampSchema.nullable().optional(),
237
272
  dispatchId: ReleaseIdSchema,
238
273
  retryCount: z.number().int().nonnegative(),
239
274
  spendUsd: z.number().nonnegative(),
@@ -9,6 +9,7 @@ export * from "./transport.js";
9
9
  export * from "./errors.js";
10
10
  export * from "./authoring.js";
11
11
  export * from "./reward-authoring.js";
12
+ export * from "./rating-reward-fixture.js";
12
13
  export * from "./reward-checks.js";
13
14
  export * from "./reward-check-worker.js";
14
15
  export * from "./reward-fixture-executor.js";
@@ -16,6 +17,7 @@ export * from "./iteration-reservation-contracts.js";
16
17
  export * from "./iteration-dispatch-contracts.js";
17
18
  export * from "./iteration-dispatch-worker.js";
18
19
  export * from "./iteration-candidate-decision.js";
20
+ export { resolveLearningTrainingParent } from "./iteration-training-parent.js";
19
21
  export * from "./schedule-contracts.js";
20
22
  export * from "./schedule-service.js";
21
23
  export * from "./schedule-worker.js";
@@ -1,13 +1,15 @@
1
1
  import { z } from "zod";
2
- import { ImmutableReleaseRefSchema, ReleaseIdSchema } from "@openpond/harness";
2
+ import { ImmutableReleaseRefSchema, ReleaseIdSchema, ReleaseTimestampSchema } from "@openpond/harness";
3
3
  import { LearningIterationSchema, LearningRevisionRefSchema } from "./contracts.js";
4
4
  import { LearningDomainError } from "./errors.js";
5
5
  import { LearningChainSchema } from "./iteration-reservation-contracts.js";
6
6
  import { requireLearningResource } from "./repository.js";
7
+ import { latestAcceptedLearningParent } from "./iteration-training-parent.js";
7
8
  export const LearningCandidateDecisionObservationSchema = z.object({
8
9
  scope: ReleaseIdSchema,
9
10
  iterationId: ReleaseIdSchema,
10
11
  decision: LearningRevisionRefSchema,
12
+ decidedAt: ReleaseTimestampSchema,
11
13
  outcome: z.enum(["accepted", "rejected"]),
12
14
  execution: ImmutableReleaseRefSchema,
13
15
  candidate: ImmutableReleaseRefSchema,
@@ -34,18 +36,21 @@ export async function reconcileLearningCandidateDecision(repository, value, opti
34
36
  if (previous && input.decision.revision < previous.revision)
35
37
  return iteration;
36
38
  if (previous && input.decision.revision === previous.revision) {
37
- if (!same(previous, input.decision) || iteration.status !== input.outcome)
39
+ if (!same(previous, input.decision) || iteration.status !== input.outcome
40
+ || (iteration.candidateDecisionAt != null && iteration.candidateDecisionAt !== input.decidedAt))
38
41
  throw new LearningDomainError("learning_candidate_decision_revision_conflict", 409);
39
- return iteration;
42
+ if (iteration.candidateDecisionAt != null)
43
+ return iteration;
40
44
  }
41
45
  const now = (options.now ?? (() => new Date().toISOString()))();
42
46
  const updated = LearningIterationSchema.parse({ ...iteration, revision: iteration.revision + 1,
43
- candidateDecision: input.decision, status: input.outcome, updatedAt: now });
47
+ candidateDecision: input.decision, candidateDecisionAt: input.decidedAt, status: input.outcome, updatedAt: now });
44
48
  await tx.put("iteration", updated, iteration.revision, { parentId: dispatch.chainId, status: updated.status });
45
49
  const chain = await requireLearningResource(tx, "chain", dispatch.chainId);
46
- if (chain.activeIterationId === iteration.id)
47
- await tx.put("chain", LearningChainSchema.parse({ ...chain,
48
- revision: chain.revision + 1, activeIterationId: null, updatedAt: now }), chain.revision, { parentId: chain.modelProjectId });
50
+ const acceptedParent = await latestAcceptedLearningParent(tx, chain.id);
51
+ await tx.put("chain", LearningChainSchema.parse({ ...chain, acceptedParent,
52
+ revision: chain.revision + 1, activeIterationId: chain.activeIterationId === iteration.id ? null : chain.activeIterationId,
53
+ updatedAt: now }), chain.revision, { parentId: chain.modelProjectId });
49
54
  return updated;
50
55
  });
51
56
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { ReleaseHashSchema, ReleaseIdSchema, ReleaseTimestampSchema } from "@openpond/harness";
3
- import { LearningRevisionRefSchema } from "./contracts.js";
3
+ import { LearningAcceptedParentSchema, LearningRevisionRefSchema } from "./contracts.js";
4
4
  export const LearningIterationTriggerSchema = z.discriminatedUnion("kind", [
5
5
  z.object({ kind: z.literal("manual"), identity: ReleaseIdSchema }).strict(),
6
6
  z.object({ kind: z.literal("schedule"), scheduledAt: ReleaseTimestampSchema }).strict(),
@@ -12,6 +12,7 @@ export const LearningChainSchema = z.object({
12
12
  revision: z.number().int().positive(),
13
13
  modelProjectId: ReleaseIdSchema,
14
14
  activeIterationId: ReleaseIdSchema.nullable(),
15
+ acceptedParent: LearningAcceptedParentSchema.nullable().optional(),
15
16
  latestIterationId: ReleaseIdSchema,
16
17
  lastReservedAt: ReleaseTimestampSchema.nullable(),
17
18
  updatedAt: ReleaseTimestampSchema,
@@ -6,6 +6,7 @@ import { inspectIterationEligibility, learningChainId, learningConsumptionId } f
6
6
  import { readLearningIterationBudget } from "./iteration-budget.js";
7
7
  import { LearningChainSchema, LearningConsumptionSchema, LearningIterationReservationSchema } from "./iteration-reservation-contracts.js";
8
8
  import { requireLearningRelease, requireLearningResource } from "./repository.js";
9
+ import { latestAcceptedLearningParent, resolveLearningTrainingParent } from "./iteration-training-parent.js";
9
10
  /** The caller owns one scope-serialized transaction. This never submits a Job. */
10
11
  export async function reserveLearningIteration(transaction, input, actorId, now) {
11
12
  const policy = await requireLearningRelease(transaction, "policy", input.policy);
@@ -44,6 +45,8 @@ export async function reserveLearningIteration(transaction, input, actorId, now)
44
45
  const ready = eligibility.counts.eligible >= policy.admission.minimumApprovedExamples;
45
46
  const status = ready ? "ready" : eligibility.counts.awaitingReview > 0 ? "waiting_for_review" : "waiting_for_data";
46
47
  const pointers = [];
48
+ const acceptedParent = await latestAcceptedLearningParent(transaction, chainId, true);
49
+ const parent = await resolveLearningTrainingParent(transaction, policy, acceptedParent);
47
50
  let batch = null;
48
51
  if (ready) {
49
52
  await assertAvailableBudget(transaction, chainId, now, policy.limits.maxIterationSpendUsd, policy.limits.maxDailySpendUsd);
@@ -68,7 +71,7 @@ export async function reserveLearningIteration(transaction, input, actorId, now)
68
71
  triggerIdentity, batch,
69
72
  // Existing evidence has no ordered ingress sequence. Consumption records,
70
73
  // not a count or lexicographic ID cursor, decide whether an example is new.
71
- sourceWatermarks: {}, trainingParent: policy.trainingParent, teacher: policy.teacher, upstreamEvent: null,
74
+ sourceWatermarks: {}, ...parent, teacher: policy.teacher, upstreamEvent: null,
72
75
  trainingJob: null, evaluationJob: null, candidateVersion: null, dispatchId: `dispatch-${contentHash(id)}`,
73
76
  retryCount: 0, spendUsd: 0, failure: null, createdAt: now, updatedAt: now,
74
77
  });
@@ -82,6 +85,7 @@ export async function reserveLearningIteration(transaction, input, actorId, now)
82
85
  const updatedChain = LearningChainSchema.parse({
83
86
  schemaVersion: "openpond.learningChain.v1", id: chainId, revision: (chain?.revision ?? 0) + 1,
84
87
  modelProjectId: policy.modelProjectId, activeIterationId: ready ? id : null, latestIterationId: id,
88
+ acceptedParent,
85
89
  lastReservedAt: ready ? now : chain?.lastReservedAt ?? null, updatedAt: now,
86
90
  });
87
91
  await transaction.put("iteration", iteration, 0, { parentId: chainId, status });
@@ -0,0 +1,47 @@
1
+ import { LearningAcceptedParentSchema, sameLearningRef } from "./contracts.js";
2
+ import { LearningDomainError } from "./errors.js";
3
+ import { requireLearningRelease, requireLearningResource } from "./repository.js";
4
+ /** Select the latest accepted human decision. A later rejection removes that
5
+ * candidate from future selection without rewriting reserved descendants. */
6
+ export async function latestAcceptedLearningParent(tx, chainId, requireComplete = false) {
7
+ let selected = null;
8
+ let afterId;
9
+ do {
10
+ const page = await tx.list("iteration", { parentId: chainId, status: "accepted", limit: 100, ...(afterId ? { afterId } : {}) });
11
+ for (const iteration of page.items) {
12
+ if (!iteration.candidateVersion || !iteration.candidateDecision || !iteration.candidateDecisionAt || !iteration.trainingJob) {
13
+ if (requireComplete)
14
+ throw new LearningDomainError("learning_accepted_parent_evidence_missing", 409);
15
+ continue;
16
+ }
17
+ const candidate = LearningAcceptedParentSchema.parse({ iterationId: iteration.id, iterationRevision: iteration.revision,
18
+ candidate: iteration.candidateVersion, decision: iteration.candidateDecision, decidedAt: iteration.candidateDecisionAt });
19
+ const order = [Date.parse(candidate.decidedAt), candidate.decision.id, candidate.decision.revision];
20
+ const previous = selected ? [Date.parse(selected.decidedAt), selected.decision.id, selected.decision.revision] : null;
21
+ if (!previous || order[0] > previous[0] || (order[0] === previous[0]
22
+ && (order[1] > previous[1] || (order[1] === previous[1] && order[2] > previous[2]))))
23
+ selected = candidate;
24
+ }
25
+ afterId = page.nextCursor ?? undefined;
26
+ } while (afterId);
27
+ return selected;
28
+ }
29
+ /** Reservation captures an accepted revision before dispatch or later reviews.
30
+ * Switching the underlying base requires a separately reviewed learning chain. */
31
+ export async function resolveLearningTrainingParent(tx, policy, selection) {
32
+ if (!selection)
33
+ return { trainingParent: policy.trainingParent, trainingParentSelection: null };
34
+ const parent = await requireLearningResource(tx, "iteration", selection.iterationId, selection.iterationRevision);
35
+ const sourcePolicy = await requireLearningRelease(tx, "policy", parent.policy);
36
+ if (parent.status !== "accepted" || !parent.candidateVersion || !parent.candidateDecision || !parent.trainingJob
37
+ || parent.candidateVersion.id !== selection.candidate.id || parent.candidateVersion.contentHash !== selection.candidate.contentHash
38
+ || !sameLearningRef(parent.candidateDecision, selection.decision) || parent.candidateDecisionAt !== selection.decidedAt
39
+ || sourcePolicy.modelProjectId !== policy.modelProjectId || sourcePolicy.executionOwner !== policy.executionOwner) {
40
+ throw new LearningDomainError("learning_accepted_parent_evidence_mismatch", 409);
41
+ }
42
+ if (sourcePolicy.trainingParent.id !== policy.trainingParent.id || sourcePolicy.trainingParent.contentHash !== policy.trainingParent.contentHash
43
+ || sourcePolicy.training.method !== policy.training.method) {
44
+ throw new LearningDomainError("learning_accepted_parent_configuration_mismatch", 409, "The accepted checkpoint belongs to a different starting model or training method.");
45
+ }
46
+ return { trainingParent: selection.candidate, trainingParentSelection: selection };
47
+ }
@@ -1,12 +1,13 @@
1
- import { ReleaseIdSchema, ReleaseTimestampSchema } from "@openpond/harness";
1
+ import { ImmutableReleaseRefSchema, ReleaseIdSchema, ReleaseTimestampSchema } from "@openpond/harness";
2
2
  import { z } from "zod";
3
- import { LearningRevisionRefSchema, learningRef, sameLearningRef } from "./contracts.js";
3
+ import { LearningAcceptedParentSchema, LearningRevisionRefSchema, learningRef, sameLearningRef } from "./contracts.js";
4
4
  import { prepareLearningBatch } from "./batch-service.js";
5
5
  import { LearningDomainError } from "./errors.js";
6
6
  import { readLearningIterationBudget } from "./iteration-budget.js";
7
7
  import { inspectIterationEligibility, learningChainId } from "./iteration-eligibility.js";
8
8
  import { LearningChainSchema, LearningEligibilityCountsSchema } from "./iteration-reservation-contracts.js";
9
9
  import { requireLearningRelease, requireLearningResource } from "./repository.js";
10
+ import { latestAcceptedLearningParent, resolveLearningTrainingParent } from "./iteration-training-parent.js";
10
11
  /** A scope-serialized snapshot of shared reservation readiness, not a Job guarantee.
11
12
  * No iteration, timer, batch, consumption or operation record is written. */
12
13
  export const LearningPolicyInspectionResultSchema = z.object({
@@ -14,6 +15,7 @@ export const LearningPolicyInspectionResultSchema = z.object({
14
15
  modelProjectId: ReleaseIdSchema,
15
16
  inspectedAt: ReleaseTimestampSchema,
16
17
  chain: LearningChainSchema.nullable(),
18
+ trainingParent: z.object({ reference: ImmutableReleaseRefSchema, selection: LearningAcceptedParentSchema.nullable() }).strict().nullable().default(null),
17
19
  counts: LearningEligibilityCountsSchema.nullable(),
18
20
  minimumApprovedExamples: z.number().int().positive(),
19
21
  canReserve: z.boolean(),
@@ -73,8 +75,18 @@ export async function inspectLearningPolicy(transaction, reference, actorId, now
73
75
  // must remain visible while the Model's settings and history can still load.
74
76
  block(error.code, error.message.slice(0, 20_000));
75
77
  }
78
+ let trainingParent = null;
79
+ try {
80
+ const resolved = await resolveLearningTrainingParent(transaction, policy, await latestAcceptedLearningParent(transaction, chainId, true));
81
+ trainingParent = { reference: resolved.trainingParent, selection: resolved.trainingParentSelection };
82
+ }
83
+ catch (error) {
84
+ if (!(error instanceof LearningDomainError))
85
+ throw error;
86
+ block(error.code, error.message.slice(0, 20_000));
87
+ }
76
88
  return LearningPolicyInspectionResultSchema.parse({
77
- policy: learningRef(policy), modelProjectId: policy.modelProjectId, inspectedAt: now, chain,
89
+ policy: learningRef(policy), modelProjectId: policy.modelProjectId, inspectedAt: now, chain, trainingParent,
78
90
  counts, minimumApprovedExamples: policy.admission.minimumApprovedExamples, canReserve: blockers.length === 0,
79
91
  blockers, cooldownUntil,
80
92
  budget: { ...budget, maximumIterationSpendUsd: policy.limits.maxIterationSpendUsd, maximumDailySpendUsd: policy.limits.maxDailySpendUsd },
@@ -0,0 +1,19 @@
1
+ import { learningRef, sameLearningRef, TaskRatingSchema } from "./contracts.js";
2
+ import { LearningDomainError } from "./errors.js";
3
+ /** Copy a retained label into editable check data while preserving its exact origin. */
4
+ export function rewardFixtureFromRating(evidence, feedback, scale = { minimum: 0, maximum: 1 }) {
5
+ if (!feedback.evidence || !sameLearningRef(feedback.evidence, learningRef(evidence)) || feedback.submission.expectedEvidenceHash !== evidence.contentHash || feedback.submission.kind !== "outcome" || !evidence.submission.observedOutput)
6
+ throw new LearningDomainError("rating_fixture_evidence_mismatch", 422);
7
+ const rating = TaskRatingSchema.parse(feedback.submission.value);
8
+ if (!Number.isFinite(scale.minimum) || !Number.isFinite(scale.maximum) || scale.minimum >= scale.maximum)
9
+ throw new LearningDomainError("rating_fixture_scale_invalid", 422);
10
+ const score = scale.minimum + (rating.score - rating.scale.minimum) / (rating.scale.maximum - rating.scale.minimum) * (scale.maximum - scale.minimum);
11
+ return {
12
+ id: `label-${feedback.id}-${feedback.revision}`, name: evidence.submission.exampleId,
13
+ input: JSON.stringify(evidence.submission.input, null, 2), output: JSON.stringify(evidence.submission.observedOutput, null, 2),
14
+ expectedOutput: evidence.submission.expected ? JSON.stringify(evidence.submission.expected, null, 2) : "",
15
+ evaluatorContext: evidence.submission.evaluatorContext ? JSON.stringify(evidence.submission.evaluatorContext, null, 2) : "",
16
+ artifactRefs: [], runtimeEventRefs: [], infrastructureError: "", expectedStatus: "scored", minimumScore: String(score), maximumScore: String(score), expectedPassed: "any",
17
+ sourceLabel: { evidence: learningRef(evidence), feedback: { id: feedback.id, revision: feedback.revision } },
18
+ };
19
+ }
@@ -70,7 +70,7 @@ export function createLearningService(repository, options = {}) {
70
70
  pointers = [await submit(transaction, input)];
71
71
  break;
72
72
  case "submit_feedback":
73
- pointers = [await feedback(transaction, input)];
73
+ pointers = [await feedback(transaction, input, context.actor)];
74
74
  break;
75
75
  case "apply_correction":
76
76
  pointers = await correct(transaction, input, context.actor.id);
@@ -193,16 +193,19 @@ export function createLearningService(repository, options = {}) {
193
193
  } while (afterId);
194
194
  return pointer("evidence", evidence);
195
195
  }
196
- async function feedback(transaction, input) {
196
+ async function feedback(transaction, input, actor) {
197
197
  const source = await requireLearningResource(transaction, "source", input.feedback.sourceId);
198
198
  if (!source.enabled)
199
199
  throw new LearningDomainError("learning_source_disabled", 409);
200
200
  const evidenceId = learningEvidenceId(source.id, input.feedback.exampleId, input.feedback.attemptId);
201
201
  const evidence = await transaction.get("evidence", evidenceId);
202
+ if (input.feedback.value.schemaVersion === "openpond.taskRating.v1" && (!evidence || evidence.contentHash !== input.feedback.expectedEvidenceHash || !evidence.submission.observedOutput))
203
+ throw new LearningDomainError("task_rating_evidence_stale", 409, "Open the current observed response before submitting a rating.");
202
204
  const record = TaskFeedbackSchema.parse({
203
205
  schemaVersion: "openpond.taskFeedbackRecord.v1", id: `feedback-${contentHash([source.id, input.feedback.idempotencyKey])}`,
204
206
  submission: input.feedback, status: evidence ? "pending_review" : "pending_example",
205
207
  evidence: evidence ? learningRef(evidence) : null, createdAt: now(), revision: 1,
208
+ submittedBy: { id: actor.id, role: actor.role, sourceId: actor.sourceId ?? null },
206
209
  });
207
210
  await transaction.put("feedback", record, 0, { parentId: evidenceId, status: record.status });
208
211
  return pointer("feedback", record);
@@ -348,6 +351,8 @@ export function createLearningService(repository, options = {}) {
348
351
  },
349
352
  async list(context, kind, query = {}) {
350
353
  authorizeRead(context);
354
+ if (query.reviewState !== undefined && (kind !== "evidence" || !["inbox", "reviewed"].includes(query.reviewState)))
355
+ throw new LearningDomainError("learning_review_filter_invalid", 400);
351
356
  const limit = Math.max(1, Math.min(100, Math.trunc(query.limit ?? 50)));
352
357
  return repository.transaction(context.scope, (transaction) => transaction.list(kind, { ...query, limit }));
353
358
  },
@@ -20,7 +20,7 @@ export const LearningReadRequestSchema = z.discriminatedUnion("action", [
20
20
  z.object({ action: z.literal("inspect_policy"), scope: ReleaseIdSchema, policy: LearningRevisionRefSchema }).strict(),
21
21
  z.object({ action: z.literal("inspect_evidence"), scope: ReleaseIdSchema, evidence: LearningRevisionRefSchema }).strict(),
22
22
  z.object({ action: z.literal("get"), scope: ReleaseIdSchema, kind: LearningResourceKindSchema, id: ReleaseIdSchema, revision: z.number().int().positive().optional() }).strict(),
23
- z.object({ action: z.literal("list"), scope: ReleaseIdSchema, kind: LearningResourceKindSchema, parentId: ReleaseIdSchema.optional(), status: z.string().min(1).max(200).optional(), afterId: ReleaseIdSchema.optional(), limit: z.number().int().min(1).max(100).default(50) }).strict(),
23
+ z.object({ action: z.literal("list"), scope: ReleaseIdSchema, kind: LearningResourceKindSchema, parentId: ReleaseIdSchema.optional(), status: z.string().min(1).max(200).optional(), reviewState: z.enum(["inbox", "reviewed"]).optional(), afterId: ReleaseIdSchema.optional(), limit: z.number().int().min(1).max(100).default(50) }).strict(),
24
24
  ]);
25
25
  export const TaskEvidenceInspectionResultSchema = z.object({
26
26
  evidence: LearningRevisionRefSchema,
@@ -130,6 +130,17 @@ export declare function currentDraft(tx: LearningTransaction, ref: LearningRevis
130
130
  minimumScore: string;
131
131
  maximumScore: string;
132
132
  expectedPassed: "any" | "true" | "false";
133
+ sourceLabel?: {
134
+ evidence: {
135
+ id: string;
136
+ contentHash: string;
137
+ revision: number;
138
+ };
139
+ feedback: {
140
+ id: string;
141
+ revision: number;
142
+ };
143
+ } | undefined;
133
144
  }[] | undefined;
134
145
  };
135
146
  schemaVersion: "openpond.authoringDraft.v1";
@@ -1 +1 @@
1
- {"version":3,"file":"authoring-service.d.ts","sourceRoot":"","sources":["../../../../../src/learning/authoring-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAqD,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAA0B,KAAK,uBAAuB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACjH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM;;;;GAY7I;AAED,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,mBAAmB,EAAE,GAAG,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM;;;;GAMzG;AAED,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,CAAC,eAAe,EAAE;IAAE,MAAM,EAAE,SAAS,GAAG,mBAAmB,CAAA;CAAE,CAAC,EAAE,SAAS,EAAE,uBAAuB,EAAE,EAAE,GAAG,EAAE,MAAM;;;;UAUpM;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,mBAAmB,EAAE,GAAG,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAMnF"}
1
+ {"version":3,"file":"authoring-service.d.ts","sourceRoot":"","sources":["../../../../../src/learning/authoring-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAqD,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAAmD,KAAK,uBAAuB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC1I,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM;;;;GAmB7I;AAED,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,mBAAmB,EAAE,GAAG,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM;;;;GAMzG;AAED,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,CAAC,eAAe,EAAE;IAAE,MAAM,EAAE,SAAS,GAAG,mBAAmB,CAAA;CAAE,CAAC,EAAE,SAAS,EAAE,uBAAuB,EAAE,EAAE,GAAG,EAAE,MAAM;;;;UAUpM;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,mBAAmB,EAAE,GAAG,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAMnF"}
@@ -23,6 +23,17 @@ export declare const RewardFixtureAuthoringFieldsSchema: z.ZodObject<{
23
23
  true: "true";
24
24
  false: "false";
25
25
  }>;
26
+ sourceLabel: z.ZodOptional<z.ZodObject<{
27
+ evidence: z.ZodObject<{
28
+ id: z.ZodString;
29
+ contentHash: z.ZodString;
30
+ revision: z.ZodNumber;
31
+ }, z.core.$strict>;
32
+ feedback: z.ZodObject<{
33
+ id: z.ZodString;
34
+ revision: z.ZodNumber;
35
+ }, z.core.$strict>;
36
+ }, z.core.$strict>>;
26
37
  }, z.core.$strict>;
27
38
  export type RewardFixtureAuthoringFields = z.infer<typeof RewardFixtureAuthoringFieldsSchema>;
28
39
  export declare const RewardAuthoringFieldsSchema: z.ZodObject<{
@@ -83,6 +94,17 @@ export declare const RewardAuthoringFieldsSchema: z.ZodObject<{
83
94
  true: "true";
84
95
  false: "false";
85
96
  }>;
97
+ sourceLabel: z.ZodOptional<z.ZodObject<{
98
+ evidence: z.ZodObject<{
99
+ id: z.ZodString;
100
+ contentHash: z.ZodString;
101
+ revision: z.ZodNumber;
102
+ }, z.core.$strict>;
103
+ feedback: z.ZodObject<{
104
+ id: z.ZodString;
105
+ revision: z.ZodNumber;
106
+ }, z.core.$strict>;
107
+ }, z.core.$strict>>;
86
108
  }, z.core.$strict>>>;
87
109
  }, z.core.$strict>;
88
110
  export declare const TaskFormatAuthoringFieldsSchema: z.ZodObject<{
@@ -291,6 +313,17 @@ export declare const AuthoringDraftInputSchema: z.ZodDiscriminatedUnion<[z.ZodOb
291
313
  true: "true";
292
314
  false: "false";
293
315
  }>;
316
+ sourceLabel: z.ZodOptional<z.ZodObject<{
317
+ evidence: z.ZodObject<{
318
+ id: z.ZodString;
319
+ contentHash: z.ZodString;
320
+ revision: z.ZodNumber;
321
+ }, z.core.$strict>;
322
+ feedback: z.ZodObject<{
323
+ id: z.ZodString;
324
+ revision: z.ZodNumber;
325
+ }, z.core.$strict>;
326
+ }, z.core.$strict>>;
294
327
  }, z.core.$strict>>>;
295
328
  }, z.core.$strict>;
296
329
  }, z.core.$strict>, z.ZodObject<{
@@ -475,6 +508,17 @@ export declare const AuthoringDraftContentSchema: z.ZodDiscriminatedUnion<[z.Zod
475
508
  true: "true";
476
509
  false: "false";
477
510
  }>;
511
+ sourceLabel: z.ZodOptional<z.ZodObject<{
512
+ evidence: z.ZodObject<{
513
+ id: z.ZodString;
514
+ contentHash: z.ZodString;
515
+ revision: z.ZodNumber;
516
+ }, z.core.$strict>;
517
+ feedback: z.ZodObject<{
518
+ id: z.ZodString;
519
+ revision: z.ZodNumber;
520
+ }, z.core.$strict>;
521
+ }, z.core.$strict>>;
478
522
  }, z.core.$strict>>>;
479
523
  }, z.core.$strict>;
480
524
  schemaVersion: z.ZodLiteral<"openpond.authoringDraft.v1">;
@@ -688,6 +732,17 @@ export declare const AuthoringDraftSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
688
732
  true: "true";
689
733
  false: "false";
690
734
  }>;
735
+ sourceLabel: z.ZodOptional<z.ZodObject<{
736
+ evidence: z.ZodObject<{
737
+ id: z.ZodString;
738
+ contentHash: z.ZodString;
739
+ revision: z.ZodNumber;
740
+ }, z.core.$strict>;
741
+ feedback: z.ZodObject<{
742
+ id: z.ZodString;
743
+ revision: z.ZodNumber;
744
+ }, z.core.$strict>;
745
+ }, z.core.$strict>>;
691
746
  }, z.core.$strict>>>;
692
747
  }, z.core.$strict>;
693
748
  schemaVersion: z.ZodLiteral<"openpond.authoringDraft.v1">;
@@ -1 +1 @@
1
- {"version":3,"file":"authoring.d.ts","sourceRoot":"","sources":["../../../../../src/learning/authoring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,sFAAsF;AACtF,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;kBAOpC,CAAC;AACZ,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAQ7B,CAAC;AACZ,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAIjC,CAAC;AACZ,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAErC,CAAC;AACZ,eAAO,MAAM,yBAAyB;;;;EAA8C,CAAC;AAErF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAIpC,CAAC;AAMH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAAoO,CAAC;AAC7Q,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAA2T,CAAC;AAC7V,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,mBAAmB,IAAI,OAAO,CAAC,cAAc,EAAE;IAAE,UAAU,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAE1G,sFAAsF;AACtF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;kBAAqI,CAAC"}
1
+ {"version":3,"file":"authoring.d.ts","sourceRoot":"","sources":["../../../../../src/learning/authoring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,sFAAsF;AACtF,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAQpC,CAAC;AACZ,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAQ7B,CAAC;AACZ,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAIjC,CAAC;AACZ,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAErC,CAAC;AACZ,eAAO,MAAM,yBAAyB;;;;EAA8C,CAAC;AAErF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAIpC,CAAC;AAMH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAAoO,CAAC;AAC7Q,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAA2T,CAAC;AAC7V,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,mBAAmB,IAAI,OAAO,CAAC,cAAc,EAAE;IAAE,UAAU,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAE1G,sFAAsF;AACtF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;kBAAqI,CAAC"}