@velum-labs/routekit-eval-setup 1.0.22 → 1.0.23
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.
|
@@ -14,7 +14,7 @@ export type EvalProjectArtifactsShape = {
|
|
|
14
14
|
readonly loadEvaluationsApproval: (repositoryRoot: string) => Effect.Effect<EvalArtifactApproval | undefined, EvalProjectArtifactError, never>;
|
|
15
15
|
readonly saveEvaluationsApproval: (repositoryRoot: string, approval: EvalArtifactApproval) => Effect.Effect<void, EvalProjectArtifactError, never>;
|
|
16
16
|
readonly savePlan: (repositoryRoot: string, plan: EvalExecutionPlan) => Effect.Effect<void, EvalProjectArtifactError, never>;
|
|
17
|
-
readonly materializePlanSuites: (repositoryRoot: string, plan: EvalExecutionPlan, proposal: EvalEvaluationProposal) => Effect.Effect<void, EvalProjectArtifactError, never>;
|
|
17
|
+
readonly materializePlanSuites: (repositoryRoot: string, plan: EvalExecutionPlan, proposal: EvalEvaluationProposal, mode?: "create" | "rematerialize") => Effect.Effect<void, EvalProjectArtifactError, never>;
|
|
18
18
|
readonly planSuitePath: (repositoryRoot: string, planId: string, dimensionId: string) => Effect.Effect<string, EvalProjectArtifactError, never>;
|
|
19
19
|
readonly compositionSuitePath: (repositoryRoot: string, planId: string) => Effect.Effect<string, EvalProjectArtifactError, never>;
|
|
20
20
|
readonly loadPlan: (repositoryRoot: string, planId: string) => Effect.Effect<EvalExecutionPlan | undefined, EvalProjectArtifactError, never>;
|
|
@@ -234,7 +234,7 @@ export const makeFileEvalProjectArtifacts = Effect.gen(function* () {
|
|
|
234
234
|
loadEvaluationsApproval: (repositoryRoot) => read(repositoryRoot, EVALUATIONS_APPROVAL, Schema.decodeUnknownEffect(EvalArtifactApprovalSchema)),
|
|
235
235
|
saveEvaluationsApproval: (repositoryRoot, approval) => write(repositoryRoot, EVALUATIONS_APPROVAL, approval),
|
|
236
236
|
savePlan: (repositoryRoot, plan) => write(repositoryRoot, paths.join("plans", `${plan.planId}.json`), plan, false),
|
|
237
|
-
materializePlanSuites: (repositoryRoot, plan, proposal) => Effect.gen(function* () {
|
|
237
|
+
materializePlanSuites: (repositoryRoot, plan, proposal, mode = "create") => Effect.gen(function* () {
|
|
238
238
|
yield* requireArtifactId("plan", plan.planId);
|
|
239
239
|
const suites = new Map(proposal.suites.map((suite) => [suite.dimensionId, suite]));
|
|
240
240
|
for (const selection of plan.selectedCaseIds) {
|
|
@@ -254,8 +254,8 @@ export const makeFileEvalProjectArtifacts = Effect.gen(function* () {
|
|
|
254
254
|
return testCase;
|
|
255
255
|
});
|
|
256
256
|
const suiteRoot = paths.join("plans", plan.planId, "dimensions", selection.dimensionId);
|
|
257
|
-
yield* writeText(repositoryRoot, paths.join(suiteRoot, `${selection.dimensionId}.eval.ts`), renderDimensionSuite(),
|
|
258
|
-
yield* write(repositoryRoot, paths.join(suiteRoot, "data", "cases.json"), cases,
|
|
257
|
+
yield* writeText(repositoryRoot, paths.join(suiteRoot, `${selection.dimensionId}.eval.ts`), renderDimensionSuite(), mode === "rematerialize");
|
|
258
|
+
yield* write(repositoryRoot, paths.join(suiteRoot, "data", "cases.json"), cases, mode === "rematerialize");
|
|
259
259
|
yield* write(repositoryRoot, paths.join(suiteRoot, "routekit.eval-manifest.json"), {
|
|
260
260
|
version: EVAL_PROJECT_VERSION,
|
|
261
261
|
profileId: selection.dimensionId,
|
|
@@ -265,7 +265,7 @@ export const makeFileEvalProjectArtifacts = Effect.gen(function* () {
|
|
|
265
265
|
caseIds: cases.map((testCase) => testCase.id),
|
|
266
266
|
maxOutputTokens: suite.maximumOutputTokens,
|
|
267
267
|
expectedCallCount: cases.length * plan.candidateModels.length * 2
|
|
268
|
-
},
|
|
268
|
+
}, mode === "rematerialize");
|
|
269
269
|
}
|
|
270
270
|
const compositionById = new Map(proposal.compositionSuite.cases.map((testCase) => [testCase.id, testCase]));
|
|
271
271
|
const compositionCases = plan.selectedCompositionCaseIds.map((caseId) => {
|
|
@@ -276,8 +276,8 @@ export const makeFileEvalProjectArtifacts = Effect.gen(function* () {
|
|
|
276
276
|
return testCase;
|
|
277
277
|
});
|
|
278
278
|
const compositionRoot = paths.join("plans", plan.planId, "composition");
|
|
279
|
-
yield* writeText(repositoryRoot, paths.join(compositionRoot, "composition.eval.ts"), renderDimensionSuite(),
|
|
280
|
-
yield* write(repositoryRoot, paths.join(compositionRoot, "data", "cases.json"), compositionCases,
|
|
279
|
+
yield* writeText(repositoryRoot, paths.join(compositionRoot, "composition.eval.ts"), renderDimensionSuite(), mode === "rematerialize");
|
|
280
|
+
yield* write(repositoryRoot, paths.join(compositionRoot, "data", "cases.json"), compositionCases, mode === "rematerialize");
|
|
281
281
|
yield* write(repositoryRoot, paths.join(compositionRoot, "routekit.eval-manifest.json"), {
|
|
282
282
|
version: EVAL_PROJECT_VERSION,
|
|
283
283
|
profileId: "composition",
|
|
@@ -287,7 +287,7 @@ export const makeFileEvalProjectArtifacts = Effect.gen(function* () {
|
|
|
287
287
|
caseIds: compositionCases.map((testCase) => testCase.id),
|
|
288
288
|
maxOutputTokens: proposal.compositionSuite.maximumOutputTokens,
|
|
289
289
|
expectedCallCount: compositionCases.length * plan.candidateModels.length * 2
|
|
290
|
-
},
|
|
290
|
+
}, mode === "rematerialize");
|
|
291
291
|
}).pipe(Effect.mapError((cause) => cause instanceof EvalProjectArtifactError
|
|
292
292
|
? cause
|
|
293
293
|
: artifactFailure("writing", artifactPath(repositoryRoot, paths.join("plans", plan.planId)), cause))),
|
package/dist/project-workflow.js
CHANGED
|
@@ -782,6 +782,13 @@ export const makeEvalProjectWorkflow = Effect.gen(function* () {
|
|
|
782
782
|
plan.judgeModel !== state.configuration.judgeModel) {
|
|
783
783
|
return yield* transitionError(state.stage, "execution plan is stale or does not match the approved project");
|
|
784
784
|
}
|
|
785
|
+
const proposal = yield* artifacts.loadEvaluationProposal(root);
|
|
786
|
+
if (proposal === undefined ||
|
|
787
|
+
proposal.evaluationDigest !== plan.evaluationDigest ||
|
|
788
|
+
proposal.basisDigest !== plan.basisDigest) {
|
|
789
|
+
return yield* transitionError(state.stage, "the approved evaluation proposal for the execution plan is missing or stale");
|
|
790
|
+
}
|
|
791
|
+
yield* artifacts.materializePlanSuites(root, plan, proposal, "rematerialize");
|
|
785
792
|
const now = yield* isoNow;
|
|
786
793
|
const runId = crypto.randomUUID();
|
|
787
794
|
const next = {
|
|
@@ -621,6 +621,55 @@ test("failed runs preserve plan revisions for retry while mismatched plans remai
|
|
|
621
621
|
assert.equal(rerun.ready.state.evaluationDigest, plan.evaluationDigest);
|
|
622
622
|
assert.equal(rerun.second.plan.planId, plan.planId);
|
|
623
623
|
});
|
|
624
|
+
test("startRun rematerializes stale dimension and composition suite templates", async () => {
|
|
625
|
+
const root = await makeRepository();
|
|
626
|
+
const plan = await createPlan(root, "pilot");
|
|
627
|
+
const selection = plan.selectedCaseIds[0];
|
|
628
|
+
const planRoot = path.join(root, ".routekit", "evals", "plans", plan.planId);
|
|
629
|
+
const dimensionRoot = path.join(planRoot, "dimensions", selection.dimensionId);
|
|
630
|
+
const dimensionSuitePath = path.join(dimensionRoot, `${selection.dimensionId}.eval.ts`);
|
|
631
|
+
const compositionRoot = path.join(planRoot, "composition");
|
|
632
|
+
const compositionSuitePath = path.join(compositionRoot, "composition.eval.ts");
|
|
633
|
+
const dimensionManifestPath = path.join(dimensionRoot, "routekit.eval-manifest.json");
|
|
634
|
+
const compositionManifestPath = path.join(compositionRoot, "routekit.eval-manifest.json");
|
|
635
|
+
const staleTemplate = `// generated by RouteKit 1.0.21
|
|
636
|
+
run.toComplete();
|
|
637
|
+
await judge.autoEvals({ criteria: testCase.rubric, prompt, run });
|
|
638
|
+
`;
|
|
639
|
+
await Promise.all([
|
|
640
|
+
writeFile(dimensionSuitePath, staleTemplate),
|
|
641
|
+
writeFile(compositionSuitePath, staleTemplate)
|
|
642
|
+
]);
|
|
643
|
+
const [dimensionManifestBefore, compositionManifestBefore] = await Promise.all([
|
|
644
|
+
readFile(dimensionManifestPath, "utf8"),
|
|
645
|
+
readFile(compositionManifestPath, "utf8")
|
|
646
|
+
]);
|
|
647
|
+
await Effect.runPromise(Effect.gen(function* () {
|
|
648
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
649
|
+
yield* workflow.startRun(root, plan.planId);
|
|
650
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
651
|
+
const [dimensionSuite, compositionSuite, dimensionManifestAfter, compositionManifestAfter] = await Promise.all([
|
|
652
|
+
readFile(dimensionSuitePath, "utf8"),
|
|
653
|
+
readFile(compositionSuitePath, "utf8"),
|
|
654
|
+
readFile(dimensionManifestPath, "utf8"),
|
|
655
|
+
readFile(compositionManifestPath, "utf8")
|
|
656
|
+
]);
|
|
657
|
+
for (const suite of [dimensionSuite, compositionSuite]) {
|
|
658
|
+
const completionAssertion = suite.indexOf("run.toComplete();");
|
|
659
|
+
const judgeCall = suite.indexOf("await judge.autoEvals");
|
|
660
|
+
const completionRethrow = suite.indexOf("throw candidateCompletionError;");
|
|
661
|
+
assert.doesNotMatch(suite, /generated by RouteKit 1\.0\.21/u);
|
|
662
|
+
assert.ok(completionAssertion >= 0);
|
|
663
|
+
assert.ok(judgeCall > completionAssertion);
|
|
664
|
+
assert.ok(completionRethrow > judgeCall);
|
|
665
|
+
}
|
|
666
|
+
assert.equal(dimensionManifestAfter, dimensionManifestBefore);
|
|
667
|
+
assert.equal(compositionManifestAfter, compositionManifestBefore);
|
|
668
|
+
const dimensionManifest = JSON.parse(dimensionManifestAfter);
|
|
669
|
+
const compositionManifest = JSON.parse(compositionManifestAfter);
|
|
670
|
+
assert.equal(dimensionManifest.expectedCallCount, selection.caseIds.length * plan.candidateModels.length * 2);
|
|
671
|
+
assert.equal(compositionManifest.expectedCallCount, plan.selectedCompositionCaseIds.length * plan.candidateModels.length * 2);
|
|
672
|
+
});
|
|
624
673
|
test("failed run reports persist the nested qualification error chain", async () => {
|
|
625
674
|
const root = await makeRepository();
|
|
626
675
|
const plan = await createPlan(root, "full");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velum-labs/routekit-eval-setup",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.23",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/velum-labs/routekit.git",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"effect": "4.0.0-rc.108",
|
|
35
|
-
"@velum-labs/routekit-eval-contracts": "1.0.
|
|
36
|
-
"@velum-labs/routekit-runtime": "1.0.
|
|
35
|
+
"@velum-labs/routekit-eval-contracts": "1.0.23",
|
|
36
|
+
"@velum-labs/routekit-runtime": "1.0.23"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|
|
39
39
|
"routekit",
|