pi-mega-compact 0.20.13 → 0.20.14
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/dist/config/vector-cortex-early.js +89 -0
- package/dist/config/vector-cortex-flag.js +25 -0
- package/dist/config/vector-cortex.js +23 -83
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/api-contracts/vector-cortex-economics.js +20 -0
- package/dist/extensions/dashboard-server/route-dispatch.js +6 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-economics.js +59 -0
- package/dist/src/config/vector-cortex-early.js +89 -0
- package/dist/src/config/vector-cortex-flag.js +25 -0
- package/dist/src/config/vector-cortex.js +23 -83
- package/dist/src/config.js +1 -1
- package/dist/src/vector-cortex/cache/_economics-fixture.js +41 -0
- package/dist/src/vector-cortex/cache/compiler.js +201 -0
- package/dist/src/vector-cortex/cache/economics-emit.js +85 -0
- package/dist/src/vector-cortex/migrations/request-hash-v2.js +161 -0
- package/dist/src/vector-cortex/provider/economics-ids.js +29 -0
- package/dist/src/vector-cortex/provider/economics.js +176 -0
- package/dist/src/vector-cortex/provider/experiments.js +166 -0
- package/dist/vector-cortex/cache/_economics-fixture.js +41 -0
- package/dist/vector-cortex/cache/compiler.js +201 -0
- package/dist/vector-cortex/cache/economics-emit.js +85 -0
- package/dist/vector-cortex/migrations/request-hash-v2.js +161 -0
- package/dist/vector-cortex/provider/economics-ids.js +29 -0
- package/dist/vector-cortex/provider/economics.js +176 -0
- package/dist/vector-cortex/provider/experiments.js +166 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-CB-U7ViX.js → AreaChart-VUd_iOtO.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-CB-U7ViX.js.map → AreaChart-VUd_iOtO.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-DnLw0fxA.js → BarChart-BoqeJ5mM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-DnLw0fxA.js.map → BarChart-BoqeJ5mM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-CjyTVDiP.js → CacheTab-1ON7meDK.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-CjyTVDiP.js.map → CacheTab-1ON7meDK.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-Bz3QEWam.js → EventsTab-Dr9eqt1k.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-Bz3QEWam.js.map → EventsTab-Dr9eqt1k.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-CUylEvEY.js → HealthTab-Ciaigd4i.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-CUylEvEY.js.map → HealthTab-Ciaigd4i.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-Cz_hj_E7.js → MaintenanceTab-DjZRr3Lf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-Cz_hj_E7.js.map → MaintenanceTab-DjZRr3Lf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dh8brIR1.js → MemoryMapTab-BpZXHA7j.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dh8brIR1.js.map → MemoryMapTab-BpZXHA7j.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CkqSXc6e.js → MetricsTab-QemocY5k.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CkqSXc6e.js.map → MetricsTab-QemocY5k.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DGbJcx6d.js → OverviewTab-VtC1F-mO.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DGbJcx6d.js.map → OverviewTab-VtC1F-mO.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-BawS3nfF.js → ReposTab-CJD1cBxp.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-BawS3nfF.js.map → ReposTab-CJD1cBxp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-CUVnmYks.js → SessionsTab-Cj0erPR4.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-CUVnmYks.js.map → SessionsTab-Cj0erPR4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-cxTuyZNk.js → SetupTab-AOVROZ4G.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-cxTuyZNk.js.map → SetupTab-AOVROZ4G.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Cv5SENkE.js → TimeSavedCard-ZliVn8DH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Cv5SENkE.js.map → TimeSavedCard-ZliVn8DH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-yl6BBG_F.js → TurnsTab-DsoTWeIh.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-yl6BBG_F.js.map → TurnsTab-DsoTWeIh.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BBa7rp6C.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BBa7rp6C.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-DKv-0xCQ.js → WikiTab-CK8fcW1l.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-DKv-0xCQ.js.map → WikiTab-CK8fcW1l.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-CVPrO4UU.js → button-DdPcvex1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-CVPrO4UU.js.map → button-DdPcvex1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-BFLnQJEo.js → card-DXFuZph_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-BFLnQJEo.js.map → card-DXFuZph_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-B-IUoLd1.js → generateCategoricalChart-BagDVGWn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-B-IUoLd1.js.map → generateCategoricalChart-BagDVGWn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-Do749WlW.js → index-DVdDtRY4.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-Do749WlW.js.map → index-DVdDtRY4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-3uFnZmtq.js → switch-CtdtGyUR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-3uFnZmtq.js.map → switch-CtdtGyUR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-C1rYLeXe.js → toggle-Cvvwlbye.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-C1rYLeXe.js.map → toggle-Cvvwlbye.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-D1qzLzgR.js → useSSE-DvlceHjV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-D1qzLzgR.js.map → useSSE-DvlceHjV.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexEconomicsCard.tsx +41 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +8 -0
- package/extensions/dashboard-client/src/types/vector-cortex.ts +14 -0
- package/extensions/dashboard-server/api-contracts/vector-cortex-economics.ts +47 -0
- package/extensions/dashboard-server/route-dispatch.ts +5 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
- package/extensions/dashboard-server/routes-vector-cortex-economics.ts +68 -0
- package/package.json +1 -1
- package/src/config/vector-cortex-early.ts +99 -0
- package/src/config/vector-cortex-flag.ts +24 -0
- package/src/config/vector-cortex.ts +34 -89
- package/src/config.ts +1 -0
- package/src/vector-cortex/cache/_economics-fixture.ts +128 -0
- package/src/vector-cortex/cache/compiler.ts +280 -0
- package/src/vector-cortex/cache/economics-emit.ts +116 -0
- package/src/vector-cortex/migrations/request-hash-v2.ts +228 -0
- package/src/vector-cortex/provider/economics-ids.ts +38 -0
- package/src/vector-cortex/provider/economics.ts +289 -0
- package/src/vector-cortex/provider/experiments.ts +248 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-94fqVjat.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-94fqVjat.js.map +0 -1
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/provider/economics.ts — VC7B cache economics (ProviderProfileV1
|
|
3
|
+
* extension).
|
|
4
|
+
*
|
|
5
|
+
* VC7A proved a render can be FROZEN and reused. VC7B answers the question that
|
|
6
|
+
* immediately follows: is reusing it actually WORTH IT? Provider prompt caching
|
|
7
|
+
* is not free — a cache WRITE typically costs MORE than an uncached token, and a
|
|
8
|
+
* cache READ costs less. So a cache is profitable only when a written prefix is
|
|
9
|
+
* read back enough times, before its TTL expires, to repay the write premium.
|
|
10
|
+
* This file is that arithmetic and nothing else.
|
|
11
|
+
*
|
|
12
|
+
* THE NET-SAVINGS MODEL. For a prefix of `cachedTokens` tokens:
|
|
13
|
+
*
|
|
14
|
+
* baseline (no cache) = cachedTokens * basePrice * (writes + hits)
|
|
15
|
+
* actual (cached) = cachedTokens * (writePrice * writes + readPrice * hits)
|
|
16
|
+
* netSavings = baseline - actual
|
|
17
|
+
*
|
|
18
|
+
* A NEGATIVE `netSavings` is a real, expected, and important outcome: it means
|
|
19
|
+
* the cache LOST money (a prefix written once and never re-read always does, by
|
|
20
|
+
* exactly the write premium). We report it rather than clamping it at zero,
|
|
21
|
+
* because a floor at zero would make every rollout look free and would hide the
|
|
22
|
+
* single failure mode this sprint exists to detect.
|
|
23
|
+
*
|
|
24
|
+
* EXACT ARITHMETIC, NO FLOATS IN THE MONEY PATH. Prices are integer
|
|
25
|
+
* MICRO-UNITS per token (1e-6 of the provider's currency unit), never floating
|
|
26
|
+
* point. `0.1 + 0.2 !== 0.3` is not an acceptable property for a cost ledger, and
|
|
27
|
+
* accumulating float error across millions of tokens produces aggregates that
|
|
28
|
+
* silently disagree with the provider's own bill. All money is integer; only the
|
|
29
|
+
* derived RATIO is a float, and it is finite-guarded.
|
|
30
|
+
*
|
|
31
|
+
* EVERY EXCLUSION NEEDS A FIXTURE (the sprint's headline safety rule). A provider
|
|
32
|
+
* profile may declare that some request field does not affect cache identity —
|
|
33
|
+
* but an UNPROVEN exclusion is how you get silent cache poisoning: the renderer
|
|
34
|
+
* folds a field the provider actually keys on, two different requests collapse to
|
|
35
|
+
* one cache entry, and one conversation is served another's prefix. So an
|
|
36
|
+
* exclusion without a `fixtureId` (or with a blank one) is REJECTED here —
|
|
37
|
+
* `ECON_EXCLUSION_UNPROVEN` — rather than trusted. There is no override.
|
|
38
|
+
*
|
|
39
|
+
* ESTIMATE VS MEASURED. Every result is labeled. Only telemetry from a
|
|
40
|
+
* RANDOMIZED live assignment may be called `measured` and fed into a causal
|
|
41
|
+
* interval; shadow or non-randomized numbers are `estimate` and are excluded
|
|
42
|
+
* from causal aggregates (see `experiments.ts`). Mixing the two would let a
|
|
43
|
+
* self-selected population masquerade as a controlled experiment.
|
|
44
|
+
*
|
|
45
|
+
* PURE. No clock, no storage, no console, no network (PREVENT-PI-004 /
|
|
46
|
+
* PREVENT-011). Runs identically with `MEGACOMPACT_VC7B` on or off — the flag
|
|
47
|
+
* gates only the reporter/dashboard seam in `../cache/economics-emit.ts`.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
import type { ProviderProfileExclusion, ProviderProfileV1 } from "./types.js";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Cache economics attached to a provider profile.
|
|
54
|
+
*
|
|
55
|
+
* Prices are integer MICRO-UNITS PER TOKEN (1e-6 currency units), so a provider
|
|
56
|
+
* charging $3.00 per million input tokens has `basePrice: 3`. Integers keep the
|
|
57
|
+
* money path exact; see the file header for why floats are refused.
|
|
58
|
+
*/
|
|
59
|
+
export interface ProviderEconomicsV1 {
|
|
60
|
+
readonly schema: "provider-economics-v1";
|
|
61
|
+
/** The `ProviderProfileV1.id` these economics belong to. */
|
|
62
|
+
readonly profileId: string;
|
|
63
|
+
/** Profile version — economics are versioned WITH the profile they price. */
|
|
64
|
+
readonly profileVersion: string;
|
|
65
|
+
/** Uncached price per token, integer micro-units. The savings baseline. */
|
|
66
|
+
readonly basePrice: number;
|
|
67
|
+
/** Cache-READ price per token, integer micro-units. Normally < basePrice. */
|
|
68
|
+
readonly readPrice: number;
|
|
69
|
+
/** Cache-WRITE price per token, integer micro-units. Normally > basePrice. */
|
|
70
|
+
readonly writePrice: number;
|
|
71
|
+
/** Cache entry lifetime in ms. A prefix older than this cannot be read back. */
|
|
72
|
+
readonly ttlMs: number;
|
|
73
|
+
/** Minimum cacheable prefix in tokens; a shorter prefix is never cached. */
|
|
74
|
+
readonly minPrefix: number;
|
|
75
|
+
/**
|
|
76
|
+
* Conformance fixture ID proving this profile's exclusion set is safe, or
|
|
77
|
+
* `null` when the profile declares NO exclusions (nothing to prove). A profile
|
|
78
|
+
* WITH exclusions and a null/blank id is rejected — see `validateEconomics`.
|
|
79
|
+
*/
|
|
80
|
+
readonly exclusionFixtureId: string | null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Observed (or shadow) cache traffic for one economics computation. */
|
|
84
|
+
export interface CacheUsageV1 {
|
|
85
|
+
/** Tokens in the cached prefix. Must be a non-negative safe integer. */
|
|
86
|
+
readonly cachedTokens: number;
|
|
87
|
+
/** Number of cache WRITES (each pays the write premium). */
|
|
88
|
+
readonly writeCount: number;
|
|
89
|
+
/** Number of cache HITS (each pays the discounted read price). */
|
|
90
|
+
readonly hitCount: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Whether a figure may be used as causal evidence.
|
|
95
|
+
*
|
|
96
|
+
* `measured` — from a RANDOMIZED live assignment; admissible in causal
|
|
97
|
+
* intervals.
|
|
98
|
+
* `estimate` — shadow, projected, or non-randomized; reportable but NEVER
|
|
99
|
+
* admissible in a causal interval.
|
|
100
|
+
*/
|
|
101
|
+
export type EconomicsEvidence = "measured" | "estimate";
|
|
102
|
+
|
|
103
|
+
/** VC7B economics failure codes. */
|
|
104
|
+
export type EconomicsFailureCode =
|
|
105
|
+
/** A profile declares exclusions but names no proving fixture. */
|
|
106
|
+
| "ECON_EXCLUSION_UNPROVEN"
|
|
107
|
+
/** A price / TTL / prefix is negative, fractional, or not finite. */
|
|
108
|
+
| "ECON_PRICE_INVALID"
|
|
109
|
+
/** A usage count is negative, fractional, or not finite. */
|
|
110
|
+
| "ECON_USAGE_INVALID"
|
|
111
|
+
/** The computed result overflowed the exact-integer safe range. */
|
|
112
|
+
| "ECON_OVERFLOW";
|
|
113
|
+
|
|
114
|
+
/** The computed economics of a cache decision. All money in micro-units. */
|
|
115
|
+
export interface EconomicsResultV1 {
|
|
116
|
+
readonly profileId: string;
|
|
117
|
+
/** What the same traffic would have cost with no cache at all. */
|
|
118
|
+
readonly baselineCost: number;
|
|
119
|
+
/** What it actually cost: write premium on writes, discount on hits. */
|
|
120
|
+
readonly actualCost: number;
|
|
121
|
+
/** `baselineCost - actualCost`. NEGATIVE means the cache lost money. */
|
|
122
|
+
readonly netSavings: number;
|
|
123
|
+
/** Tokens billed at the discounted read price (the cache's token benefit). */
|
|
124
|
+
readonly tokenSavings: number;
|
|
125
|
+
/** `netSavings / baselineCost`, or 0 when the baseline is 0. Always finite. */
|
|
126
|
+
readonly savingsRatio: number;
|
|
127
|
+
/** Hits needed to break even on one write, or null when never profitable. */
|
|
128
|
+
readonly breakEvenHits: number | null;
|
|
129
|
+
/** Whether this figure may enter a causal interval. */
|
|
130
|
+
readonly evidence: EconomicsEvidence;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The verdict of an economics computation. */
|
|
134
|
+
export type EconomicsResult =
|
|
135
|
+
| { readonly ok: true; readonly result: EconomicsResultV1 }
|
|
136
|
+
| { readonly ok: false; readonly codes: readonly EconomicsFailureCode[] };
|
|
137
|
+
|
|
138
|
+
/** A non-negative safe integer — the only shape money and counts may take. */
|
|
139
|
+
function isCount(n: number): boolean {
|
|
140
|
+
return Number.isSafeInteger(n) && n >= 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Validate a profile's economics, enforcing the exclusion-proof rule.
|
|
145
|
+
*
|
|
146
|
+
* An exclusion claims "this field cannot affect provider cache identity". That
|
|
147
|
+
* claim is only as good as the fixture that proves it, so a profile carrying
|
|
148
|
+
* exclusions MUST name a `exclusionFixtureId`, and every individual exclusion
|
|
149
|
+
* must carry its own `fixtureId` too (VC5B already models that field; VC7B makes
|
|
150
|
+
* a blank one fatal instead of decorative). Returns deduplicated codes in a
|
|
151
|
+
* deterministic order.
|
|
152
|
+
*/
|
|
153
|
+
export function validateEconomics(
|
|
154
|
+
econ: ProviderEconomicsV1,
|
|
155
|
+
exclusions: readonly ProviderProfileExclusion[],
|
|
156
|
+
): readonly EconomicsFailureCode[] {
|
|
157
|
+
const codes = new Set<EconomicsFailureCode>();
|
|
158
|
+
|
|
159
|
+
for (const n of [econ.basePrice, econ.readPrice, econ.writePrice, econ.ttlMs, econ.minPrefix]) {
|
|
160
|
+
if (!isCount(n)) codes.add("ECON_PRICE_INVALID");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// The headline rule: exclusions without a proving fixture are never trusted.
|
|
164
|
+
if (exclusions.length > 0) {
|
|
165
|
+
const id = econ.exclusionFixtureId;
|
|
166
|
+
if (id === null || id.trim() === "") codes.add("ECON_EXCLUSION_UNPROVEN");
|
|
167
|
+
for (const ex of exclusions) {
|
|
168
|
+
if (ex.fixtureId.trim() === "") codes.add("ECON_EXCLUSION_UNPROVEN");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const order: EconomicsFailureCode[] = ["ECON_EXCLUSION_UNPROVEN", "ECON_PRICE_INVALID"];
|
|
173
|
+
return order.filter((c) => codes.has(c));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Validate a profile + its economics together. Convenience over
|
|
178
|
+
* `validateEconomics` for callers holding a whole `ProviderProfileV1`: the
|
|
179
|
+
* exclusion list is read from the profile itself, so the two can never disagree.
|
|
180
|
+
*/
|
|
181
|
+
export function validateProfileEconomics(
|
|
182
|
+
profile: ProviderProfileV1,
|
|
183
|
+
econ: ProviderEconomicsV1,
|
|
184
|
+
): readonly EconomicsFailureCode[] {
|
|
185
|
+
return validateEconomics(econ, profile.excludedJsonPointers);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Hits required for ONE write to break even.
|
|
190
|
+
*
|
|
191
|
+
* One write costs `(writePrice - basePrice)` extra per token; each subsequent
|
|
192
|
+
* hit saves `(basePrice - readPrice)` per token. So break-even is the ceiling of
|
|
193
|
+
* premium/discount. Returns `null` when the cache can NEVER pay for itself
|
|
194
|
+
* (a read that costs at least as much as an uncached token) — reporting a huge
|
|
195
|
+
* number there would imply "just get more hits", which is false.
|
|
196
|
+
* Returns 0 when writing is already free or cheaper than not caching.
|
|
197
|
+
*/
|
|
198
|
+
export function breakEvenHits(econ: ProviderEconomicsV1): number | null {
|
|
199
|
+
const premium = econ.writePrice - econ.basePrice;
|
|
200
|
+
const discount = econ.basePrice - econ.readPrice;
|
|
201
|
+
if (premium <= 0) return 0;
|
|
202
|
+
if (discount <= 0) return null;
|
|
203
|
+
return Math.ceil(premium / discount);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Compute net cache savings for observed (or shadow) usage.
|
|
208
|
+
*
|
|
209
|
+
* Pure integer arithmetic in micro-units. The only float is `savingsRatio`, and
|
|
210
|
+
* it is zero-guarded so a zero baseline yields 0 rather than NaN/Infinity — the
|
|
211
|
+
* sprint invariant is that every reported aggregate is FINITE.
|
|
212
|
+
*
|
|
213
|
+
* `evidence` is supplied by the caller and simply carried through: this function
|
|
214
|
+
* cannot know whether its inputs came from a randomized arm, and guessing would
|
|
215
|
+
* be exactly the mislabeling the causal rules forbid.
|
|
216
|
+
*/
|
|
217
|
+
export function computeEconomics(
|
|
218
|
+
econ: ProviderEconomicsV1,
|
|
219
|
+
usage: CacheUsageV1,
|
|
220
|
+
evidence: EconomicsEvidence,
|
|
221
|
+
): EconomicsResult {
|
|
222
|
+
const codes: EconomicsFailureCode[] = [];
|
|
223
|
+
for (const n of [econ.basePrice, econ.readPrice, econ.writePrice, econ.ttlMs, econ.minPrefix]) {
|
|
224
|
+
if (!isCount(n)) {
|
|
225
|
+
codes.push("ECON_PRICE_INVALID");
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
for (const n of [usage.cachedTokens, usage.writeCount, usage.hitCount]) {
|
|
230
|
+
if (!isCount(n)) {
|
|
231
|
+
codes.push("ECON_USAGE_INVALID");
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (codes.length > 0) return { ok: false, codes };
|
|
236
|
+
|
|
237
|
+
const { cachedTokens, writeCount, hitCount } = usage;
|
|
238
|
+
const baselineCost = cachedTokens * econ.basePrice * (writeCount + hitCount);
|
|
239
|
+
const actualCost = cachedTokens * (econ.writePrice * writeCount + econ.readPrice * hitCount);
|
|
240
|
+
|
|
241
|
+
// Exactness is the whole point of the integer model: if any product leaves the
|
|
242
|
+
// safe-integer range the result is silently wrong, so we fail instead.
|
|
243
|
+
if (!Number.isSafeInteger(baselineCost) || !Number.isSafeInteger(actualCost)) {
|
|
244
|
+
return { ok: false, codes: ["ECON_OVERFLOW"] };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const netSavings = baselineCost - actualCost;
|
|
248
|
+
// Tokens served at the discounted read price — the cache's token-level benefit,
|
|
249
|
+
// independent of price (a hit re-reads the whole prefix).
|
|
250
|
+
const tokenSavings = cachedTokens * hitCount;
|
|
251
|
+
const savingsRatio = baselineCost === 0 ? 0 : netSavings / baselineCost;
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
ok: true,
|
|
255
|
+
result: {
|
|
256
|
+
profileId: econ.profileId,
|
|
257
|
+
baselineCost,
|
|
258
|
+
actualCost,
|
|
259
|
+
netSavings,
|
|
260
|
+
tokenSavings,
|
|
261
|
+
savingsRatio,
|
|
262
|
+
breakEvenHits: breakEvenHits(econ),
|
|
263
|
+
evidence,
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Whether a prefix is eligible to be cached at all: it must meet the profile's
|
|
270
|
+
* minimum prefix, and it must still be within TTL. A prefix below `minPrefix` is
|
|
271
|
+
* not "a small win", it is not cacheable by the provider at all.
|
|
272
|
+
*/
|
|
273
|
+
export function isCacheEligible(
|
|
274
|
+
econ: ProviderEconomicsV1,
|
|
275
|
+
prefixTokens: number,
|
|
276
|
+
ageMs: number,
|
|
277
|
+
): boolean {
|
|
278
|
+
if (!isCount(prefixTokens) || !isCount(ageMs)) return false;
|
|
279
|
+
return prefixTokens >= econ.minPrefix && ageMs < econ.ttlMs;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Conformance ID ranges + named rows extracted to economics-ids.ts to keep this
|
|
283
|
+
// file under the 300-line soft limit (soft-as-hard gate). Re-exported here so no
|
|
284
|
+
// consumer import path changes.
|
|
285
|
+
export {
|
|
286
|
+
CACHE_IDS,
|
|
287
|
+
ECONOMICS_PROVIDER_IDS,
|
|
288
|
+
ECONOMICS_NAMED_IDS,
|
|
289
|
+
} from "./economics-ids.js";
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/provider/experiments.ts — VC7B session-level cache experiments.
|
|
3
|
+
*
|
|
4
|
+
* To claim a cache strategy CAUSED a saving you need a randomized comparison,
|
|
5
|
+
* not a before/after. This file assigns each session to an experiment arm and
|
|
6
|
+
* guarantees the two properties that make the resulting telemetry admissible as
|
|
7
|
+
* causal evidence.
|
|
8
|
+
*
|
|
9
|
+
* 1. STABLE, JOURNAL-FREE ASSIGNMENT. The arm is a pure function of
|
|
10
|
+
* `sha256(experimentId, sessionId)` — NOT a random draw recorded in a
|
|
11
|
+
* journal. So the assignment survives anything: process restart, a lost or
|
|
12
|
+
* corrupted journal, a different host, a replay months later. This is the
|
|
13
|
+
* sprint's unique failure-injection case (lose the journal after the first
|
|
14
|
+
* event, restart, and the same arm must come back), and a hash-derived arm
|
|
15
|
+
* passes it by construction rather than by careful recovery code. A journal
|
|
16
|
+
* can still be kept as an audit trail, but nothing DEPENDS on it.
|
|
17
|
+
*
|
|
18
|
+
* The corollary matters just as much: every event in one session shares one
|
|
19
|
+
* arm (CACHE-RANDOM-003). If a session could drift between arms mid-flight,
|
|
20
|
+
* its rows would appear in both arms of the comparison and the contrast would
|
|
21
|
+
* be measuring nothing.
|
|
22
|
+
*
|
|
23
|
+
* 2. RANDOMIZED ⇒ CAUSAL; EVERYTHING ELSE ⇒ ESTIMATE. Hashing a session id is
|
|
24
|
+
* randomization only when the id itself is unpredictable and every session is
|
|
25
|
+
* eligible. A FORCED arm (operator override), a SHADOW arm (computed but not
|
|
26
|
+
* served), and a non-randomized rollout are all labeled `estimate` and are
|
|
27
|
+
* excluded from causal intervals by `isCausallyAdmissible`. Shadow numbers are
|
|
28
|
+
* genuinely useful — they are just not evidence of an effect.
|
|
29
|
+
*
|
|
30
|
+
* WHY 10_000 BUCKETS. Bucket granularity bounds how finely traffic can be split;
|
|
31
|
+
* 10k gives 0.01% resolution, matching VC5C's rollout bucketing so the two
|
|
32
|
+
* subsystems partition traffic on the same scale rather than on quietly
|
|
33
|
+
* different ones.
|
|
34
|
+
*
|
|
35
|
+
* PURE. No clock, no storage, no console, no network (PREVENT-PI-004 /
|
|
36
|
+
* PREVENT-011). `assignedAt` is an INJECTED timestamp, never `Date.now()` read
|
|
37
|
+
* here, so assignment is fully reproducible in tests and replays. Runs
|
|
38
|
+
* identically with `MEGACOMPACT_VC7B` on or off.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import { createHash } from "node:crypto";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Experiment arms.
|
|
45
|
+
*
|
|
46
|
+
* A — control: the predecessor path, no VC7B crystal compiler.
|
|
47
|
+
* B — treatment: provider-safe compiled crystal boundaries.
|
|
48
|
+
* C — holdout: cache bypassed entirely, the uncached baseline that prices what
|
|
49
|
+
* the cache is being compared AGAINST.
|
|
50
|
+
*/
|
|
51
|
+
export type ExperimentArm = "A" | "B" | "C";
|
|
52
|
+
|
|
53
|
+
/** The registered arms, in canonical bucket order. */
|
|
54
|
+
export const EXPERIMENT_ARMS: readonly ExperimentArm[] = ["A", "B", "C"] as const;
|
|
55
|
+
|
|
56
|
+
/** Bucket space: 10k buckets = 0.01% resolution (matches VC5C rollout). */
|
|
57
|
+
export const EXPERIMENT_BUCKETS = 10_000;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* How a session came to be in its arm. Only `randomized` is causal evidence.
|
|
61
|
+
*
|
|
62
|
+
* `randomized` — derived from the stable hash over every eligible session.
|
|
63
|
+
* `forced` — an operator/test override; self-selected, never causal.
|
|
64
|
+
* `shadow` — computed for comparison but not served; never causal.
|
|
65
|
+
*/
|
|
66
|
+
export type AssignmentSource = "randomized" | "forced" | "shadow";
|
|
67
|
+
|
|
68
|
+
/** A session's experiment assignment (VC7B contract type). */
|
|
69
|
+
export interface CacheExperimentV1 {
|
|
70
|
+
readonly schema: "cache-experiment-v1";
|
|
71
|
+
/** Names the experiment; folded into the hash so arms differ per experiment. */
|
|
72
|
+
readonly experimentId: string;
|
|
73
|
+
/** The session this assignment belongs to. */
|
|
74
|
+
readonly sessionId: string;
|
|
75
|
+
/** The assigned arm. */
|
|
76
|
+
readonly arm: ExperimentArm;
|
|
77
|
+
/** The session's stable bucket in `[0, EXPERIMENT_BUCKETS)`. */
|
|
78
|
+
readonly bucket: number;
|
|
79
|
+
/** How the arm was chosen — gates causal admissibility. */
|
|
80
|
+
readonly source: AssignmentSource;
|
|
81
|
+
/** INJECTED assignment timestamp (epoch ms). Never read from a clock here. */
|
|
82
|
+
readonly assignedAt: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** VC7B experiment failure codes. */
|
|
86
|
+
export type ExperimentFailureCode =
|
|
87
|
+
/** A blank experiment id or session id — an unnamed experiment is unanalyzable. */
|
|
88
|
+
| "EXP_ID_INVALID"
|
|
89
|
+
/** An arm split that does not sum to the full bucket space. */
|
|
90
|
+
| "EXP_SPLIT_INVALID"
|
|
91
|
+
/** A forced arm that is not one of the registered arms. */
|
|
92
|
+
| "EXP_ARM_UNKNOWN";
|
|
93
|
+
|
|
94
|
+
/** The verdict of an assignment. */
|
|
95
|
+
export type ExperimentResult =
|
|
96
|
+
| { readonly ok: true; readonly assignment: CacheExperimentV1 }
|
|
97
|
+
| { readonly ok: false; readonly codes: readonly ExperimentFailureCode[] };
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Traffic split across arms, in buckets. Must sum to exactly
|
|
101
|
+
* `EXPERIMENT_BUCKETS`: a split that sums to less would leave sessions
|
|
102
|
+
* unassigned, and one that sums to more would make the last arm unreachable —
|
|
103
|
+
* both silently bias the comparison, so both are rejected.
|
|
104
|
+
*/
|
|
105
|
+
export interface ExperimentSplit {
|
|
106
|
+
readonly A: number;
|
|
107
|
+
readonly B: number;
|
|
108
|
+
readonly C: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Even three-way split (the default): 3334 / 3333 / 3333 = 10000. */
|
|
112
|
+
export const EVEN_SPLIT: ExperimentSplit = { A: 3334, B: 3333, C: 3333 };
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Length-prefixed field framing, identical in spirit to the crystal key encoder:
|
|
116
|
+
* `<byteLength>:<value>` makes the concatenation injective, so experiment
|
|
117
|
+
* `"a"` + session `"b:c"` cannot collide with experiment `"a:b"` + session `"c"`.
|
|
118
|
+
* Without it, two different (experiment, session) pairs could share a bucket and
|
|
119
|
+
* a session could appear to switch arms when an experiment is renamed.
|
|
120
|
+
*/
|
|
121
|
+
function field(value: string): string {
|
|
122
|
+
return `${Buffer.byteLength(value, "utf8")}:${value}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The stable bucket for a session: the low 32 bits of
|
|
127
|
+
* `sha256(experimentId, sessionId)`, modulo the bucket space.
|
|
128
|
+
*
|
|
129
|
+
* Deterministic and storage-free — this is what makes a lost assignment journal
|
|
130
|
+
* a non-event. Reading 4 bytes (rather than the whole digest) keeps the value in
|
|
131
|
+
* exact integer range; SHA-256's avalanche means any 32 bits are equidistributed.
|
|
132
|
+
*/
|
|
133
|
+
export function experimentBucket(experimentId: string, sessionId: string): number {
|
|
134
|
+
const h = createHash("sha256")
|
|
135
|
+
.update(field(experimentId) + field(sessionId), "utf8")
|
|
136
|
+
.digest();
|
|
137
|
+
return h.readUInt32BE(0) % EXPERIMENT_BUCKETS;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Validate that a split covers the bucket space exactly once. */
|
|
141
|
+
export function validateSplit(split: ExperimentSplit): boolean {
|
|
142
|
+
for (const n of [split.A, split.B, split.C]) {
|
|
143
|
+
if (!Number.isSafeInteger(n) || n < 0) return false;
|
|
144
|
+
}
|
|
145
|
+
return split.A + split.B + split.C === EXPERIMENT_BUCKETS;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Map a bucket to an arm under a split. Arms occupy contiguous bucket ranges in
|
|
150
|
+
* the fixed order A, B, C, so the mapping is stable: widening arm C's share can
|
|
151
|
+
* never reshuffle a session already sitting in arm A.
|
|
152
|
+
*/
|
|
153
|
+
export function armForBucket(bucket: number, split: ExperimentSplit): ExperimentArm {
|
|
154
|
+
if (bucket < split.A) return "A";
|
|
155
|
+
if (bucket < split.A + split.B) return "B";
|
|
156
|
+
return "C";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Assign a session to an experiment arm.
|
|
161
|
+
*
|
|
162
|
+
* The default path is `randomized`: bucket = stable hash, arm = bucket's range.
|
|
163
|
+
* Passing `forced` overrides the arm and downgrades the source (and therefore
|
|
164
|
+
* causal admissibility) — the bucket is still computed and reported so the
|
|
165
|
+
* override remains auditable against what the session WOULD have got.
|
|
166
|
+
*
|
|
167
|
+
* `assignedAt` is injected by the caller. Assignment is a pure function of its
|
|
168
|
+
* arguments, so the same session always yields the same arm no matter when, or
|
|
169
|
+
* how many times, it is asked.
|
|
170
|
+
*/
|
|
171
|
+
export function assignExperiment(input: {
|
|
172
|
+
readonly experimentId: string;
|
|
173
|
+
readonly sessionId: string;
|
|
174
|
+
readonly assignedAt: number;
|
|
175
|
+
readonly split?: ExperimentSplit;
|
|
176
|
+
readonly forced?: ExperimentArm;
|
|
177
|
+
readonly shadow?: boolean;
|
|
178
|
+
}): ExperimentResult {
|
|
179
|
+
const codes: ExperimentFailureCode[] = [];
|
|
180
|
+
if (input.experimentId.trim() === "" || input.sessionId.trim() === "") {
|
|
181
|
+
codes.push("EXP_ID_INVALID");
|
|
182
|
+
}
|
|
183
|
+
const split = input.split ?? EVEN_SPLIT;
|
|
184
|
+
if (!validateSplit(split)) codes.push("EXP_SPLIT_INVALID");
|
|
185
|
+
if (input.forced !== undefined && !EXPERIMENT_ARMS.includes(input.forced)) {
|
|
186
|
+
codes.push("EXP_ARM_UNKNOWN");
|
|
187
|
+
}
|
|
188
|
+
if (codes.length > 0) return { ok: false, codes };
|
|
189
|
+
|
|
190
|
+
const bucket = experimentBucket(input.experimentId, input.sessionId);
|
|
191
|
+
const natural = armForBucket(bucket, split);
|
|
192
|
+
const arm = input.forced ?? natural;
|
|
193
|
+
// Precedence: a forced arm is self-selected, so it is never causal even when
|
|
194
|
+
// it happens to agree with the natural arm. Shadow is likewise non-causal.
|
|
195
|
+
const source: AssignmentSource =
|
|
196
|
+
input.forced !== undefined ? "forced" : input.shadow === true ? "shadow" : "randomized";
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
ok: true,
|
|
200
|
+
assignment: {
|
|
201
|
+
schema: "cache-experiment-v1",
|
|
202
|
+
experimentId: input.experimentId,
|
|
203
|
+
sessionId: input.sessionId,
|
|
204
|
+
arm,
|
|
205
|
+
bucket,
|
|
206
|
+
source,
|
|
207
|
+
assignedAt: input.assignedAt,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Whether an assignment's telemetry may enter a causal interval.
|
|
214
|
+
*
|
|
215
|
+
* ONLY `randomized`. This single predicate is what keeps forced and shadow rows
|
|
216
|
+
* out of the causal aggregate; every consumer computing an interval must filter
|
|
217
|
+
* through it rather than re-deriving the rule.
|
|
218
|
+
*/
|
|
219
|
+
export function isCausallyAdmissible(a: CacheExperimentV1): boolean {
|
|
220
|
+
return a.source === "randomized";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Keep only causally admissible assignments. The complement is not discarded by
|
|
225
|
+
* the caller — it is reported as `estimate` — but it never reaches an interval.
|
|
226
|
+
*/
|
|
227
|
+
export function causalOnly(
|
|
228
|
+
assignments: readonly CacheExperimentV1[],
|
|
229
|
+
): readonly CacheExperimentV1[] {
|
|
230
|
+
return assignments.filter(isCausallyAdmissible);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Whether every assignment for one session agrees on the arm (CACHE-RANDOM-003).
|
|
235
|
+
* A session that appears in two arms would contaminate both sides of the
|
|
236
|
+
* comparison, so this is asserted directly rather than assumed.
|
|
237
|
+
*/
|
|
238
|
+
export function sessionArmsConsistent(
|
|
239
|
+
assignments: readonly CacheExperimentV1[],
|
|
240
|
+
): boolean {
|
|
241
|
+
const bySession = new Map<string, ExperimentArm>();
|
|
242
|
+
for (const a of assignments) {
|
|
243
|
+
const seen = bySession.get(a.sessionId);
|
|
244
|
+
if (seen !== undefined && seen !== a.arm) return false;
|
|
245
|
+
bySession.set(a.sessionId, a.arm);
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,B as r,r as n}from"./index-Do749WlW.js";import{C as o,a as d,b as i,c as x}from"./card-BFLnQJEo.js";async function K(){const s=await fetch("/api/vector-cortex/evaluation");if(!s.ok)throw new Error(`vector-cortex evaluation: ${s.status}`);return s.json()}async function C(){const s=await fetch("/api/vector-cortex/health");if(!s.ok)throw new Error(`vector-cortex health: ${s.status}`);return s.json()}async function Z(){const s=await fetch("/api/vector-cortex/topology");if(!s.ok)throw new Error(`vector-cortex topology: ${s.status}`);return s.json()}async function J(){const s=await fetch("/api/vector-cortex/query");if(!s.ok)throw new Error(`vector-cortex query: ${s.status}`);return s.json()}async function X(){const s=await fetch("/api/vector-cortex/shards");if(!s.ok)throw new Error(`vector-cortex shards: ${s.status}`);return s.json()}async function ee(){const s=await fetch("/api/vector-cortex/reconstruct");if(!s.ok)throw new Error(`vector-cortex reconstruct: ${s.status}`);return s.json()}async function se(){const s=await fetch("/api/vector-cortex/plans");if(!s.ok)throw new Error(`vector-cortex plans: ${s.status}`);return s.json()}async function te(){const s=await fetch("/api/vector-cortex/render");if(!s.ok)throw new Error(`vector-cortex render: ${s.status}`);return s.json()}async function re(){const s=await fetch("/api/vector-cortex/rollout");if(!s.ok)throw new Error(`vector-cortex rollout: ${s.status}`);return s.json()}async function ae(){const s=await fetch("/api/vector-cortex/closure-proof");if(!s.ok)throw new Error(`vector-cortex closure-proof: ${s.status}`);return s.json()}async function ne(){const s=await fetch("/api/vector-cortex/restore");if(!s.ok)throw new Error(`vector-cortex restore: ${s.status}`);return s.json()}async function le(){const s=await fetch("/api/vector-cortex/repair");if(!s.ok)throw new Error(`vector-cortex repair: ${s.status}`);return s.json()}async function ce(s="default"){const a=await fetch(`/api/vector-cortex/ledger?session=${encodeURIComponent(s)}`);if(!a.ok)throw new Error(`vector-cortex ledger: ${a.status}`);return a.json()}async function oe(s){const a=await fetch("/api/vector-cortex/breakers/reset",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({subsystem:s})});if(!a.ok)throw new Error(`vector-cortex reset: ${a.status}`);return a.json()}async function de(){const s=await fetch("/api/vector-cortex/cache-crystals");if(!s.ok)throw new Error(`vector-cortex cache-crystals: ${s.status}`);return s.json()}function t({label:s,value:a}){return e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-muted-foreground",children:s}),e.jsx("div",{className:"font-mono text-sm",children:a})]})}function ie({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Validated Prompt Renderer (VC5B)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Render fixtures",value:String(s.renderCount)}),e.jsx(t,{label:"Provider profiles",value:String(s.providerCount)}),e.jsx(t,{label:"Known profiles",value:String(s.knownProfiles.length)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only render conformance route — no prompt text or session payloads exposed."})]}):e.jsx("div",{className:"vc-empty",children:"Renderer disabled (VC5B off)."})})]})}function xe({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Live Graduated Rollout (VC5C)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Gate",value:`${s.gatePct}%`}),e.jsx(t,{label:"Exposed buckets",value:String(s.bucketCount)}),e.jsx(t,{label:"Total buckets",value:String(s.buckets)}),e.jsx(t,{label:"Promotion",value:s.promotionBlocked?"FROZEN":"OPEN"}),e.jsx(t,{label:"Events",value:String(s.events)}),e.jsx(t,{label:"Sessions",value:String(s.sessions)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only rollout route — no session payloads or bucket→session mappings exposed."})]}):e.jsx("div",{className:"vc-empty",children:"Rollout disabled (VC5C off)."})})]})}function ue({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Closure Optimization (VC6A)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Mode",value:s.mode}),e.jsx(t,{label:"Optimizations",value:String(s.optimizations)}),e.jsx(t,{label:"Proof rejections",value:String(s.proofRejections)}),e.jsx(t,{label:"Last rejection",value:s.lastRejection??"none"}),e.jsx(t,{label:"Retained edges",value:String(s.retainedEdgeTotal)}),e.jsx(t,{label:"Removed edges",value:String(s.removedEdgeTotal)}),e.jsx(t,{label:"Conservative walks",value:String(s.conservativeTraversalTotal)}),e.jsx(t,{label:"Optimized walks",value:String(s.optimizedTraversalTotal)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only closure diagnostics — aggregate counts only; per-edge proof rows, node ids, and source payloads are never exposed (SECURITY_PRIVACY)."})]}):e.jsx("div",{className:"vc-empty",children:"Closure optimization disabled (VC6A off) — conservative VC4C closure used directly (mode B)."})})]})}function he({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Exact Source Restoration (VC6B)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Mode",value:s.mode}),e.jsx(t,{label:"Restore attempts",value:String(s.restoreAttempts)}),e.jsx(t,{label:"Restored",value:String(s.restoredCount)}),e.jsx(t,{label:"Missing source",value:String(s.missingCount)}),e.jsx(t,{label:"Digest rejections",value:String(s.digestRejections)}),e.jsx(t,{label:"Last rejection",value:s.lastRejection??"none"}),e.jsx(t,{label:"Updated",value:s.updatedAt})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only restore diagnostics — counts and HEAL_RESTORE_* codes only. There is no payload endpoint: restored bytes, span ids, node ids, and ledger text are never exposed (SECURITY_PRIVACY)."})]}):e.jsx("div",{className:"vc-empty",children:"Exact source restoration disabled (VC6B off) — no exact source is restored; omitted context is disclosed as loss (mode C)."})})]})}function je({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Self-Healing Derived State (VC6C)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Mode",value:s.mode}),e.jsx(t,{label:"Repair attempts",value:String(s.repairAttempts)}),e.jsx(t,{label:"Repairs planned",value:String(s.repairsPlanned)}),e.jsx(t,{label:"Pointers switched",value:String(s.pointersSwitched)}),e.jsx(t,{label:"Backoffs",value:String(s.backoffs)}),e.jsx(t,{label:"Last backoff",value:s.lastBackoffMs===null?"none":`${s.lastBackoffMs}ms`}),e.jsx(t,{label:"Last failure",value:s.lastFailure??"none"}),e.jsx(t,{label:"Updated",value:s.updatedAt})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only repair diagnostics — counts and HEAL_REPAIR_* codes only. There is no payload endpoint: subsystem source bytes, gap ranges, high-water marks, root digests, and ledger text are never exposed (SECURITY_PRIVACY)."})]}):e.jsx("div",{className:"vc-empty",children:"Self-healing derived state disabled (VC6C off) — no controller detects gaps or rebuilds derived state; derived state is reported disabled rather than healed (mode C)."})})]})}function me({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Frozen Range Crystals (VC7A)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Mode",value:s.mode}),e.jsx(t,{label:"Crystals",value:String(s.crystalCount)}),e.jsx(t,{label:"Frozen bytes",value:String(s.totalBytes)}),e.jsx(t,{label:"Hits",value:String(s.hits)}),e.jsx(t,{label:"Misses",value:String(s.misses)}),e.jsx(t,{label:"Hit bytes",value:String(s.hitBytes)}),e.jsx(t,{label:"Writes",value:String(s.writes)}),e.jsx(t,{label:"Duplicate writes",value:String(s.duplicateWrites)}),e.jsx(t,{label:"Collisions",value:String(s.collisions)}),e.jsx(t,{label:"Last failure",value:s.lastFailure??"none"}),e.jsx(t,{label:"Updated",value:s.updatedAt})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only crystal diagnostics — counts, byte volumes, and CRY_* codes only. A crystal is a frozen rendered prompt, so frozen bytes, covered ranges, span/covered digests, request digests, and session ids are never exposed (SECURITY_PRIVACY). Keys exclude the global ledger frontier, so an unrelated append does not invalidate a crystal; a covered-byte, dependency, renderer, or profile change does. Collisions mean two renders of one identity disagreed — the stored crystal is never overwritten."})]}):e.jsx("div",{className:"vc-empty",children:"Frozen range crystals disabled (VC7A off) — nothing is served from the crystal cache and every render is produced fresh; the cache is reported bypassed rather than hit (mode C)."})})]})}function ge({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Dual-Tier Shards (VC4A)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Semantic shards",value:String(s.semanticCount)}),e.jsx(t,{label:"Exact shards",value:String(s.exactCount)}),e.jsx(t,{label:"Byte total",value:String(s.byteTotal)}),e.jsx(t,{label:"Protected bytes",value:String(s.protectedByteTotal)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only count/byte aggregate; staged in-memory this sprint."})]}):e.jsx("div",{className:"vc-empty",children:"Dual-tier shards disabled (VC4A off)."})})]})}function fe({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Reconstruction Fidelity (VC4C)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Closure attempts",value:String(s.closureAttempts)}),e.jsx(t,{label:"Closure rejections",value:String(s.closureRejections)}),e.jsx(t,{label:"Validated",value:String(s.validatedCount)}),e.jsx(t,{label:"Invalidated",value:String(s.invalidatedCount)}),e.jsx(t,{label:"Span total",value:String(s.spanTotal)}),e.jsx(t,{label:"Byte total",value:String(s.byteTotal)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only closure/validation aggregate; staged in-memory this sprint."})]}):e.jsx("div",{className:"vc-empty",children:"Reconstruction fidelity disabled (VC4C off)."})})]})}function ve({view:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Plan Manifests (VC5A)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"DAG fixtures",value:String(s.dagCount)}),e.jsx(t,{label:"Plan fixtures",value:String(s.plannerCount)}),e.jsx(t,{label:"Plans exposed",value:String(s.plans.length)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only plan manifests only — no session payloads or prompt text. Per-run plan outputs are staged in-memory this sprint."})]}):e.jsx("div",{className:"vc-empty",children:"PromptDagV1 + budgeted planner disabled (VC5A off)."})})]})}function pe({topology:s,query:a}){return e.jsxs(e.Fragment,{children:[e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Derived Cortex Store (VC3A)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-3",children:[e.jsx(t,{label:"Generation",value:s.generationId??"—"}),e.jsx(t,{label:"Ordinal",value:s.ordinal??"—"}),e.jsx(t,{label:"Records",value:String(s.recordCount)}),e.jsx(t,{label:"Frontier (HW)",value:s.sourceHighWater}),e.jsx(t,{label:"Root digest",value:s.rootDigest?s.rootDigest.slice(0,16):"—"}),s.nodes!==void 0&&e.jsxs(e.Fragment,{children:[e.jsx(t,{label:"Nodes",value:String(s.nodes?.length??0)}),e.jsx(t,{label:"Edges",value:String(s.edges?.length??0)}),e.jsx(t,{label:"Graph digest",value:s.generationDigest?s.generationDigest.slice(0,16):"—"})]})]}),s.nodes!==void 0&&(s.edges?.length??0)>0&&e.jsxs("div",{className:"mt-4 max-h-56 overflow-y-auto",children:[e.jsx("div",{className:"mb-1 text-xs font-medium text-muted-foreground",children:"Topology edges (VC3B)"}),e.jsxs("table",{className:"w-full text-left text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border/50 text-muted-foreground",children:[e.jsx("th",{className:"py-1 pr-2",children:"source"}),e.jsx("th",{className:"py-1 pr-2",children:"target"}),e.jsx("th",{className:"py-1 pr-2",children:"head"}),e.jsx("th",{className:"py-1 pr-2",children:"dir"}),e.jsx("th",{className:"py-1",children:"score"})]})}),e.jsx("tbody",{children:s.edges?.map((u,h)=>e.jsxs("tr",{className:"border-b border-border/30",children:[e.jsx("td",{className:"py-1 pr-2 font-mono",children:u.source}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:u.target}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:u.head}),e.jsx("td",{className:"py-1 pr-2",children:u.direction}),e.jsx("td",{className:"py-1 font-mono",children:u.score})]},`${u.source}-${u.target}-${u.head}-${h}`))})]})]})]}):e.jsx("div",{className:"vc-empty",children:"Cortex store disabled (VC3A off)."})})]}),e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Topology Query Diagnostics (VC3C)"}),a?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:a?.enabled?e.jsx("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-3",children:e.jsx(t,{label:"Router version",value:String(a.routerVersion)})}):e.jsx("div",{className:"vc-empty",children:"Query diagnostics unavailable (VC3C off)."})})]})]})}function be({ledger:s}){return e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Occurrence Ledger (VC1B)"}),s?.enabled?e.jsx(r,{variant:"success",children:"ACTIVE"}):e.jsx(r,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 grid grid-cols-2 gap-4 md:grid-cols-3",children:[e.jsx(t,{label:"Session",value:s.session}),e.jsx(t,{label:"High-water",value:s.highWater}),e.jsx(t,{label:"Occurrences",value:String(s.count)})]}),s.occurrences.length===0?e.jsx("div",{className:"vc-empty",children:"No occurrences recorded."}):e.jsx("div",{className:"max-h-64 overflow-y-auto",children:e.jsxs("table",{className:"w-full text-left text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border/50 text-muted-foreground",children:[e.jsx("th",{className:"py-1 pr-2",children:"seq"}),e.jsx("th",{className:"py-1 pr-2",children:"eventId"}),e.jsx("th",{className:"py-1 pr-2",children:"kind"}),e.jsx("th",{className:"py-1 pr-2",children:"toolCall"}),e.jsx("th",{className:"py-1",children:"digest"})]})}),e.jsx("tbody",{children:s.occurrences.map(a=>e.jsxs("tr",{className:"border-b border-border/30",children:[e.jsx("td",{className:"py-1 pr-2 font-mono",children:a.seq}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:a.eventId}),e.jsx("td",{className:"py-1 pr-2",children:a.kind}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:a.toolCallId??"—"}),e.jsx("td",{className:"max-w-[180px] truncate font-mono text-muted-foreground",children:a.digest})]},`${a.seq}-${a.eventId}`))})]})})]}):e.jsx("div",{className:"vc-empty",children:"Ledger disabled (VC1B off)."})})]})}function m({mode:s,count:a}){return e.jsxs("div",{className:"flex items-center justify-between border-b border-border/50 py-1 text-sm",children:[e.jsxs("span",{className:"text-muted-foreground",children:["Mode ",s]}),e.jsx("span",{className:"font-mono",children:a})]})}function Ne(){const[s,a]=n.useState(null),[u,h]=n.useState(null),[y,N]=n.useState(!0),[c,g]=n.useState(null),[f,v]=n.useState(null),[S,V]=n.useState(null),[R,E]=n.useState(null),[F,A]=n.useState(null),[k,T]=n.useState(null),[I,$]=n.useState(null),[w,P]=n.useState(null),[O,M]=n.useState(null),[B,L]=n.useState(null),[D,H]=n.useState(null),[z,Y]=n.useState(null),[_,U]=n.useState(null),[G,W]=n.useState(null),j=n.useCallback(()=>{K().then(a).catch(l=>h(l instanceof Error?l.message:String(l))).finally(()=>N(!1)),C().then(g).catch(()=>{}),ce().then(V).catch(()=>{}),Z().then(E).catch(()=>{}),J().then(A).catch(()=>{}),X().then(T).catch(()=>{}),ee().then($).catch(()=>{}),se().then(P).catch(()=>{}),te().then(M).catch(()=>{}),re().then(L).catch(()=>{}),ae().then(H).catch(()=>{}),ne().then(Y).catch(()=>{}),le().then(U).catch(()=>{}),de().then(W).catch(()=>{})},[]);n.useEffect(()=>{j();const l=setInterval(j,5e3);return()=>clearInterval(l)},[j]);const q=()=>{oe("provider").then(l=>(v(`Breaker "${l.subsystem}" ${l.state}; evidence retained: ${l.failures} failures / ${l.attempts} attempts`),C())).then(g).catch(l=>v(l instanceof Error?l.message:String(l)))};if(y&&!s)return e.jsx("div",{className:"vc-loading",children:"Loading vector-cortex evaluation…"});if(u&&!s)return e.jsxs("div",{className:"vc-error",children:["Error: ",u]});if(!s)return e.jsx("div",{className:"vc-empty",children:"No evaluation data available."});const p=Math.max(1,...s.histogram.cells,s.histogram.overflow);return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-lg font-semibold",children:"Vector Cortex Evaluation"}),s.enabled?e.jsx(r,{variant:"warning",children:"OBSERVER PENDING (A)"}):e.jsx(r,{variant:"danger",children:"MODE C (OFF)"})]}),e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-3",children:[e.jsxs(o,{children:[e.jsx(d,{children:e.jsx(i,{children:"Samples"})}),e.jsxs(x,{children:[e.jsx("div",{className:"text-3xl font-bold",children:s.samples}),e.jsxs("div",{className:"mt-2 flex flex-col",children:[e.jsx(m,{mode:"A",count:s.byMode.A}),e.jsx(m,{mode:"B",count:s.byMode.B}),e.jsx(m,{mode:"C",count:s.byMode.C})]})]})]}),e.jsxs(o,{className:"md:col-span-2",children:[e.jsx(d,{children:e.jsx(i,{children:"Latency Histogram (ms, inclusive edges)"})}),e.jsxs(x,{children:[e.jsxs("div",{className:"flex items-end gap-2",style:{height:160},children:[s.histogram.edges.map((l,b)=>{const Q=s.histogram.cells[b]/p*140;return e.jsxs("div",{className:"flex flex-1 flex-col items-center gap-1",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:s.histogram.cells[b]}),e.jsx("div",{className:"w-full rounded-t",style:{height:Math.max(2,Q),background:"#6366f1"}}),e.jsxs("span",{className:"text-[10px] text-muted-foreground",children:["≤",l]})]},l)}),e.jsxs("div",{className:"flex flex-1 flex-col items-center gap-1",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:s.histogram.overflow}),e.jsx("div",{className:"w-full rounded-t",style:{height:Math.max(2,s.histogram.overflow/p*140),background:"#f87171"}}),e.jsx("span",{className:"text-[10px] text-muted-foreground",children:">250"})]})]}),e.jsxs("div",{className:"mt-1 text-xs text-muted-foreground",children:["Total: ",s.histogram.total," · Rejects: ",s.rejects.length]})]})]})]}),e.jsxs(o,{children:[e.jsx(d,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(i,{children:"Live Safety Envelope (VC0C)"}),e.jsxs("div",{className:"flex items-center gap-2",children:[c?.stateSource==="ephemeral"&&e.jsx(r,{variant:"outline",children:"EPHEMERAL (non-live)"}),e.jsx("button",{onClick:q,disabled:!c?.enabled,className:"rounded border border-border px-2 py-1 text-xs disabled:opacity-50",children:"Reset Cooldown"})]})]})}),e.jsxs(x,{children:[f&&e.jsx("div",{className:"mb-2 text-xs text-muted-foreground",children:f}),c?e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"State",value:c.state}),e.jsx(t,{label:"Mode",value:c.mode}),e.jsx(t,{label:"Window",value:`${c.windowMs}ms`}),e.jsx(t,{label:"Probes",value:String(c.probeCount)}),e.jsx(t,{label:"Backoff",value:`${c.backoffDelayMs}ms`}),e.jsx(t,{label:"Failures",value:`${c.failures}/${c.attempts}`}),e.jsx(t,{label:"Frontier",value:c.stateSource==="ephemeral"?"non-live":c.frontierFrozen?"FROZEN":"LIVE"}),e.jsx(t,{label:"Spool lag",value:String(c.spoolLag)}),e.jsx(t,{label:"Encoder mode",value:c.encoderMode}),e.jsx(t,{label:"Encoder asset",value:c.encoderAssetDigest?c.encoderAssetDigest.slice(0,12):"none"})]}):e.jsx("div",{className:"vc-empty",children:"Health unavailable (VC0C off)."})]})]}),e.jsx(pe,{topology:R,query:F}),e.jsx(ge,{view:k}),e.jsx(fe,{view:I}),e.jsx(ve,{view:w}),e.jsx(ie,{view:O}),e.jsx(xe,{view:B}),e.jsx(ue,{view:D}),e.jsx(he,{view:z}),e.jsx(je,{view:_}),e.jsx(me,{view:G}),e.jsx(be,{ledger:S})]})}export{Ne as default};
|
|
2
|
-
//# sourceMappingURL=VectorCortexTab-94fqVjat.js.map
|