@phosphorco/bb-thread-metrics 0.2.0 → 0.2.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/CONTRACT.md +130 -0
- package/dist/bb.js +267 -17
- package/dist/index.js +428 -69
- package/dist/testing.js +70 -1
- package/dist/types/bb.d.ts +1064 -8
- package/dist/types/heat-settings.d.ts +413 -0
- package/dist/types/index.d.ts +19 -16
- package/dist/types/recent-session.d.ts +51 -0
- package/dist/types/recent.d.ts +5356 -363
- package/dist/types/testing.d.ts +8 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/recent.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { UsageVectorSchema } from "@phosphorco/bb-usage-algebra/vector";
|
|
3
4
|
var RECENT_LIMITS = Object.freeze({ rows: 1000, bytes: 4 * 1024 * 1024, rowBytes: 8 * 1024, inputBytes: 1024, providers: 32, windowMs: 3 * 24 * 60 * 60 * 1000 });
|
|
4
5
|
var id = z.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
5
6
|
var revision = z.string().min(1).max(256);
|
|
@@ -16,17 +17,37 @@ var recentDollarSchema = z.discriminatedUnion("kind", [
|
|
|
16
17
|
z.object({ kind: z.literal("unavailable"), amount: z.null() }).strip()
|
|
17
18
|
]);
|
|
18
19
|
var recentWindowsSchema = windowShape(recentDollarSchema);
|
|
20
|
+
var flags = windowShape(z.boolean());
|
|
21
|
+
var recentAttributionSchema = z.object({ unknownProvider: recentWindowsSchema, attributionComplete: flags, unpriced: flags }).strip();
|
|
22
|
+
var recentPresentationSchema = z.object({ title: z.string().trim().min(1).max(512).nullable(), navigation: z.discriminatedUnion("kind", [
|
|
23
|
+
z.object({ kind: z.literal("personal") }).strip(),
|
|
24
|
+
z.object({ kind: z.literal("project"), projectId: id }).strip()
|
|
25
|
+
]).nullable() }).strip();
|
|
19
26
|
var recentApproximationSchema = z.object({ approximate: z.boolean(), reasons: z.array(z.enum(["closest-model", "provider-default", "model-attribution", "bucket-boundary", "missing-price", "source-gap"])).max(6) }).strip().superRefine((v, c) => {
|
|
20
27
|
if (!v.approximate && v.reasons.length)
|
|
21
28
|
c.addIssue({ code: "custom", message: "Approximation reasons require approximate flag" });
|
|
22
29
|
});
|
|
23
30
|
var provider = z.object({ providerId: id, windows: recentWindowsSchema, approximation: recentApproximationSchema }).strip();
|
|
31
|
+
var recentTokenValueSchema = z.discriminatedUnion("kind", [
|
|
32
|
+
z.object({ kind: z.literal("estimate"), value: UsageVectorSchema }).strip(),
|
|
33
|
+
z.object({ kind: z.literal("floor"), value: UsageVectorSchema }).strip(),
|
|
34
|
+
z.object({ kind: z.literal("unavailable"), value: z.null() }).strip()
|
|
35
|
+
]);
|
|
36
|
+
var recentTokenWindowsSchema = windowShape(recentTokenValueSchema);
|
|
37
|
+
var recentUsageSchema = z.object({
|
|
38
|
+
windows: recentTokenWindowsSchema,
|
|
39
|
+
providers: z.array(z.object({ providerId: id, windows: recentTokenWindowsSchema }).strip()).max(RECENT_LIMITS.providers),
|
|
40
|
+
omittedProviders: count,
|
|
41
|
+
descendants: z.object({ count, exact: z.boolean() }).strip()
|
|
42
|
+
}).strip();
|
|
24
43
|
var recentScopeSchema = z.object({
|
|
25
44
|
memberCount: count,
|
|
26
45
|
coverage,
|
|
27
46
|
coverageReasons: reasons,
|
|
28
47
|
priceVersion: revision,
|
|
29
48
|
approximation: recentApproximationSchema,
|
|
49
|
+
usage: recentUsageSchema.optional(),
|
|
50
|
+
attribution: recentAttributionSchema.optional(),
|
|
30
51
|
windows: recentWindowsSchema,
|
|
31
52
|
scores: recentWindowsSchema,
|
|
32
53
|
providers: z.array(provider).max(RECENT_LIMITS.providers),
|
|
@@ -38,17 +59,123 @@ var recentScopeSchema = z.object({
|
|
|
38
59
|
c.addIssue({ code: "custom", message: "Duplicate providers" });
|
|
39
60
|
if (v.omittedProviders && v.coverage === "complete")
|
|
40
61
|
c.addIssue({ code: "custom", message: "Omitted providers require partial coverage" });
|
|
62
|
+
if (v.usage) {
|
|
63
|
+
const u = v.usage, fail = (message) => c.addIssue({ code: "custom", path: ["usage"], message });
|
|
64
|
+
const providers = new Set(v.providers.map((p) => p.providerId));
|
|
65
|
+
if (u.providers.length !== providers.size || new Set(u.providers.map((p) => p.providerId)).size !== u.providers.length || u.providers.some((p) => !providers.has(p.providerId)) || u.omittedProviders !== v.omittedProviders)
|
|
66
|
+
fail("Token provider attribution mismatch");
|
|
67
|
+
if (u.descendants.count !== Math.max(0, v.memberCount - 1))
|
|
68
|
+
fail("Descendant/member count mismatch");
|
|
69
|
+
if (u.descendants.exact && v.coverageReasons.some((reason2) => ["member-cap", "depth-cap", "edge-cap"].includes(reason2)))
|
|
70
|
+
fail("Membership caps cannot carry an exact descendant count");
|
|
71
|
+
const near = (a, b) => Math.abs(a - b) <= 4 * Number.EPSILON * Math.max(a, b);
|
|
72
|
+
for (const w of ["m20", "h24", "d7", "lifetime"]) {
|
|
73
|
+
const total = u.windows[w], known = u.providers.filter((p) => p.windows[w].value !== null), all = !u.omittedProviders && known.length === u.providers.length;
|
|
74
|
+
if (total.kind === "estimate" && (u.omittedProviders > 0 || u.providers.some((p) => p.windows[w].kind !== "estimate")))
|
|
75
|
+
fail("Incomplete provider tokens cannot carry an aggregate estimate");
|
|
76
|
+
for (const k of ["inputUncached", "cacheRead", "cacheWrite", "output"]) {
|
|
77
|
+
const sum = known.reduce((n, p) => n + p.windows[w].value[k], 0), value = total.value?.[k];
|
|
78
|
+
if (!Number.isFinite(sum) || value === undefined && sum > 0 || value !== undefined && (sum > value && !near(sum, value) || all && !near(sum, value)))
|
|
79
|
+
fail("Token provider conservation mismatch");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
41
83
|
});
|
|
42
|
-
var recentRowSchema = z.object({ threadId: id, revision, configRevision: revision, asOf: time, ownLastFoldAt: time.nullable(), familyLastFoldAt: time.nullable(), own: recentScopeSchema, family: recentScopeSchema }).strip();
|
|
43
84
|
var recentSnapshotInputSchema = z.object({ ifRevision: revision.optional() }).strict();
|
|
44
|
-
var thresholds = windowShape(z.number().finite().
|
|
45
|
-
var weights = z.record(id, z.number().finite().
|
|
85
|
+
var thresholds = windowShape(z.number().finite().min(0).max(1e6));
|
|
86
|
+
var weights = z.record(id, z.number().finite().min(0).max(3)).refine((v) => Object.keys(v).length <= RECENT_LIMITS.providers, "Too many provider weights");
|
|
46
87
|
var configBase = { schemaVersion: z.literal(2), providerWeights: weights };
|
|
47
88
|
var scoringConfigV2Schema = z.discriminatedUnion("mode", [
|
|
48
89
|
z.object({ ...configBase, mode: z.literal("percentile"), gates: z.object({ m20: z.number().finite().min(0).max(1), h24: z.number().finite().min(0).max(1), d7: z.number().finite().min(0).max(1) }).strip() }).strip(),
|
|
49
|
-
z.object({ ...configBase, mode: z.literal("absolute"), thresholds }).strip()
|
|
90
|
+
z.object({ ...configBase, mode: z.literal("absolute"), thresholds, lifetimeEnabled: z.boolean().optional() }).strip()
|
|
50
91
|
]);
|
|
51
92
|
var DEFAULT_SCORING_CONFIG_V2 = Object.freeze({ schemaVersion: 2, mode: "percentile", providerWeights: Object.freeze({}), gates: Object.freeze({ m20: 0.9, h24: 0.9, d7: 0.9 }) });
|
|
93
|
+
var ROOT_FAMILY_HEAT_DOMAIN_V1 = Object.freeze({ version: 1, kind: "root-family" });
|
|
94
|
+
var rootFamilyHeatDomainV1Schema = z.object({ version: z.literal(1), kind: z.literal("root-family") }).strip();
|
|
95
|
+
var configV3Base = { schemaVersion: z.literal(3), domain: rootFamilyHeatDomainV1Schema, providerWeights: weights };
|
|
96
|
+
var scoringConfigV3Schema = z.discriminatedUnion("mode", [
|
|
97
|
+
z.object({ ...configV3Base, mode: z.literal("percentile"), gates: z.object({ m20: z.number().finite().min(0).max(1), h24: z.number().finite().min(0).max(1), d7: z.number().finite().min(0).max(1) }).strip() }).strip(),
|
|
98
|
+
z.object({ ...configV3Base, mode: z.literal("absolute"), thresholds, lifetimeEnabled: z.literal(false).optional() }).strip()
|
|
99
|
+
]);
|
|
100
|
+
var storedScoringConfigSchema = z.union([scoringConfigV2Schema, scoringConfigV3Schema]);
|
|
101
|
+
var DEFAULT_SCORING_CONFIG_V3 = Object.freeze({ schemaVersion: 3, domain: ROOT_FAMILY_HEAT_DOMAIN_V1, mode: "percentile", providerWeights: Object.freeze({}), gates: Object.freeze({ m20: 0.9, h24: 0.8, d7: 0.8 }) });
|
|
102
|
+
var HEAT_WINDOWS = ["m20", "h24", "d7"];
|
|
103
|
+
var rankWindows = z.object({ m20: rank, h24: rank, d7: rank }).strip();
|
|
104
|
+
var rootTopologyV1Schema = z.discriminatedUnion("kind", [
|
|
105
|
+
z.object({ kind: z.literal("attested"), parentThreadId: id.nullable(), lifecycleOwnerThreadId: id.nullable(), deleted: z.boolean() }).strip(),
|
|
106
|
+
z.object({ kind: z.literal("unknown"), reasons }).strip()
|
|
107
|
+
]);
|
|
108
|
+
function rootFamilyEligibilityV1(input) {
|
|
109
|
+
const topology = rootTopologyV1Schema.parse(input);
|
|
110
|
+
if (topology.kind === "unknown")
|
|
111
|
+
return "unknown";
|
|
112
|
+
if (topology.deleted)
|
|
113
|
+
return "deleted";
|
|
114
|
+
return topology.parentThreadId === null && topology.lifecycleOwnerThreadId === null ? "eligible-root" : "ineligible-child";
|
|
115
|
+
}
|
|
116
|
+
var coveredPopulation = z.object({ count, coverage, reasons }).strip();
|
|
117
|
+
var rootPopulations = z.object({ m20: coveredPopulation, h24: coveredPopulation, d7: coveredPopulation }).strip();
|
|
118
|
+
var interval = z.object({ start: time, end: time }).strip();
|
|
119
|
+
var rootFamilyHeatContextV1Schema = z.object({
|
|
120
|
+
version: z.literal(1),
|
|
121
|
+
domain: rootFamilyHeatDomainV1Schema,
|
|
122
|
+
contextRevision: revision,
|
|
123
|
+
configRevision: revision,
|
|
124
|
+
topologyRevision: revision,
|
|
125
|
+
rankRevision: revision,
|
|
126
|
+
rankAsOf: time,
|
|
127
|
+
windows: z.object({ m20: interval, h24: interval, d7: interval }).strip(),
|
|
128
|
+
population: z.object({ dollars: rootPopulations, scores: rootPopulations }).strip()
|
|
129
|
+
}).strip().superRefine((v, c) => {
|
|
130
|
+
const duration = { m20: 20 * 60 * 1000, h24: 24 * 60 * 60 * 1000, d7: 7 * 24 * 60 * 60 * 1000 };
|
|
131
|
+
for (const w of HEAT_WINDOWS)
|
|
132
|
+
if (v.windows[w].end !== v.rankAsOf || v.windows[w].start !== Math.max(0, v.rankAsOf - duration[w]))
|
|
133
|
+
c.addIssue({ code: "custom", message: "Invalid root heat window provenance", path: ["windows", w] });
|
|
134
|
+
});
|
|
135
|
+
var decision = z.object({ decision: z.enum(["qualifies", "below", "suppressed", "unavailable"]), reason: z.enum(["threshold-met", "below-threshold", "gate-met", "below-gate", "not-root", "deleted", "topology-unknown", "unsupported-value", "zero", "insufficient-population", "rank-unavailable", "context-unavailable"]) }).strip();
|
|
136
|
+
var rootFamilyDecisionSetV1Schema = z.object({ contextRevision: revision, signals: z.object({ m20: decision, h24: decision, d7: decision }).strip(), winner: z.enum(["on-fire", "hot", "heavy"]).nullable() }).strip();
|
|
137
|
+
var recentRootHeatSchema = z.object({ topology: rootTopologyV1Schema, rootDollarRanks: rankWindows, rootScoreRanks: rankWindows, decisions: rootFamilyDecisionSetV1Schema }).strip();
|
|
138
|
+
function rootFamilyHeatV1(input) {
|
|
139
|
+
const context = rootFamilyHeatContextV1Schema.parse(input.context), config = scoringConfigV3Schema.parse(input.config), family = input.family;
|
|
140
|
+
if (family.configRevision !== context.configRevision || family.contextRevision !== context.contextRevision)
|
|
141
|
+
throw new Error("Root heat configuration/context mismatch");
|
|
142
|
+
const eligibility = rootFamilyEligibilityV1(input.topology), signals = {};
|
|
143
|
+
for (const w of HEAT_WINDOWS) {
|
|
144
|
+
const score = recentDollarSchema.parse(family.scores[w]), r = rank.parse(family.rootScoreRanks[w]), pop = context.population.scores[w];
|
|
145
|
+
if (r !== null && (pop.count === 0 || pop.count === 1 && r !== 0 || pop.coverage === "unknown"))
|
|
146
|
+
throw new Error("Root rank population mismatch");
|
|
147
|
+
if (eligibility !== "eligible-root") {
|
|
148
|
+
signals[w] = { decision: eligibility === "unknown" ? "unavailable" : "suppressed", reason: eligibility === "unknown" ? "topology-unknown" : eligibility === "deleted" ? "deleted" : "not-root" };
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (score.amount === null) {
|
|
152
|
+
signals[w] = { decision: "unavailable", reason: "unsupported-value" };
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (score.amount === 0) {
|
|
156
|
+
signals[w] = score.kind === "floor" ? { decision: "unavailable", reason: "unsupported-value" } : { decision: "suppressed", reason: "zero" };
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (config.mode === "absolute") {
|
|
160
|
+
const hit = score.amount >= config.thresholds[w];
|
|
161
|
+
signals[w] = !hit && score.kind === "floor" ? { decision: "unavailable", reason: "unsupported-value" } : { decision: hit ? "qualifies" : "below", reason: hit ? "threshold-met" : "below-threshold" };
|
|
162
|
+
} else if (pop.coverage === "unknown")
|
|
163
|
+
signals[w] = { decision: "unavailable", reason: "rank-unavailable" };
|
|
164
|
+
else if (pop.count < 2)
|
|
165
|
+
signals[w] = { decision: "suppressed", reason: "insufficient-population" };
|
|
166
|
+
else if (r === null)
|
|
167
|
+
signals[w] = { decision: "unavailable", reason: "rank-unavailable" };
|
|
168
|
+
else {
|
|
169
|
+
const hit = r >= config.gates[w];
|
|
170
|
+
signals[w] = { decision: hit ? "qualifies" : "below", reason: hit ? "gate-met" : "below-gate" };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const winner = signals.m20.decision === "qualifies" ? "on-fire" : signals.h24.decision === "qualifies" ? "hot" : signals.d7.decision === "qualifies" ? "heavy" : null;
|
|
174
|
+
return { contextRevision: context.contextRevision, signals, winner };
|
|
175
|
+
}
|
|
176
|
+
var recentRowSchema = z.object({ threadId: id, revision, configRevision: revision, asOf: time, presentation: recentPresentationSchema.optional(), ownLastFoldAt: time.nullable(), familyLastFoldAt: time.nullable(), own: recentScopeSchema, family: recentScopeSchema, heat: recentRootHeatSchema.optional() }).strip();
|
|
177
|
+
var recentRootInputSchema = z.object({ threadId: id, asOf: time, configRevision: revision, presentation: recentPresentationSchema.optional(), family: recentScopeSchema, heat: recentRootHeatSchema }).strip();
|
|
178
|
+
var recentHeatInputsSchema = z.object({ contextRevision: revision, topologyRevision: revision, cohortComplete: z.boolean(), pending: count, omitted: z.object({ count, exact: z.boolean() }).strip(), rows: z.array(recentRootInputSchema).max(RECENT_LIMITS.rows) }).strip();
|
|
52
179
|
function weightedScoreV2(values, providerWeights) {
|
|
53
180
|
if (values.length > RECENT_LIMITS.providers)
|
|
54
181
|
throw new Error("Too many score providers");
|
|
@@ -76,9 +203,11 @@ function weightedScoreV2(values, providerWeights) {
|
|
|
76
203
|
var population = z.object({ count, coverage }).strip();
|
|
77
204
|
var populations = z.object({ m20: population, h24: population, d7: population }).strip();
|
|
78
205
|
var scopedPopulations = z.object({ dollars: populations, scores: populations }).strip();
|
|
206
|
+
var recentPublicationIdentitySchema = z.object({ instance: z.string().min(1).max(128), sequence: z.number().int().positive().max(Number.MAX_SAFE_INTEGER) }).strip();
|
|
79
207
|
var recentSnapshotFullSchema = z.object({
|
|
80
208
|
version: z.literal(1),
|
|
81
209
|
revision,
|
|
210
|
+
publication: recentPublicationIdentitySchema.optional(),
|
|
82
211
|
unchanged: z.literal(false),
|
|
83
212
|
currency: z.literal("USD"),
|
|
84
213
|
estimated: z.literal(true),
|
|
@@ -86,7 +215,9 @@ var recentSnapshotFullSchema = z.object({
|
|
|
86
215
|
asOf: time,
|
|
87
216
|
rankAsOf: time,
|
|
88
217
|
recentWindowMs: z.literal(RECENT_LIMITS.windowMs),
|
|
89
|
-
config: z.object({ revision, value:
|
|
218
|
+
config: z.object({ revision, value: storedScoringConfigSchema }).strip(),
|
|
219
|
+
heat: rootFamilyHeatContextV1Schema.optional(),
|
|
220
|
+
heatInputs: recentHeatInputsSchema.optional(),
|
|
90
221
|
coverage,
|
|
91
222
|
coverageReasons: reasons,
|
|
92
223
|
materializationPending: count,
|
|
@@ -101,13 +232,15 @@ function validateRecentSnapshot(input, value) {
|
|
|
101
232
|
if (bytes(value) > RECENT_LIMITS.bytes)
|
|
102
233
|
throw new Error("Snapshot byte limit");
|
|
103
234
|
const raw = value;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
235
|
+
let entries = 0;
|
|
236
|
+
for (const rows of [raw?.rows, raw?.heatInputs?.rows])
|
|
237
|
+
if (rows !== undefined) {
|
|
238
|
+
if (!Array.isArray(rows) || (entries += rows.length) > RECENT_LIMITS.rows)
|
|
239
|
+
throw new Error("Snapshot aggregate row limit");
|
|
240
|
+
for (const row of rows)
|
|
241
|
+
if (bytes(row) > RECENT_LIMITS.rowBytes)
|
|
242
|
+
throw new Error("Snapshot row byte limit");
|
|
243
|
+
}
|
|
111
244
|
if (value && typeof value === "object" && value.unchanged === true) {
|
|
112
245
|
for (const field of Object.keys(recentSnapshotFullSchema.shape))
|
|
113
246
|
if (!["version", "revision", "unchanged"].includes(field) && Object.hasOwn(value, field))
|
|
@@ -119,6 +252,10 @@ function validateRecentSnapshot(input, value) {
|
|
|
119
252
|
throw new Error("Unmatched snapshot revision");
|
|
120
253
|
return out;
|
|
121
254
|
}
|
|
255
|
+
if (out.config.value.schemaVersion === 3 && !out.heat)
|
|
256
|
+
throw new Error("Root-family configuration requires heat context");
|
|
257
|
+
if (out.heat && (out.config.value.schemaVersion !== 3 || out.heat.configRevision !== out.config.revision || out.heat.rankAsOf !== out.rankAsOf))
|
|
258
|
+
throw new Error("Root heat configuration/rank epoch mismatch");
|
|
122
259
|
if (out.rankAsOf > out.asOf)
|
|
123
260
|
throw new Error("Future rank time");
|
|
124
261
|
if ((out.materializationPending || out.omittedRows.count || !out.omittedRows.exact) && out.coverage === "complete")
|
|
@@ -146,7 +283,19 @@ function validateRecentSnapshot(input, value) {
|
|
|
146
283
|
validateProjection(row[scope], out.config.value.providerWeights);
|
|
147
284
|
validateRanks(row[scope], out.population[scope], out.rankAsOf >= row.asOf);
|
|
148
285
|
}
|
|
286
|
+
validateRootHeatRow(row, out.heat, out.config.value);
|
|
287
|
+
}
|
|
288
|
+
if (out.heat) {
|
|
289
|
+
for (const [axis, field] of [["dollars", "windows"], ["scores", "scores"]]) {
|
|
290
|
+
for (const w of HEAT_WINDOWS) {
|
|
291
|
+
const pop = out.heat.population[axis][w], supported = out.rows.filter((row) => row.heat && rootFamilyEligibilityV1(row.heat.topology) === "eligible-root" && row.asOf <= out.heat.rankAsOf && (row.family[field][w].amount ?? 0) > 0).length;
|
|
292
|
+
if (pop.coverage === "complete" && pop.count < supported)
|
|
293
|
+
throw new Error("Root population below supported displayed members");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
149
296
|
}
|
|
297
|
+
if (out.heatInputs)
|
|
298
|
+
validateHeatInputs(out);
|
|
150
299
|
if (out.omittedRows.count && out.rows.length && out.cutoffLastFoldAt !== previousActivity)
|
|
151
300
|
throw new Error("Missing eviction cutoff");
|
|
152
301
|
return out;
|
|
@@ -173,6 +322,21 @@ function validateProjection(projection, providerWeights) {
|
|
|
173
322
|
const incomplete = projection.omittedProviders > 0 || known.length !== projection.providers.length;
|
|
174
323
|
const floor = incomplete || known.some((p) => p.windows[window].kind === "floor");
|
|
175
324
|
const equal = (a, b) => Math.abs(a - b) <= 8 * Number.EPSILON * Math.max(a, b);
|
|
325
|
+
const attribution = projection.attribution;
|
|
326
|
+
if (projection.windows[window].kind === "unavailable" && (dollars > 0 || (attribution?.unknownProvider[window].amount ?? 0) > 0))
|
|
327
|
+
throw new Error("Supported provider dollars require an aggregate floor or estimate");
|
|
328
|
+
if (attribution) {
|
|
329
|
+
const unknown = attribution.unknownProvider[window], complete = attribution.attributionComplete[window];
|
|
330
|
+
if (complete && (projection.omittedProviders > 0 || unknown.kind !== "estimate" || unknown.amount !== 0))
|
|
331
|
+
throw new Error("Complete attribution requires no unknown provider remainder");
|
|
332
|
+
if (attribution.unpriced[window] && !projection.approximation.reasons.includes("missing-price"))
|
|
333
|
+
throw new Error("Unpriced attribution requires missing-price provenance");
|
|
334
|
+
if (projection.windows[window].kind === "estimate" && (!complete || attribution.unpriced[window] || unknown.kind !== "estimate" || unknown.amount !== 0))
|
|
335
|
+
throw new Error("Incomplete attribution cannot carry an estimate");
|
|
336
|
+
const total = projection.windows[window], reconciled = dollars + (unknown.amount ?? 0);
|
|
337
|
+
if (!Number.isFinite(reconciled) || total.amount !== null && (reconciled > total.amount && !equal(reconciled, total.amount) || unknown.kind === "estimate" && !equal(reconciled, total.amount)))
|
|
338
|
+
throw new Error("Unknown provider reconciliation mismatch");
|
|
339
|
+
}
|
|
176
340
|
for (const [total, subtotal, label] of [[projection.windows[window], dollars, "dollar"], [projection.scores[window], score, "score"]]) {
|
|
177
341
|
if (total.kind === "unavailable")
|
|
178
342
|
continue;
|
|
@@ -197,21 +361,180 @@ function validateRanks(projection, population2, contemporaneous) {
|
|
|
197
361
|
}
|
|
198
362
|
}
|
|
199
363
|
}
|
|
200
|
-
function recentPercentileHeat(projection, gates = { m20: 0.9, h24: 0.9, d7: 0.9 }) {
|
|
364
|
+
function recentPercentileHeat(projection, gates = { m20: 0.9, h24: 0.9, d7: 0.9 }, population2) {
|
|
201
365
|
for (const w of ["m20", "h24", "d7"])
|
|
202
366
|
if (!Number.isFinite(gates[w]) || gates[w] < 0 || gates[w] > 1)
|
|
203
367
|
throw new Error("Invalid gate");
|
|
204
368
|
for (const [w, label] of [["m20", "on-fire"], ["h24", "hot"], ["d7", "heavy"]]) {
|
|
205
|
-
const amount = projection.scores[w].amount, r = projection.scoreRanks[w];
|
|
206
|
-
if (amount !== null && amount > 0 && r !== null && r >= gates[w])
|
|
369
|
+
const amount = projection.scores[w].amount, r = projection.scoreRanks[w], n = population2?.[w].count ?? (r !== null && r > 0 ? 2 : 0);
|
|
370
|
+
if (n >= 2 && amount !== null && amount > 0 && r !== null && r >= gates[w])
|
|
207
371
|
return label;
|
|
208
372
|
}
|
|
209
373
|
return null;
|
|
210
374
|
}
|
|
375
|
+
function isFirstIndexRank(r, n) {
|
|
376
|
+
const nearest = Math.round(r * (n - 1)) / (n - 1);
|
|
377
|
+
return Math.abs(r - nearest) <= 4 * Number.EPSILON * Math.max(r, nearest);
|
|
378
|
+
}
|
|
379
|
+
function validateRootHeatRow(row, context, config) {
|
|
380
|
+
if (!context) {
|
|
381
|
+
if (row.heat)
|
|
382
|
+
throw new Error("Root row requires context");
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!row.heat || config.schemaVersion !== 3)
|
|
386
|
+
throw new Error("Missing root row topology/decisions");
|
|
387
|
+
const heat = row.heat, eligible = rootFamilyEligibilityV1(heat.topology) === "eligible-root";
|
|
388
|
+
for (const [ranks, population2, values] of [[heat.rootDollarRanks, context.population.dollars, row.family.windows], [heat.rootScoreRanks, context.population.scores, row.family.scores]]) {
|
|
389
|
+
for (const w of HEAT_WINDOWS) {
|
|
390
|
+
const r = ranks[w], n = population2[w].count;
|
|
391
|
+
if (!eligible && r !== null)
|
|
392
|
+
throw new Error("Ineligible thread carries root-domain rank");
|
|
393
|
+
if (r !== null && (n === 0 || n === 1 && r !== 0 || population2[w].coverage === "unknown"))
|
|
394
|
+
throw new Error("Root rank population mismatch");
|
|
395
|
+
if (r !== null && n >= 2 && population2[w].coverage === "complete" && !isFirstIndexRank(r, n))
|
|
396
|
+
throw new Error("Root rank outside complete first-index grid");
|
|
397
|
+
if (context.rankAsOf >= row.asOf && r !== null && population2[w].coverage === "complete" && values[w].kind !== "estimate")
|
|
398
|
+
throw new Error("Uncertified value cannot carry a complete root rank");
|
|
399
|
+
if (context.rankAsOf >= row.asOf && r !== null && (values[w].amount === null || values[w].amount <= 0))
|
|
400
|
+
throw new Error("Root rank needs supported positive amount");
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const expected = rootFamilyHeatV1({ topology: heat.topology, family: { contextRevision: heat.decisions.contextRevision, configRevision: row.configRevision, scores: row.family.scores, rootScoreRanks: heat.rootScoreRanks }, context, config });
|
|
404
|
+
if (JSON.stringify(expected) !== JSON.stringify(heat.decisions))
|
|
405
|
+
throw new Error("Root heat decisions/winner mismatch");
|
|
406
|
+
}
|
|
407
|
+
function validateHeatInputs(snapshot) {
|
|
408
|
+
const { heatInputs: input, heat: context } = snapshot;
|
|
409
|
+
if (!context || snapshot.config.value.schemaVersion !== 3 || input.contextRevision !== context.contextRevision || input.topologyRevision !== context.topologyRevision)
|
|
410
|
+
throw new Error("Root input context mismatch");
|
|
411
|
+
if (!input.cohortComplete && snapshot.coverage === "complete")
|
|
412
|
+
throw new Error("Incomplete root inputs require partial snapshot");
|
|
413
|
+
if (input.cohortComplete && (input.pending || input.omitted.count || !input.omitted.exact))
|
|
414
|
+
throw new Error("Incomplete root input cohort");
|
|
415
|
+
const ids = new Set;
|
|
416
|
+
for (const row of input.rows) {
|
|
417
|
+
if (ids.has(row.threadId))
|
|
418
|
+
throw new Error("Duplicate root input");
|
|
419
|
+
ids.add(row.threadId);
|
|
420
|
+
if (row.asOf > context.rankAsOf || row.configRevision !== snapshot.config.revision || rootFamilyEligibilityV1(row.heat.topology) !== "eligible-root" || !row.family.attribution)
|
|
421
|
+
throw new Error("Invalid root input capture");
|
|
422
|
+
validateProjection(row.family, snapshot.config.value.providerWeights);
|
|
423
|
+
validateRootHeatRow(row, context, snapshot.config.value);
|
|
424
|
+
}
|
|
425
|
+
for (const [axis, field, ranks] of [["dollars", "windows", "rootDollarRanks"], ["scores", "scores", "rootScoreRanks"]])
|
|
426
|
+
for (const w of HEAT_WINDOWS) {
|
|
427
|
+
const pop = context.population[axis][w], supported = input.rows.filter((row) => (row.family[field][w].amount ?? 0) > 0);
|
|
428
|
+
if (input.cohortComplete && pop.count > input.rows.length)
|
|
429
|
+
throw new Error("Root cohort below declared population");
|
|
430
|
+
if (pop.coverage === "complete" && pop.count < supported.length)
|
|
431
|
+
throw new Error("Root input population below supported members");
|
|
432
|
+
if (!input.cohortComplete || pop.coverage !== "complete")
|
|
433
|
+
continue;
|
|
434
|
+
if (supported.some((row) => row.family[field][w].kind !== "estimate") || pop.count !== supported.length)
|
|
435
|
+
throw new Error("Complete root input population mismatch");
|
|
436
|
+
const sorted = supported.slice().sort((a, b) => a.family[field][w].amount - b.family[field][w].amount || (a.threadId < b.threadId ? -1 : 1)), expected = new Map;
|
|
437
|
+
let first = 0;
|
|
438
|
+
for (let i = 0;i < sorted.length; i++) {
|
|
439
|
+
if (!i || sorted[i].family[field][w].amount !== sorted[i - 1].family[field][w].amount)
|
|
440
|
+
first = i;
|
|
441
|
+
expected.set(sorted[i].threadId, sorted.length === 1 ? 0 : first / (sorted.length - 1));
|
|
442
|
+
}
|
|
443
|
+
for (const row of input.rows) {
|
|
444
|
+
const actual = row.heat[ranks][w], rank2 = expected.get(row.threadId) ?? null;
|
|
445
|
+
if (actual !== rank2)
|
|
446
|
+
throw new Error("Root input rank/order mismatch");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// src/heat-settings.ts
|
|
451
|
+
import { z as z2 } from "zod";
|
|
452
|
+
var HEAT_SETTINGS_METHODS = Object.freeze({ read: "threadMetricsHeatSettingsReadV1", save: "threadMetricsHeatSettingsSaveV1" });
|
|
453
|
+
var HEAT_SETTINGS_LIMITS = Object.freeze({ configBytes: 16 * 1024, inputBytes: 18 * 1024, outputBytes: 40 * 1024, operationKeys: 128 });
|
|
454
|
+
var version = z2.string().min(1).max(256);
|
|
455
|
+
var key = z2.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
456
|
+
var providerId = z2.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
457
|
+
var weights2 = z2.record(providerId, z2.number().finite().min(0).max(3)).refine((v) => Object.keys(v).length <= 32, "Too many provider weights");
|
|
458
|
+
var gates = z2.object({ m20: z2.number().finite().min(0).max(1), h24: z2.number().finite().min(0).max(1), d7: z2.number().finite().min(0).max(1) }).strict();
|
|
459
|
+
var thresholds2 = z2.object({ m20: z2.number().finite().min(0).max(1e6), h24: z2.number().finite().min(0).max(1e6), d7: z2.number().finite().min(0).max(1e6), lifetime: z2.number().finite().min(0).max(1e6) }).strict();
|
|
460
|
+
var legacyHeatConfigInputSchema = z2.discriminatedUnion("mode", [
|
|
461
|
+
z2.object({ schemaVersion: z2.literal(2), mode: z2.literal("percentile"), providerWeights: weights2, gates }).strict(),
|
|
462
|
+
z2.object({ schemaVersion: z2.literal(2), mode: z2.literal("absolute"), providerWeights: weights2, thresholds: thresholds2, lifetimeEnabled: z2.boolean().optional() }).strict()
|
|
463
|
+
]);
|
|
464
|
+
var domain = z2.object({ version: z2.literal(1), kind: z2.literal("root-family") }).strict();
|
|
465
|
+
var heatConfigInputSchema = z2.discriminatedUnion("mode", [
|
|
466
|
+
z2.object({ schemaVersion: z2.literal(3), domain, mode: z2.literal("percentile"), providerWeights: weights2, gates }).strict(),
|
|
467
|
+
z2.object({ schemaVersion: z2.literal(3), domain, mode: z2.literal("absolute"), providerWeights: weights2, thresholds: thresholds2, lifetimeEnabled: z2.literal(false).optional() }).strict()
|
|
468
|
+
]);
|
|
469
|
+
function canonicalHeatConfig(value) {
|
|
470
|
+
const v = value.schemaVersion === 2 ? legacyHeatConfigInputSchema.parse(value) : heatConfigInputSchema.parse(value);
|
|
471
|
+
return JSON.stringify({ ...v, providerWeights: Object.fromEntries(Object.entries(v.providerWeights).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)), ...v.mode === "absolute" ? { lifetimeEnabled: v.lifetimeEnabled ?? false } : {} });
|
|
472
|
+
}
|
|
473
|
+
function validateHeatConfig(value) {
|
|
474
|
+
const parsed = bounded(z2.union([legacyHeatConfigInputSchema, heatConfigInputSchema]), value, HEAT_SETTINGS_LIMITS.configBytes);
|
|
475
|
+
if (new TextEncoder().encode(canonicalHeatConfig(parsed)).byteLength > HEAT_SETTINGS_LIMITS.configBytes)
|
|
476
|
+
throw new Error("Config byte limit");
|
|
477
|
+
return parsed;
|
|
478
|
+
}
|
|
479
|
+
function validateRootFamilyHeatConfig(value) {
|
|
480
|
+
return bounded(heatConfigInputSchema, value, HEAT_SETTINGS_LIMITS.configBytes);
|
|
481
|
+
}
|
|
482
|
+
function prepareRootFamilyMigration(record, review) {
|
|
483
|
+
const value = validateHeatConfig(record.value);
|
|
484
|
+
if (value.schemaVersion === 3)
|
|
485
|
+
return { kind: "review", base: record, candidate: value };
|
|
486
|
+
if (value.mode === "absolute" && value.lifetimeEnabled && !review?.disableLifetime)
|
|
487
|
+
return { kind: "blocked", reason: "Legacy lifetime heat is enabled. Explicitly review and disable that behavior before root-family migration; stored values were preserved." };
|
|
488
|
+
const candidate = value.mode === "percentile" ? { schemaVersion: 3, domain: ROOT_FAMILY_HEAT_DOMAIN_V1, mode: value.mode, providerWeights: { ...value.providerWeights }, gates: { ...value.gates } } : { schemaVersion: 3, domain: ROOT_FAMILY_HEAT_DOMAIN_V1, mode: value.mode, providerWeights: { ...value.providerWeights }, thresholds: { ...value.thresholds }, lifetimeEnabled: false };
|
|
489
|
+
return { kind: "review", base: record, candidate };
|
|
490
|
+
}
|
|
491
|
+
var heatSettingsReadInputSchema = z2.object({ protocolVersion: z2.literal(1), operationKey: key.optional() }).strict();
|
|
492
|
+
var heatSettingsSaveInputSchema = z2.object({ protocolVersion: z2.literal(1), baseConfigVersion: version, operationKey: key, candidate: heatConfigInputSchema }).strict();
|
|
493
|
+
var heatConfigRecordSchema = z2.object({ version, value: storedScoringConfigSchema }).strip();
|
|
494
|
+
var observation = z2.object({ instance: version, sequence: z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER), configVersion: version.nullable() }).strip();
|
|
495
|
+
var outcome = z2.discriminatedUnion("kind", [
|
|
496
|
+
z2.object({ kind: z2.literal("saved"), operationKey: key, record: heatConfigRecordSchema }).strip(),
|
|
497
|
+
z2.object({ kind: z2.literal("conflict"), operationKey: key, record: heatConfigRecordSchema }).strip(),
|
|
498
|
+
z2.object({ kind: z2.literal("unknown"), operationKey: key }).strip()
|
|
499
|
+
]);
|
|
500
|
+
var heatSettingsReadResultSchema = z2.object({ protocolVersion: z2.literal(1), current: heatConfigRecordSchema, published: observation, writable: z2.boolean(), reason: z2.string().max(512).nullable(), migration: z2.object({ kind: z2.literal("domain-required"), target: z2.object({ version: z2.literal(1), kind: z2.literal("root-family") }).strip() }).strip().optional(), outcome: outcome.optional() }).strip();
|
|
501
|
+
var heatSettingsSaveResultSchema = z2.object({ protocolVersion: z2.literal(1), kind: z2.enum(["saved", "conflict"]), operationKey: key, record: heatConfigRecordSchema, publicationPending: z2.boolean() }).strip();
|
|
502
|
+
function scoringHeatV2(values, ranks, population2, config) {
|
|
503
|
+
legacyHeatConfigInputSchema.parse(config);
|
|
504
|
+
for (const w of ["m20", "h24", "d7"])
|
|
505
|
+
if (!Number.isSafeInteger(population2[w]) || population2[w] < 0 || ranks[w] !== null && (!Number.isFinite(ranks[w]) || ranks[w] < 0 || ranks[w] > 1))
|
|
506
|
+
throw new Error("Invalid heat rank population");
|
|
507
|
+
for (const [w, label] of [["m20", "on-fire"], ["h24", "hot"], ["d7", "heavy"]]) {
|
|
508
|
+
const n2 = values[w].amount;
|
|
509
|
+
if (n2 !== null && Number.isFinite(n2) && n2 > 0 && (config.mode === "absolute" ? n2 >= config.thresholds[w] : population2[w] >= 2 && ranks[w] !== null && ranks[w] >= config.gates[w]))
|
|
510
|
+
return label;
|
|
511
|
+
}
|
|
512
|
+
const n = values.lifetime.amount;
|
|
513
|
+
return config.mode === "absolute" && config.lifetimeEnabled && n !== null && Number.isFinite(n) && n > 0 && n >= config.thresholds.lifetime ? "heavy" : null;
|
|
514
|
+
}
|
|
515
|
+
function bounded(schema, value, maxBytes) {
|
|
516
|
+
const json = JSON.stringify(value);
|
|
517
|
+
if (json === undefined || new TextEncoder().encode(json).byteLength > maxBytes)
|
|
518
|
+
throw new Error("Heat settings byte limit");
|
|
519
|
+
return schema.parse(value);
|
|
520
|
+
}
|
|
521
|
+
function validateHeatSaveInput(value) {
|
|
522
|
+
const input = bounded(heatSettingsSaveInputSchema, value, HEAT_SETTINGS_LIMITS.inputBytes);
|
|
523
|
+
validateHeatConfig(input.candidate);
|
|
524
|
+
return input;
|
|
525
|
+
}
|
|
526
|
+
function reconcileHeatSave(input, read) {
|
|
527
|
+
const o = read.outcome;
|
|
528
|
+
if (o?.operationKey === input.operationKey && o.kind === "saved")
|
|
529
|
+
return canonicalHeatConfig(o.record.value) === canonicalHeatConfig(input.candidate) ? "saved" : "conflict";
|
|
530
|
+
if (o?.operationKey === input.operationKey && o.kind === "conflict")
|
|
531
|
+
return "conflict";
|
|
532
|
+
return read.current.version === input.baseConfigVersion ? "unchanged" : "unknown";
|
|
533
|
+
}
|
|
211
534
|
|
|
212
535
|
// src/index.ts
|
|
213
|
-
import { z as
|
|
214
|
-
import { UsageVectorSchema } from "@phosphorco/bb-usage-algebra/vector";
|
|
536
|
+
import { z as z3 } from "zod";
|
|
537
|
+
import { UsageVectorSchema as UsageVectorSchema2 } from "@phosphorco/bb-usage-algebra/vector";
|
|
215
538
|
var LIMITS = Object.freeze({ threadIds: 200, seriesPoints: 288, detailRows: 200, inputBytes: 65536, outputBytes: 2097152, describeBytes: 16384, candidates: 128, listedPlugins: 1e4, concurrency: 4, callMs: 2000, discoveryMs: 5000, familyEdges: 50000, familyMembers: 1e4, familyDepth: 256 });
|
|
216
539
|
var METRIC_BUCKET_MS = 300000;
|
|
217
540
|
var WINDOW_BUCKETS = { m20: 4, h24: 288, d7: 2016, lifetime: 2016 };
|
|
@@ -221,19 +544,19 @@ function retainedWindowStart(asOf, window) {
|
|
|
221
544
|
return Math.max(0, Math.floor(asOf / METRIC_BUCKET_MS) * METRIC_BUCKET_MS - (WINDOW_BUCKETS[window] - 1) * METRIC_BUCKET_MS);
|
|
222
545
|
}
|
|
223
546
|
var METHODS = Object.freeze({ describe: "threadMetricsDescribe", read: "threadMetricsReadV1", detail: "threadMetricsDetailV1", recentSnapshot: "threadMetricsRecentSnapshotV1" });
|
|
224
|
-
var id2 =
|
|
225
|
-
var text =
|
|
226
|
-
var time2 =
|
|
227
|
-
var money =
|
|
228
|
-
var scope =
|
|
229
|
-
var windowSchema =
|
|
230
|
-
var coverageSchema =
|
|
231
|
-
var coverageReasonSchema =
|
|
232
|
-
var reasons2 =
|
|
233
|
-
var approximationSchema =
|
|
234
|
-
approximate:
|
|
235
|
-
reasons:
|
|
236
|
-
rates:
|
|
547
|
+
var id2 = z3.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
548
|
+
var text = z3.string().min(1).max(256);
|
|
549
|
+
var time2 = z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
550
|
+
var money = z3.number().finite().nonnegative().nullable();
|
|
551
|
+
var scope = z3.enum(["own", "family"]);
|
|
552
|
+
var windowSchema = z3.enum(["m20", "h24", "d7", "lifetime"]);
|
|
553
|
+
var coverageSchema = z3.enum(["complete", "partial", "unknown"]);
|
|
554
|
+
var coverageReasonSchema = z3.enum(["source-gap", "root-unattested", "history-unavailable", "member-cap", "depth-cap", "edge-cap", "missing-thread", "unpriced", "retention", "row-cap", "pagination", "population-incomplete"]);
|
|
555
|
+
var reasons2 = z3.array(coverageReasonSchema).max(16);
|
|
556
|
+
var approximationSchema = z3.object({
|
|
557
|
+
approximate: z3.boolean(),
|
|
558
|
+
reasons: z3.array(z3.enum(["closest-model", "provider-default", "model-attribution", "bucket-boundary", "missing-price", "source-gap"])).max(6),
|
|
559
|
+
rates: z3.array(z3.object({ providerId: id2, modelId: text, rateId: text.nullable(), basis: z3.enum(["matched", "closest-model", "provider-default", "unpriced"]) }).superRefine((rate, ctx) => {
|
|
237
560
|
if (rate.basis === "unpriced" !== (rate.rateId === null))
|
|
238
561
|
ctx.addIssue({ code: "custom", path: ["rateId"], message: "Priced basis requires rateId; unpriced basis requires null" });
|
|
239
562
|
})).max(32).optional()
|
|
@@ -241,40 +564,40 @@ var approximationSchema = z2.object({
|
|
|
241
564
|
if (!v.approximate && (v.reasons.length || v.rates?.some((r) => r.basis !== "matched")))
|
|
242
565
|
ctx.addIssue({ code: "custom", message: "Approximation provenance requires approximate flag" });
|
|
243
566
|
});
|
|
244
|
-
var rank2 =
|
|
245
|
-
var ranksSchema =
|
|
246
|
-
var population2 =
|
|
247
|
-
var rankPopulationSchema =
|
|
248
|
-
var windowValueSchema =
|
|
249
|
-
var windowsSchema =
|
|
250
|
-
var bucketSchema =
|
|
567
|
+
var rank2 = z3.number().finite().min(0).max(1).nullable();
|
|
568
|
+
var ranksSchema = z3.object({ m20: rank2, h24: rank2, d7: rank2 }).strip();
|
|
569
|
+
var population2 = z3.object({ asOf: time2, count: z3.number().int().nonnegative(), coverage: coverageSchema }).strip();
|
|
570
|
+
var rankPopulationSchema = z3.object({ m20: population2, h24: population2, d7: population2 }).strip();
|
|
571
|
+
var windowValueSchema = z3.object({ tokens: UsageVectorSchema2, amount: money }).strip();
|
|
572
|
+
var windowsSchema = z3.object({ m20: windowValueSchema, h24: windowValueSchema, d7: windowValueSchema, lifetime: windowValueSchema }).strip();
|
|
573
|
+
var bucketSchema = z3.object({ start: time2, amount: money }).strip();
|
|
251
574
|
var metadata = {
|
|
252
575
|
threadId: id2,
|
|
253
576
|
scope,
|
|
254
|
-
memberCount:
|
|
577
|
+
memberCount: z3.number().int().nonnegative().max(LIMITS.familyMembers),
|
|
255
578
|
coverage: coverageSchema,
|
|
256
579
|
coverageReasons: reasons2,
|
|
257
|
-
excluded:
|
|
580
|
+
excluded: z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
258
581
|
asOf: time2,
|
|
259
582
|
revision: text,
|
|
260
583
|
priceVersion: text,
|
|
261
|
-
currency:
|
|
262
|
-
estimated:
|
|
584
|
+
currency: z3.literal("USD"),
|
|
585
|
+
estimated: z3.literal(true),
|
|
263
586
|
approximation: approximationSchema,
|
|
264
587
|
ranks: ranksSchema,
|
|
265
588
|
rankPopulation: rankPopulationSchema,
|
|
266
|
-
reported:
|
|
589
|
+
reported: z3.object({ amount: z3.number().finite().nonnegative(), currency: z3.string().min(1).max(16), asOf: time2 }).strip().optional()
|
|
267
590
|
};
|
|
268
|
-
var summaryRowSchema =
|
|
269
|
-
|
|
270
|
-
|
|
591
|
+
var summaryRowSchema = z3.discriminatedUnion("changed", [
|
|
592
|
+
z3.object({ ...metadata, changed: z3.literal(true), windows: windowsSchema, buckets: z3.array(bucketSchema).max(LIMITS.seriesPoints).optional() }).strip(),
|
|
593
|
+
z3.object({ ...metadata, changed: z3.literal(false), windows: z3.never().optional(), buckets: z3.never().optional() }).strip()
|
|
271
594
|
]);
|
|
272
|
-
var readInputSchema =
|
|
273
|
-
threadIds:
|
|
595
|
+
var readInputSchema = z3.object({
|
|
596
|
+
threadIds: z3.array(id2).min(1).max(LIMITS.threadIds),
|
|
274
597
|
scope,
|
|
275
|
-
includeArchived:
|
|
276
|
-
knownRevisions:
|
|
277
|
-
series:
|
|
598
|
+
includeArchived: z3.boolean().optional(),
|
|
599
|
+
knownRevisions: z3.record(id2, text).optional(),
|
|
600
|
+
series: z3.object({ activeThreadId: id2, maxPoints: z3.number().int().min(1).max(LIMITS.seriesPoints) }).strict().optional()
|
|
278
601
|
}).strict().superRefine((v, ctx) => {
|
|
279
602
|
if (new Set(v.threadIds).size !== v.threadIds.length)
|
|
280
603
|
ctx.addIssue({ code: "custom", message: "Duplicate thread IDs" });
|
|
@@ -283,17 +606,17 @@ var readInputSchema = z2.object({
|
|
|
283
606
|
if (v.knownRevisions && Object.keys(v.knownRevisions).some((k) => !v.threadIds.includes(k)))
|
|
284
607
|
ctx.addIssue({ code: "custom", message: "Unrequested revision key" });
|
|
285
608
|
});
|
|
286
|
-
var readResultSchema =
|
|
287
|
-
var detailInputSchema =
|
|
288
|
-
var detailBase = { amount: money, tokens:
|
|
289
|
-
var detailRowSchema =
|
|
290
|
-
|
|
291
|
-
|
|
609
|
+
var readResultSchema = z3.object({ version: z3.literal(1), rows: z3.array(summaryRowSchema).max(LIMITS.threadIds) }).strip();
|
|
610
|
+
var detailInputSchema = z3.object({ threadId: id2, scope, includeArchived: z3.boolean().optional(), axis: z3.enum(["model", "time"]), window: windowSchema, cursor: z3.string().min(1).max(1024).optional(), limit: z3.number().int().min(1).max(LIMITS.detailRows).optional() }).strict();
|
|
611
|
+
var detailBase = { amount: money, tokens: UsageVectorSchema2, approximation: approximationSchema };
|
|
612
|
+
var detailRowSchema = z3.discriminatedUnion("axis", [
|
|
613
|
+
z3.object({ ...detailBase, axis: z3.literal("model"), providerId: id2, modelId: text }).strip(),
|
|
614
|
+
z3.object({ ...detailBase, axis: z3.literal("time"), start: time2, end: time2 }).strip().refine((v) => v.end > v.start, { message: "Empty time interval" })
|
|
292
615
|
]);
|
|
293
|
-
var detailResultSchema =
|
|
294
|
-
var describeInputSchema =
|
|
295
|
-
var describeEnvelopeSchema =
|
|
296
|
-
var capabilitiesSchema =
|
|
616
|
+
var detailResultSchema = z3.object({ version: z3.literal(1), threadId: id2, scope, axis: z3.enum(["model", "time"]), window: windowSchema, asOf: time2, revision: text, priceVersion: text, currency: z3.literal("USD"), estimated: z3.literal(true), coverage: coverageSchema, coverageReasons: reasons2, excluded: z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(), approximation: approximationSchema, total: windowValueSchema, rows: z3.array(detailRowSchema).max(LIMITS.detailRows), nextCursor: z3.string().min(1).max(1024).optional() }).strip();
|
|
617
|
+
var describeInputSchema = z3.object({}).strict();
|
|
618
|
+
var describeEnvelopeSchema = z3.object({ protocol: z3.literal("bb-thread-metrics"), versions: z3.array(z3.number().int().positive()).max(16) }).passthrough();
|
|
619
|
+
var capabilitiesSchema = z3.object({ summary: z3.literal(true), series: z3.boolean(), detail: z3.boolean(), recentSnapshotV1: z3.boolean().optional(), heatSettingsV1: z3.boolean().optional() }).strip();
|
|
297
620
|
var describeV1Schema = describeEnvelopeSchema.extend({ capabilities: capabilitiesSchema });
|
|
298
621
|
function parseBounded(schema, value, maxBytes) {
|
|
299
622
|
const json = JSON.stringify(value);
|
|
@@ -329,11 +652,11 @@ function dollarPercentiles(population3) {
|
|
|
329
652
|
});
|
|
330
653
|
return new Map(population3.map((p) => [p.threadId, p.amount === null || p.amount === 0 ? null : active.length < 2 ? 0 : lower.get(p.amount) / (active.length - 1)]));
|
|
331
654
|
}
|
|
332
|
-
function heat(ranks,
|
|
655
|
+
function heat(ranks, gates2 = { m20: 0.9, h24: 0.9, d7: 0.9 }) {
|
|
333
656
|
for (const w of ["m20", "h24", "d7"])
|
|
334
|
-
if (!Number.isFinite(
|
|
657
|
+
if (!Number.isFinite(gates2[w]) || gates2[w] < 0 || gates2[w] > 1)
|
|
335
658
|
throw new Error("Invalid gate");
|
|
336
|
-
return ranks.m20 !== null && ranks.m20 >=
|
|
659
|
+
return ranks.m20 !== null && ranks.m20 >= gates2.m20 ? "on-fire" : ranks.h24 !== null && ranks.h24 >= gates2.h24 ? "hot" : ranks.d7 !== null && ranks.d7 >= gates2.d7 ? "heavy" : null;
|
|
337
660
|
}
|
|
338
661
|
function familyClosure(edges, roots, policy = {}) {
|
|
339
662
|
const cap = (n, fallback) => {
|
|
@@ -444,15 +767,15 @@ function validateDetailResult(input, value) {
|
|
|
444
767
|
throw new Error("Invalid time detail range/order/overlap");
|
|
445
768
|
priorEnd = row.end;
|
|
446
769
|
}
|
|
447
|
-
const
|
|
448
|
-
if (keys.has(
|
|
770
|
+
const key2 = row.axis === "model" ? JSON.stringify([row.providerId, row.modelId]) : String(row.start);
|
|
771
|
+
if (keys.has(key2))
|
|
449
772
|
throw new Error("Duplicate detail row");
|
|
450
|
-
keys.add(
|
|
773
|
+
keys.add(key2);
|
|
451
774
|
}
|
|
452
775
|
if (out.coverage === "complete" && !input.cursor && !out.nextCursor) {
|
|
453
|
-
for (const
|
|
454
|
-
const sum = out.rows.reduce((n, r) => n + r.tokens[
|
|
455
|
-
if (!Number.isFinite(sum) || Math.abs(sum - out.total.tokens[
|
|
776
|
+
for (const key2 of ["inputUncached", "cacheRead", "cacheWrite", "output"]) {
|
|
777
|
+
const sum = out.rows.reduce((n, r) => n + r.tokens[key2], 0);
|
|
778
|
+
if (!Number.isFinite(sum) || Math.abs(sum - out.total.tokens[key2]) > 0.00000001 * Math.max(1, sum, out.total.tokens[key2]))
|
|
456
779
|
throw new Error("Detail token total mismatch");
|
|
457
780
|
}
|
|
458
781
|
const hasUnknown = out.rows.some((r) => r.amount === null);
|
|
@@ -485,27 +808,56 @@ export {
|
|
|
485
808
|
windowValueSchema,
|
|
486
809
|
windowSchema,
|
|
487
810
|
weightedScoreV2,
|
|
811
|
+
validateRootFamilyHeatConfig,
|
|
488
812
|
validateRecentSnapshot,
|
|
489
813
|
validateReadResult,
|
|
814
|
+
validateHeatSaveInput,
|
|
815
|
+
validateHeatConfig,
|
|
490
816
|
validateDetailResult,
|
|
491
817
|
summaryRowSchema,
|
|
818
|
+
storedScoringConfigSchema,
|
|
819
|
+
scoringHeatV2,
|
|
820
|
+
scoringConfigV3Schema,
|
|
492
821
|
scoringConfigV2Schema,
|
|
822
|
+
rootTopologyV1Schema,
|
|
823
|
+
rootFamilyHeatV1,
|
|
824
|
+
rootFamilyHeatDomainV1Schema,
|
|
825
|
+
rootFamilyHeatContextV1Schema,
|
|
826
|
+
rootFamilyEligibilityV1,
|
|
827
|
+
rootFamilyDecisionSetV1Schema,
|
|
493
828
|
retainedWindowStart,
|
|
829
|
+
reconcileHeatSave,
|
|
494
830
|
recentWindowsSchema,
|
|
831
|
+
recentUsageSchema,
|
|
832
|
+
recentTokenWindowsSchema,
|
|
833
|
+
recentTokenValueSchema,
|
|
495
834
|
recentSnapshotResultSchema,
|
|
496
835
|
recentSnapshotInputSchema,
|
|
497
836
|
recentSnapshotFullSchema,
|
|
498
837
|
recentScopeSchema,
|
|
499
838
|
recentRowSchema,
|
|
839
|
+
recentRootInputSchema,
|
|
840
|
+
recentRootHeatSchema,
|
|
841
|
+
recentPublicationIdentitySchema,
|
|
842
|
+
recentPresentationSchema,
|
|
500
843
|
recentPercentileHeat,
|
|
844
|
+
recentHeatInputsSchema,
|
|
501
845
|
recentDollarSchema,
|
|
846
|
+
recentAttributionSchema,
|
|
502
847
|
recentApproximationSchema,
|
|
503
848
|
readResultSchema,
|
|
504
849
|
readInputSchema,
|
|
505
850
|
ranksSchema,
|
|
506
851
|
rankPopulationSchema,
|
|
852
|
+
prepareRootFamilyMigration,
|
|
507
853
|
parseBounded,
|
|
508
854
|
negotiateVersion,
|
|
855
|
+
heatSettingsSaveResultSchema,
|
|
856
|
+
heatSettingsSaveInputSchema,
|
|
857
|
+
heatSettingsReadResultSchema,
|
|
858
|
+
heatSettingsReadInputSchema,
|
|
859
|
+
heatConfigRecordSchema,
|
|
860
|
+
heatConfigInputSchema,
|
|
509
861
|
heat,
|
|
510
862
|
familyClosure,
|
|
511
863
|
dollarPercentiles,
|
|
@@ -519,11 +871,18 @@ export {
|
|
|
519
871
|
coverageReasonSchema,
|
|
520
872
|
classifyOwnerError,
|
|
521
873
|
capabilitiesSchema,
|
|
874
|
+
canonicalHeatConfig,
|
|
522
875
|
bucketSchema,
|
|
876
|
+
bounded,
|
|
523
877
|
approximationSchema,
|
|
878
|
+
ROOT_FAMILY_HEAT_DOMAIN_V1,
|
|
524
879
|
RECENT_LIMITS,
|
|
525
880
|
METRIC_BUCKET_MS,
|
|
526
881
|
METHODS,
|
|
527
882
|
LIMITS,
|
|
883
|
+
HEAT_WINDOWS,
|
|
884
|
+
HEAT_SETTINGS_METHODS,
|
|
885
|
+
HEAT_SETTINGS_LIMITS,
|
|
886
|
+
DEFAULT_SCORING_CONFIG_V3,
|
|
528
887
|
DEFAULT_SCORING_CONFIG_V2
|
|
529
888
|
};
|