@policyengine/calibration-tree 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +58 -2
- package/package.json +73 -4
- package/src/calibration-build-comparison.ts +77 -0
- package/src/calibration-comparison-blob.ts +367 -0
- package/src/calibration-comparison-bundle.ts +149 -0
- package/src/calibration-explorer.ts +202 -0
- package/src/calibration-release-locator.ts +353 -0
- package/src/calibration-tree-artifact.ts +1974 -0
- package/src/calibration-tree-blob.ts +403 -0
- package/src/calibration-tree-bundle.ts +569 -0
- package/src/calibration-tree-manifest.ts +349 -0
- package/src/calibration-tree-publication.ts +399 -0
- package/src/calibration-tree-worker.ts +637 -0
- package/src/calibration-tree.ts +1284 -0
- package/src/chronicle-metadata.ts +42 -0
- package/src/countries.ts +355 -0
- package/src/geography-labels.ts +59 -0
- package/src/hierarchy-catalog-reader.ts +297 -0
- package/src/hierarchy-target-reader.ts +243 -0
- package/src/historical-weight-summary.ts +53 -0
- package/src/latest-artifact.ts +3767 -0
- package/src/legacy-target-reader.ts +337 -0
- package/src/program-label.ts +64 -0
- package/src/publish-calibration-tree.ts +1984 -0
- package/src/reforms.ts +224 -0
- package/src/release-cut.ts +32 -0
- package/src/release-record.ts +277 -0
- package/src/source-label.ts +5 -0
- package/src/source-labels.ts +71 -0
- package/src/staging-artifact.ts +1411 -0
- package/src/staging-contract.ts +916 -0
- package/src/structured-dimension-reader.ts +137 -0
- package/src/structured-target-reader.ts +76 -0
- package/src/target-change.ts +421 -0
- package/src/target-loss-attribution-manifest.ts +436 -0
- package/src/target-loss-attribution.ts +1012 -0
- package/src/target-representation.ts +69 -0
- package/src/target-surface-matcher.ts +266 -0
- package/types/next-fetch-cache.d.ts +8 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CountryNotConfiguredError,
|
|
3
|
+
isCountry,
|
|
4
|
+
type MicrocosmCountry,
|
|
5
|
+
} from "./countries";
|
|
6
|
+
import {
|
|
7
|
+
CALIBRATION_TREE_SCHEMA_VERSION,
|
|
8
|
+
isHfCommitSha,
|
|
9
|
+
isSha256,
|
|
10
|
+
type CalibrationTreeBuildKind,
|
|
11
|
+
} from "./calibration-tree-artifact";
|
|
12
|
+
import { isReleaseDirectory } from "./release-cut";
|
|
13
|
+
|
|
14
|
+
export const CALIBRATION_TREE_MANIFEST_SCHEMA_VERSION = 6 as const;
|
|
15
|
+
export const CALIBRATION_TREE_MANIFEST_PATH = "calibration-trees/manifest.json";
|
|
16
|
+
|
|
17
|
+
export interface CalibrationTreeManifestEntry {
|
|
18
|
+
buildArtifactId: string;
|
|
19
|
+
kind: Exclude<CalibrationTreeBuildKind, "comparison">;
|
|
20
|
+
sourceId: string;
|
|
21
|
+
label: string;
|
|
22
|
+
releaseId: string | null;
|
|
23
|
+
/**
|
|
24
|
+
* The `releases/<folder>/` holding the build's files, recorded only when it
|
|
25
|
+
* differs from releaseId: a Microcosm cut is released as its tag but read
|
|
26
|
+
* from its line's folder (microcosm#823). Absent means the folder is
|
|
27
|
+
* releaseId.
|
|
28
|
+
*/
|
|
29
|
+
releaseDirectory?: string;
|
|
30
|
+
stagingRunId: string | null;
|
|
31
|
+
hfRepo: string;
|
|
32
|
+
hfCommitSha: string;
|
|
33
|
+
treeSchemaVersion: typeof CALIBRATION_TREE_SCHEMA_VERSION;
|
|
34
|
+
indexSha256: string;
|
|
35
|
+
indexBytes: number;
|
|
36
|
+
createdAt: string | null;
|
|
37
|
+
/** Null when the publisher could not date the build; never a placeholder. */
|
|
38
|
+
updatedAt: string | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface CalibrationTreeCountryManifest {
|
|
42
|
+
latestReleaseBuildArtifactId: string | null;
|
|
43
|
+
builds: CalibrationTreeManifestEntry[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CalibrationTreeManifest {
|
|
47
|
+
schemaVersion: typeof CALIBRATION_TREE_MANIFEST_SCHEMA_VERSION;
|
|
48
|
+
countries: Partial<Record<MicrocosmCountry, CalibrationTreeCountryManifest>>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function emptyCalibrationTreeManifest(): CalibrationTreeManifest {
|
|
52
|
+
return {
|
|
53
|
+
schemaVersion: CALIBRATION_TREE_MANIFEST_SCHEMA_VERSION,
|
|
54
|
+
countries: {},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function record(value: unknown, label: string): Record<string, unknown> {
|
|
59
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
60
|
+
throw new Error(`${label} must be an object.`);
|
|
61
|
+
}
|
|
62
|
+
return value as Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function optionalDate(value: unknown, label: string): string | null {
|
|
66
|
+
if (value == null) return null;
|
|
67
|
+
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
|
|
68
|
+
throw new Error(`${label} must be null or an RFC 3339 date-time.`);
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function optionalIdentifier(value: unknown, label: string): string | null {
|
|
74
|
+
if (value == null) return null;
|
|
75
|
+
if (typeof value !== "string" || !value) {
|
|
76
|
+
throw new Error(`${label} must be null or a non-empty string.`);
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function parseEntry(value: unknown, country: string): CalibrationTreeManifestEntry {
|
|
82
|
+
const entry = record(value, `Calibration tree manifest entry ${country}`);
|
|
83
|
+
if (
|
|
84
|
+
typeof entry.buildArtifactId !== "string" ||
|
|
85
|
+
!isSha256(entry.buildArtifactId)
|
|
86
|
+
) {
|
|
87
|
+
throw new Error(`Calibration tree manifest entry ${country} has an invalid build id.`);
|
|
88
|
+
}
|
|
89
|
+
if (entry.kind !== "release" && entry.kind !== "staging") {
|
|
90
|
+
throw new Error(`Calibration tree manifest entry ${country} has an invalid kind.`);
|
|
91
|
+
}
|
|
92
|
+
for (const key of ["sourceId", "label", "hfRepo"] as const) {
|
|
93
|
+
if (typeof entry[key] !== "string" || !entry[key]) {
|
|
94
|
+
throw new Error(`Calibration tree manifest entry ${country} has no ${key}.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (
|
|
98
|
+
typeof entry.hfCommitSha !== "string" ||
|
|
99
|
+
!isHfCommitSha(entry.hfCommitSha)
|
|
100
|
+
) {
|
|
101
|
+
throw new Error(`Calibration tree manifest entry ${country} has an invalid HF commit SHA.`);
|
|
102
|
+
}
|
|
103
|
+
const releaseId = optionalIdentifier(entry.releaseId, `${country}.releaseId`);
|
|
104
|
+
const stagingRunId = optionalIdentifier(
|
|
105
|
+
entry.stagingRunId,
|
|
106
|
+
`${country}.stagingRunId`,
|
|
107
|
+
);
|
|
108
|
+
if (
|
|
109
|
+
(entry.kind === "release" && (!releaseId || stagingRunId)) ||
|
|
110
|
+
(entry.kind === "staging" && (!stagingRunId || releaseId))
|
|
111
|
+
) {
|
|
112
|
+
throw new Error(`Calibration tree manifest entry ${country} has inconsistent aliases.`);
|
|
113
|
+
}
|
|
114
|
+
// The folder is interpolated into authenticated Hugging Face URLs.
|
|
115
|
+
if (entry.releaseDirectory != null && !isReleaseDirectory(entry.releaseDirectory)) {
|
|
116
|
+
throw new Error(`Calibration tree manifest entry ${country} has an invalid release directory.`);
|
|
117
|
+
}
|
|
118
|
+
if (entry.treeSchemaVersion !== CALIBRATION_TREE_SCHEMA_VERSION) {
|
|
119
|
+
throw new Error(`Calibration tree manifest entry ${country} has an unsupported tree schema.`);
|
|
120
|
+
}
|
|
121
|
+
if (typeof entry.indexSha256 !== "string" || !isSha256(entry.indexSha256)) {
|
|
122
|
+
throw new Error(`Calibration tree manifest entry ${country} has an invalid index hash.`);
|
|
123
|
+
}
|
|
124
|
+
if (!Number.isSafeInteger(entry.indexBytes) || (entry.indexBytes as number) <= 0) {
|
|
125
|
+
throw new Error(`Calibration tree manifest entry ${country} has an invalid index byte count.`);
|
|
126
|
+
}
|
|
127
|
+
optionalDate(entry.createdAt, `${country}.createdAt`);
|
|
128
|
+
optionalDate(entry.updatedAt, `${country}.updatedAt`);
|
|
129
|
+
return entry as unknown as CalibrationTreeManifestEntry;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function parseCountryManifest(
|
|
133
|
+
value: unknown,
|
|
134
|
+
country: MicrocosmCountry,
|
|
135
|
+
): CalibrationTreeCountryManifest {
|
|
136
|
+
const parsed = record(value, `Calibration tree manifest country ${country}`);
|
|
137
|
+
if (!Array.isArray(parsed.builds)) {
|
|
138
|
+
throw new Error(`Calibration tree manifest country ${country} has no build list.`);
|
|
139
|
+
}
|
|
140
|
+
const builds = parsed.builds.map((entry) => parseEntry(entry, country));
|
|
141
|
+
const ids = new Set(builds.map((entry) => entry.buildArtifactId));
|
|
142
|
+
if (ids.size !== builds.length) {
|
|
143
|
+
throw new Error(`Calibration tree manifest country ${country} repeats a build id.`);
|
|
144
|
+
}
|
|
145
|
+
const aliases = builds.map((entry) =>
|
|
146
|
+
entry.kind === "release"
|
|
147
|
+
? `release:${entry.releaseId}`
|
|
148
|
+
: `staging:${entry.stagingRunId}`
|
|
149
|
+
);
|
|
150
|
+
if (new Set(aliases).size !== aliases.length) {
|
|
151
|
+
throw new Error(`Calibration tree manifest country ${country} repeats a source alias.`);
|
|
152
|
+
}
|
|
153
|
+
const latest = optionalIdentifier(
|
|
154
|
+
parsed.latestReleaseBuildArtifactId,
|
|
155
|
+
`${country}.latestReleaseBuildArtifactId`,
|
|
156
|
+
);
|
|
157
|
+
if (
|
|
158
|
+
latest &&
|
|
159
|
+
!builds.some(
|
|
160
|
+
(entry) => entry.buildArtifactId === latest && entry.kind === "release",
|
|
161
|
+
)
|
|
162
|
+
) {
|
|
163
|
+
throw new Error(`Calibration tree manifest country ${country} has an invalid latest build.`);
|
|
164
|
+
}
|
|
165
|
+
return { latestReleaseBuildArtifactId: latest, builds };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function parseCalibrationTreeManifest(value: unknown): CalibrationTreeManifest {
|
|
169
|
+
const manifest = record(value, "Calibration tree manifest");
|
|
170
|
+
if (manifest.schemaVersion !== CALIBRATION_TREE_MANIFEST_SCHEMA_VERSION) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`Unsupported calibration tree manifest schema ${String(manifest.schemaVersion)}.`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const countries = record(manifest.countries, "Calibration tree manifest countries");
|
|
176
|
+
const parsedCountries: CalibrationTreeManifest["countries"] = {};
|
|
177
|
+
for (const [country, entry] of Object.entries(countries)) {
|
|
178
|
+
if (!isCountry(country)) {
|
|
179
|
+
throw new Error(`Calibration tree manifest contains unknown country ${country}.`);
|
|
180
|
+
}
|
|
181
|
+
parsedCountries[country] = parseCountryManifest(entry, country);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
schemaVersion: CALIBRATION_TREE_MANIFEST_SCHEMA_VERSION,
|
|
185
|
+
countries: parsedCountries,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function serializeCalibrationTreeManifest(
|
|
190
|
+
manifest: CalibrationTreeManifest,
|
|
191
|
+
): string {
|
|
192
|
+
const parsed = parseCalibrationTreeManifest(manifest);
|
|
193
|
+
const countries = Object.fromEntries(
|
|
194
|
+
Object.entries(parsed.countries)
|
|
195
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
196
|
+
.map(([country, entry]) => [
|
|
197
|
+
country,
|
|
198
|
+
{
|
|
199
|
+
...entry,
|
|
200
|
+
builds: [...entry.builds].sort((left, right) =>
|
|
201
|
+
left.buildArtifactId.localeCompare(right.buildArtifactId),
|
|
202
|
+
),
|
|
203
|
+
},
|
|
204
|
+
]),
|
|
205
|
+
);
|
|
206
|
+
return `${JSON.stringify({ ...parsed, countries })}\n`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function calibrationTreeCountryManifest(
|
|
210
|
+
manifest: CalibrationTreeManifest,
|
|
211
|
+
country: MicrocosmCountry,
|
|
212
|
+
): CalibrationTreeCountryManifest {
|
|
213
|
+
const entry = manifest.countries[country];
|
|
214
|
+
// A registered country the publisher has not written yet: an honest
|
|
215
|
+
// not-configured state, never another country's builds.
|
|
216
|
+
if (!entry) throw new CountryNotConfiguredError(country, "no_published_release");
|
|
217
|
+
return entry;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** The `releases/` folder a release build's files are read from. */
|
|
221
|
+
export function releaseDirectoryOf(
|
|
222
|
+
entry: Pick<CalibrationTreeManifestEntry, "releaseId" | "releaseDirectory">,
|
|
223
|
+
): string | null {
|
|
224
|
+
return entry.releaseDirectory ?? entry.releaseId;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function calibrationTreeManifestEntry(
|
|
228
|
+
manifest: CalibrationTreeManifest,
|
|
229
|
+
country: MicrocosmCountry,
|
|
230
|
+
selector: { buildArtifactId?: string; releaseId?: string; stagingRunId?: string },
|
|
231
|
+
): CalibrationTreeManifestEntry {
|
|
232
|
+
const countryManifest = calibrationTreeCountryManifest(manifest, country);
|
|
233
|
+
const wantsLatest = !selector.buildArtifactId && (
|
|
234
|
+
selector.releaseId === "latest" ||
|
|
235
|
+
(!selector.releaseId && !selector.stagingRunId)
|
|
236
|
+
);
|
|
237
|
+
// Builds (staging runs, say) without a published release: the country has
|
|
238
|
+
// no current release to serve yet.
|
|
239
|
+
if (wantsLatest && !countryManifest.latestReleaseBuildArtifactId) {
|
|
240
|
+
throw new CountryNotConfiguredError(country, "no_published_release");
|
|
241
|
+
}
|
|
242
|
+
const buildArtifactId = selector.buildArtifactId ?? (
|
|
243
|
+
wantsLatest ? countryManifest.latestReleaseBuildArtifactId ?? undefined : undefined
|
|
244
|
+
);
|
|
245
|
+
const result = countryManifest.builds.find((entry) =>
|
|
246
|
+
buildArtifactId
|
|
247
|
+
? entry.buildArtifactId === buildArtifactId
|
|
248
|
+
: selector.releaseId
|
|
249
|
+
? entry.releaseId === selector.releaseId
|
|
250
|
+
: entry.stagingRunId === selector.stagingRunId
|
|
251
|
+
);
|
|
252
|
+
if (!result) throw new Error(`No matching calibration build exists for ${country}.`);
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function withCalibrationTreeManifestEntry(
|
|
257
|
+
manifest: CalibrationTreeManifest,
|
|
258
|
+
country: MicrocosmCountry,
|
|
259
|
+
entry: CalibrationTreeManifestEntry,
|
|
260
|
+
makeLatest = false,
|
|
261
|
+
): CalibrationTreeManifest {
|
|
262
|
+
parseEntry(entry, country);
|
|
263
|
+
const current = manifest.countries[country] ?? {
|
|
264
|
+
latestReleaseBuildArtifactId: null,
|
|
265
|
+
builds: [],
|
|
266
|
+
};
|
|
267
|
+
const previous = current.builds.find(
|
|
268
|
+
(build) => build.buildArtifactId === entry.buildArtifactId,
|
|
269
|
+
);
|
|
270
|
+
const aliasConflict = current.builds.find((build) =>
|
|
271
|
+
build.buildArtifactId !== entry.buildArtifactId &&
|
|
272
|
+
build.kind === entry.kind &&
|
|
273
|
+
(entry.kind === "release"
|
|
274
|
+
? build.releaseId === entry.releaseId
|
|
275
|
+
: build.stagingRunId === entry.stagingRunId)
|
|
276
|
+
);
|
|
277
|
+
if (aliasConflict) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Calibration source ${entry.sourceId} already resolves to build ${aliasConflict.buildArtifactId}.`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
if (previous) {
|
|
283
|
+
const immutableFields: Array<keyof CalibrationTreeManifestEntry> = [
|
|
284
|
+
"buildArtifactId",
|
|
285
|
+
"kind",
|
|
286
|
+
"sourceId",
|
|
287
|
+
"releaseId",
|
|
288
|
+
"releaseDirectory",
|
|
289
|
+
"stagingRunId",
|
|
290
|
+
"hfRepo",
|
|
291
|
+
"hfCommitSha",
|
|
292
|
+
"treeSchemaVersion",
|
|
293
|
+
"indexSha256",
|
|
294
|
+
"indexBytes",
|
|
295
|
+
];
|
|
296
|
+
if (immutableFields.some((field) => previous[field] !== entry[field])) {
|
|
297
|
+
throw new Error(`Calibration build ${entry.buildArtifactId} is immutable.`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// A build's content identity is immutable; its metadata (label and the
|
|
301
|
+
// publication timestamps) is replaced, so a reconciliation can correct a
|
|
302
|
+
// stored timestamp without re-publishing the bundle.
|
|
303
|
+
const builds = previous
|
|
304
|
+
? current.builds.map((build) =>
|
|
305
|
+
build.buildArtifactId === entry.buildArtifactId ? entry : build,
|
|
306
|
+
)
|
|
307
|
+
: [...current.builds, entry];
|
|
308
|
+
const latestReleaseBuildArtifactId = makeLatest
|
|
309
|
+
? entry.buildArtifactId
|
|
310
|
+
: current.latestReleaseBuildArtifactId;
|
|
311
|
+
if (makeLatest && entry.kind !== "release") {
|
|
312
|
+
throw new Error("Only a release build can become the latest release.");
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
schemaVersion: CALIBRATION_TREE_MANIFEST_SCHEMA_VERSION,
|
|
316
|
+
countries: {
|
|
317
|
+
...manifest.countries,
|
|
318
|
+
[country]: { latestReleaseBuildArtifactId, builds },
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
interface TimestampedCalibrationBuild {
|
|
324
|
+
buildArtifactId: string;
|
|
325
|
+
createdAt: string | null;
|
|
326
|
+
updatedAt: string | null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// When a build was published: createdAt, else updatedAt. An undated build
|
|
330
|
+
// sorts last rather than wherever NaN lands.
|
|
331
|
+
function buildPublishedInstant(build: TimestampedCalibrationBuild): number {
|
|
332
|
+
const value = build.createdAt ?? build.updatedAt;
|
|
333
|
+
const time = value == null ? Number.NaN : Date.parse(value);
|
|
334
|
+
return Number.isFinite(time) ? time : Number.NEGATIVE_INFINITY;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Build records newest first, by their actual RFC 3339 instant rather than
|
|
339
|
+
* timestamp text; builds published at the same instant are ordered by build
|
|
340
|
+
* ID.
|
|
341
|
+
*/
|
|
342
|
+
export function calibrationBuildsNewestFirst<
|
|
343
|
+
T extends TimestampedCalibrationBuild,
|
|
344
|
+
>(builds: readonly T[]): T[] {
|
|
345
|
+
return [...builds].sort((left, right) =>
|
|
346
|
+
buildPublishedInstant(right) - buildPublishedInstant(left) ||
|
|
347
|
+
left.buildArtifactId.localeCompare(right.buildArtifactId)
|
|
348
|
+
);
|
|
349
|
+
}
|