@prismatic-io/lux 0.0.1 → 0.0.2-preview.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.
- package/lib/assertions/authoring.d.ts +14 -0
- package/lib/assertions/authoring.d.ts.map +1 -0
- package/lib/assertions/authoring.js +3 -0
- package/lib/assertions/authoring.js.map +1 -0
- package/lib/authoring.d.ts +1 -0
- package/lib/authoring.d.ts.map +1 -1
- package/lib/authoring.js +1 -0
- package/lib/authoring.js.map +1 -1
- package/lib/core/assertion.d.ts +7 -9
- package/lib/core/assertion.d.ts.map +1 -1
- package/lib/core/assertion.js +13 -0
- package/lib/core/assertion.js.map +1 -1
- package/lib/core/case-authoring-state.d.ts +12 -0
- package/lib/core/case-authoring-state.d.ts.map +1 -0
- package/lib/core/case-authoring-state.js +43 -0
- package/lib/core/case-authoring-state.js.map +1 -0
- package/lib/core/case.d.ts +29 -1
- package/lib/core/case.d.ts.map +1 -1
- package/lib/core/case.js +14 -6
- package/lib/core/case.js.map +1 -1
- package/lib/core/driver.d.ts +3 -0
- package/lib/core/driver.d.ts.map +1 -1
- package/lib/core/driver.js.map +1 -1
- package/lib/core/lifecycle-fixtures.d.ts +3 -1
- package/lib/core/lifecycle-fixtures.d.ts.map +1 -1
- package/lib/core/lifecycle-fixtures.js +4 -2
- package/lib/core/lifecycle-fixtures.js.map +1 -1
- package/lib/core/lifecycle-hooks.d.ts +9 -0
- package/lib/core/lifecycle-hooks.d.ts.map +1 -1
- package/lib/core/lifecycle-hooks.js +8 -2
- package/lib/core/lifecycle-hooks.js.map +1 -1
- package/lib/drivers/subprocess/index.d.ts.map +1 -1
- package/lib/drivers/subprocess/index.js +6 -0
- package/lib/drivers/subprocess/index.js.map +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/orchestrator/annotation-store.d.ts.map +1 -1
- package/lib/orchestrator/annotation-store.js +6 -2
- package/lib/orchestrator/annotation-store.js.map +1 -1
- package/lib/orchestrator/case-extensions.d.ts +6 -0
- package/lib/orchestrator/case-extensions.d.ts.map +1 -0
- package/lib/orchestrator/case-extensions.js +28 -0
- package/lib/orchestrator/case-extensions.js.map +1 -0
- package/lib/orchestrator/doctor.d.ts.map +1 -1
- package/lib/orchestrator/doctor.js +5 -3
- package/lib/orchestrator/doctor.js.map +1 -1
- package/lib/orchestrator/loader.d.ts.map +1 -1
- package/lib/orchestrator/loader.js +2 -0
- package/lib/orchestrator/loader.js.map +1 -1
- package/lib/orchestrator/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator/orchestrator.js +39 -14
- package/lib/orchestrator/orchestrator.js.map +1 -1
- package/lib/orchestrator/owned-lock.d.ts.map +1 -1
- package/lib/orchestrator/owned-lock.js +52 -38
- package/lib/orchestrator/owned-lock.js.map +1 -1
- package/lib/orchestrator/regrade.d.ts.map +1 -1
- package/lib/orchestrator/regrade.js +8 -4
- package/lib/orchestrator/regrade.js.map +1 -1
- package/lib/orchestrator/run-execution.d.ts +2 -1
- package/lib/orchestrator/run-execution.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.js +1 -0
- package/lib/orchestrator/run-execution.js.map +1 -1
- package/package.json +1 -1
- package/skills/lux-answerer/SKILL.md +1 -1
- package/src/assertions/authoring.ts +79 -0
- package/src/authoring.ts +1 -0
- package/src/core/assertion.ts +26 -22
- package/src/core/case-authoring-state.ts +65 -0
- package/src/core/case.ts +57 -6
- package/src/core/driver.ts +3 -0
- package/src/core/lifecycle-fixtures.ts +7 -2
- package/src/core/lifecycle-hooks.ts +16 -2
- package/src/drivers/subprocess/index.ts +6 -0
- package/src/index.ts +10 -1
- package/src/orchestrator/annotation-store.ts +5 -1
- package/src/orchestrator/case-extensions.ts +44 -0
- package/src/orchestrator/doctor.ts +5 -3
- package/src/orchestrator/loader.ts +2 -0
- package/src/orchestrator/orchestrator.ts +80 -40
- package/src/orchestrator/owned-lock.ts +54 -34
- package/src/orchestrator/regrade.ts +8 -7
- package/src/orchestrator/run-execution.ts +3 -0
|
@@ -5,6 +5,7 @@ import { glob } from "tinyglobby";
|
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { RubricConfigSchema } from "../assertions/rubric/index.js";
|
|
7
7
|
import type { EvalCase, Registry } from "../core/index.js";
|
|
8
|
+
import { registryForEvalCase } from "./case-extensions.js";
|
|
8
9
|
import { buildRegistryFromConfig, loadLuxConfig, type ResolvedLuxConfig } from "./config.js";
|
|
9
10
|
import { type DiscoveredCase, type DiscoveryResult, discoverCases } from "./discover.js";
|
|
10
11
|
import { cliIdentity, ExperimentCampaignPlanSchema, planExperimentCampaign } from "./experiment.js";
|
|
@@ -559,12 +560,13 @@ const inspectCase = async (
|
|
|
559
560
|
): Promise<CliTarget[]> => {
|
|
560
561
|
try {
|
|
561
562
|
const authored = await loadEvalCase(discovered.path);
|
|
563
|
+
const caseRegistry = registryForEvalCase(registry, authored);
|
|
562
564
|
const drivers = caseDriverReferences(authored.driver, config);
|
|
563
565
|
const answerer = authored.answerer ?? config.defaultAnswerer;
|
|
564
|
-
validateCaseDrivers(discovered.id, drivers,
|
|
565
|
-
validateCaseAnswerer(discovered.id, answerer,
|
|
566
|
+
validateCaseDrivers(discovered.id, drivers, caseRegistry, recorder);
|
|
567
|
+
validateCaseAnswerer(discovered.id, answerer, caseRegistry, recorder);
|
|
566
568
|
return [
|
|
567
|
-
...(await validateCaseAssertions(discovered.id, authored, config,
|
|
569
|
+
...(await validateCaseAssertions(discovered.id, authored, config, caseRegistry, recorder)),
|
|
568
570
|
...executionTargetsForCase(discovered.id, drivers, answerer),
|
|
569
571
|
];
|
|
570
572
|
} catch (error) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { transferEvalCaseExtensions } from "../core/case-authoring-state.js";
|
|
3
4
|
import { type EvalCase, EvalCaseSchema, formatZodIssues } from "../core/index.js";
|
|
4
5
|
|
|
5
6
|
export const loadEvalCase = async (filePath: string): Promise<EvalCase> => {
|
|
@@ -15,5 +16,6 @@ export const loadEvalCase = async (filePath: string): Promise<EvalCase> => {
|
|
|
15
16
|
if (!result.success) {
|
|
16
17
|
throw new Error(`Invalid eval case at ${absolute}: ${formatZodIssues(result.error)}`);
|
|
17
18
|
}
|
|
19
|
+
transferEvalCaseExtensions(mod.default as EvalCase, result.data);
|
|
18
20
|
return result.data;
|
|
19
21
|
};
|
|
@@ -18,6 +18,12 @@ import {
|
|
|
18
18
|
type SuiteLifecycleHookContext,
|
|
19
19
|
valueHash,
|
|
20
20
|
} from "../core/index.js";
|
|
21
|
+
import {
|
|
22
|
+
fixturesForEvalCase,
|
|
23
|
+
mergeLifecycleFixtures,
|
|
24
|
+
registryForEvalCase,
|
|
25
|
+
valuesForFixtures,
|
|
26
|
+
} from "./case-extensions.js";
|
|
21
27
|
import { evaluatorImplementationHash } from "./evaluator-identity.js";
|
|
22
28
|
import {
|
|
23
29
|
assertionEvaluatorIdentities,
|
|
@@ -476,11 +482,6 @@ export class Orchestrator {
|
|
|
476
482
|
}
|
|
477
483
|
|
|
478
484
|
async run(request: RunRequest): Promise<RunResult> {
|
|
479
|
-
if (this.lifecycleFixtures.length > 0 && !request.suiteId) {
|
|
480
|
-
throw new Error(
|
|
481
|
-
"Orchestrator.run() cannot execute configured lifecycle fixtures; use runSuite() for a single case",
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
485
|
if (!request.case && !request.casePath) {
|
|
485
486
|
throw new Error("RunRequest requires either 'case' or 'casePath'");
|
|
486
487
|
}
|
|
@@ -489,11 +490,24 @@ export class Orchestrator {
|
|
|
489
490
|
}
|
|
490
491
|
const casePath = request.casePath ? resolve(request.casePath) : INLINE_CASE_PATH;
|
|
491
492
|
const authoredCase = request.case ?? (await loadEvalCase(casePath));
|
|
493
|
+
const lifecycleFixtures = mergeLifecycleFixtures(
|
|
494
|
+
this.lifecycleFixtures,
|
|
495
|
+
fixturesForEvalCase(authoredCase),
|
|
496
|
+
);
|
|
497
|
+
if (
|
|
498
|
+
lifecycleFixtures.some(({ setupSuite, setupCase }) => setupSuite || setupCase) &&
|
|
499
|
+
!request.suiteId
|
|
500
|
+
) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
"Orchestrator.run() cannot execute lifecycle fixtures; use runSuite() for a single case",
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
const registry = registryForEvalCase(this.registry, authoredCase);
|
|
492
506
|
const evalCase = bindSubjectRoot(authoredCase, request.subjectRoot);
|
|
493
507
|
const caseId = resolveCaseId(casePath, evalCase, request.caseId);
|
|
494
508
|
const validatedAssertionConfigs = await validateAssertionPlan(
|
|
495
509
|
evalCase.assertions ?? [],
|
|
496
|
-
|
|
510
|
+
registry,
|
|
497
511
|
);
|
|
498
512
|
|
|
499
513
|
const driverRef = resolveDriverReference(evalCase, this.defaultDriver);
|
|
@@ -506,11 +520,11 @@ export class Orchestrator {
|
|
|
506
520
|
);
|
|
507
521
|
const answererName = answererNameOf(answererRef);
|
|
508
522
|
|
|
509
|
-
const driverPlugin =
|
|
523
|
+
const driverPlugin = registry.getDriver(driverName);
|
|
510
524
|
if (!driverPlugin) {
|
|
511
525
|
throw new Error(`no driver registered for '${driverName}'`);
|
|
512
526
|
}
|
|
513
|
-
const answererPlugin =
|
|
527
|
+
const answererPlugin = registry.getAnswerer(answererName);
|
|
514
528
|
if (!answererPlugin) {
|
|
515
529
|
throw new Error(`no answerer registered for '${answererName}'`);
|
|
516
530
|
}
|
|
@@ -544,7 +558,7 @@ export class Orchestrator {
|
|
|
544
558
|
try {
|
|
545
559
|
const assertionImplementations = assertionEvaluatorIdentities(
|
|
546
560
|
authoredCase.assertions ?? [],
|
|
547
|
-
|
|
561
|
+
registry,
|
|
548
562
|
);
|
|
549
563
|
const validatedAssertionsHash = valueHash(validatedAssertionConfigs);
|
|
550
564
|
const implementationHash = await this.evaluatorImplementationIdentity();
|
|
@@ -555,33 +569,37 @@ export class Orchestrator {
|
|
|
555
569
|
validatedAssertionsHash,
|
|
556
570
|
graderContext: this.harness,
|
|
557
571
|
});
|
|
558
|
-
return await this.runWithPlugins(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
graderConfigHash: graderHash,
|
|
572
|
-
evaluatorConfigHash: valueHash({
|
|
573
|
-
luxVersion: LUX_VERSION,
|
|
574
|
-
implementationHash,
|
|
575
|
-
harness: this.harness,
|
|
576
|
-
driver: { name: driverPlugin.name, create: driverPlugin.create },
|
|
577
|
-
answerer: { name: answererPlugin.name, create: answererPlugin.create },
|
|
578
|
-
assertions: assertionImplementations,
|
|
579
|
-
lifecycleFixtures: this.lifecycleFixtures,
|
|
572
|
+
return await this.runWithPlugins(
|
|
573
|
+
{
|
|
574
|
+
request,
|
|
575
|
+
casePath,
|
|
576
|
+
authoredCase,
|
|
577
|
+
evalCase,
|
|
578
|
+
validatedAssertionConfigs,
|
|
579
|
+
caseId,
|
|
580
|
+
driverName,
|
|
581
|
+
answererName,
|
|
582
|
+
answererConfig: answererCfg,
|
|
583
|
+
driverConfigHash: valueHash(driverCfg),
|
|
584
|
+
answererConfigHash: valueHash(answererCfg),
|
|
580
585
|
validatedAssertionsHash,
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
586
|
+
graderConfigHash: graderHash,
|
|
587
|
+
evaluatorConfigHash: valueHash({
|
|
588
|
+
luxVersion: LUX_VERSION,
|
|
589
|
+
implementationHash,
|
|
590
|
+
harness: this.harness,
|
|
591
|
+
driver: { name: driverPlugin.name, create: driverPlugin.create },
|
|
592
|
+
answerer: { name: answererPlugin.name, create: answererPlugin.create },
|
|
593
|
+
assertions: assertionImplementations,
|
|
594
|
+
lifecycleFixtures,
|
|
595
|
+
validatedAssertionsHash,
|
|
596
|
+
}),
|
|
597
|
+
fixtureValues: valuesForFixtures(lifecycleFixtures),
|
|
598
|
+
driver,
|
|
599
|
+
answerer,
|
|
600
|
+
},
|
|
601
|
+
registry,
|
|
602
|
+
);
|
|
585
603
|
} finally {
|
|
586
604
|
await closeQuietly(() => driver.close(), `driver '${driverName}'`);
|
|
587
605
|
await closeQuietly(() => answerer.close(), `answerer '${answererName}'`);
|
|
@@ -628,6 +646,20 @@ export class Orchestrator {
|
|
|
628
646
|
const observer = opts.observer;
|
|
629
647
|
const requestedConcurrency = validateSuiteConcurrency(opts.concurrency);
|
|
630
648
|
const concurrency = Math.min(requestedConcurrency, Math.max(items.length, 1));
|
|
649
|
+
const authoredCases = await Promise.allSettled(items.map((item) => loadEvalCase(item.path)));
|
|
650
|
+
const localFixtures = authoredCases.map((result) =>
|
|
651
|
+
result.status === "fulfilled" ? fixturesForEvalCase(result.value) : [],
|
|
652
|
+
);
|
|
653
|
+
const lifecycleFixtures = mergeLifecycleFixtures(this.lifecycleFixtures, ...localFixtures);
|
|
654
|
+
const globalFixtureNames = new Set(this.lifecycleFixtures.map((fixture) => fixture.name));
|
|
655
|
+
const itemIndexesByFixture = new Map<string, number[]>();
|
|
656
|
+
for (const [index, fixtures] of localFixtures.entries()) {
|
|
657
|
+
for (const fixture of fixtures) {
|
|
658
|
+
const indexes = itemIndexesByFixture.get(fixture.name) ?? [];
|
|
659
|
+
indexes.push(index);
|
|
660
|
+
itemIndexesByFixture.set(fixture.name, indexes);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
631
663
|
const suite = await openSuiteRun({
|
|
632
664
|
runsRoot: opts.runsRoot,
|
|
633
665
|
concurrency,
|
|
@@ -639,7 +671,7 @@ export class Orchestrator {
|
|
|
639
671
|
...(opts.variantId ? { variantId: opts.variantId } : {}),
|
|
640
672
|
...(opts.candidateId ? { candidateId: opts.candidateId } : {}),
|
|
641
673
|
...(opts.subjectHash ? { subjectHash: opts.subjectHash } : {}),
|
|
642
|
-
fixtures:
|
|
674
|
+
fixtures: lifecycleFixtures.map(({ name, pluginVersion }) => ({
|
|
643
675
|
name,
|
|
644
676
|
pluginVersion,
|
|
645
677
|
})),
|
|
@@ -677,9 +709,16 @@ export class Orchestrator {
|
|
|
677
709
|
if (!opts.abortSignal?.aborted) {
|
|
678
710
|
try {
|
|
679
711
|
cleanupSuiteHooks = await setupLifecycleHooks(
|
|
680
|
-
|
|
712
|
+
lifecycleFixtures,
|
|
681
713
|
"suite",
|
|
682
|
-
(hook) =>
|
|
714
|
+
(hook) => {
|
|
715
|
+
if (globalFixtureNames.has(hook.name)) return hook.setupSuite?.(hookContext);
|
|
716
|
+
const indexes = itemIndexesByFixture.get(hook.name) ?? [];
|
|
717
|
+
return hook.setupSuite?.({
|
|
718
|
+
...hookContext,
|
|
719
|
+
items: indexes.flatMap((index) => hookContext.items[index] ?? []),
|
|
720
|
+
});
|
|
721
|
+
},
|
|
683
722
|
opts.abortSignal,
|
|
684
723
|
);
|
|
685
724
|
} catch (error) {
|
|
@@ -718,7 +757,7 @@ export class Orchestrator {
|
|
|
718
757
|
let cleanupCaseHooks: () => void | Promise<void>;
|
|
719
758
|
try {
|
|
720
759
|
cleanupCaseHooks = await setupLifecycleHooks(
|
|
721
|
-
this.lifecycleFixtures,
|
|
760
|
+
mergeLifecycleFixtures(this.lifecycleFixtures, localFixtures[index] ?? []),
|
|
722
761
|
"case",
|
|
723
762
|
(hook) =>
|
|
724
763
|
hook.setupCase?.({
|
|
@@ -906,11 +945,12 @@ export class Orchestrator {
|
|
|
906
945
|
|
|
907
946
|
private runWithPlugins(
|
|
908
947
|
args: Omit<RunWithPluginsArgs, "luxVersion" | "registry">,
|
|
948
|
+
registry: Registry = this.registry,
|
|
909
949
|
): Promise<RunResult> {
|
|
910
950
|
return executeRunWithPlugins({
|
|
911
951
|
...args,
|
|
912
952
|
luxVersion: LUX_VERSION,
|
|
913
|
-
registry
|
|
953
|
+
registry,
|
|
914
954
|
});
|
|
915
955
|
}
|
|
916
956
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { link, lstat, open,
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { link, lstat, open, readFile, rm, unlink } from "node:fs/promises";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
|
-
import { basename, dirname, join } from "node:path";
|
|
5
|
-
import { setTimeout as delay } from "node:timers/promises";
|
|
6
4
|
|
|
7
5
|
export type LockRecoveryClaim = {
|
|
8
6
|
path: string;
|
|
@@ -60,36 +58,54 @@ const linkedClaim = (lockPath: string, claimPath: string): LockRecoveryClaim =>
|
|
|
60
58
|
release: () => rm(claimPath, { force: true }),
|
|
61
59
|
});
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
type RecoveryMutexOwner = {
|
|
62
|
+
pid: number;
|
|
63
|
+
hostname: string;
|
|
64
|
+
lockToken: string;
|
|
65
|
+
};
|
|
64
66
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
if (claimHost !== recoveryHost || !rawPid || !id || extra !== undefined) return false;
|
|
67
|
+
const recoveryOwnerIsDead = (owner: RecoveryMutexOwner): boolean => {
|
|
68
|
+
if (owner.hostname !== hostname()) return false;
|
|
68
69
|
try {
|
|
69
|
-
process.kill(
|
|
70
|
+
process.kill(owner.pid, 0);
|
|
70
71
|
return false;
|
|
71
72
|
} catch (error) {
|
|
72
73
|
return (error as NodeJS.ErrnoException).code === "ESRCH";
|
|
73
74
|
}
|
|
74
75
|
};
|
|
75
76
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
const acquireRecoveryMutex = async (
|
|
78
|
+
lockPath: string,
|
|
79
|
+
): Promise<{ path: string; lockToken: string } | null> => {
|
|
80
|
+
const path = `${lockPath}.recovery`;
|
|
81
|
+
while (true) {
|
|
82
|
+
try {
|
|
83
|
+
const owner = await publishOwnedLock(path, {
|
|
84
|
+
pid: process.pid,
|
|
85
|
+
hostname: hostname(),
|
|
86
|
+
});
|
|
87
|
+
return { path, lockToken: owner.lockToken };
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
89
90
|
}
|
|
90
|
-
|
|
91
|
+
let owner: RecoveryMutexOwner;
|
|
92
|
+
try {
|
|
93
|
+
const value = JSON.parse(await readFile(path, "utf8")) as Partial<RecoveryMutexOwner>;
|
|
94
|
+
if (
|
|
95
|
+
!Number.isSafeInteger(value.pid) ||
|
|
96
|
+
typeof value.hostname !== "string" ||
|
|
97
|
+
typeof value.lockToken !== "string"
|
|
98
|
+
) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
owner = value as RecoveryMutexOwner;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (!recoveryOwnerIsDead(owner)) return null;
|
|
107
|
+
await releaseOwnedLock(path, owner.lockToken);
|
|
91
108
|
}
|
|
92
|
-
return false;
|
|
93
109
|
};
|
|
94
110
|
|
|
95
111
|
/**
|
|
@@ -97,24 +113,28 @@ const hasCompetingRecoveryClaim = async (lockPath: string, claimPath: string): P
|
|
|
97
113
|
* Foreign claims stay conservative because foreign lock owners do too.
|
|
98
114
|
*/
|
|
99
115
|
export const claimLockForRecovery = async (lockPath: string): Promise<LockRecoveryClaim | null> => {
|
|
100
|
-
const
|
|
116
|
+
const mutex = await acquireRecoveryMutex(lockPath);
|
|
117
|
+
if (!mutex) return null;
|
|
118
|
+
const claimPath = `${lockPath}.recovering-${mutex.lockToken}`;
|
|
101
119
|
try {
|
|
102
120
|
await link(lockPath, claimPath);
|
|
103
121
|
} catch (error) {
|
|
104
122
|
const code = (error as NodeJS.ErrnoException).code;
|
|
123
|
+
await releaseOwnedLock(mutex.path, mutex.lockToken);
|
|
105
124
|
if (code === "ENOENT") return null;
|
|
106
125
|
throw error;
|
|
107
126
|
}
|
|
108
127
|
const claim = linkedClaim(lockPath, claimPath);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
return {
|
|
129
|
+
...claim,
|
|
130
|
+
release: async () => {
|
|
131
|
+
try {
|
|
132
|
+
await claim.release();
|
|
133
|
+
} finally {
|
|
134
|
+
await releaseOwnedLock(mutex.path, mutex.lockToken);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
};
|
|
118
138
|
};
|
|
119
139
|
|
|
120
140
|
/** Remove only the canonical lock that still carries this acquisition token. */
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
readRunArtifactBytes,
|
|
34
34
|
valueHash,
|
|
35
35
|
} from "../core/index.js";
|
|
36
|
+
import { registryForEvalCase } from "./case-extensions.js";
|
|
36
37
|
import { evaluatorImplementationHash } from "./evaluator-identity.js";
|
|
37
38
|
import { parsePersistedJson, readBoundedHandle, safeReadJson } from "./fs-read.js";
|
|
38
39
|
import {
|
|
@@ -526,18 +527,18 @@ export const regradeRun = async (options: {
|
|
|
526
527
|
now?: Date;
|
|
527
528
|
}): Promise<{ path: string; snapshot: GradingSnapshot }> => {
|
|
528
529
|
const run = await loadFrozenRegradableRun(options.runDir, options.case);
|
|
530
|
+
const registry = options.case
|
|
531
|
+
? registryForEvalCase(options.registry, options.case)
|
|
532
|
+
: options.registry;
|
|
529
533
|
try {
|
|
530
534
|
const evidenceHash = await gradingEvidenceHash(run);
|
|
531
535
|
const validatedAssertionConfigs = await validateAssertionPlan(
|
|
532
536
|
run.case.assertions ?? [],
|
|
533
|
-
|
|
537
|
+
registry,
|
|
534
538
|
);
|
|
535
539
|
const validatedAssertionsHash = valueHash(validatedAssertionConfigs);
|
|
536
|
-
const assertionEvaluators = assertionEvaluatorIdentities(
|
|
537
|
-
|
|
538
|
-
options.registry,
|
|
539
|
-
);
|
|
540
|
-
const assertionPlugins = assertionPluginIdentities(run.case.assertions ?? [], options.registry);
|
|
540
|
+
const assertionEvaluators = assertionEvaluatorIdentities(run.case.assertions ?? [], registry);
|
|
541
|
+
const assertionPlugins = assertionPluginIdentities(run.case.assertions ?? [], registry);
|
|
541
542
|
const graderHash = graderConfigHash({
|
|
542
543
|
luxVersion: LUX_VERSION,
|
|
543
544
|
implementationHash: await currentEvaluatorImplementationHash(),
|
|
@@ -547,7 +548,7 @@ export const regradeRun = async (options: {
|
|
|
547
548
|
});
|
|
548
549
|
const report: GradingReport = await gradeRun(
|
|
549
550
|
run,
|
|
550
|
-
|
|
551
|
+
registry,
|
|
551
552
|
undefined,
|
|
552
553
|
options.abortSignal,
|
|
553
554
|
undefined,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type EvalCase,
|
|
14
14
|
emptyUsage,
|
|
15
15
|
type FixturesRef,
|
|
16
|
+
type FixtureValueBinding,
|
|
16
17
|
type GradingReport,
|
|
17
18
|
guardRunObserver,
|
|
18
19
|
inspectRunArtifact,
|
|
@@ -70,6 +71,7 @@ export type RunWithPluginsArgs = {
|
|
|
70
71
|
validatedAssertionsHash: string;
|
|
71
72
|
graderConfigHash: string;
|
|
72
73
|
evaluatorConfigHash: string;
|
|
74
|
+
fixtureValues: readonly FixtureValueBinding[];
|
|
73
75
|
luxVersion: string;
|
|
74
76
|
registry: Registry;
|
|
75
77
|
driver: AgentDriver;
|
|
@@ -360,6 +362,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
360
362
|
caseId: this.args.caseId,
|
|
361
363
|
prompt: evalCase.prompt,
|
|
362
364
|
fixtures: staged?.fixtures,
|
|
365
|
+
...(this.args.fixtureValues.length > 0 ? { fixtureValues: this.args.fixtureValues } : {}),
|
|
363
366
|
runsRoot: resolve(request.runsRoot),
|
|
364
367
|
runDir: this.runDir.path,
|
|
365
368
|
artifactsDir: this.runDir.artifactsDir,
|