@hraness/kb 0.17.1
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/DISCLOSURE +43 -0
- package/LICENSE +21 -0
- package/README.md +545 -0
- package/dist/agent-context.js +35 -0
- package/dist/agent-guide-audit.js +18 -0
- package/dist/attachments.js +19 -0
- package/dist/authoring.js +33 -0
- package/dist/benchmark.js +19 -0
- package/dist/capture.js +49 -0
- package/dist/cli.js +3646 -0
- package/dist/clip/acquire.js +57 -0
- package/dist/clip/args.js +21 -0
- package/dist/clip/bounded-byte-buffer.js +9 -0
- package/dist/clip/browser-profiles.js +17 -0
- package/dist/clip/bundle-reader.js +11 -0
- package/dist/clip/cli.js +27 -0
- package/dist/clip/cookies.js +21 -0
- package/dist/clip/defuddle-worker.js +84 -0
- package/dist/clip/doctor.js +32 -0
- package/dist/clip/jobs.js +784 -0
- package/dist/clip/network-proxy.js +9 -0
- package/dist/clip/network.js +28 -0
- package/dist/clip/persist.js +30 -0
- package/dist/clip/refresh.js +11 -0
- package/dist/clip/terminal.js +11 -0
- package/dist/evaluation-builder.js +12238 -0
- package/dist/evaluation-kb.js +26 -0
- package/dist/evaluation.js +35 -0
- package/dist/git.js +39 -0
- package/dist/graph.js +39 -0
- package/dist/index-01jj6rbv.js +1103 -0
- package/dist/index-06c9ctr6.js +738 -0
- package/dist/index-0kavxzqj.js +2480 -0
- package/dist/index-1gwbassd.js +743 -0
- package/dist/index-1n418kb9.js +827 -0
- package/dist/index-1vrd1rmn.js +56 -0
- package/dist/index-1xxnjn0d.js +171 -0
- package/dist/index-3rm7cz6h.js +373 -0
- package/dist/index-3v2z4f0q.js +328 -0
- package/dist/index-48pz4jpc.js +392 -0
- package/dist/index-4j3tt0c3.js +234 -0
- package/dist/index-5n05se68.js +307 -0
- package/dist/index-5vwpzb5a.js +764 -0
- package/dist/index-6g2pv9d2.js +370 -0
- package/dist/index-7fzc32gf.js +712 -0
- package/dist/index-84x0vjjp.js +432 -0
- package/dist/index-adx6khj5.js +488 -0
- package/dist/index-b88v3vtm.js +805 -0
- package/dist/index-cv6fh7z5.js +462 -0
- package/dist/index-cxfrakt7.js +1067 -0
- package/dist/index-d13v9ckt.js +150 -0
- package/dist/index-dycjbhw2.js +1943 -0
- package/dist/index-dyqwejk5.js +531 -0
- package/dist/index-e5fbsywq.js +658 -0
- package/dist/index-f984hw45.js +927 -0
- package/dist/index-gh719d91.js +63 -0
- package/dist/index-hgve9rh2.js +2046 -0
- package/dist/index-hya40gb2.js +474 -0
- package/dist/index-j4zgmzjr.js +152 -0
- package/dist/index-jsmvyyvf.js +1166 -0
- package/dist/index-kh0pwvdd.js +1974 -0
- package/dist/index-mqx4nd6v.js +119 -0
- package/dist/index-mxxxytys.js +1254 -0
- package/dist/index-n5dd7r0v.js +711 -0
- package/dist/index-npg9z1a4.js +431 -0
- package/dist/index-pj501bh1.js +140 -0
- package/dist/index-qry4vhxk.js +1041 -0
- package/dist/index-s2gw5aw9.js +199 -0
- package/dist/index-vvdj6kca.js +70 -0
- package/dist/index-w2zc0vwa.js +571 -0
- package/dist/index-x3fthpsc.js +585 -0
- package/dist/index-zr53sf63.js +44 -0
- package/dist/index-zxdy5pby.js +2728 -0
- package/dist/index-zzhgcwyt.js +966 -0
- package/dist/index.js +453 -0
- package/dist/navigation.js +13 -0
- package/dist/pdf.js +85 -0
- package/dist/percolate.js +24 -0
- package/dist/portfolio.js +77 -0
- package/dist/query.js +29 -0
- package/dist/repository-memory.js +60 -0
- package/dist/sdk.js +36 -0
- package/dist/search-rules.js +34 -0
- package/dist/search.js +22 -0
- package/dist/semantic.js +39 -0
- package/dist/source-inbox.js +17 -0
- package/dist/untrusted-content.js +32 -0
- package/dist/url-intelligence.js +74 -0
- package/dist/workflow.js +23 -0
- package/dist/workflows/decision-context.js +20 -0
- package/dist/workflows/explain-change.js +8 -0
- package/dist/workflows/index.js +28 -0
- package/dist/workflows/plan-radar.js +10 -0
- package/package.json +409 -0
- package/skills/kb/AGENTS.md +19 -0
- package/skills/kb/SKILL.md +82 -0
- package/skills/kb/agents/openai.yaml +4 -0
- package/skills/kb/references/pdf-review.md +51 -0
- package/skills/kb/references/percolate.md +144 -0
- package/skills/kb/references/plan-structure.md +105 -0
- package/skills/kb/references/plan.md +115 -0
- package/skills/kb/references/query.md +231 -0
- package/skills/kb/references/refresh.md +133 -0
- package/skills/kb/references/save-pdf.md +125 -0
- package/skills/kb/references/save-url.md +178 -0
- package/skills/kb/references/url-authentication.md +75 -0
- package/skills/kb/references/url-platforms.md +25 -0
- package/src/agent-context.ts +1241 -0
- package/src/agent-guide-audit.ts +750 -0
- package/src/attachments.ts +723 -0
- package/src/authoring.ts +1725 -0
- package/src/benchmark.ts +362 -0
- package/src/capture.ts +68 -0
- package/src/cli.ts +3660 -0
- package/src/clip/acquire.ts +1273 -0
- package/src/clip/archive-today.ts +402 -0
- package/src/clip/args.ts +423 -0
- package/src/clip/assets.ts +262 -0
- package/src/clip/bounded-byte-buffer.ts +75 -0
- package/src/clip/browser-profiles.ts +372 -0
- package/src/clip/bundle-reader.ts +608 -0
- package/src/clip/capture.ts +1054 -0
- package/src/clip/cli.ts +181 -0
- package/src/clip/cookies.ts +512 -0
- package/src/clip/doctor.ts +1110 -0
- package/src/clip/extract.ts +868 -0
- package/src/clip/jobs.ts +959 -0
- package/src/clip/lib.ts +861 -0
- package/src/clip/media.ts +1474 -0
- package/src/clip/metadata-search-tool/Cargo.lock +2498 -0
- package/src/clip/metadata-search-tool/Cargo.toml +18 -0
- package/src/clip/metadata-search-tool/runner.ts +685 -0
- package/src/clip/metadata-search-tool/src/main.rs +389 -0
- package/src/clip/metadata-search.ts +687 -0
- package/src/clip/network-proxy.ts +627 -0
- package/src/clip/network.ts +950 -0
- package/src/clip/package-root.ts +54 -0
- package/src/clip/persist.ts +1472 -0
- package/src/clip/platforms.ts +1824 -0
- package/src/clip/refresh.ts +197 -0
- package/src/clip/structured.ts +432 -0
- package/src/clip/terminal.ts +197 -0
- package/src/clip/url-intelligence.ts +581 -0
- package/src/clip/url-metadata-backfill.ts +616 -0
- package/src/clip/url-metadata-cli.ts +251 -0
- package/src/clip/url-metadata.ts +1397 -0
- package/src/evaluation-analysis-v2.ts +1440 -0
- package/src/evaluation-builder.ts +2539 -0
- package/src/evaluation-corpus-authoring.ts +2052 -0
- package/src/evaluation-evidence.ts +1147 -0
- package/src/evaluation-execution-v2.ts +426 -0
- package/src/evaluation-implementation.ts +131 -0
- package/src/evaluation-kb-closure.ts +1430 -0
- package/src/evaluation-kb-runner-v2.ts +735 -0
- package/src/evaluation-kb-v2.ts +2567 -0
- package/src/evaluation-kb.ts +1018 -0
- package/src/evaluation-measurement-v2.ts +1462 -0
- package/src/evaluation-packing-v2.ts +452 -0
- package/src/evaluation-redaction.ts +28 -0
- package/src/evaluation-v2.ts +3857 -0
- package/src/evaluation.ts +1220 -0
- package/src/git.ts +1276 -0
- package/src/graph.ts +1487 -0
- package/src/index.ts +20 -0
- package/src/init.ts +123 -0
- package/src/navigation.ts +277 -0
- package/src/note-lock.ts +529 -0
- package/src/pdf/args.ts +202 -0
- package/src/pdf/capture.ts +298 -0
- package/src/pdf/cli.ts +159 -0
- package/src/pdf/extract.ts +303 -0
- package/src/pdf/layout.ts +618 -0
- package/src/pdf/markdown.ts +154 -0
- package/src/pdf/model.ts +234 -0
- package/src/pdf/ocr.ts +190 -0
- package/src/pdf/persist.ts +314 -0
- package/src/pdf/source.ts +135 -0
- package/src/pdf/tools.ts +146 -0
- package/src/pdf.ts +92 -0
- package/src/percolate.ts +877 -0
- package/src/portfolio-audit.ts +574 -0
- package/src/portfolio-identity.ts +162 -0
- package/src/portfolio-registry.ts +473 -0
- package/src/portfolio.ts +765 -0
- package/src/query.ts +646 -0
- package/src/repository-memory.ts +1150 -0
- package/src/sdk.ts +1452 -0
- package/src/search-rules.ts +712 -0
- package/src/search.ts +710 -0
- package/src/semantic-runtime.ts +1139 -0
- package/src/semantic.ts +2480 -0
- package/src/source-inbox.ts +232 -0
- package/src/untrusted-content.ts +401 -0
- package/src/url-intelligence.ts +104 -0
- package/src/vault.ts +636 -0
- package/src/workflow.ts +653 -0
- package/src/workflows/decision-context.ts +81 -0
- package/src/workflows/explain-change.ts +73 -0
- package/src/workflows/index.ts +3 -0
- package/src/workflows/plan-radar.ts +101 -0
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertVerifiedKnowledgeBaseEvaluationV2,
|
|
3
|
+
createKnowledgeBaseEvaluationRepeatedSampleV2,
|
|
4
|
+
type KnowledgeBaseEvaluationRetrieverResultV2,
|
|
5
|
+
type KnowledgeBaseEvaluationRetrieverV2,
|
|
6
|
+
type KnowledgeBaseEvaluationV2,
|
|
7
|
+
} from "./evaluation-kb-v2.js";
|
|
8
|
+
import {
|
|
9
|
+
createEvaluationExecutionRequestV2,
|
|
10
|
+
parseRetrievalEvaluationCorpusV2,
|
|
11
|
+
parseRetrievalEvaluationReportV2,
|
|
12
|
+
validatePromotionCorpusV2,
|
|
13
|
+
type EvaluationExternalCorpusSealV2,
|
|
14
|
+
type EvaluationMeasurementProfileV2,
|
|
15
|
+
type EvaluationQueryV2,
|
|
16
|
+
type EvaluationRepeatedSampleV2,
|
|
17
|
+
type EvaluationResourceAccountingV2,
|
|
18
|
+
type EvaluationRetrieverDescriptorV2,
|
|
19
|
+
type EvaluationRetrieverTraceV2,
|
|
20
|
+
type RetrievalEvaluationCorpusV2,
|
|
21
|
+
type RetrievalEvaluationReportV2,
|
|
22
|
+
} from "./evaluation-v2.js";
|
|
23
|
+
|
|
24
|
+
export const KNOWLEDGE_BASE_EVALUATION_MAX_SAMPLE_TIMEOUT_MS = 5 * 60_000;
|
|
25
|
+
const MAX_ABORT_SETTLEMENT_MS = 5_000;
|
|
26
|
+
const EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
27
|
+
|
|
28
|
+
export type EvaluationRetrieverOperationMeasurementV2 = Readonly<{
|
|
29
|
+
readonly status: EvaluationRepeatedSampleV2["status"];
|
|
30
|
+
readonly timings: EvaluationRepeatedSampleV2["timings"];
|
|
31
|
+
readonly resources: EvaluationResourceAccountingV2;
|
|
32
|
+
readonly trace: EvaluationRetrieverTraceV2;
|
|
33
|
+
readonly failure?: EvaluationRepeatedSampleV2["failure"];
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export type EvaluationPackingMeasurementV2 = Readonly<{
|
|
37
|
+
readonly durationMs: number;
|
|
38
|
+
readonly packedContext: EvaluationResourceAccountingV2["packedContext"];
|
|
39
|
+
readonly includedEvidenceUnitIds: readonly string[];
|
|
40
|
+
readonly truncated: boolean;
|
|
41
|
+
readonly packedBytesSha256: string;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
export type EvaluationPackingContractV2 = Readonly<{
|
|
45
|
+
readonly suiteSha256: string;
|
|
46
|
+
readonly tokenizer: RetrievalEvaluationCorpusV2["experiment"]["environment"]["tokenizer"];
|
|
47
|
+
readonly contextCeilings: RetrievalEvaluationCorpusV2["experiment"]["protocol"]["contextCeilings"];
|
|
48
|
+
}>;
|
|
49
|
+
|
|
50
|
+
export type KnowledgeBaseEvaluationFourReaderBatchV2 = Readonly<{
|
|
51
|
+
readonly id: string;
|
|
52
|
+
readonly sha256: string;
|
|
53
|
+
readonly evaluations: readonly KnowledgeBaseEvaluationV2[];
|
|
54
|
+
readonly verifyCacheFingerprint: (signal: AbortSignal) => Promise<string>;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
export type KnowledgeBaseEvaluationRunnerV2Dependencies = Readonly<{
|
|
58
|
+
readonly measureRetrieverOperation: (input: Readonly<{
|
|
59
|
+
readonly operation: "cold-index" | "incremental-update";
|
|
60
|
+
readonly corpus: RetrievalEvaluationCorpusV2;
|
|
61
|
+
readonly descriptor: EvaluationRetrieverDescriptorV2;
|
|
62
|
+
readonly profile: EvaluationMeasurementProfileV2;
|
|
63
|
+
readonly repetition: number;
|
|
64
|
+
readonly signal: AbortSignal;
|
|
65
|
+
}>) => Promise<EvaluationRetrieverOperationMeasurementV2>;
|
|
66
|
+
readonly pack: (input: Readonly<{
|
|
67
|
+
readonly contract: EvaluationPackingContractV2;
|
|
68
|
+
readonly descriptor: EvaluationRetrieverDescriptorV2;
|
|
69
|
+
readonly profileId: string;
|
|
70
|
+
readonly queryId: string;
|
|
71
|
+
readonly result: KnowledgeBaseEvaluationRetrieverResultV2;
|
|
72
|
+
readonly repetition: number;
|
|
73
|
+
readonly signal: AbortSignal;
|
|
74
|
+
}>) => Promise<EvaluationPackingMeasurementV2>;
|
|
75
|
+
readonly openFourReaderBatch: (input: Readonly<{
|
|
76
|
+
readonly profileId: string;
|
|
77
|
+
readonly repetition: number;
|
|
78
|
+
readonly signal: AbortSignal;
|
|
79
|
+
}>) => Promise<KnowledgeBaseEvaluationFourReaderBatchV2>;
|
|
80
|
+
readonly verifyWarmCacheFingerprint: (input: Readonly<{
|
|
81
|
+
readonly profileId: string;
|
|
82
|
+
readonly operation: "packing" | "warm-query";
|
|
83
|
+
readonly repetition: number;
|
|
84
|
+
readonly phase: "before" | "after";
|
|
85
|
+
readonly signal: AbortSignal;
|
|
86
|
+
}>) => Promise<string>;
|
|
87
|
+
readonly now?: () => number;
|
|
88
|
+
}>;
|
|
89
|
+
|
|
90
|
+
export type RunKnowledgeBaseEvaluationV2Options = Readonly<{
|
|
91
|
+
readonly corpus: RetrievalEvaluationCorpusV2;
|
|
92
|
+
readonly evaluation: KnowledgeBaseEvaluationV2;
|
|
93
|
+
readonly split: "all" | "development" | "test";
|
|
94
|
+
/** Required for every run that exposes held-out test observations. */
|
|
95
|
+
readonly promotionSeal?: EvaluationExternalCorpusSealV2;
|
|
96
|
+
readonly timeoutMs: number;
|
|
97
|
+
readonly dependencies: KnowledgeBaseEvaluationRunnerV2Dependencies;
|
|
98
|
+
}>;
|
|
99
|
+
|
|
100
|
+
function nonnegative(value: number, label: string): number {
|
|
101
|
+
if (!Number.isFinite(value) || value < 0) throw new TypeError(`${label} must be non-negative and finite.`);
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function timeout(value: number): number {
|
|
106
|
+
if (
|
|
107
|
+
!Number.isSafeInteger(value)
|
|
108
|
+
|| value < 1
|
|
109
|
+
|| value > KNOWLEDGE_BASE_EVALUATION_MAX_SAMPLE_TIMEOUT_MS
|
|
110
|
+
) {
|
|
111
|
+
throw new TypeError(
|
|
112
|
+
`timeoutMs must be an integer from 1 through ${KNOWLEDGE_BASE_EVALUATION_MAX_SAMPLE_TIMEOUT_MS}.`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function message(error: unknown): string {
|
|
119
|
+
const source = error instanceof Error ? error.message : String(error);
|
|
120
|
+
const normalized = source.replace(/[\0\r\n]+/gu, " ").trim() || "Unknown evaluation failure.";
|
|
121
|
+
return Buffer.from(normalized, "utf8").subarray(0, 2_000).toString("utf8");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function limit(descriptor: EvaluationRetrieverDescriptorV2): number {
|
|
125
|
+
const value = descriptor.configuration["retrieve-limit"]
|
|
126
|
+
?? descriptor.configuration["output-limit"]
|
|
127
|
+
?? descriptor.configuration.limit;
|
|
128
|
+
if (!Number.isSafeInteger(value) || (value as number) < 1 || (value as number) > 100) {
|
|
129
|
+
throw new TypeError(`Retriever ${descriptor.id} must declare a bounded retrieval limit.`);
|
|
130
|
+
}
|
|
131
|
+
return value as number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function emptyTrace(descriptor: EvaluationRetrieverDescriptorV2): EvaluationRetrieverTraceV2 {
|
|
135
|
+
return Object.freeze({
|
|
136
|
+
laneOutcomes: Object.freeze(descriptor.lanes.toSorted().map((laneId) => Object.freeze({
|
|
137
|
+
laneId,
|
|
138
|
+
applicability: "skipped" as const,
|
|
139
|
+
status: "unavailable" as const,
|
|
140
|
+
reasonCodes: Object.freeze(["measurement-failed"]),
|
|
141
|
+
rawRanking: Object.freeze([]),
|
|
142
|
+
}))),
|
|
143
|
+
candidateDecisions: Object.freeze([]),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function zeroResources(): EvaluationResourceAccountingV2 {
|
|
148
|
+
return Object.freeze({
|
|
149
|
+
llm: Object.freeze({ calls: 0, inputTokens: 0, outputTokens: 0 }),
|
|
150
|
+
embedding: Object.freeze({ calls: 0, inputTokens: 0, durationMs: 0 }),
|
|
151
|
+
packedContext: Object.freeze({ utf8Bytes: 0, readerTokens: 0 }),
|
|
152
|
+
peakRssBytes: 0,
|
|
153
|
+
cacheBytes: 0,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function bounded<T>(options: Readonly<{
|
|
158
|
+
readonly timeoutMs: number;
|
|
159
|
+
readonly now: () => number;
|
|
160
|
+
readonly operation: (signal: AbortSignal) => Promise<T>;
|
|
161
|
+
readonly disposeTimedOutValue?: (value: T) => Promise<void>;
|
|
162
|
+
}>): Promise<Readonly<{ readonly status: "ok"; readonly value: T; readonly elapsedMs: number }>
|
|
163
|
+
| Readonly<{
|
|
164
|
+
readonly status: "failed";
|
|
165
|
+
readonly failure: NonNullable<EvaluationRepeatedSampleV2["failure"]>;
|
|
166
|
+
readonly elapsedMs: number;
|
|
167
|
+
}>> {
|
|
168
|
+
const controller = new AbortController();
|
|
169
|
+
const startedAt = options.now();
|
|
170
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
171
|
+
let timedOut = false;
|
|
172
|
+
const running = Promise.resolve().then(() => options.operation(controller.signal));
|
|
173
|
+
void running.catch(() => undefined);
|
|
174
|
+
const timeoutFailure = new Promise<never>((_resolve, reject) => {
|
|
175
|
+
timer = setTimeout(() => {
|
|
176
|
+
timedOut = true;
|
|
177
|
+
const timeoutError = new Error(`Evaluation sample exceeded ${options.timeoutMs} ms.`);
|
|
178
|
+
controller.abort(timeoutError);
|
|
179
|
+
reject(timeoutError);
|
|
180
|
+
}, options.timeoutMs);
|
|
181
|
+
});
|
|
182
|
+
try {
|
|
183
|
+
const value = await Promise.race([running, timeoutFailure]);
|
|
184
|
+
return Object.freeze({
|
|
185
|
+
status: "ok",
|
|
186
|
+
value,
|
|
187
|
+
elapsedMs: nonnegative(options.now() - startedAt, "elapsedMs"),
|
|
188
|
+
});
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (timedOut) {
|
|
191
|
+
let settlementTimer: ReturnType<typeof setTimeout> | undefined;
|
|
192
|
+
const settled = await Promise.race([
|
|
193
|
+
running.then(async (value) => {
|
|
194
|
+
await options.disposeTimedOutValue?.(value);
|
|
195
|
+
return true as const;
|
|
196
|
+
}, () => true as const),
|
|
197
|
+
new Promise<false>((resolve) => {
|
|
198
|
+
settlementTimer = setTimeout(() => resolve(false), MAX_ABORT_SETTLEMENT_MS);
|
|
199
|
+
}),
|
|
200
|
+
]);
|
|
201
|
+
if (settlementTimer !== undefined) clearTimeout(settlementTimer);
|
|
202
|
+
if (!settled) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Evaluation operation did not settle within ${MAX_ABORT_SETTLEMENT_MS} ms after abort.`,
|
|
205
|
+
{ cause: error },
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return Object.freeze({
|
|
210
|
+
status: "failed",
|
|
211
|
+
failure: Object.freeze({
|
|
212
|
+
kind: timedOut ? "timeout" as const : "exception" as const,
|
|
213
|
+
message: message(error),
|
|
214
|
+
}),
|
|
215
|
+
elapsedMs: nonnegative(options.now() - startedAt, "elapsedMs"),
|
|
216
|
+
});
|
|
217
|
+
} finally {
|
|
218
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function failedSample(options: Readonly<{
|
|
223
|
+
readonly descriptor: EvaluationRetrieverDescriptorV2;
|
|
224
|
+
readonly profile: EvaluationMeasurementProfileV2;
|
|
225
|
+
readonly repetition: number;
|
|
226
|
+
readonly elapsedMs: number;
|
|
227
|
+
readonly failure: NonNullable<EvaluationRepeatedSampleV2["failure"]>;
|
|
228
|
+
readonly queryId?: string;
|
|
229
|
+
readonly concurrencyBatchIdentity?: string;
|
|
230
|
+
}>): EvaluationRepeatedSampleV2 {
|
|
231
|
+
return Object.freeze({
|
|
232
|
+
retrieverId: options.descriptor.id,
|
|
233
|
+
profileId: options.profile.id,
|
|
234
|
+
...(options.queryId === undefined ? {} : { queryId: options.queryId }),
|
|
235
|
+
repetition: options.repetition,
|
|
236
|
+
...(options.concurrencyBatchIdentity === undefined
|
|
237
|
+
? {}
|
|
238
|
+
: { concurrencyBatchIdentity: options.concurrencyBatchIdentity }),
|
|
239
|
+
status: "failed",
|
|
240
|
+
timings: Object.freeze({
|
|
241
|
+
elapsedMs: options.elapsedMs,
|
|
242
|
+
indexMs: 0,
|
|
243
|
+
updateMs: 0,
|
|
244
|
+
queryMs: options.profile.scope === "query" ? options.elapsedMs : 0,
|
|
245
|
+
packingMs: 0,
|
|
246
|
+
}),
|
|
247
|
+
resources: zeroResources(),
|
|
248
|
+
trace: emptyTrace(options.descriptor),
|
|
249
|
+
rawEvidence: Object.freeze([]),
|
|
250
|
+
failure: options.failure,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function validateProfiles(corpus: RetrievalEvaluationCorpusV2): void {
|
|
255
|
+
for (const profile of corpus.measurementProfiles) {
|
|
256
|
+
const retrieverScope = profile.operation === "cold-index" || profile.operation === "incremental-update";
|
|
257
|
+
if ((profile.scope === "retriever") !== retrieverScope) {
|
|
258
|
+
throw new TypeError(`Profile ${profile.id} has the wrong scope for ${profile.operation}.`);
|
|
259
|
+
}
|
|
260
|
+
const expectedConcurrency = profile.operation === "four-reader-query" ? 4 : 1;
|
|
261
|
+
if (profile.concurrency !== expectedConcurrency) {
|
|
262
|
+
throw new TypeError(`Profile ${profile.id} must declare concurrency ${expectedConcurrency}.`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function packingContract(corpus: RetrievalEvaluationCorpusV2): EvaluationPackingContractV2 {
|
|
268
|
+
return Object.freeze({
|
|
269
|
+
suiteSha256: corpus.manifest.corpusSha256,
|
|
270
|
+
tokenizer: corpus.experiment.environment.tokenizer,
|
|
271
|
+
contextCeilings: corpus.experiment.protocol.contextCeilings,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function compareSamples(left: EvaluationRepeatedSampleV2, right: EvaluationRepeatedSampleV2): number {
|
|
276
|
+
return left.retrieverId.localeCompare(right.retrieverId)
|
|
277
|
+
|| left.profileId.localeCompare(right.profileId)
|
|
278
|
+
|| (left.queryId ?? "").localeCompare(right.queryId ?? "")
|
|
279
|
+
|| left.repetition - right.repetition;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function retrieversById(
|
|
283
|
+
evaluation: KnowledgeBaseEvaluationV2,
|
|
284
|
+
corpus: RetrievalEvaluationCorpusV2,
|
|
285
|
+
): ReadonlyMap<string, KnowledgeBaseEvaluationRetrieverV2> {
|
|
286
|
+
assertVerifiedKnowledgeBaseEvaluationV2(evaluation, corpus);
|
|
287
|
+
const output = new Map(evaluation.retrievers.map((retriever) => [
|
|
288
|
+
retriever.descriptor.id,
|
|
289
|
+
retriever,
|
|
290
|
+
]));
|
|
291
|
+
if (
|
|
292
|
+
output.size !== evaluation.retrievers.length
|
|
293
|
+
|| output.size !== corpus.retrievers.length
|
|
294
|
+
|| corpus.retrievers.some(({ id }) => !output.has(id))
|
|
295
|
+
) throw new TypeError("Evaluation retrievers must match the sealed descriptor set exactly.");
|
|
296
|
+
return output;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function assertWarmCacheFingerprint(options: Readonly<{
|
|
300
|
+
readonly corpus: RetrievalEvaluationCorpusV2;
|
|
301
|
+
readonly dependencies: KnowledgeBaseEvaluationRunnerV2Dependencies;
|
|
302
|
+
readonly profile: EvaluationMeasurementProfileV2;
|
|
303
|
+
readonly repetition: number;
|
|
304
|
+
readonly phase: "before" | "after";
|
|
305
|
+
readonly timeoutMs: number;
|
|
306
|
+
}>): Promise<void> {
|
|
307
|
+
if (options.profile.operation !== "packing" && options.profile.operation !== "warm-query") {
|
|
308
|
+
throw new TypeError(`Profile ${options.profile.id} is not a primary warm-cache operation.`);
|
|
309
|
+
}
|
|
310
|
+
const measured = await bounded({
|
|
311
|
+
timeoutMs: options.timeoutMs,
|
|
312
|
+
now: options.dependencies.now ?? performance.now.bind(performance),
|
|
313
|
+
operation: (signal) => options.dependencies.verifyWarmCacheFingerprint({
|
|
314
|
+
profileId: options.profile.id,
|
|
315
|
+
operation: options.profile.operation as "packing" | "warm-query",
|
|
316
|
+
repetition: options.repetition,
|
|
317
|
+
phase: options.phase,
|
|
318
|
+
signal,
|
|
319
|
+
}),
|
|
320
|
+
});
|
|
321
|
+
if (measured.status === "failed") {
|
|
322
|
+
throw new Error(
|
|
323
|
+
`Warm cache fingerprint verification failed ${options.phase} ${options.profile.id}: ${measured.failure.message}`,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (measured.value !== options.corpus.experiment.environment.cache.fingerprintSha256) {
|
|
327
|
+
throw new TypeError(
|
|
328
|
+
`Warm cache fingerprint drifted ${options.phase} profile ${options.profile.id}.`,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function validateFourReaderBatch(
|
|
334
|
+
batch: KnowledgeBaseEvaluationFourReaderBatchV2,
|
|
335
|
+
corpus: RetrievalEvaluationCorpusV2,
|
|
336
|
+
): readonly ReadonlyMap<string, KnowledgeBaseEvaluationRetrieverV2>[] {
|
|
337
|
+
const sealed = corpus.experiment.environment.fourReaderBatch;
|
|
338
|
+
const candidateEvaluations: unknown = batch.evaluations;
|
|
339
|
+
if (batch.id !== sealed.id || batch.sha256 !== sealed.sha256) {
|
|
340
|
+
throw new TypeError("Four-reader batch implementation does not match the sealed identity.");
|
|
341
|
+
}
|
|
342
|
+
if (
|
|
343
|
+
!Array.isArray(candidateEvaluations)
|
|
344
|
+
|| batch.evaluations.length !== 4
|
|
345
|
+
|| new Set(batch.evaluations).size !== 4
|
|
346
|
+
) throw new TypeError("Four-reader measurement requires four distinct verified evaluations.");
|
|
347
|
+
if (typeof batch.verifyCacheFingerprint !== "function") {
|
|
348
|
+
throw new TypeError("Four-reader batch must expose cache fingerprint verification.");
|
|
349
|
+
}
|
|
350
|
+
return Object.freeze(batch.evaluations.map((evaluation) => retrieversById(evaluation, corpus)));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function assertFourReaderCacheFingerprint(options: Readonly<{
|
|
354
|
+
readonly batch: KnowledgeBaseEvaluationFourReaderBatchV2;
|
|
355
|
+
readonly corpus: RetrievalEvaluationCorpusV2;
|
|
356
|
+
readonly timeoutMs: number;
|
|
357
|
+
readonly now: () => number;
|
|
358
|
+
readonly phase: "before" | "after";
|
|
359
|
+
}>): Promise<void> {
|
|
360
|
+
const measured = await bounded({
|
|
361
|
+
timeoutMs: options.timeoutMs,
|
|
362
|
+
now: options.now,
|
|
363
|
+
operation: options.batch.verifyCacheFingerprint,
|
|
364
|
+
});
|
|
365
|
+
if (measured.status === "failed") {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`Four-reader cache verification failed ${options.phase}: ${measured.failure.message}`,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
if (measured.value !== options.corpus.experiment.environment.cache.fingerprintSha256) {
|
|
371
|
+
throw new TypeError(`Four-reader cache fingerprint drifted ${options.phase} measurement.`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async function closeFourReaderBatch(batch: KnowledgeBaseEvaluationFourReaderBatchV2): Promise<void> {
|
|
376
|
+
const evaluations: readonly unknown[] = Array.isArray(batch?.evaluations) ? batch.evaluations : [];
|
|
377
|
+
const closers = evaluations.flatMap((evaluation) => {
|
|
378
|
+
if (evaluation === null || typeof evaluation !== "object") return [];
|
|
379
|
+
const candidate: { readonly close?: unknown } = evaluation;
|
|
380
|
+
if (typeof candidate.close !== "function") return [];
|
|
381
|
+
const closeable = candidate as { readonly close: () => unknown };
|
|
382
|
+
return [async (): Promise<void> => {
|
|
383
|
+
await closeable.close();
|
|
384
|
+
}];
|
|
385
|
+
});
|
|
386
|
+
const settlements = await Promise.allSettled(closers.map((close) => Promise.resolve().then(close)));
|
|
387
|
+
const failures = settlements.filter((result): result is PromiseRejectedResult => result.status === "rejected");
|
|
388
|
+
if (failures.length > 0) {
|
|
389
|
+
throw new AggregateError(
|
|
390
|
+
failures.map((failure): unknown => {
|
|
391
|
+
const reason: unknown = failure.reason;
|
|
392
|
+
return reason;
|
|
393
|
+
}),
|
|
394
|
+
"Four-reader evaluation sessions did not close cleanly.",
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async function simultaneousQuerySamples(options: Readonly<{
|
|
400
|
+
readonly corpus: RetrievalEvaluationCorpusV2;
|
|
401
|
+
readonly descriptor: EvaluationRetrieverDescriptorV2;
|
|
402
|
+
readonly profile: EvaluationMeasurementProfileV2;
|
|
403
|
+
readonly queries: readonly EvaluationQueryV2[];
|
|
404
|
+
readonly retrievers: readonly KnowledgeBaseEvaluationRetrieverV2[];
|
|
405
|
+
readonly repetition: number;
|
|
406
|
+
readonly timeoutMs: number;
|
|
407
|
+
readonly dependencies: KnowledgeBaseEvaluationRunnerV2Dependencies;
|
|
408
|
+
}>): Promise<readonly EvaluationRepeatedSampleV2[]> {
|
|
409
|
+
if (options.queries.length !== 4 || options.retrievers.length !== 4) {
|
|
410
|
+
throw new TypeError("Four-reader query batches must contain exactly four queries and readers.");
|
|
411
|
+
}
|
|
412
|
+
let release: (() => void) | undefined;
|
|
413
|
+
const barrier = new Promise<void>((resolve) => {
|
|
414
|
+
release = resolve;
|
|
415
|
+
});
|
|
416
|
+
const pending = options.queries.map(async (query, index) => {
|
|
417
|
+
await barrier;
|
|
418
|
+
const retriever = options.retrievers[index];
|
|
419
|
+
if (retriever === undefined) throw new TypeError("Four-reader batch lost a reader.");
|
|
420
|
+
return querySample({
|
|
421
|
+
corpus: options.corpus,
|
|
422
|
+
descriptor: options.descriptor,
|
|
423
|
+
profile: options.profile,
|
|
424
|
+
query,
|
|
425
|
+
repetition: options.repetition,
|
|
426
|
+
retriever,
|
|
427
|
+
timeoutMs: options.timeoutMs,
|
|
428
|
+
dependencies: options.dependencies,
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
release?.();
|
|
432
|
+
return Object.freeze(await Promise.all(pending));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async function querySample(options: Readonly<{
|
|
436
|
+
readonly corpus: RetrievalEvaluationCorpusV2;
|
|
437
|
+
readonly descriptor: EvaluationRetrieverDescriptorV2;
|
|
438
|
+
readonly profile: EvaluationMeasurementProfileV2;
|
|
439
|
+
readonly query: EvaluationQueryV2;
|
|
440
|
+
readonly repetition: number;
|
|
441
|
+
readonly retriever: KnowledgeBaseEvaluationRetrieverV2;
|
|
442
|
+
readonly timeoutMs: number;
|
|
443
|
+
readonly dependencies: KnowledgeBaseEvaluationRunnerV2Dependencies;
|
|
444
|
+
}>): Promise<EvaluationRepeatedSampleV2> {
|
|
445
|
+
const concurrencyBatchIdentity = options.profile.operation === "four-reader-query"
|
|
446
|
+
? options.corpus.experiment.environment.fourReaderBatch.id
|
|
447
|
+
: undefined;
|
|
448
|
+
const measured = await bounded({
|
|
449
|
+
timeoutMs: options.timeoutMs,
|
|
450
|
+
now: options.dependencies.now ?? performance.now.bind(performance),
|
|
451
|
+
operation: async (signal) => {
|
|
452
|
+
const now = options.dependencies.now ?? performance.now.bind(performance);
|
|
453
|
+
const queryStartedAt = now();
|
|
454
|
+
const result = await options.retriever.retrieve(createEvaluationExecutionRequestV2({
|
|
455
|
+
corpus: options.corpus,
|
|
456
|
+
query: options.query,
|
|
457
|
+
descriptor: options.descriptor,
|
|
458
|
+
limit: limit(options.descriptor),
|
|
459
|
+
signal,
|
|
460
|
+
}));
|
|
461
|
+
const queryElapsedMs = nonnegative(now() - queryStartedAt, "query elapsedMs");
|
|
462
|
+
if (options.profile.operation !== "packing") {
|
|
463
|
+
return { result, queryElapsedMs, packingElapsedMs: 0 } as const;
|
|
464
|
+
}
|
|
465
|
+
if (result.status === "unavailable") {
|
|
466
|
+
return {
|
|
467
|
+
result,
|
|
468
|
+
queryElapsedMs,
|
|
469
|
+
packingElapsedMs: 0,
|
|
470
|
+
packing: Object.freeze({
|
|
471
|
+
durationMs: 0,
|
|
472
|
+
packedContext: Object.freeze({ utf8Bytes: 0, readerTokens: 0 }),
|
|
473
|
+
includedEvidenceUnitIds: Object.freeze([]),
|
|
474
|
+
truncated: false,
|
|
475
|
+
packedBytesSha256: EMPTY_SHA256,
|
|
476
|
+
}),
|
|
477
|
+
} as const;
|
|
478
|
+
}
|
|
479
|
+
const packingStartedAt = now();
|
|
480
|
+
const packing = await options.dependencies.pack({
|
|
481
|
+
contract: packingContract(options.corpus),
|
|
482
|
+
descriptor: options.descriptor,
|
|
483
|
+
profileId: options.profile.id,
|
|
484
|
+
queryId: options.query.id,
|
|
485
|
+
result,
|
|
486
|
+
repetition: options.repetition,
|
|
487
|
+
signal,
|
|
488
|
+
});
|
|
489
|
+
const packingElapsedMs = nonnegative(now() - packingStartedAt, "packing elapsedMs");
|
|
490
|
+
return { result, packing, queryElapsedMs, packingElapsedMs } as const;
|
|
491
|
+
},
|
|
492
|
+
});
|
|
493
|
+
if (measured.status === "failed") {
|
|
494
|
+
return failedSample({
|
|
495
|
+
descriptor: options.descriptor,
|
|
496
|
+
profile: options.profile,
|
|
497
|
+
queryId: options.query.id,
|
|
498
|
+
repetition: options.repetition,
|
|
499
|
+
elapsedMs: measured.elapsedMs,
|
|
500
|
+
failure: measured.failure,
|
|
501
|
+
...(concurrencyBatchIdentity === undefined ? {} : { concurrencyBatchIdentity }),
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
const { result, packing, queryElapsedMs, packingElapsedMs } = measured.value;
|
|
505
|
+
return createKnowledgeBaseEvaluationRepeatedSampleV2({
|
|
506
|
+
result,
|
|
507
|
+
profileId: options.profile.id,
|
|
508
|
+
queryId: options.query.id,
|
|
509
|
+
repetition: options.repetition,
|
|
510
|
+
timings: {
|
|
511
|
+
elapsedMs: measured.elapsedMs,
|
|
512
|
+
queryMs: queryElapsedMs,
|
|
513
|
+
packingMs: packingElapsedMs,
|
|
514
|
+
},
|
|
515
|
+
...(concurrencyBatchIdentity === undefined ? {} : { concurrencyBatchIdentity }),
|
|
516
|
+
...(packing === undefined
|
|
517
|
+
? {}
|
|
518
|
+
: {
|
|
519
|
+
packedContext: packing.packedContext,
|
|
520
|
+
packedContextTrace: {
|
|
521
|
+
evidenceUnitIds: packing.includedEvidenceUnitIds,
|
|
522
|
+
truncated: packing.truncated,
|
|
523
|
+
packedBytesSha256: packing.packedBytesSha256,
|
|
524
|
+
},
|
|
525
|
+
}),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
async function retrieverSample(options: Readonly<{
|
|
530
|
+
readonly corpus: RetrievalEvaluationCorpusV2;
|
|
531
|
+
readonly descriptor: EvaluationRetrieverDescriptorV2;
|
|
532
|
+
readonly profile: EvaluationMeasurementProfileV2;
|
|
533
|
+
readonly repetition: number;
|
|
534
|
+
readonly timeoutMs: number;
|
|
535
|
+
readonly dependencies: KnowledgeBaseEvaluationRunnerV2Dependencies;
|
|
536
|
+
}>): Promise<EvaluationRepeatedSampleV2> {
|
|
537
|
+
if (options.profile.operation !== "cold-index" && options.profile.operation !== "incremental-update") {
|
|
538
|
+
throw new TypeError(`Retriever-scope profile ${options.profile.id} has an unsupported operation.`);
|
|
539
|
+
}
|
|
540
|
+
const measured = await bounded({
|
|
541
|
+
timeoutMs: options.timeoutMs,
|
|
542
|
+
now: options.dependencies.now ?? performance.now.bind(performance),
|
|
543
|
+
operation: (signal) => options.dependencies.measureRetrieverOperation({
|
|
544
|
+
operation: options.profile.operation as "cold-index" | "incremental-update",
|
|
545
|
+
corpus: options.corpus,
|
|
546
|
+
descriptor: options.descriptor,
|
|
547
|
+
profile: options.profile,
|
|
548
|
+
repetition: options.repetition,
|
|
549
|
+
signal,
|
|
550
|
+
}),
|
|
551
|
+
});
|
|
552
|
+
if (measured.status === "failed") {
|
|
553
|
+
return failedSample({
|
|
554
|
+
descriptor: options.descriptor,
|
|
555
|
+
profile: options.profile,
|
|
556
|
+
repetition: options.repetition,
|
|
557
|
+
elapsedMs: measured.elapsedMs,
|
|
558
|
+
failure: measured.failure,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
const sample = measured.value;
|
|
562
|
+
return Object.freeze({
|
|
563
|
+
retrieverId: options.descriptor.id,
|
|
564
|
+
profileId: options.profile.id,
|
|
565
|
+
repetition: options.repetition,
|
|
566
|
+
status: sample.status,
|
|
567
|
+
timings: sample.timings,
|
|
568
|
+
resources: sample.resources,
|
|
569
|
+
trace: sample.trace,
|
|
570
|
+
rawEvidence: Object.freeze([]),
|
|
571
|
+
...(sample.failure === undefined ? {} : { failure: sample.failure }),
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** Run the complete sealed sample matrix and validate its canonical report before returning. */
|
|
576
|
+
export async function runKnowledgeBaseEvaluationV2(
|
|
577
|
+
options: RunKnowledgeBaseEvaluationV2Options,
|
|
578
|
+
): Promise<RetrievalEvaluationReportV2> {
|
|
579
|
+
const timeoutMs = timeout(options.timeoutMs);
|
|
580
|
+
const corpus = options.split === "development"
|
|
581
|
+
? parseRetrievalEvaluationCorpusV2(options.corpus, { claimPromotion: false })
|
|
582
|
+
: options.promotionSeal === undefined
|
|
583
|
+
? (() => {
|
|
584
|
+
throw new TypeError("Held-out or all-split evaluation requires the independent promotion seal.");
|
|
585
|
+
})()
|
|
586
|
+
: validatePromotionCorpusV2(options.corpus, options.promotionSeal);
|
|
587
|
+
const now = options.dependencies.now ?? performance.now.bind(performance);
|
|
588
|
+
validateProfiles(corpus);
|
|
589
|
+
const queries = corpus.queries
|
|
590
|
+
.filter(({ split }) => options.split === "all" || split === options.split)
|
|
591
|
+
.toSorted((left, right) => left.id.localeCompare(right.id));
|
|
592
|
+
const retrieverById = retrieversById(options.evaluation, corpus);
|
|
593
|
+
const descriptors = corpus.retrievers.toSorted((left, right) => left.id.localeCompare(right.id));
|
|
594
|
+
|
|
595
|
+
const samples: EvaluationRepeatedSampleV2[] = [];
|
|
596
|
+
for (const profile of corpus.measurementProfiles.toSorted((left, right) => left.id.localeCompare(right.id))) {
|
|
597
|
+
if (profile.scope === "retriever") {
|
|
598
|
+
for (let repetition = 1; repetition <= profile.repetitions; repetition += 1) {
|
|
599
|
+
for (const descriptor of descriptors) {
|
|
600
|
+
samples.push(await retrieverSample({
|
|
601
|
+
corpus,
|
|
602
|
+
descriptor,
|
|
603
|
+
profile,
|
|
604
|
+
repetition,
|
|
605
|
+
timeoutMs,
|
|
606
|
+
dependencies: options.dependencies,
|
|
607
|
+
}));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (profile.operation === "four-reader-query") {
|
|
614
|
+
if (queries.length % profile.concurrency !== 0) {
|
|
615
|
+
throw new TypeError(`Four-reader profile ${profile.id} requires complete batches of ${profile.concurrency}.`);
|
|
616
|
+
}
|
|
617
|
+
for (let repetition = 1; repetition <= profile.repetitions; repetition += 1) {
|
|
618
|
+
const opened = await bounded({
|
|
619
|
+
timeoutMs,
|
|
620
|
+
now,
|
|
621
|
+
disposeTimedOutValue: closeFourReaderBatch,
|
|
622
|
+
operation: (signal) => options.dependencies.openFourReaderBatch({
|
|
623
|
+
profileId: profile.id,
|
|
624
|
+
repetition,
|
|
625
|
+
signal,
|
|
626
|
+
}),
|
|
627
|
+
});
|
|
628
|
+
if (opened.status === "failed") {
|
|
629
|
+
for (const descriptor of descriptors) {
|
|
630
|
+
for (const query of queries) {
|
|
631
|
+
samples.push(failedSample({
|
|
632
|
+
descriptor,
|
|
633
|
+
profile,
|
|
634
|
+
queryId: query.id,
|
|
635
|
+
repetition,
|
|
636
|
+
elapsedMs: opened.elapsedMs,
|
|
637
|
+
failure: opened.failure,
|
|
638
|
+
concurrencyBatchIdentity: corpus.experiment.environment.fourReaderBatch.id,
|
|
639
|
+
}));
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
const batch = opened.value;
|
|
645
|
+
try {
|
|
646
|
+
const readerMaps = validateFourReaderBatch(batch, corpus);
|
|
647
|
+
await assertFourReaderCacheFingerprint({
|
|
648
|
+
batch,
|
|
649
|
+
corpus,
|
|
650
|
+
timeoutMs,
|
|
651
|
+
now,
|
|
652
|
+
phase: "before",
|
|
653
|
+
});
|
|
654
|
+
for (const descriptor of descriptors) {
|
|
655
|
+
const readers = readerMaps.map((map) => {
|
|
656
|
+
const retriever = map.get(descriptor.id);
|
|
657
|
+
if (retriever === undefined) {
|
|
658
|
+
throw new TypeError(`Four-reader evaluation is missing ${descriptor.id}.`);
|
|
659
|
+
}
|
|
660
|
+
return retriever;
|
|
661
|
+
});
|
|
662
|
+
for (let index = 0; index < queries.length; index += profile.concurrency) {
|
|
663
|
+
samples.push(...await simultaneousQuerySamples({
|
|
664
|
+
corpus,
|
|
665
|
+
descriptor,
|
|
666
|
+
profile,
|
|
667
|
+
queries: queries.slice(index, index + profile.concurrency),
|
|
668
|
+
retrievers: readers,
|
|
669
|
+
repetition,
|
|
670
|
+
timeoutMs,
|
|
671
|
+
dependencies: options.dependencies,
|
|
672
|
+
}));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
await assertFourReaderCacheFingerprint({
|
|
676
|
+
batch,
|
|
677
|
+
corpus,
|
|
678
|
+
timeoutMs,
|
|
679
|
+
now,
|
|
680
|
+
phase: "after",
|
|
681
|
+
});
|
|
682
|
+
} finally {
|
|
683
|
+
await closeFourReaderBatch(batch);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (profile.operation !== "packing" && profile.operation !== "warm-query") {
|
|
690
|
+
throw new TypeError(`Query profile ${profile.id} has an unsupported operation.`);
|
|
691
|
+
}
|
|
692
|
+
for (let repetition = 1; repetition <= profile.repetitions; repetition += 1) {
|
|
693
|
+
await assertWarmCacheFingerprint({
|
|
694
|
+
corpus,
|
|
695
|
+
dependencies: options.dependencies,
|
|
696
|
+
profile,
|
|
697
|
+
repetition,
|
|
698
|
+
phase: "before",
|
|
699
|
+
timeoutMs,
|
|
700
|
+
});
|
|
701
|
+
for (const descriptor of descriptors) {
|
|
702
|
+
const retriever = retrieverById.get(descriptor.id);
|
|
703
|
+
if (retriever === undefined) throw new TypeError(`Evaluation retriever ${descriptor.id} is missing.`);
|
|
704
|
+
for (const query of queries) {
|
|
705
|
+
samples.push(await querySample({
|
|
706
|
+
corpus,
|
|
707
|
+
descriptor,
|
|
708
|
+
profile,
|
|
709
|
+
query,
|
|
710
|
+
repetition,
|
|
711
|
+
retriever,
|
|
712
|
+
timeoutMs,
|
|
713
|
+
dependencies: options.dependencies,
|
|
714
|
+
}));
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
await assertWarmCacheFingerprint({
|
|
718
|
+
corpus,
|
|
719
|
+
dependencies: options.dependencies,
|
|
720
|
+
profile,
|
|
721
|
+
repetition,
|
|
722
|
+
phase: "after",
|
|
723
|
+
timeoutMs,
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const report = Object.freeze({
|
|
728
|
+
schemaVersion: 2 as const,
|
|
729
|
+
suiteSha256: corpus.manifest.corpusSha256,
|
|
730
|
+
candidateLockSha256: corpus.manifest.candidateLockSha256,
|
|
731
|
+
split: options.split,
|
|
732
|
+
samples: Object.freeze(samples.toSorted(compareSamples)),
|
|
733
|
+
});
|
|
734
|
+
return parseRetrievalEvaluationReportV2(report, corpus);
|
|
735
|
+
}
|