@dailephd/my-dev-kit-lab 0.6.2 → 0.7.0
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/CHANGELOG.md +26 -0
- package/README.md +11 -8
- package/benchmarks/contracts/context-window-scaling-cases.json +220 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/README.md +14 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-1/carrier-depot-catalog-01.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-1/course-module-catalog-06.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-1/library-shelf-catalog-07.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-1/sensor-station-catalog-05.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-1/staff-rota-catalog-03.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-1/supplier-contract-catalog-02.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-1/vehicle-route-catalog-04.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/archive-box-catalog-12.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/clinic-ward-catalog-08.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/greenhouse-bay-catalog-13.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/harbor-berth-catalog-10.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/kitchen-section-catalog-15.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/market-stall-catalog-09.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/station-platform-catalog-14.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-2/workshop-bench-catalog-11.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/archive-box-catalog-28.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/carrier-depot-catalog-17.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/clinic-ward-catalog-24.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/course-module-catalog-22.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/greenhouse-bay-catalog-29.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/harbor-berth-catalog-26.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/kitchen-section-catalog-31.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/library-shelf-catalog-23.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/market-stall-catalog-25.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/sensor-station-catalog-21.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/staff-rota-catalog-19.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/station-platform-catalog-30.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/supplier-contract-catalog-18.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/vehicle-route-catalog-20.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/warehouse-zone-catalog-16.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-3/workshop-bench-catalog-27.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/archive-box-catalog-44.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/carrier-depot-catalog-33.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/carrier-depot-catalog-49.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/clinic-ward-catalog-40.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/clinic-ward-catalog-56.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/course-module-catalog-38.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/course-module-catalog-54.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/greenhouse-bay-catalog-45.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/harbor-berth-catalog-42.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/harbor-berth-catalog-58.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/kitchen-section-catalog-47.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/library-shelf-catalog-39.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/library-shelf-catalog-55.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/market-stall-catalog-41.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/market-stall-catalog-57.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/sensor-station-catalog-37.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/sensor-station-catalog-53.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/staff-rota-catalog-35.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/staff-rota-catalog-51.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/station-platform-catalog-46.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/supplier-contract-catalog-34.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/supplier-contract-catalog-50.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/vehicle-route-catalog-36.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/vehicle-route-catalog-52.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/warehouse-zone-catalog-32.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/warehouse-zone-catalog-48.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/filler/tier-4/workshop-bench-catalog-43.ts +302 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/tasks/auditRedaction.ts +25 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/tasks/inventoryReservation.ts +26 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/tasks/invoiceTotals.ts +25 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/src/tasks/shippingQuote.ts +32 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/tests/tasks/auditRedaction.test.ts +17 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/tests/tasks/inventoryReservation.test.ts +19 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/tests/tasks/invoiceTotals.test.ts +14 -0
- package/benchmarks/projects/context-window-scaling-fixed-ts/tests/tasks/shippingQuote.test.ts +19 -0
- package/dist/src/cli/help.js +27 -10
- package/dist/src/commands/runExperimentDescribeCommand.js +5 -2
- package/dist/src/commands/runExperimentRunCommand.js +64 -8
- package/dist/src/evaluation/benchmarkMetadata.js +5 -1
- package/dist/src/evaluation/parseAgentAnswer.js +172 -24
- package/dist/src/evaluation/runMyDevKitRetrieval.js +120 -7
- package/dist/src/experiments/defaultRegistry.js +2 -0
- package/dist/src/experiments/plugins/contextWindowScaling/cliBudgets.js +31 -0
- package/dist/src/experiments/plugins/contextWindowScaling/config.js +84 -0
- package/dist/src/experiments/plugins/contextWindowScaling/contextBudget.js +43 -0
- package/dist/src/experiments/plugins/contextWindowScaling/evaluation.js +36 -0
- package/dist/src/experiments/plugins/contextWindowScaling/execution.js +187 -0
- package/dist/src/experiments/plugins/contextWindowScaling/executionArtifact.js +15 -0
- package/dist/src/experiments/plugins/contextWindowScaling/executionArtifactReader.js +186 -0
- package/dist/src/experiments/plugins/contextWindowScaling/index.js +14 -0
- package/dist/src/experiments/plugins/contextWindowScaling/metadata.js +12 -0
- package/dist/src/experiments/plugins/contextWindowScaling/metrics.js +149 -0
- package/dist/src/experiments/plugins/contextWindowScaling/plugin.js +185 -0
- package/dist/src/experiments/plugins/contextWindowScaling/projectProfile.js +51 -0
- package/dist/src/experiments/plugins/contextWindowScaling/relevantFiles.js +76 -0
- package/dist/src/experiments/plugins/contextWindowScaling/successEvidence.js +60 -0
- package/dist/src/experiments/plugins/contextWindowScaling/types.js +1 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/comparisonV2.js +49 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/config.js +5 -4
- package/dist/src/experiments/plugins/incrementalChangeStaleness/disposableTarget.js +20 -2
- package/dist/src/experiments/plugins/incrementalChangeStaleness/execution.js +42 -16
- package/dist/src/experiments/plugins/incrementalChangeStaleness/executionArtifact.js +4 -4
- package/dist/src/experiments/plugins/incrementalChangeStaleness/executionArtifactV2.js +289 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/executionV2.js +195 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/indexClone.js +76 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecycle.js +64 -32
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecyclePolicy.js +6 -4
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecyclePolicyV2.js +78 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecycleV2.js +298 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/plugin.js +213 -12
- package/dist/src/experiments/plugins/incrementalChangeStaleness/treatmentSessionV2.js +5 -0
- package/dist/src/experiments/plugins/index.js +1 -0
- package/dist/src/experiments/plugins/warmIndexReuse/agentEvaluation.js +1 -1
- package/dist/src/experiments/plugins/warmIndexReuse/warmIndexSession.js +3 -1
- package/dist/src/mobile/android/advancedSecurity/candidateEvidence.js +9 -2
- package/dist/src/mobile/android/advancedSecurity/redaction.js +19 -10
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/analyzeSecretCandidates.js +12 -2
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/analyzeSigningConfiguration.js +1 -1
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/extractSigningConfigurations.js +7 -7
- package/dist/src/plots/buildContextWindowScalingPlotData.js +96 -0
- package/dist/src/plots/index.js +1 -0
- package/dist/src/plots/writePlotArtifacts.js +12 -3
- package/dist/src/report/experiments/buildContextWindowScalingReport.js +84 -0
- package/dist/src/report/experiments/buildIncrementalChangeStalenessPluginReport.js +33 -0
- package/dist/src/report/experiments/buildIncrementalChangeStalenessReport.js +1 -1
- package/dist/src/report/experiments/buildIncrementalChangeStalenessReportV2.js +80 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +24 -4
- package/dist/src/report/experiments/contextWindowScalingReportModel.js +3 -0
- package/dist/src/report/experiments/incrementalChangeStalenessReportModelV2.js +3 -0
- package/dist/src/report/experiments/incrementalChangeStalenessReportRowsV2.js +146 -0
- package/dist/src/report/experiments/index.js +7 -0
- package/dist/src/report/experiments/renderContextWindowScalingHtml.js +99 -0
- package/dist/src/report/experiments/renderContextWindowScalingText.js +108 -0
- package/dist/src/report/experiments/renderIncrementalChangeStalenessHtml.js +4 -0
- package/dist/src/report/experiments/renderIncrementalChangeStalenessHtmlV2.js +111 -0
- package/dist/src/report/experiments/renderIncrementalChangeStalenessTextV2.js +96 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +14 -0
- package/dist/src/securityValidation/staticScans/codeql.js +8 -8
- package/docs/METRICS.md +58 -0
- package/examples/tutorial-browser/prepare.mjs +13 -13
- package/examples/tutorial-browser/server.mjs +13 -13
- package/package.json +1 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { CONTEXT_WINDOW_SCALING_TREATMENT_IDS } from "./metadata.js";
|
|
2
|
+
export function aggregateContextWindowScaling(args) {
|
|
3
|
+
const budgets = [...args.contextBudgets].sort((a, b) => a - b);
|
|
4
|
+
const treatments = [...CONTEXT_WINDOW_SCALING_TREATMENT_IDS];
|
|
5
|
+
const treatmentsOf = (variantId) => args.cases.map((caseEvidence) => ({ caseEvidence, treatment: requireTreatment(caseEvidence, variantId, budgets) }));
|
|
6
|
+
const budgetTreatmentSummaries = [];
|
|
7
|
+
for (const contextBudgetTokens of budgets) {
|
|
8
|
+
for (const variantId of treatments) {
|
|
9
|
+
const cells = treatmentsOf(variantId).map(({ treatment }) => cellFor(treatment, contextBudgetTokens));
|
|
10
|
+
budgetTreatmentSummaries.push(summarizeCells(contextBudgetTokens, variantId, cells));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const caseTreatmentContextSummaries = args.cases.flatMap((caseEvidence) => treatments.map((variantId) => {
|
|
14
|
+
const treatment = requireTreatment(caseEvidence, variantId, budgets);
|
|
15
|
+
return {
|
|
16
|
+
caseId: caseEvidence.caseId,
|
|
17
|
+
caseName: caseEvidence.caseName,
|
|
18
|
+
variantId,
|
|
19
|
+
contextStatus: treatment.context.status,
|
|
20
|
+
characterCount: treatment.context.characterCount,
|
|
21
|
+
estimatedTokens: treatment.context.estimatedTokens,
|
|
22
|
+
tokenCountMethod: treatment.context.tokenCountMethod,
|
|
23
|
+
observedFileCount: treatment.context.observedFiles === null ? null : treatment.context.observedFiles.length,
|
|
24
|
+
relevantFileEvidence: structuredClone(treatment.relevantFileEvidence),
|
|
25
|
+
};
|
|
26
|
+
}));
|
|
27
|
+
const allCells = budgetTreatmentSummaries;
|
|
28
|
+
const relevant = caseTreatmentContextSummaries.map((summary) => summary.relevantFileEvidence);
|
|
29
|
+
const relevantCount = (status) => relevant.filter((evidence) => evidence.status === status).length;
|
|
30
|
+
const relevantFileSummary = {
|
|
31
|
+
relevantFileEvidenceAvailableCount: relevantCount("available"),
|
|
32
|
+
relevantFileEvidenceUnavailableCount: relevantCount("unavailable"),
|
|
33
|
+
relevantFileEvidenceNotApplicableCount: relevantCount("not-applicable"),
|
|
34
|
+
totalOmittedRelevantFileCount: relevant.reduce((sum, evidence) => sum + (evidence.omittedRelevantFileCount ?? 0), 0),
|
|
35
|
+
};
|
|
36
|
+
const sumOf = (pick) => allCells.reduce((sum, summary) => sum + pick(summary), 0);
|
|
37
|
+
const evaluatedBudgetCellCount = args.cases
|
|
38
|
+
.flatMap((caseEvidence) => caseEvidence.treatments)
|
|
39
|
+
.flatMap((treatment) => treatment.budgetCells)
|
|
40
|
+
.filter((cell) => cell.evaluationStatus === "evaluated").length;
|
|
41
|
+
return {
|
|
42
|
+
budgets,
|
|
43
|
+
treatments,
|
|
44
|
+
runSummary: {
|
|
45
|
+
caseCount: args.cases.length,
|
|
46
|
+
treatmentCount: treatments.length,
|
|
47
|
+
budgetCount: budgets.length,
|
|
48
|
+
totalBudgetCellCount: sumOf((s) => s.totalCellCount),
|
|
49
|
+
evaluatedBudgetCellCount,
|
|
50
|
+
contextTooLargeCellCount: sumOf((s) => s.contextTooLargeCount),
|
|
51
|
+
contextUnavailableCellCount: sumOf((s) => s.contextUnavailableCount),
|
|
52
|
+
correctnessAvailableCellCount: sumOf((s) => s.correctnessAvailableCount),
|
|
53
|
+
correctnessUnavailableCellCount: sumOf((s) => s.correctnessUnavailableCount),
|
|
54
|
+
successEvidenceAvailableCellCount: sumOf((s) => s.successEvidenceAvailableCount),
|
|
55
|
+
successEvidenceUnavailableCellCount: sumOf((s) => s.successEvidenceUnavailableCount),
|
|
56
|
+
relevantFileEvidenceAvailableTreatmentCount: relevantFileSummary.relevantFileEvidenceAvailableCount,
|
|
57
|
+
relevantFileEvidenceUnavailableTreatmentCount: relevantFileSummary.relevantFileEvidenceUnavailableCount,
|
|
58
|
+
relevantFileEvidenceNotApplicableTreatmentCount: relevantFileSummary.relevantFileEvidenceNotApplicableCount,
|
|
59
|
+
},
|
|
60
|
+
budgetTreatmentSummaries,
|
|
61
|
+
caseTreatmentContextSummaries,
|
|
62
|
+
relevantFileSummary,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function requireTreatment(caseEvidence, variantId, budgets) {
|
|
66
|
+
const matches = caseEvidence.treatments.filter((treatment) => treatment.variantId === variantId);
|
|
67
|
+
const treatment = matches[0];
|
|
68
|
+
if (matches.length !== 1 || !treatment) {
|
|
69
|
+
throw new Error(`Invalid context-window-scaling evidence: case ${caseEvidence.caseId} must have exactly one ${variantId} treatment.`);
|
|
70
|
+
}
|
|
71
|
+
const cellBudgets = treatment.budgetCells.map((cell) => cell.contextBudgetTokens);
|
|
72
|
+
if (cellBudgets.length !== budgets.length || budgets.some((budget) => cellBudgets.filter((value) => value === budget).length !== 1)) {
|
|
73
|
+
throw new Error(`Invalid context-window-scaling evidence: case ${caseEvidence.caseId} ${variantId} cells do not match the selected budgets.`);
|
|
74
|
+
}
|
|
75
|
+
return treatment;
|
|
76
|
+
}
|
|
77
|
+
function cellFor(treatment, budget) {
|
|
78
|
+
return treatment.budgetCells.find((cell) => cell.contextBudgetTokens === budget);
|
|
79
|
+
}
|
|
80
|
+
function isFiniteNumber(value) {
|
|
81
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
82
|
+
}
|
|
83
|
+
function summarizeCells(contextBudgetTokens, variantId, cells) {
|
|
84
|
+
const fitCount = cells.filter((cell) => cell.contextFitStatus === "fits").length;
|
|
85
|
+
const contextTooLargeCount = cells.filter((cell) => cell.contextFitStatus === "context-too-large").length;
|
|
86
|
+
const contextUnavailableCount = cells.filter((cell) => cell.contextFitStatus === "unavailable").length;
|
|
87
|
+
const successAvailable = cells.filter((cell) => cell.successEvidence.status === "available");
|
|
88
|
+
const successfulCellCount = successAvailable.filter((cell) => cell.successEvidence.success === true).length;
|
|
89
|
+
const scores = cells
|
|
90
|
+
.filter((cell) => cell.correctness.availability === "available")
|
|
91
|
+
.map((cell) => cell.correctness.score)
|
|
92
|
+
.filter(isFiniteNumber);
|
|
93
|
+
const utilizations = cells.map((cell) => cell.contextBudgetUtilizationPercent).filter(isFiniteNumber);
|
|
94
|
+
const mean = (values) => (values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0) / values.length);
|
|
95
|
+
return {
|
|
96
|
+
contextBudgetTokens,
|
|
97
|
+
variantId,
|
|
98
|
+
totalCellCount: cells.length,
|
|
99
|
+
contextMeasuredCount: fitCount + contextTooLargeCount,
|
|
100
|
+
fitCount,
|
|
101
|
+
contextTooLargeCount,
|
|
102
|
+
contextUnavailableCount,
|
|
103
|
+
successfulCellCount,
|
|
104
|
+
notSuccessfulCellCount: successAvailable.length - successfulCellCount,
|
|
105
|
+
successEvidenceAvailableCount: successAvailable.length,
|
|
106
|
+
successEvidenceUnavailableCount: cells.length - successAvailable.length,
|
|
107
|
+
successRatePercent: successAvailable.length === 0 ? null : (successfulCellCount / successAvailable.length) * 100,
|
|
108
|
+
correctnessAvailableCount: scores.length,
|
|
109
|
+
correctnessUnavailableCount: cells.length - scores.length,
|
|
110
|
+
meanCorrectnessScore: mean(scores),
|
|
111
|
+
utilizationAvailableCount: utilizations.length,
|
|
112
|
+
utilizationUnavailableCount: cells.length - utilizations.length,
|
|
113
|
+
meanContextBudgetUtilizationPercent: mean(utilizations),
|
|
114
|
+
minContextBudgetUtilizationPercent: utilizations.length === 0 ? null : Math.min(...utilizations),
|
|
115
|
+
maxContextBudgetUtilizationPercent: utilizations.length === 0 ? null : Math.max(...utilizations),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** Run-level scalar metrics: one set per budget x treatment, scoped by variantId and budget-bearing ids. */
|
|
119
|
+
export function toAggregateExperimentMetrics(aggregate) {
|
|
120
|
+
const metrics = [
|
|
121
|
+
{ id: "context-window-scaling-case-count", name: "Context-window-scaling case count", value: aggregate.runSummary.caseCount, unit: "count" },
|
|
122
|
+
{ id: "context-window-scaling-budget-count", name: "Context budget count", value: aggregate.runSummary.budgetCount, unit: "count" },
|
|
123
|
+
];
|
|
124
|
+
for (const summary of aggregate.budgetTreatmentSummaries) {
|
|
125
|
+
const prefix = `context-budget-${summary.contextBudgetTokens}`;
|
|
126
|
+
const scoped = (suffix, name, value, unit, description) => ({
|
|
127
|
+
id: `${prefix}-${suffix}`,
|
|
128
|
+
name: `${name} (${summary.contextBudgetTokens} token budget)`,
|
|
129
|
+
value,
|
|
130
|
+
unit,
|
|
131
|
+
...(description ? { description } : {}),
|
|
132
|
+
variantId: summary.variantId,
|
|
133
|
+
});
|
|
134
|
+
metrics.push(scoped("fit-count", "Cases whose context fits", summary.fitCount, "count"), scoped("context-too-large-count", "Cases whose context is too large", summary.contextTooLargeCount, "count"), scoped("successful-cell-count", "Successful cells", summary.successfulCellCount, "count"), scoped("success-rate-percent", "Success rate", summary.successRatePercent, "percent", "Successful cells over success-evidence-available cells; null when none are available."), scoped("mean-correctness-score", "Mean correctness score", summary.meanCorrectnessScore, "score", "Mean of available existing correctness scores; unavailable correctness is excluded, not zero."), scoped("mean-context-budget-utilization-percent", "Mean context budget utilization", summary.meanContextBudgetUtilizationPercent, "percent", "Uncapped; may exceed 100."));
|
|
135
|
+
}
|
|
136
|
+
return metrics;
|
|
137
|
+
}
|
|
138
|
+
/** Per-outcome scalar metrics for one treatment, one set per budget cell. */
|
|
139
|
+
export function toOutcomeBudgetMetrics(treatment, caseId) {
|
|
140
|
+
return treatment.budgetCells.flatMap((cell) => {
|
|
141
|
+
const base = { variantId: treatment.variantId, caseId };
|
|
142
|
+
const prefix = `budget-${cell.contextBudgetTokens}`;
|
|
143
|
+
return [
|
|
144
|
+
{ id: `${prefix}-context-budget-utilization-percent`, name: `Context budget utilization (${cell.contextBudgetTokens})`, value: cell.contextBudgetUtilizationPercent, unit: "percent", ...base },
|
|
145
|
+
{ id: `${prefix}-correctness-score`, name: `Correctness score (${cell.contextBudgetTokens})`, value: cell.correctness.score, unit: "score", ...base },
|
|
146
|
+
{ id: `${prefix}-success`, name: `Success (${cell.contextBudgetTokens})`, value: cell.successEvidence.success, ...base },
|
|
147
|
+
];
|
|
148
|
+
});
|
|
149
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { tokenCountMethod } from "../../../core/countTokens.js";
|
|
4
|
+
import { resolveWithinRoot } from "../../../core/pathSafety.js";
|
|
5
|
+
import { summarizeExperimentRun } from "../../results.js";
|
|
6
|
+
import { aggregateStatus } from "../warmIndexReuse/execution.js";
|
|
7
|
+
import { contextWindowScalingConfigDefinition, defaultContextWindowScalingConfig, validateContextWindowScalingConfig, } from "./config.js";
|
|
8
|
+
import { executeContextWindowScalingCases, } from "./execution.js";
|
|
9
|
+
import { buildContextWindowScalingExecutionArtifact, CONTEXT_WINDOW_SCALING_EXECUTION_ARTIFACT_FILE, } from "./executionArtifact.js";
|
|
10
|
+
import { CONTEXT_WINDOW_SCALING_TREATMENT_IDS, contextWindowScalingMetadata } from "./metadata.js";
|
|
11
|
+
import { aggregateContextWindowScaling, toAggregateExperimentMetrics, toOutcomeBudgetMetrics, } from "./metrics.js";
|
|
12
|
+
import { resolveScalingProjectProfiles } from "./projectProfile.js";
|
|
13
|
+
const VARIANTS = [
|
|
14
|
+
{
|
|
15
|
+
id: "raw-full-file",
|
|
16
|
+
name: "Raw full file",
|
|
17
|
+
description: "Every file matched by the case's raw include globs, constructed once per case.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "my-dev-kit-guided",
|
|
21
|
+
name: "my-dev-kit guided",
|
|
22
|
+
description: "my-dev-kit retrieval context, constructed once per case.",
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
/** Internal-only seam: tests inject construction/evaluation owners through inputs.dependencies. */
|
|
26
|
+
export const contextWindowScalingPlugin = {
|
|
27
|
+
metadata: contextWindowScalingMetadata,
|
|
28
|
+
defaultConfig: defaultContextWindowScalingConfig,
|
|
29
|
+
configDefinition: contextWindowScalingConfigDefinition,
|
|
30
|
+
supportedVariants: CONTEXT_WINDOW_SCALING_TREATMENT_IDS,
|
|
31
|
+
validateConfig: validateContextWindowScalingConfig,
|
|
32
|
+
async run(context) {
|
|
33
|
+
const startedAt = context.startedAt.toISOString();
|
|
34
|
+
const cases = readCasesInput(context.inputs);
|
|
35
|
+
const projectProfiles = resolveScalingProjectProfiles(cases, readProjectProfilesInput(context.inputs));
|
|
36
|
+
const outDir = context.outputRoot ?? path.resolve(context.toolRoot, "lab-output", "context-window-scaling");
|
|
37
|
+
await mkdir(outDir, { recursive: true });
|
|
38
|
+
const executionEvidence = await executeContextWindowScalingCases({
|
|
39
|
+
cases,
|
|
40
|
+
contextBudgets: context.config.contextBudgets,
|
|
41
|
+
kitCommand: context.config.kitCommand,
|
|
42
|
+
outputRoot: outDir,
|
|
43
|
+
projectProfiles,
|
|
44
|
+
cwd: context.target.targetRoot,
|
|
45
|
+
env: readEnvInput(context.inputs),
|
|
46
|
+
dependencies: readDependenciesInput(context.inputs),
|
|
47
|
+
});
|
|
48
|
+
const completedAt = new Date().toISOString();
|
|
49
|
+
const artifact = buildContextWindowScalingExecutionArtifact({
|
|
50
|
+
runId: context.runId,
|
|
51
|
+
pluginId: contextWindowScalingMetadata.id,
|
|
52
|
+
pluginSchemaVersion: contextWindowScalingMetadata.schemaVersion,
|
|
53
|
+
startedAt,
|
|
54
|
+
completedAt,
|
|
55
|
+
tokenCountMethod,
|
|
56
|
+
contextBudgets: context.config.contextBudgets,
|
|
57
|
+
cases: executionEvidence,
|
|
58
|
+
});
|
|
59
|
+
const artifactPath = resolveWithinRoot(outDir, CONTEXT_WINDOW_SCALING_EXECUTION_ARTIFACT_FILE);
|
|
60
|
+
await writeFile(artifactPath, `${JSON.stringify(artifact, null, 2)}\n`, "utf8");
|
|
61
|
+
return mapExecutionToRun({
|
|
62
|
+
runId: context.runId,
|
|
63
|
+
startedAt,
|
|
64
|
+
completedAt,
|
|
65
|
+
target: context.target,
|
|
66
|
+
contextBudgets: context.config.contextBudgets,
|
|
67
|
+
executionEvidence,
|
|
68
|
+
artifactPath,
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
summarize(result) {
|
|
72
|
+
return result.summary ?? summarizeExperimentRun(result);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
export function mapExecutionToRun(args) {
|
|
76
|
+
const cases = args.executionEvidence.map((caseEvidence) => ({
|
|
77
|
+
id: caseEvidence.caseId,
|
|
78
|
+
name: caseEvidence.caseName,
|
|
79
|
+
outcomes: caseEvidence.treatments.map((treatment) => buildOutcome(caseEvidence, treatment)),
|
|
80
|
+
metadata: { benchmarkProject: caseEvidence.benchmarkProject },
|
|
81
|
+
}));
|
|
82
|
+
const aggregate = aggregateContextWindowScaling({ contextBudgets: args.contextBudgets, cases: args.executionEvidence });
|
|
83
|
+
const run = {
|
|
84
|
+
runId: args.runId,
|
|
85
|
+
pluginId: contextWindowScalingMetadata.id,
|
|
86
|
+
startedAt: args.startedAt,
|
|
87
|
+
completedAt: args.completedAt,
|
|
88
|
+
status: aggregateStatus(cases.flatMap((experimentCase) => experimentCase.outcomes.map((outcome) => outcome.status))),
|
|
89
|
+
target: args.target,
|
|
90
|
+
variants: VARIANTS.map((variant) => ({ ...variant })),
|
|
91
|
+
cases,
|
|
92
|
+
metrics: toAggregateExperimentMetrics(aggregate),
|
|
93
|
+
artifacts: [
|
|
94
|
+
{
|
|
95
|
+
id: "context-window-scaling-execution",
|
|
96
|
+
label: "Context window scaling execution evidence",
|
|
97
|
+
path: args.artifactPath,
|
|
98
|
+
kind: "artifact",
|
|
99
|
+
mimeType: "application/json",
|
|
100
|
+
description: "Per-case, per-treatment, per-budget fit, correctness, success, and omitted-file evidence without context text.",
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
warnings: [],
|
|
104
|
+
failures: [],
|
|
105
|
+
metadata: { executionArtifactPath: args.artifactPath },
|
|
106
|
+
contextBudgets: [...args.contextBudgets],
|
|
107
|
+
executionEvidence: structuredClone([...args.executionEvidence]),
|
|
108
|
+
aggregate,
|
|
109
|
+
};
|
|
110
|
+
run.summary = summarizeExperimentRun(run);
|
|
111
|
+
return run;
|
|
112
|
+
}
|
|
113
|
+
function buildOutcome(caseEvidence, treatment) {
|
|
114
|
+
const { caseId } = caseEvidence;
|
|
115
|
+
const variantId = treatment.variantId;
|
|
116
|
+
const metrics = [];
|
|
117
|
+
if (treatment.context.estimatedTokens !== null) {
|
|
118
|
+
metrics.push({
|
|
119
|
+
id: "context-estimated-tokens",
|
|
120
|
+
name: "Context estimated tokens",
|
|
121
|
+
value: treatment.context.estimatedTokens,
|
|
122
|
+
unit: "tokens",
|
|
123
|
+
variantId,
|
|
124
|
+
caseId,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (treatment.relevantFileEvidence.omittedRelevantFileCount !== null) {
|
|
128
|
+
metrics.push({
|
|
129
|
+
id: "omitted-relevant-file-count",
|
|
130
|
+
name: "Omitted relevant files",
|
|
131
|
+
value: treatment.relevantFileEvidence.omittedRelevantFileCount,
|
|
132
|
+
unit: "files",
|
|
133
|
+
variantId,
|
|
134
|
+
caseId,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
metrics.push(...toOutcomeBudgetMetrics(treatment, caseId));
|
|
138
|
+
const warnings = [
|
|
139
|
+
...treatment.context.warnings.map((message) => ({ code: "guided-retrieval-warning", message, variantId, caseId })),
|
|
140
|
+
...(treatment.evaluation.status === "unavailable"
|
|
141
|
+
? [{ code: "deterministic-evaluation-unavailable", message: treatment.evaluation.reason ?? "Evaluation unavailable.", variantId, caseId }]
|
|
142
|
+
: []),
|
|
143
|
+
];
|
|
144
|
+
const failures = treatment.errors.map((error) => ({
|
|
145
|
+
code: error.code,
|
|
146
|
+
message: error.message,
|
|
147
|
+
variantId,
|
|
148
|
+
caseId,
|
|
149
|
+
recoverable: true,
|
|
150
|
+
}));
|
|
151
|
+
return {
|
|
152
|
+
id: `${caseId}:${variantId}`,
|
|
153
|
+
caseId,
|
|
154
|
+
variantId,
|
|
155
|
+
status: treatment.status,
|
|
156
|
+
metrics,
|
|
157
|
+
artifacts: [],
|
|
158
|
+
warnings,
|
|
159
|
+
failures,
|
|
160
|
+
metadata: {
|
|
161
|
+
benchmarkProject: caseEvidence.benchmarkProject,
|
|
162
|
+
contextStatus: treatment.context.status,
|
|
163
|
+
evaluationStatus: treatment.evaluation.status,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function readCasesInput(inputs) {
|
|
168
|
+
const value = inputs?.cases;
|
|
169
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
170
|
+
throw new Error("Context window scaling requires a non-empty cases input.");
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
function readProjectProfilesInput(inputs) {
|
|
175
|
+
const value = inputs?.projectProfiles;
|
|
176
|
+
return Array.isArray(value) ? value : [];
|
|
177
|
+
}
|
|
178
|
+
function readEnvInput(inputs) {
|
|
179
|
+
const env = inputs?.env;
|
|
180
|
+
return env && typeof env === "object" && !Array.isArray(env) ? env : undefined;
|
|
181
|
+
}
|
|
182
|
+
function readDependenciesInput(inputs) {
|
|
183
|
+
const value = inputs?.dependencies;
|
|
184
|
+
return value && typeof value === "object" ? value : undefined;
|
|
185
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { calculateProjectComplexityScore, computeProjectComplexityMetrics, PROJECT_COMPLEXITY_FORMULA, } from "../../../evaluation/projectComplexity.js";
|
|
3
|
+
import { buildProjectFileTree } from "../../../evaluation/projectFileTree.js";
|
|
4
|
+
/**
|
|
5
|
+
* The fixed scaling project has no entry in the shared benchmark-project-profiles contract. The
|
|
6
|
+
* existing prompt owner requires a profile per case, so a missing one is derived in memory from
|
|
7
|
+
* the existing file-tree and complexity owners. Supplied profiles always win; nothing is written.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveScalingProjectProfiles(cases, supplied) {
|
|
10
|
+
const profiles = [...supplied];
|
|
11
|
+
const known = new Set(profiles.map((profile) => profile.projectId));
|
|
12
|
+
for (const evaluationCase of cases) {
|
|
13
|
+
const profileId = evaluationCase.projectProfileRef ?? evaluationCase.benchmarkProject;
|
|
14
|
+
if (known.has(profileId))
|
|
15
|
+
continue;
|
|
16
|
+
known.add(profileId);
|
|
17
|
+
profiles.push(deriveProfile(profileId, evaluationCase, cases));
|
|
18
|
+
}
|
|
19
|
+
return profiles;
|
|
20
|
+
}
|
|
21
|
+
function deriveProfile(profileId, evaluationCase, cases) {
|
|
22
|
+
const siblings = cases.filter((candidate) => (candidate.projectProfileRef ?? candidate.benchmarkProject) === profileId);
|
|
23
|
+
const fileTree = buildProjectFileTree(evaluationCase.absoluteTargetRoot);
|
|
24
|
+
const average = (values) => (values.length === 0 ? 0 : values.reduce((a, b) => a + b, 0) / values.length);
|
|
25
|
+
const complexityMetrics = computeProjectComplexityMetrics(evaluationCase.absoluteTargetRoot, fileTree, {
|
|
26
|
+
taskCount: siblings.length,
|
|
27
|
+
expectedRelevantFilesAverage: average(siblings.map((candidate) => candidate.expectedFiles.length)),
|
|
28
|
+
expectedRelevantSymbolsAverage: average(siblings.map((candidate) => candidate.expectedSymbols.length)),
|
|
29
|
+
});
|
|
30
|
+
const languages = [
|
|
31
|
+
...new Set(fileTree.entries.map((entry) => entry.language).filter((language) => Boolean(language))),
|
|
32
|
+
].sort();
|
|
33
|
+
return {
|
|
34
|
+
projectId: profileId,
|
|
35
|
+
displayName: profileId,
|
|
36
|
+
description: "Profile derived in memory for the context-window-scaling fixed corpus.",
|
|
37
|
+
languageMix: languages.length <= 1 ? `single-language ${languages[0] ?? "unknown"}` : "multi-language",
|
|
38
|
+
primaryLanguage: languages[0] ?? "unknown",
|
|
39
|
+
languages,
|
|
40
|
+
complexityLevel: "large",
|
|
41
|
+
complexityScore: calculateProjectComplexityScore(complexityMetrics),
|
|
42
|
+
complexityMetrics,
|
|
43
|
+
complexityFormula: PROJECT_COMPLEXITY_FORMULA,
|
|
44
|
+
rootPath: path.posix.normalize(evaluationCase.targetRoot.replace(/\\/g, "/")),
|
|
45
|
+
sourceRoots: [...evaluationCase.sourceRoots],
|
|
46
|
+
testRoots: ["tests"],
|
|
47
|
+
fileTree,
|
|
48
|
+
benchmarkPurpose: "Context-window scaling evaluation.",
|
|
49
|
+
expectedUseCases: ["context budget fit evaluation"],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded repository-relative identity: "\" becomes "/", leading "./" is removed, case is kept.
|
|
3
|
+
* Returns null for absolute paths, drive paths, parent traversal, empty segments, or oversized values.
|
|
4
|
+
*/
|
|
5
|
+
export function normalizeRepositoryRelativePath(value) {
|
|
6
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 1024) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
let normalized = value.replace(/\\/g, "/");
|
|
10
|
+
while (normalized.startsWith("./")) {
|
|
11
|
+
normalized = normalized.slice(2);
|
|
12
|
+
}
|
|
13
|
+
if (normalized.length === 0 || normalized.startsWith("/") || /^[A-Za-z]:/.test(normalized)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
if (normalized.split("/").some((segment) => segment === ".." || segment === "")) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return normalized;
|
|
20
|
+
}
|
|
21
|
+
/** observedFiles === null means observed-file provenance is unavailable. */
|
|
22
|
+
export function computeRelevantFileEvidence(args) {
|
|
23
|
+
if (args.expectedFiles.length === 0) {
|
|
24
|
+
return {
|
|
25
|
+
status: "not-applicable",
|
|
26
|
+
expectedRelevantFiles: [],
|
|
27
|
+
expectedRelevantFileCount: 0,
|
|
28
|
+
observedExpectedFileCount: null,
|
|
29
|
+
omittedRelevantFileCount: null,
|
|
30
|
+
omittedRelevantFiles: [],
|
|
31
|
+
reason: "No expected relevant files are defined for this case.",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const expected = [];
|
|
35
|
+
for (const file of args.expectedFiles) {
|
|
36
|
+
const normalized = normalizeRepositoryRelativePath(file);
|
|
37
|
+
if (normalized === null) {
|
|
38
|
+
return unavailable([...args.expectedFiles], "An expected relevant file is not a bounded repository-relative path.");
|
|
39
|
+
}
|
|
40
|
+
if (!expected.includes(normalized)) {
|
|
41
|
+
expected.push(normalized);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (args.observedFiles === null) {
|
|
45
|
+
return unavailable(expected, "Observed-file provenance is unavailable.");
|
|
46
|
+
}
|
|
47
|
+
const observed = new Set();
|
|
48
|
+
for (const file of args.observedFiles) {
|
|
49
|
+
const normalized = normalizeRepositoryRelativePath(file);
|
|
50
|
+
if (normalized === null) {
|
|
51
|
+
return unavailable(expected, "An observed file is not a bounded repository-relative path.");
|
|
52
|
+
}
|
|
53
|
+
observed.add(normalized);
|
|
54
|
+
}
|
|
55
|
+
const omitted = expected.filter((file) => !observed.has(file));
|
|
56
|
+
return {
|
|
57
|
+
status: "available",
|
|
58
|
+
expectedRelevantFiles: expected,
|
|
59
|
+
expectedRelevantFileCount: expected.length,
|
|
60
|
+
observedExpectedFileCount: expected.length - omitted.length,
|
|
61
|
+
omittedRelevantFileCount: omitted.length,
|
|
62
|
+
omittedRelevantFiles: omitted,
|
|
63
|
+
reason: null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function unavailable(expected, reason) {
|
|
67
|
+
return {
|
|
68
|
+
status: "unavailable",
|
|
69
|
+
expectedRelevantFiles: expected,
|
|
70
|
+
expectedRelevantFileCount: expected.length,
|
|
71
|
+
observedExpectedFileCount: null,
|
|
72
|
+
omittedRelevantFileCount: null,
|
|
73
|
+
omittedRelevantFiles: [],
|
|
74
|
+
reason,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { calculateContextBudgetUtilizationPercent, classifyContextFit } from "./contextBudget.js";
|
|
2
|
+
export const UNAVAILABLE_CORRECTNESS = { availability: "unavailable", score: null, pass: null };
|
|
3
|
+
/** The existing scorer's own passed result is used as-is; no score threshold is defined here. */
|
|
4
|
+
export function toCorrectnessEvidence(scored) {
|
|
5
|
+
if (!scored.available || scored.score === null || scored.passed === null) {
|
|
6
|
+
return { ...UNAVAILABLE_CORRECTNESS };
|
|
7
|
+
}
|
|
8
|
+
return { availability: "available", score: scored.score, pass: scored.passed };
|
|
9
|
+
}
|
|
10
|
+
export function deriveSuccessEvidence(args) {
|
|
11
|
+
if (args.contextFitStatus === "unavailable") {
|
|
12
|
+
return { status: "unavailable", success: null, reason: "context-unavailable" };
|
|
13
|
+
}
|
|
14
|
+
if (args.contextFitStatus === "context-too-large") {
|
|
15
|
+
return { status: "available", success: false, reason: "context-too-large" };
|
|
16
|
+
}
|
|
17
|
+
if (args.correctness.availability !== "available" || args.correctness.pass === null) {
|
|
18
|
+
return { status: "unavailable", success: null, reason: "evaluation-unavailable" };
|
|
19
|
+
}
|
|
20
|
+
return args.correctness.pass
|
|
21
|
+
? { status: "available", success: true, reason: "correctness-pass" }
|
|
22
|
+
: { status: "available", success: false, reason: "correctness-fail" };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One cell per budget. `shared` is the single deterministic evaluation of this treatment's context
|
|
26
|
+
* (null when it was never requested because no budget fits or the context is unavailable).
|
|
27
|
+
*/
|
|
28
|
+
export function buildBudgetCell(args) {
|
|
29
|
+
if (args.contextEstimatedTokens === null) {
|
|
30
|
+
return {
|
|
31
|
+
contextBudgetTokens: args.contextBudgetTokens,
|
|
32
|
+
contextFitStatus: "unavailable",
|
|
33
|
+
contextBudgetUtilizationPercent: null,
|
|
34
|
+
evaluationStatus: "unavailable",
|
|
35
|
+
correctness: { ...UNAVAILABLE_CORRECTNESS },
|
|
36
|
+
successEvidence: deriveSuccessEvidence({ contextFitStatus: "unavailable", correctness: UNAVAILABLE_CORRECTNESS }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const fit = classifyContextFit(args.contextEstimatedTokens, args.contextBudgetTokens);
|
|
40
|
+
const utilization = calculateContextBudgetUtilizationPercent(args.contextEstimatedTokens, args.contextBudgetTokens);
|
|
41
|
+
if (fit === "context-too-large") {
|
|
42
|
+
return {
|
|
43
|
+
contextBudgetTokens: args.contextBudgetTokens,
|
|
44
|
+
contextFitStatus: fit,
|
|
45
|
+
contextBudgetUtilizationPercent: utilization,
|
|
46
|
+
evaluationStatus: "not-evaluated-context-too-large",
|
|
47
|
+
correctness: { ...UNAVAILABLE_CORRECTNESS },
|
|
48
|
+
successEvidence: deriveSuccessEvidence({ contextFitStatus: fit, correctness: UNAVAILABLE_CORRECTNESS }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const correctness = args.shared ?? { ...UNAVAILABLE_CORRECTNESS };
|
|
52
|
+
return {
|
|
53
|
+
contextBudgetTokens: args.contextBudgetTokens,
|
|
54
|
+
contextFitStatus: fit,
|
|
55
|
+
contextBudgetUtilizationPercent: utilization,
|
|
56
|
+
evaluationStatus: correctness.availability === "available" ? "evaluated" : "unavailable",
|
|
57
|
+
correctness: { ...correctness },
|
|
58
|
+
successEvidence: deriveSuccessEvidence({ contextFitStatus: fit, correctness }),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { compareCorrectness, compareRequiredFileEvidence } from "./comparison.js";
|
|
2
|
+
function neutral(relation) {
|
|
3
|
+
if (relation === "stale-worse")
|
|
4
|
+
return "candidate-worse";
|
|
5
|
+
if (relation === "stale-better")
|
|
6
|
+
return "candidate-better";
|
|
7
|
+
return relation;
|
|
8
|
+
}
|
|
9
|
+
/** Same rules as the v0.6.2 correctness comparison, candidate vs reference. */
|
|
10
|
+
export function compareCandidateCorrectness(candidate, reference) {
|
|
11
|
+
return neutral(compareCorrectness(candidate, reference));
|
|
12
|
+
}
|
|
13
|
+
/** Same rules as the v0.6.2 required-file comparison, candidate vs reference. */
|
|
14
|
+
export function compareCandidateRequiredFileEvidence(candidate, reference) {
|
|
15
|
+
return neutral(compareRequiredFileEvidence(candidate, reference));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Frozen precedence. Rule 0 (realization gate) first: a full fallback is never evidence about
|
|
19
|
+
* partial-refresh performance. Then observed regression (either dimension worse), then inconclusive
|
|
20
|
+
* (a dimension unknown), then no observed regression (including candidate-better, which is not a
|
|
21
|
+
* winner claim). affected-neighborhood evidence and reindexRecommendation are never inputs.
|
|
22
|
+
*/
|
|
23
|
+
export function classifyPartialRefreshAgainstFull(args) {
|
|
24
|
+
const { realization, correctnessRelation, requiredFileEvidenceRelation } = args;
|
|
25
|
+
// RULE 0 -- realization gate.
|
|
26
|
+
if (realization === "FALLBACK_FULL") {
|
|
27
|
+
return { classification: "not-comparable-as-partial-refresh", reasonCodes: ["candidate-fell-back-to-full"] };
|
|
28
|
+
}
|
|
29
|
+
// RULE 1 -- observed regression wins even if the other dimension is unknown.
|
|
30
|
+
if (correctnessRelation === "candidate-worse" || requiredFileEvidenceRelation === "candidate-worse") {
|
|
31
|
+
const reasonCodes = [];
|
|
32
|
+
if (correctnessRelation === "candidate-worse")
|
|
33
|
+
reasonCodes.push("candidate-correctness-lower");
|
|
34
|
+
if (requiredFileEvidenceRelation === "candidate-worse")
|
|
35
|
+
reasonCodes.push("candidate-missing-required-file");
|
|
36
|
+
return { classification: "observed-regression-relative-to-full", reasonCodes };
|
|
37
|
+
}
|
|
38
|
+
// RULE 2 -- inconclusive.
|
|
39
|
+
if (correctnessRelation === "unknown" || requiredFileEvidenceRelation === "unknown") {
|
|
40
|
+
const reasonCodes = [];
|
|
41
|
+
if (correctnessRelation === "unknown")
|
|
42
|
+
reasonCodes.push("correctness-unavailable");
|
|
43
|
+
if (requiredFileEvidenceRelation === "unknown")
|
|
44
|
+
reasonCodes.push("required-file-evidence-unavailable");
|
|
45
|
+
return { classification: "inconclusive", reasonCodes };
|
|
46
|
+
}
|
|
47
|
+
// RULE 3 -- no observed regression.
|
|
48
|
+
return { classification: "no-observed-regression-relative-to-full", reasonCodes: ["no-reference-regression-observed"] };
|
|
49
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { invalidExperimentConfig, isPlainObject, mergeConfig, validExperimentConfig } from "../../config.js";
|
|
2
2
|
import { FROZEN_INCREMENTAL_CHANGE_STALENESS_SCENARIO_IDS } from "./scenarioTypes.js";
|
|
3
3
|
/**
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
4
|
+
* The upstream tool version the four-treatment experiment expects: 1.12.5 is the first published
|
|
5
|
+
* my-dev-kit that exposes the incremental refresh-scope options. Every index of one matched scenario
|
|
6
|
+
* must come from this one configured command.
|
|
6
7
|
*/
|
|
7
|
-
export const INCREMENTAL_CHANGE_STALENESS_DEFAULT_KIT_COMMAND = "npx @dailephd/my-dev-kit@1.12.
|
|
8
|
+
export const INCREMENTAL_CHANGE_STALENESS_DEFAULT_KIT_COMMAND = "npx @dailephd/my-dev-kit@1.12.5";
|
|
8
9
|
export const defaultIncrementalChangeStalenessConfig = {
|
|
9
10
|
outDir: "lab-output/incremental-change-staleness",
|
|
10
11
|
kitCommand: INCREMENTAL_CHANGE_STALENESS_DEFAULT_KIT_COMMAND
|
|
@@ -17,7 +18,7 @@ export const incrementalChangeStalenessConfigDefinition = {
|
|
|
17
18
|
{
|
|
18
19
|
name: "caseIds",
|
|
19
20
|
type: "array",
|
|
20
|
-
description: `Frozen scenario ids to include (${FROZEN_INCREMENTAL_CHANGE_STALENESS_SCENARIO_IDS.join(", ")}); defaults to all.
|
|
21
|
+
description: `Frozen scenario ids to include (${FROZEN_INCREMENTAL_CHANGE_STALENESS_SCENARIO_IDS.join(", ")}); defaults to all. All four treatments run for each selected scenario: stale-index, changed-files-refresh, affected-neighborhood-refresh, and full-refresh.`
|
|
21
22
|
}
|
|
22
23
|
]
|
|
23
24
|
};
|
|
@@ -8,6 +8,24 @@ import { resolveWithinRoot } from "../../../core/pathSafety.js";
|
|
|
8
8
|
* target copies; it never builds a treatment index or runs retrieval.
|
|
9
9
|
*/
|
|
10
10
|
export const INCREMENTAL_CHANGE_STALENESS_TREATMENT_IDS = ["stale-index", "full-refresh"];
|
|
11
|
+
/**
|
|
12
|
+
* The four v0.6.3 internal lifecycle treatment identities in their fixed order
|
|
13
|
+
* (never alphabetical). The released v0.6.2 public constant above is unchanged;
|
|
14
|
+
* this set is used only by the internal v0.6.3 lifecycle.
|
|
15
|
+
*/
|
|
16
|
+
export const INCREMENTAL_CHANGE_STALENESS_V2_TREATMENT_IDS = [
|
|
17
|
+
"stale-index",
|
|
18
|
+
"changed-files-refresh",
|
|
19
|
+
"affected-neighborhood-refresh",
|
|
20
|
+
"full-refresh"
|
|
21
|
+
];
|
|
22
|
+
/** Treatment ID and treatment intent are separate runtime evidence. */
|
|
23
|
+
export const INCREMENTAL_CHANGE_STALENESS_V2_TREATMENT_INTENTS = {
|
|
24
|
+
"stale-index": "my-dev-kit-no-refresh",
|
|
25
|
+
"changed-files-refresh": "my-dev-kit-changed-files-refresh",
|
|
26
|
+
"affected-neighborhood-refresh": "my-dev-kit-affected-neighborhood-refresh",
|
|
27
|
+
"full-refresh": "my-dev-kit-full-refresh"
|
|
28
|
+
};
|
|
11
29
|
/**
|
|
12
30
|
* Default project-relative, gitignored runtime location for disposable
|
|
13
31
|
* treatment targets. Callers may pass a different `runRoot`, but it must
|
|
@@ -38,8 +56,8 @@ function assertDisposableRootDoesNotOverlapCanonical(targetRoot, canonicalProjec
|
|
|
38
56
|
*/
|
|
39
57
|
export async function createDisposableTreatmentTarget(options) {
|
|
40
58
|
const { repoRoot, scenarioId, treatmentId, benchmarkProjectId, canonicalProjectRootRelative } = options;
|
|
41
|
-
if (!
|
|
42
|
-
throw new Error(`Unknown treatment id: ${treatmentId}. Expected one of ${
|
|
59
|
+
if (!INCREMENTAL_CHANGE_STALENESS_V2_TREATMENT_IDS.includes(treatmentId)) {
|
|
60
|
+
throw new Error(`Unknown treatment id: ${treatmentId}. Expected one of ${INCREMENTAL_CHANGE_STALENESS_V2_TREATMENT_IDS.join(", ")}.`);
|
|
43
61
|
}
|
|
44
62
|
if (typeof scenarioId !== "string" || scenarioId.length === 0) {
|
|
45
63
|
throw new Error("scenarioId must be a nonempty string.");
|