@prismatic-io/lux 0.0.1 → 0.0.2-preview.11
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/answerers/persona/index.d.ts +2 -1
- package/lib/answerers/persona/index.d.ts.map +1 -1
- package/lib/answerers/persona/index.js +20 -5
- package/lib/answerers/persona/index.js.map +1 -1
- package/lib/answerers/terminal/index.js +1 -1
- package/lib/answerers/terminal/index.js.map +1 -1
- 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/assertions/core/helpers.d.ts +2 -7
- package/lib/assertions/core/helpers.d.ts.map +1 -1
- package/lib/assertions/core/helpers.js +2 -6
- package/lib/assertions/core/helpers.js.map +1 -1
- package/lib/assertions/core/index.d.ts.map +1 -1
- package/lib/assertions/core/index.js +2 -1
- package/lib/assertions/core/index.js.map +1 -1
- package/lib/assertions/core/resource-checks.d.ts +19 -0
- package/lib/assertions/core/resource-checks.d.ts.map +1 -1
- package/lib/assertions/core/resource-checks.js +32 -0
- package/lib/assertions/core/resource-checks.js.map +1 -1
- 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/cli/program.d.ts.map +1 -1
- package/lib/cli/program.js +12 -67
- package/lib/cli/program.js.map +1 -1
- package/lib/cli/render/reporter.d.ts.map +1 -1
- package/lib/cli/render/reporter.js +16 -3
- package/lib/cli/render/reporter.js.map +1 -1
- package/lib/cli/run-options.d.ts +1 -0
- package/lib/cli/run-options.d.ts.map +1 -1
- package/lib/cli/run-options.js.map +1 -1
- package/lib/cli/view.d.ts +13 -0
- package/lib/cli/view.d.ts.map +1 -0
- package/lib/cli/view.js +126 -0
- package/lib/cli/view.js.map +1 -0
- package/lib/core/action-events.d.ts +29 -0
- package/lib/core/action-events.d.ts.map +1 -0
- package/lib/core/action-events.js +51 -0
- package/lib/core/action-events.js.map +1 -0
- package/lib/core/agent-activity.d.ts +49 -0
- package/lib/core/agent-activity.d.ts.map +1 -0
- package/lib/core/agent-activity.js +206 -0
- package/lib/core/agent-activity.js.map +1 -0
- package/lib/core/agent-events.d.ts +26 -0
- package/lib/core/agent-events.d.ts.map +1 -0
- package/lib/core/agent-events.js +13 -0
- package/lib/core/agent-events.js.map +1 -0
- package/lib/core/artifact-evidence.d.ts +8 -0
- package/lib/core/artifact-evidence.d.ts.map +1 -1
- package/lib/core/artifact-evidence.js +36 -4
- package/lib/core/artifact-evidence.js.map +1 -1
- package/lib/core/assertion.d.ts +19 -9
- package/lib/core/assertion.d.ts.map +1 -1
- package/lib/core/assertion.js +26 -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-metrics.d.ts +98 -0
- package/lib/core/driver-metrics.d.ts.map +1 -0
- package/lib/core/driver-metrics.js +36 -0
- package/lib/core/driver-metrics.js.map +1 -0
- 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/events.d.ts +36 -0
- package/lib/core/events.d.ts.map +1 -1
- package/lib/core/events.js +12 -2
- package/lib/core/events.js.map +1 -1
- package/lib/core/experiment.d.ts +19 -0
- package/lib/core/experiment.d.ts.map +1 -1
- package/lib/core/experiment.js +2 -0
- package/lib/core/experiment.js.map +1 -1
- package/lib/core/index.d.ts +4 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +4 -0
- package/lib/core/index.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/core/run.d.ts +150 -0
- package/lib/core/run.d.ts.map +1 -1
- package/lib/core/run.js +9 -0
- package/lib/core/run.js.map +1 -1
- package/lib/core/wire.d.ts +12 -0
- package/lib/core/wire.d.ts.map +1 -1
- package/lib/core/wire.js +5 -1
- package/lib/core/wire.js.map +1 -1
- package/lib/drivers/claude-code/parse-events.d.ts.map +1 -1
- package/lib/drivers/claude-code/parse-events.js +73 -8
- package/lib/drivers/claude-code/parse-events.js.map +1 -1
- package/lib/drivers/codex/app-events.d.ts +9 -2
- package/lib/drivers/codex/app-events.d.ts.map +1 -1
- package/lib/drivers/codex/app-events.js +93 -12
- package/lib/drivers/codex/app-events.js.map +1 -1
- package/lib/drivers/codex/index.d.ts.map +1 -1
- package/lib/drivers/codex/index.js +10 -14
- package/lib/drivers/codex/index.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 +10 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +10 -6
- 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/compare.d.ts +13 -1
- package/lib/orchestrator/compare.d.ts.map +1 -1
- package/lib/orchestrator/compare.js +12 -0
- package/lib/orchestrator/compare.js.map +1 -1
- package/lib/orchestrator/config.d.ts.map +1 -1
- package/lib/orchestrator/config.js +0 -2
- package/lib/orchestrator/config.js.map +1 -1
- 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/experiment-contracts.d.ts +19 -0
- package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
- package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -1
- package/lib/orchestrator/experiment-evaluation.js +17 -0
- package/lib/orchestrator/experiment-evaluation.js.map +1 -1
- package/lib/orchestrator/experiment-report.d.ts +19 -0
- package/lib/orchestrator/experiment-report.d.ts.map +1 -1
- package/lib/orchestrator/experiment-runs.d.ts +38 -0
- package/lib/orchestrator/experiment-runs.d.ts.map +1 -1
- package/lib/orchestrator/grader-experiment.d.ts +12 -0
- package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
- package/lib/orchestrator/grading.d.ts +2 -1
- package/lib/orchestrator/grading.d.ts.map +1 -1
- package/lib/orchestrator/grading.js +16 -4
- package/lib/orchestrator/grading.js.map +1 -1
- package/lib/orchestrator/index.d.ts +1 -1
- package/lib/orchestrator/index.d.ts.map +1 -1
- package/lib/orchestrator/index.js +1 -1
- package/lib/orchestrator/index.js.map +1 -1
- package/lib/orchestrator/list-runs.d.ts +7 -1
- package/lib/orchestrator/list-runs.d.ts.map +1 -1
- package/lib/orchestrator/list-runs.js +50 -6
- package/lib/orchestrator/list-runs.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 +3 -0
- package/lib/orchestrator/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator/orchestrator.js +42 -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 +6 -0
- 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 +20 -5
- package/lib/orchestrator/run-execution.js.map +1 -1
- package/lib/orchestrator/suite-compare.d.ts +34 -0
- package/lib/orchestrator/suite-compare.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.d.ts +68 -0
- package/lib/orchestrator/suite-summary.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.js +45 -7
- package/lib/orchestrator/suite-summary.js.map +1 -1
- package/lib/previewer/artifacts.d.ts +25 -0
- package/lib/previewer/artifacts.d.ts.map +1 -0
- package/lib/previewer/artifacts.js +180 -0
- package/lib/previewer/artifacts.js.map +1 -0
- package/lib/previewer/server.d.ts +105 -0
- package/lib/previewer/server.d.ts.map +1 -0
- package/lib/previewer/server.js +542 -0
- package/lib/previewer/server.js.map +1 -0
- package/package.json +2 -2
- package/skills/lux/SKILL.md +7 -3
- package/skills/lux/references/cli.md +25 -2
- package/skills/lux/references/eval-authoring.md +158 -4
- package/skills/lux-answerer/SKILL.md +1 -1
- package/src/answerers/persona/index.ts +21 -4
- package/src/answerers/terminal/index.ts +1 -1
- package/src/assertions/authoring.ts +81 -0
- package/src/assertions/core/helpers.ts +3 -6
- package/src/assertions/core/index.ts +2 -0
- package/src/assertions/core/resource-checks.ts +35 -0
- package/src/authoring.ts +1 -0
- package/src/cli/program.ts +14 -93
- package/src/cli/render/reporter.ts +18 -3
- package/src/cli/run-options.ts +1 -0
- package/src/cli/view.ts +168 -0
- package/src/core/action-events.ts +59 -0
- package/src/core/agent-activity.ts +236 -0
- package/src/core/agent-events.ts +17 -0
- package/src/core/artifact-evidence.ts +47 -4
- package/src/core/assertion.ts +41 -22
- package/src/core/case-authoring-state.ts +65 -0
- package/src/core/case.ts +57 -6
- package/src/core/driver-metrics.ts +51 -0
- package/src/core/driver.ts +3 -0
- package/src/core/events.ts +14 -2
- package/src/core/experiment.ts +2 -0
- package/src/core/index.ts +4 -0
- package/src/core/lifecycle-fixtures.ts +7 -2
- package/src/core/lifecycle-hooks.ts +16 -2
- package/src/core/run.ts +9 -0
- package/src/core/wire.ts +5 -1
- package/src/drivers/claude-code/parse-events.ts +98 -12
- package/src/drivers/codex/app-events.ts +116 -16
- package/src/drivers/codex/index.ts +16 -14
- package/src/drivers/subprocess/index.ts +6 -0
- package/src/index.ts +50 -6
- package/src/orchestrator/annotation-store.ts +5 -1
- package/src/orchestrator/case-extensions.ts +44 -0
- package/src/orchestrator/compare.ts +18 -0
- package/src/orchestrator/config.ts +0 -2
- package/src/orchestrator/doctor.ts +5 -3
- package/src/orchestrator/experiment-evaluation.ts +23 -0
- package/src/orchestrator/grading.ts +21 -2
- package/src/orchestrator/index.ts +1 -0
- package/src/orchestrator/list-runs.ts +66 -5
- package/src/orchestrator/loader.ts +2 -0
- package/src/orchestrator/orchestrator.ts +86 -40
- package/src/orchestrator/owned-lock.ts +54 -34
- package/src/orchestrator/regrade.ts +8 -7
- package/src/orchestrator/run-execution.ts +24 -3
- package/src/orchestrator/suite-summary.ts +101 -8
- package/src/previewer/artifacts.ts +221 -0
- package/src/previewer/server.ts +765 -0
- package/viewer/app.css +2075 -0
- package/viewer/app.js +606 -0
- package/viewer/artifact-view.js +48 -0
- package/viewer/charts.js +86 -0
- package/viewer/detail.js +1214 -0
- package/viewer/format.js +129 -0
- package/viewer/index.html +151 -0
- package/lib/answerers/scripted/index.d.ts +0 -8
- package/lib/answerers/scripted/index.d.ts.map +0 -1
- package/lib/answerers/scripted/index.js +0 -163
- package/lib/answerers/scripted/index.js.map +0 -1
- package/src/answerers/scripted/index.ts +0 -186
|
@@ -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,
|
|
@@ -122,6 +128,8 @@ export type RunRequest = {
|
|
|
122
128
|
/** Root that relative `fixtures` dir paths resolve against. */
|
|
123
129
|
fixturesRoot?: string | undefined;
|
|
124
130
|
answererOverride?: string | undefined;
|
|
131
|
+
/** Skip built-in rubric judge assertions without changing subject execution. */
|
|
132
|
+
skipJudge?: boolean | undefined;
|
|
125
133
|
startedAt?: Date | undefined;
|
|
126
134
|
/** The `runSuite` invocation this case attempt belongs to. */
|
|
127
135
|
suiteId?: string | undefined;
|
|
@@ -476,11 +484,6 @@ export class Orchestrator {
|
|
|
476
484
|
}
|
|
477
485
|
|
|
478
486
|
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
487
|
if (!request.case && !request.casePath) {
|
|
485
488
|
throw new Error("RunRequest requires either 'case' or 'casePath'");
|
|
486
489
|
}
|
|
@@ -489,11 +492,24 @@ export class Orchestrator {
|
|
|
489
492
|
}
|
|
490
493
|
const casePath = request.casePath ? resolve(request.casePath) : INLINE_CASE_PATH;
|
|
491
494
|
const authoredCase = request.case ?? (await loadEvalCase(casePath));
|
|
495
|
+
const lifecycleFixtures = mergeLifecycleFixtures(
|
|
496
|
+
this.lifecycleFixtures,
|
|
497
|
+
fixturesForEvalCase(authoredCase),
|
|
498
|
+
);
|
|
499
|
+
if (
|
|
500
|
+
lifecycleFixtures.some(({ setupSuite, setupCase }) => setupSuite || setupCase) &&
|
|
501
|
+
!request.suiteId
|
|
502
|
+
) {
|
|
503
|
+
throw new Error(
|
|
504
|
+
"Orchestrator.run() cannot execute lifecycle fixtures; use runSuite() for a single case",
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
const registry = registryForEvalCase(this.registry, authoredCase);
|
|
492
508
|
const evalCase = bindSubjectRoot(authoredCase, request.subjectRoot);
|
|
493
509
|
const caseId = resolveCaseId(casePath, evalCase, request.caseId);
|
|
494
510
|
const validatedAssertionConfigs = await validateAssertionPlan(
|
|
495
511
|
evalCase.assertions ?? [],
|
|
496
|
-
|
|
512
|
+
registry,
|
|
497
513
|
);
|
|
498
514
|
|
|
499
515
|
const driverRef = resolveDriverReference(evalCase, this.defaultDriver);
|
|
@@ -506,11 +522,11 @@ export class Orchestrator {
|
|
|
506
522
|
);
|
|
507
523
|
const answererName = answererNameOf(answererRef);
|
|
508
524
|
|
|
509
|
-
const driverPlugin =
|
|
525
|
+
const driverPlugin = registry.getDriver(driverName);
|
|
510
526
|
if (!driverPlugin) {
|
|
511
527
|
throw new Error(`no driver registered for '${driverName}'`);
|
|
512
528
|
}
|
|
513
|
-
const answererPlugin =
|
|
529
|
+
const answererPlugin = registry.getAnswerer(answererName);
|
|
514
530
|
if (!answererPlugin) {
|
|
515
531
|
throw new Error(`no answerer registered for '${answererName}'`);
|
|
516
532
|
}
|
|
@@ -544,7 +560,7 @@ export class Orchestrator {
|
|
|
544
560
|
try {
|
|
545
561
|
const assertionImplementations = assertionEvaluatorIdentities(
|
|
546
562
|
authoredCase.assertions ?? [],
|
|
547
|
-
|
|
563
|
+
registry,
|
|
548
564
|
);
|
|
549
565
|
const validatedAssertionsHash = valueHash(validatedAssertionConfigs);
|
|
550
566
|
const implementationHash = await this.evaluatorImplementationIdentity();
|
|
@@ -554,34 +570,40 @@ export class Orchestrator {
|
|
|
554
570
|
assertions: assertionImplementations,
|
|
555
571
|
validatedAssertionsHash,
|
|
556
572
|
graderContext: this.harness,
|
|
573
|
+
skipJudge: request.skipJudge ?? false,
|
|
557
574
|
});
|
|
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,
|
|
575
|
+
return await this.runWithPlugins(
|
|
576
|
+
{
|
|
577
|
+
request,
|
|
578
|
+
casePath,
|
|
579
|
+
authoredCase,
|
|
580
|
+
evalCase,
|
|
581
|
+
validatedAssertionConfigs,
|
|
582
|
+
caseId,
|
|
583
|
+
driverName,
|
|
584
|
+
answererName,
|
|
585
|
+
answererConfig: answererCfg,
|
|
586
|
+
driverConfigHash: valueHash(driverCfg),
|
|
587
|
+
answererConfigHash: valueHash(answererCfg),
|
|
580
588
|
validatedAssertionsHash,
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
589
|
+
graderConfigHash: graderHash,
|
|
590
|
+
evaluatorConfigHash: valueHash({
|
|
591
|
+
luxVersion: LUX_VERSION,
|
|
592
|
+
implementationHash,
|
|
593
|
+
harness: this.harness,
|
|
594
|
+
driver: { name: driverPlugin.name, create: driverPlugin.create },
|
|
595
|
+
answerer: { name: answererPlugin.name, create: answererPlugin.create },
|
|
596
|
+
assertions: assertionImplementations,
|
|
597
|
+
lifecycleFixtures,
|
|
598
|
+
validatedAssertionsHash,
|
|
599
|
+
skipJudge: request.skipJudge ?? false,
|
|
600
|
+
}),
|
|
601
|
+
fixtureValues: valuesForFixtures(lifecycleFixtures),
|
|
602
|
+
driver,
|
|
603
|
+
answerer,
|
|
604
|
+
},
|
|
605
|
+
registry,
|
|
606
|
+
);
|
|
585
607
|
} finally {
|
|
586
608
|
await closeQuietly(() => driver.close(), `driver '${driverName}'`);
|
|
587
609
|
await closeQuietly(() => answerer.close(), `answerer '${answererName}'`);
|
|
@@ -599,6 +621,7 @@ export class Orchestrator {
|
|
|
599
621
|
runsRoot: string;
|
|
600
622
|
fixturesRoot?: string | undefined;
|
|
601
623
|
answererOverride?: string | undefined;
|
|
624
|
+
skipJudge?: boolean | undefined;
|
|
602
625
|
observer?: SuiteObserver | undefined;
|
|
603
626
|
/** Aborts the cases in flight and stops the pool pulling new ones. */
|
|
604
627
|
abortSignal?: AbortSignal | undefined;
|
|
@@ -628,6 +651,20 @@ export class Orchestrator {
|
|
|
628
651
|
const observer = opts.observer;
|
|
629
652
|
const requestedConcurrency = validateSuiteConcurrency(opts.concurrency);
|
|
630
653
|
const concurrency = Math.min(requestedConcurrency, Math.max(items.length, 1));
|
|
654
|
+
const authoredCases = await Promise.allSettled(items.map((item) => loadEvalCase(item.path)));
|
|
655
|
+
const localFixtures = authoredCases.map((result) =>
|
|
656
|
+
result.status === "fulfilled" ? fixturesForEvalCase(result.value) : [],
|
|
657
|
+
);
|
|
658
|
+
const lifecycleFixtures = mergeLifecycleFixtures(this.lifecycleFixtures, ...localFixtures);
|
|
659
|
+
const globalFixtureNames = new Set(this.lifecycleFixtures.map((fixture) => fixture.name));
|
|
660
|
+
const itemIndexesByFixture = new Map<string, number[]>();
|
|
661
|
+
for (const [index, fixtures] of localFixtures.entries()) {
|
|
662
|
+
for (const fixture of fixtures) {
|
|
663
|
+
const indexes = itemIndexesByFixture.get(fixture.name) ?? [];
|
|
664
|
+
indexes.push(index);
|
|
665
|
+
itemIndexesByFixture.set(fixture.name, indexes);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
631
668
|
const suite = await openSuiteRun({
|
|
632
669
|
runsRoot: opts.runsRoot,
|
|
633
670
|
concurrency,
|
|
@@ -639,7 +676,7 @@ export class Orchestrator {
|
|
|
639
676
|
...(opts.variantId ? { variantId: opts.variantId } : {}),
|
|
640
677
|
...(opts.candidateId ? { candidateId: opts.candidateId } : {}),
|
|
641
678
|
...(opts.subjectHash ? { subjectHash: opts.subjectHash } : {}),
|
|
642
|
-
fixtures:
|
|
679
|
+
fixtures: lifecycleFixtures.map(({ name, pluginVersion }) => ({
|
|
643
680
|
name,
|
|
644
681
|
pluginVersion,
|
|
645
682
|
})),
|
|
@@ -677,9 +714,16 @@ export class Orchestrator {
|
|
|
677
714
|
if (!opts.abortSignal?.aborted) {
|
|
678
715
|
try {
|
|
679
716
|
cleanupSuiteHooks = await setupLifecycleHooks(
|
|
680
|
-
|
|
717
|
+
lifecycleFixtures,
|
|
681
718
|
"suite",
|
|
682
|
-
(hook) =>
|
|
719
|
+
(hook) => {
|
|
720
|
+
if (globalFixtureNames.has(hook.name)) return hook.setupSuite?.(hookContext);
|
|
721
|
+
const indexes = itemIndexesByFixture.get(hook.name) ?? [];
|
|
722
|
+
return hook.setupSuite?.({
|
|
723
|
+
...hookContext,
|
|
724
|
+
items: indexes.flatMap((index) => hookContext.items[index] ?? []),
|
|
725
|
+
});
|
|
726
|
+
},
|
|
683
727
|
opts.abortSignal,
|
|
684
728
|
);
|
|
685
729
|
} catch (error) {
|
|
@@ -718,7 +762,7 @@ export class Orchestrator {
|
|
|
718
762
|
let cleanupCaseHooks: () => void | Promise<void>;
|
|
719
763
|
try {
|
|
720
764
|
cleanupCaseHooks = await setupLifecycleHooks(
|
|
721
|
-
this.lifecycleFixtures,
|
|
765
|
+
mergeLifecycleFixtures(this.lifecycleFixtures, localFixtures[index] ?? []),
|
|
722
766
|
"case",
|
|
723
767
|
(hook) =>
|
|
724
768
|
hook.setupCase?.({
|
|
@@ -749,6 +793,7 @@ export class Orchestrator {
|
|
|
749
793
|
runsRoot: opts.runsRoot,
|
|
750
794
|
fixturesRoot: opts.fixturesRoot,
|
|
751
795
|
answererOverride: opts.answererOverride,
|
|
796
|
+
skipJudge: opts.skipJudge,
|
|
752
797
|
observer: handle ?? observer,
|
|
753
798
|
abortSignal: opts.abortSignal,
|
|
754
799
|
suiteId: suite.manifest.suiteId,
|
|
@@ -906,11 +951,12 @@ export class Orchestrator {
|
|
|
906
951
|
|
|
907
952
|
private runWithPlugins(
|
|
908
953
|
args: Omit<RunWithPluginsArgs, "luxVersion" | "registry">,
|
|
954
|
+
registry: Registry = this.registry,
|
|
909
955
|
): Promise<RunResult> {
|
|
910
956
|
return executeRunWithPlugins({
|
|
911
957
|
...args,
|
|
912
958
|
luxVersion: LUX_VERSION,
|
|
913
|
-
registry
|
|
959
|
+
registry,
|
|
914
960
|
});
|
|
915
961
|
}
|
|
916
962
|
}
|
|
@@ -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,
|
|
@@ -10,13 +10,16 @@ import {
|
|
|
10
10
|
AnswererModelCallAccountingSchema,
|
|
11
11
|
type AnswerHistoryEntry,
|
|
12
12
|
type Artifact,
|
|
13
|
+
actionTiming,
|
|
13
14
|
type EvalCase,
|
|
14
15
|
emptyUsage,
|
|
15
16
|
type FixturesRef,
|
|
17
|
+
type FixtureValueBinding,
|
|
16
18
|
type GradingReport,
|
|
17
19
|
guardRunObserver,
|
|
18
20
|
inspectRunArtifact,
|
|
19
21
|
type LifecycleStage,
|
|
22
|
+
normalizeDriverMetrics,
|
|
20
23
|
type OperationalFailure,
|
|
21
24
|
type PhaseDurations,
|
|
22
25
|
type ReadyState,
|
|
@@ -27,6 +30,7 @@ import {
|
|
|
27
30
|
type RunMetadata,
|
|
28
31
|
type RunProvenance,
|
|
29
32
|
type RunUsage,
|
|
33
|
+
summarizeAgentActivity,
|
|
30
34
|
summarizeToolOutcomes,
|
|
31
35
|
sumUsage,
|
|
32
36
|
type TokenUsage,
|
|
@@ -70,6 +74,7 @@ export type RunWithPluginsArgs = {
|
|
|
70
74
|
validatedAssertionsHash: string;
|
|
71
75
|
graderConfigHash: string;
|
|
72
76
|
evaluatorConfigHash: string;
|
|
77
|
+
fixtureValues: readonly FixtureValueBinding[];
|
|
73
78
|
luxVersion: string;
|
|
74
79
|
registry: Registry;
|
|
75
80
|
driver: AgentDriver;
|
|
@@ -181,6 +186,11 @@ const modelAccountingFromSummary = (
|
|
|
181
186
|
): { modelCalls: number; unpricedModelCalls?: number | undefined } | null =>
|
|
182
187
|
SummaryModelAccountingSchema.safeParse(summary).data ?? null;
|
|
183
188
|
|
|
189
|
+
const metricsFromSummary = (summary: unknown) => {
|
|
190
|
+
if (!summary || typeof summary !== "object") return {};
|
|
191
|
+
return normalizeDriverMetrics((summary as { metrics?: unknown }).metrics);
|
|
192
|
+
};
|
|
193
|
+
|
|
184
194
|
const artifactMeasurements = async (
|
|
185
195
|
artifacts: readonly Artifact[],
|
|
186
196
|
runDir: string,
|
|
@@ -360,6 +370,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
360
370
|
caseId: this.args.caseId,
|
|
361
371
|
prompt: evalCase.prompt,
|
|
362
372
|
fixtures: staged?.fixtures,
|
|
373
|
+
...(this.args.fixtureValues.length > 0 ? { fixtureValues: this.args.fixtureValues } : {}),
|
|
363
374
|
runsRoot: resolve(request.runsRoot),
|
|
364
375
|
runDir: this.runDir.path,
|
|
365
376
|
artifactsDir: this.runDir.artifactsDir,
|
|
@@ -387,6 +398,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
387
398
|
metadata.exitReason = "error";
|
|
388
399
|
metadata.operationalFailure = { phase: operationalPhase, reason };
|
|
389
400
|
metadata.phaseDurationMs = phaseDurations(this.runDir.recorded);
|
|
401
|
+
metadata.actionTiming = actionTiming(this.runDir.recorded);
|
|
390
402
|
await this.runDir.writeJson("run.json", metadata);
|
|
391
403
|
await this.args.request.recordPreGradingAccounting?.(metadata);
|
|
392
404
|
await this.args.request.recordModelAttemptsComplete?.(this.subjectAttemptIds);
|
|
@@ -404,6 +416,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
404
416
|
this.lifecycle("collect-end");
|
|
405
417
|
await this.runDir.events.drain();
|
|
406
418
|
const completed = await this.completedAgentRun();
|
|
419
|
+
completed.metadata.actionTiming = actionTiming(this.runDir.recorded);
|
|
407
420
|
this.metadata = completed.metadata;
|
|
408
421
|
this.usage = completed.usage;
|
|
409
422
|
this.run = completed.run;
|
|
@@ -427,6 +440,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
427
440
|
grading.assertions.flatMap((assertion) => (assertion.usage ? [assertion.usage] : [])),
|
|
428
441
|
);
|
|
429
442
|
metadata.phaseDurationMs = phaseDurations(this.runDir.recorded);
|
|
443
|
+
metadata.actionTiming = actionTiming(this.runDir.recorded);
|
|
430
444
|
await this.runDir.events.drain();
|
|
431
445
|
await this.runDir.writeJson("run.json", metadata);
|
|
432
446
|
await this.runDir.writeJson("artifacts.json", this.collection.artifacts);
|
|
@@ -503,6 +517,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
503
517
|
private fallbackFailureMetadata(): RunMetadata {
|
|
504
518
|
const failedAt = new Date();
|
|
505
519
|
const answererAccounting = readAnswererModelAccounting(this.args.answerer);
|
|
520
|
+
const ready = this.ready ?? { agentId: "start-failed" };
|
|
506
521
|
return {
|
|
507
522
|
schemaVersion: 1,
|
|
508
523
|
luxVersion: this.args.luxVersion,
|
|
@@ -511,7 +526,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
511
526
|
agentEndedAt: failedAt.toISOString(),
|
|
512
527
|
endedAt: failedAt.toISOString(),
|
|
513
528
|
exitReason: "error",
|
|
514
|
-
driver: { name: this.args.driverName, ...
|
|
529
|
+
driver: { name: this.args.driverName, ...ready },
|
|
515
530
|
answerer: answererMetadata(this.args.answererName, this.args.answererConfig),
|
|
516
531
|
usage: {
|
|
517
532
|
agent: emptyUsage(),
|
|
@@ -527,6 +542,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
527
542
|
interruptCounts: this.interruptCounts,
|
|
528
543
|
toolCallCount: this.toolCallCount,
|
|
529
544
|
toolOutcomes: summarizeToolOutcomes(this.runDir.recorded),
|
|
545
|
+
agentActivity: summarizeAgentActivity(this.runDir.recorded, ready),
|
|
530
546
|
artifacts: { count: this.collection.artifacts.length },
|
|
531
547
|
provenance: this.runProvenance(),
|
|
532
548
|
};
|
|
@@ -557,9 +573,10 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
557
573
|
const outcome = this.require(this.loopOutcome, "driver outcome");
|
|
558
574
|
const agentEndedAt = new Date();
|
|
559
575
|
const exitReason = agentExitReason(outcome, this.collection.error);
|
|
560
|
-
const summary = outcome.kind === "done" ? outcome.done.summary :
|
|
576
|
+
const summary = outcome.kind === "done" ? outcome.done.summary : outcome.error.summary;
|
|
561
577
|
const driverCost = costFromSummary(summary);
|
|
562
578
|
const reportedModelAccounting = modelAccountingFromSummary(summary);
|
|
579
|
+
const driverMetrics = metricsFromSummary(summary);
|
|
563
580
|
const answererAccounting = readAnswererModelAccounting(this.args.answerer);
|
|
564
581
|
const agentCalls = this.ready ? (reportedModelAccounting?.modelCalls ?? 1) : 0;
|
|
565
582
|
const operationalFailure = loopFailure(outcome, this.collection.error);
|
|
@@ -568,6 +585,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
568
585
|
answerer: readAnswererUsage(this.args.answerer),
|
|
569
586
|
grading: emptyUsage(),
|
|
570
587
|
};
|
|
588
|
+
const ready = this.ready ?? { agentId: "not-started" };
|
|
571
589
|
const metadata: RunMetadata = {
|
|
572
590
|
schemaVersion: 1,
|
|
573
591
|
luxVersion: this.args.luxVersion,
|
|
@@ -576,7 +594,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
576
594
|
agentEndedAt: agentEndedAt.toISOString(),
|
|
577
595
|
endedAt: agentEndedAt.toISOString(),
|
|
578
596
|
exitReason,
|
|
579
|
-
driver: { name: this.args.driverName, ...
|
|
597
|
+
driver: { name: this.args.driverName, ...ready },
|
|
580
598
|
answerer: answererMetadata(this.args.answererName, this.args.answererConfig),
|
|
581
599
|
usage,
|
|
582
600
|
modelAccounting: {
|
|
@@ -590,10 +608,12 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
590
608
|
interruptCounts: this.interruptCounts,
|
|
591
609
|
toolCallCount: this.toolCallCount,
|
|
592
610
|
toolOutcomes: summarizeToolOutcomes(this.runDir.recorded),
|
|
611
|
+
agentActivity: summarizeAgentActivity(this.runDir.recorded, ready),
|
|
593
612
|
artifacts: await artifactMeasurements(this.collection.artifacts, this.runDir.path),
|
|
594
613
|
...(operationalFailure ? { operationalFailure } : {}),
|
|
595
614
|
provenance: this.runProvenance(),
|
|
596
615
|
...(driverCost !== undefined ? { cost: { usd: driverCost } } : {}),
|
|
616
|
+
...(Object.keys(driverMetrics).length > 0 ? { driverMetrics } : {}),
|
|
597
617
|
};
|
|
598
618
|
return {
|
|
599
619
|
metadata,
|
|
@@ -654,6 +674,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
|
|
|
654
674
|
request.recordGradingAssertion,
|
|
655
675
|
modelAttempts,
|
|
656
676
|
this.args.validatedAssertionConfigs,
|
|
677
|
+
request.skipJudge ?? false,
|
|
657
678
|
);
|
|
658
679
|
}
|
|
659
680
|
|