@animalabs/context-manager 0.5.3 → 0.5.5
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/src/adaptive/index.d.ts +6 -0
- package/dist/src/adaptive/index.d.ts.map +1 -1
- package/dist/src/adaptive/index.js +8 -0
- package/dist/src/adaptive/index.js.map +1 -1
- package/dist/src/adaptive/kv-cache-sim.d.ts +128 -0
- package/dist/src/adaptive/kv-cache-sim.d.ts.map +1 -0
- package/dist/src/adaptive/kv-cache-sim.js +0 -0
- package/dist/src/adaptive/kv-cache-sim.js.map +1 -0
- package/dist/src/adaptive/kv-control.d.ts +166 -0
- package/dist/src/adaptive/kv-control.d.ts.map +1 -0
- package/dist/src/adaptive/kv-control.js +434 -0
- package/dist/src/adaptive/kv-control.js.map +1 -0
- package/dist/src/adaptive/kv-replay.d.ts +76 -0
- package/dist/src/adaptive/kv-replay.d.ts.map +1 -0
- package/dist/src/adaptive/kv-replay.js +175 -0
- package/dist/src/adaptive/kv-replay.js.map +1 -0
- package/dist/src/adaptive/picker.d.ts +13 -0
- package/dist/src/adaptive/picker.d.ts.map +1 -1
- package/dist/src/adaptive/picker.js.map +1 -1
- package/dist/src/adaptive/render-offsets.d.ts +61 -0
- package/dist/src/adaptive/render-offsets.d.ts.map +1 -0
- package/dist/src/adaptive/render-offsets.js +88 -0
- package/dist/src/adaptive/render-offsets.js.map +1 -0
- package/dist/src/adaptive/strategies/kv-stable.d.ts +48 -0
- package/dist/src/adaptive/strategies/kv-stable.d.ts.map +1 -0
- package/dist/src/adaptive/strategies/kv-stable.js +153 -0
- package/dist/src/adaptive/strategies/kv-stable.js.map +1 -0
- package/dist/src/adaptive/summary-tree.d.ts +98 -0
- package/dist/src/adaptive/summary-tree.d.ts.map +1 -0
- package/dist/src/adaptive/summary-tree.js +214 -0
- package/dist/src/adaptive/summary-tree.js.map +1 -0
- package/dist/src/context-log.d.ts.map +1 -1
- package/dist/src/context-log.js +7 -2
- package/dist/src/context-log.js.map +1 -1
- package/dist/src/context-manager.d.ts +10 -5
- package/dist/src/context-manager.d.ts.map +1 -1
- package/dist/src/context-manager.js +9 -0
- package/dist/src/context-manager.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/message-store.js +1 -1
- package/dist/src/message-store.js.map +1 -1
- package/dist/src/phase-channel.d.ts +13 -0
- package/dist/src/phase-channel.d.ts.map +1 -0
- package/dist/src/phase-channel.js +55 -0
- package/dist/src/phase-channel.js.map +1 -0
- package/dist/src/strategies/autobiographical.d.ts +107 -44
- package/dist/src/strategies/autobiographical.d.ts.map +1 -1
- package/dist/src/strategies/autobiographical.js +437 -230
- package/dist/src/strategies/autobiographical.js.map +1 -1
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/strategy.d.ts +70 -7
- package/dist/src/types/strategy.d.ts.map +1 -1
- package/dist/src/types/strategy.js +2 -0
- package/dist/src/types/strategy.js.map +1 -1
- package/dist/test/adaptive/kv-cache-sim.test.d.ts +16 -0
- package/dist/test/adaptive/kv-cache-sim.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-cache-sim.test.js +173 -0
- package/dist/test/adaptive/kv-cache-sim.test.js.map +1 -0
- package/dist/test/adaptive/kv-control.test.d.ts +14 -0
- package/dist/test/adaptive/kv-control.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-control.test.js +130 -0
- package/dist/test/adaptive/kv-control.test.js.map +1 -0
- package/dist/test/adaptive/kv-replay.test.d.ts +15 -0
- package/dist/test/adaptive/kv-replay.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-replay.test.js +112 -0
- package/dist/test/adaptive/kv-replay.test.js.map +1 -0
- package/dist/test/adaptive/kv-stable-strategy.test.d.ts +10 -0
- package/dist/test/adaptive/kv-stable-strategy.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-stable-strategy.test.js +89 -0
- package/dist/test/adaptive/kv-stable-strategy.test.js.map +1 -0
- package/dist/test/adaptive/pin-at-level.test.d.ts +16 -0
- package/dist/test/adaptive/pin-at-level.test.d.ts.map +1 -0
- package/dist/test/adaptive/pin-at-level.test.js +133 -0
- package/dist/test/adaptive/pin-at-level.test.js.map +1 -0
- package/dist/test/adaptive/render-offsets.test.d.ts +11 -0
- package/dist/test/adaptive/render-offsets.test.d.ts.map +1 -0
- package/dist/test/adaptive/render-offsets.test.js +85 -0
- package/dist/test/adaptive/render-offsets.test.js.map +1 -0
- package/dist/test/adaptive/summary-tree.test.d.ts +5 -0
- package/dist/test/adaptive/summary-tree.test.d.ts.map +1 -0
- package/dist/test/adaptive/summary-tree.test.js +72 -0
- package/dist/test/adaptive/summary-tree.test.js.map +1 -0
- package/dist/test/image-strip-render-stats.test.d.ts +2 -0
- package/dist/test/image-strip-render-stats.test.d.ts.map +1 -0
- package/dist/test/image-strip-render-stats.test.js +130 -0
- package/dist/test/image-strip-render-stats.test.js.map +1 -0
- package/dist/test/image-stripping.test.d.ts +13 -0
- package/dist/test/image-stripping.test.d.ts.map +1 -0
- package/dist/test/image-stripping.test.js +104 -0
- package/dist/test/image-stripping.test.js.map +1 -0
- package/dist/test/pins.test.js +40 -0
- package/dist/test/pins.test.js.map +1 -1
- package/dist/test/render-stats.test.d.ts +2 -0
- package/dist/test/render-stats.test.d.ts.map +1 -0
- package/dist/test/render-stats.test.js +91 -0
- package/dist/test/render-stats.test.js.map +1 -0
- package/dist/test/speculation-cap.test.d.ts +9 -3
- package/dist/test/speculation-cap.test.d.ts.map +1 -1
- package/dist/test/speculation-cap.test.js +175 -3
- package/dist/test/speculation-cap.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/adaptive/index.ts +48 -0
- package/src/adaptive/kv-cache-sim.ts +0 -0
- package/src/adaptive/kv-control.ts +574 -0
- package/src/adaptive/kv-replay.ts +238 -0
- package/src/adaptive/picker.ts +13 -0
- package/src/adaptive/render-offsets.ts +116 -0
- package/src/adaptive/strategies/kv-stable.ts +161 -0
- package/src/adaptive/summary-tree.ts +252 -0
- package/src/context-log.ts +7 -2
- package/src/context-manager.ts +13 -2
- package/src/index.ts +3 -0
- package/src/message-store.ts +1 -1
- package/src/phase-channel.ts +57 -0
- package/src/strategies/autobiographical.ts +453 -282
- package/src/types/index.ts +1 -0
- package/src/types/strategy.ts +71 -3
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for session replay (kv-replay.ts) — the flat-profile BASELINE replay.
|
|
3
|
+
*
|
|
4
|
+
* Load-bearing properties:
|
|
5
|
+
* - deterministic: same inputs → identical ReplayResult;
|
|
6
|
+
* - growth is monotonic (visible chunks never shrink) and the cold start has
|
|
7
|
+
* no cache;
|
|
8
|
+
* - the availability rule holds: never folds a chunk to a level whose summary
|
|
9
|
+
* couldn't exist yet (lastSequence > now);
|
|
10
|
+
* - per-step bounds: 0 ≤ recomputed ≤ rendered, hitRate ∈ [0, 1];
|
|
11
|
+
* - the controller (replayControlled) is no worse than the flat baseline on
|
|
12
|
+
* average per-turn perturbation under the same budget.
|
|
13
|
+
*/
|
|
14
|
+
import { test } from 'node:test';
|
|
15
|
+
import assert from 'node:assert/strict';
|
|
16
|
+
import { SummaryTree } from '../../src/adaptive/summary-tree.js';
|
|
17
|
+
import { replaySession } from '../../src/adaptive/kv-replay.js';
|
|
18
|
+
import { replayControlled } from '../../src/adaptive/kv-control.js';
|
|
19
|
+
import { buildChronicleWithChain } from './harness.js';
|
|
20
|
+
function inputsOf(ch) {
|
|
21
|
+
return {
|
|
22
|
+
chunks: ch.chunks,
|
|
23
|
+
summaries: ch.summaries,
|
|
24
|
+
recallPairTokens: ch.recallPairTokens,
|
|
25
|
+
headTokens: 0,
|
|
26
|
+
tailTokens: 0,
|
|
27
|
+
headChunkIds: new Set(),
|
|
28
|
+
tailChunkIds: new Set(),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** A session big enough that a fixed cap forces folding partway through. */
|
|
32
|
+
function session() {
|
|
33
|
+
const ch = buildChronicleWithChain({
|
|
34
|
+
chunkCount: 72, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
35
|
+
});
|
|
36
|
+
return inputsOf(ch);
|
|
37
|
+
}
|
|
38
|
+
const COMMON = {
|
|
39
|
+
budgetTokens: 24_000, // 72k raw will blow past this → folding kicks in
|
|
40
|
+
markerCount: 4,
|
|
41
|
+
rawTailTokens: 4_000,
|
|
42
|
+
};
|
|
43
|
+
test('replay is deterministic', () => {
|
|
44
|
+
const inputs = session();
|
|
45
|
+
assert.deepEqual(replaySession(inputs, COMMON), replaySession(inputs, COMMON));
|
|
46
|
+
});
|
|
47
|
+
test('growth is monotonic and the cold start has no cache', () => {
|
|
48
|
+
const r = replaySession(session(), COMMON);
|
|
49
|
+
assert.ok(r.steps.length > 1);
|
|
50
|
+
assert.equal(r.steps[0].cachedTokens, 0, 'first step is a cold cache');
|
|
51
|
+
assert.equal(r.steps[0].recomputedTokens, r.steps[0].renderedTokens);
|
|
52
|
+
for (let i = 1; i < r.steps.length; i++) {
|
|
53
|
+
assert.ok(r.steps[i].numChunks >= r.steps[i - 1].numChunks, 'visible chunks never shrink');
|
|
54
|
+
assert.ok(r.steps[i].now >= r.steps[i - 1].now, 'now advances');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
test('per-step bounds: 0 ≤ recomputed ≤ rendered, hitRate ∈ [0,1]', () => {
|
|
58
|
+
const r = replaySession(session(), COMMON);
|
|
59
|
+
for (const s of r.steps) {
|
|
60
|
+
assert.ok(s.recomputedTokens >= 0 && s.recomputedTokens <= s.renderedTokens, 'recompute in range');
|
|
61
|
+
assert.ok(s.cachedTokens >= 0, 'cached ≥ 0');
|
|
62
|
+
assert.equal(s.cachedTokens + s.recomputedTokens, s.renderedTokens, 'cached + recompute = rendered');
|
|
63
|
+
assert.ok(s.hitRate >= 0 && s.hitRate <= 1, 'hitRate in [0,1]');
|
|
64
|
+
}
|
|
65
|
+
assert.ok(r.overallHitRate >= 0 && r.overallHitRate <= 1);
|
|
66
|
+
});
|
|
67
|
+
test('availability rule: never folds to a level that does not exist yet', () => {
|
|
68
|
+
const inputs = session();
|
|
69
|
+
const tree = new SummaryTree(inputs);
|
|
70
|
+
const maxAvailableLevel = (now) => {
|
|
71
|
+
let max = 0;
|
|
72
|
+
for (const s of tree.allSummaries()) {
|
|
73
|
+
if (s.lastSequence >= 0 && s.lastSequence <= now && s.level > max)
|
|
74
|
+
max = s.level;
|
|
75
|
+
}
|
|
76
|
+
return max;
|
|
77
|
+
};
|
|
78
|
+
const r = replaySession(inputs, COMMON);
|
|
79
|
+
let sawDeep = false;
|
|
80
|
+
for (const s of r.steps) {
|
|
81
|
+
assert.ok(s.deepestLevel <= maxAvailableLevel(s.now), `step now=${s.now}: folded to L${s.deepestLevel} but only L${maxAvailableLevel(s.now)} available`);
|
|
82
|
+
if (maxAvailableLevel(s.now) >= 2)
|
|
83
|
+
sawDeep = true;
|
|
84
|
+
}
|
|
85
|
+
assert.ok(sawDeep, 'fixture should expose L2+ summaries by the end');
|
|
86
|
+
});
|
|
87
|
+
test('controller (kv-stable) is no worse than the flat baseline on average per-turn perturbation', () => {
|
|
88
|
+
const inputs = session();
|
|
89
|
+
const flat = replaySession(inputs, COMMON);
|
|
90
|
+
const kv = replayControlled(inputs, {
|
|
91
|
+
windowTokens: 60_000,
|
|
92
|
+
budgetTokens: COMMON.budgetTokens,
|
|
93
|
+
attendedWindowTokens: COMMON.rawTailTokens,
|
|
94
|
+
markerCount: COMMON.markerCount,
|
|
95
|
+
targetSteps: 120,
|
|
96
|
+
});
|
|
97
|
+
// Per-turn perturbation for the flat baseline (recompute beyond new content).
|
|
98
|
+
const flatRms = (() => {
|
|
99
|
+
let prevNow = -1, sumsq = 0;
|
|
100
|
+
for (const s of flat.steps) {
|
|
101
|
+
const newContent = inputs.chunks
|
|
102
|
+
.filter((c) => c.sequence > prevNow && c.sequence <= s.now)
|
|
103
|
+
.reduce((a, c) => a + c.rawTokens, 0);
|
|
104
|
+
const p = Math.max(0, s.recomputedTokens - newContent);
|
|
105
|
+
sumsq += p * p;
|
|
106
|
+
prevNow = s.now;
|
|
107
|
+
}
|
|
108
|
+
return Math.sqrt(sumsq / flat.steps.length);
|
|
109
|
+
})();
|
|
110
|
+
assert.ok(kv.rmsPerturbation <= flatRms * 1.05, `controller RMS perturbation ${kv.rmsPerturbation.toFixed(0)} ≤ flat ${flatRms.toFixed(0)} (×1.05)`);
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=kv-replay.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-replay.test.js","sourceRoot":"","sources":["../../../test/adaptive/kv-replay.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end: KvStableStrategy driven through the real Picker.run loop.
|
|
3
|
+
*
|
|
4
|
+
* Asserts the controller policy works as a drop-in FoldingStrategy: the picker
|
|
5
|
+
* walks to exactly the planned frontier, the result fits the budget, pins/locks
|
|
6
|
+
* are honored, and the same `planControlledFrontier` the replay harness measures
|
|
7
|
+
* is what the picker reaches.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=kv-stable-strategy.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-stable-strategy.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/kv-stable-strategy.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end: KvStableStrategy driven through the real Picker.run loop.
|
|
3
|
+
*
|
|
4
|
+
* Asserts the controller policy works as a drop-in FoldingStrategy: the picker
|
|
5
|
+
* walks to exactly the planned frontier, the result fits the budget, pins/locks
|
|
6
|
+
* are honored, and the same `planControlledFrontier` the replay harness measures
|
|
7
|
+
* is what the picker reaches.
|
|
8
|
+
*/
|
|
9
|
+
import { test } from 'node:test';
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { Picker } from '../../src/adaptive/picker.js';
|
|
12
|
+
import { SummaryTree } from '../../src/adaptive/summary-tree.js';
|
|
13
|
+
import { renderLayout } from '../../src/adaptive/render-offsets.js';
|
|
14
|
+
import { planControlledFrontier } from '../../src/adaptive/kv-control.js';
|
|
15
|
+
import { KvStableStrategy } from '../../src/adaptive/strategies/kv-stable.js';
|
|
16
|
+
import { buildChronicleWithChain } from './harness.js';
|
|
17
|
+
const BUDGET = (totalBudget, slack = 0.1) => ({
|
|
18
|
+
totalBudget,
|
|
19
|
+
targetBudget: totalBudget * (1 - slack),
|
|
20
|
+
slack,
|
|
21
|
+
});
|
|
22
|
+
function inputsOf(ch) {
|
|
23
|
+
return {
|
|
24
|
+
chunks: ch.chunks, summaries: ch.summaries, recallPairTokens: ch.recallPairTokens,
|
|
25
|
+
headTokens: 0, tailTokens: 0, headChunkIds: new Set(), tailChunkIds: new Set(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function sameMap(a, b) {
|
|
29
|
+
if (a.size !== b.size)
|
|
30
|
+
return false;
|
|
31
|
+
for (const [k, v] of a)
|
|
32
|
+
if (b.get(k) !== v)
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
test('kv-stable strategy: picker walks to exactly the planned frontier and fits budget', () => {
|
|
37
|
+
const ch = buildChronicleWithChain({
|
|
38
|
+
chunkCount: 48, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
39
|
+
});
|
|
40
|
+
const inputs = inputsOf(ch);
|
|
41
|
+
const tree = new SummaryTree(inputs);
|
|
42
|
+
const budget = BUDGET(15_000); // targetBudget 13_500
|
|
43
|
+
// Independent reference plan (same policy the strategy uses internally).
|
|
44
|
+
let now = 0;
|
|
45
|
+
for (const c of inputs.chunks)
|
|
46
|
+
if (c.sequence > now)
|
|
47
|
+
now = c.sequence;
|
|
48
|
+
const plan = planControlledFrontier(inputs, tree, {
|
|
49
|
+
previous: new Map(), foldAtTokens: budget.totalBudget, expandAtTokens: budget.targetBudget,
|
|
50
|
+
targetTokens: budget.targetBudget, windowTokens: budget.totalBudget, rawZone: new Set(), now, mergeThreshold: 6,
|
|
51
|
+
});
|
|
52
|
+
const strategy = new KvStableStrategy(inputs, {});
|
|
53
|
+
const result = new Picker(strategy).run(inputs, budget);
|
|
54
|
+
assert.ok(sameMap(result.finalResolutions, plan.resolutions), 'picker reaches the planned frontier');
|
|
55
|
+
assert.ok(result.finalTokens <= budget.totalBudget, `fits hard wall: ${result.finalTokens} ≤ 15000`);
|
|
56
|
+
assert.equal(renderLayout(inputs, tree, result.finalResolutions).totalTokens, result.finalTokens);
|
|
57
|
+
});
|
|
58
|
+
test('kv-stable strategy: converges to null and leaves a small budget untouched', () => {
|
|
59
|
+
const ch = buildChronicleWithChain({ chunkCount: 8, tokensPerChunk: 500, mergeThreshold: 6, recallPairTokens: 200 });
|
|
60
|
+
const inputs = inputsOf(ch); // 4k raw
|
|
61
|
+
const result = new Picker(new KvStableStrategy(inputs, {})).run(inputs, BUDGET(100_000));
|
|
62
|
+
for (const c of inputs.chunks) {
|
|
63
|
+
assert.equal(result.finalResolutions.get(c.id), 0, 'nothing folded under a generous budget');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
test('kv-stable strategy: pinned chunks stay raw, locked chunks keep their resolution', () => {
|
|
67
|
+
const ch = buildChronicleWithChain({ chunkCount: 48, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200 });
|
|
68
|
+
ch.chunks[2].pinned = true; // old → would otherwise fold
|
|
69
|
+
ch.chunks[5].lockedByAgent = true;
|
|
70
|
+
ch.chunks[5].currentResolution = 1; // frozen at L1
|
|
71
|
+
const inputs = inputsOf(ch);
|
|
72
|
+
const result = new Picker(new KvStableStrategy(inputs, {})).run(inputs, BUDGET(15_000));
|
|
73
|
+
assert.equal(result.finalResolutions.get(ch.chunks[2].id), 0, 'pinned chunk stays raw');
|
|
74
|
+
assert.equal(result.finalResolutions.get(ch.chunks[5].id), 1, 'locked chunk keeps its resolution');
|
|
75
|
+
});
|
|
76
|
+
test('kv-stable strategy: a tighter reach cap holds the deep prefix raw (shallower divergence)', () => {
|
|
77
|
+
const ch = buildChronicleWithChain({ chunkCount: 60, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200 });
|
|
78
|
+
const inputs = inputsOf(ch);
|
|
79
|
+
const budget = BUDGET(20_000);
|
|
80
|
+
const tight = new Picker(new KvStableStrategy(inputs, { reachTokens: 8_000 })).run(inputs, budget);
|
|
81
|
+
const wide = new Picker(new KvStableStrategy(inputs, { reachTokens: 60_000 })).run(inputs, budget);
|
|
82
|
+
// Deepest level among the oldest third of chunks: a tight reach can't fold
|
|
83
|
+
// that far back, so the deep prefix stays shallower (more raw) than with wide.
|
|
84
|
+
const ordered = [...inputs.chunks].sort((a, b) => a.sequence - b.sequence);
|
|
85
|
+
const oldThird = ordered.slice(0, 20);
|
|
86
|
+
const deepest = (r) => Math.max(...oldThird.map((c) => r.finalResolutions.get(c.id) ?? 0));
|
|
87
|
+
assert.ok(deepest(tight) <= deepest(wide), `tight reach keeps the deep prefix shallower (${deepest(tight)} ≤ ${deepest(wide)})`);
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=kv-stable-strategy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-stable-strategy.test.js","sourceRoot":"","sources":["../../../test/adaptive/kv-stable-strategy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for V2 dynamic pins — pin-at-level-k and pin-max-level
|
|
3
|
+
* (`docs/best-fit-frontier-resolution.md` §7). These extend the classic
|
|
4
|
+
* pin-as-raw with a fold-depth bound honored by the KV-stable controller:
|
|
5
|
+
*
|
|
6
|
+
* - `fixedLevels` (ProtectedRange.level): the chunk is fixed at EXACTLY level k
|
|
7
|
+
* — neither un-folded shallower (even under generous budget) nor folded
|
|
8
|
+
* deeper (even under budget pressure);
|
|
9
|
+
* - `pinCaps` (ProtectedRange.maxLevel): a HARD cap on fold depth — the chunk
|
|
10
|
+
* may render raw..k but never deeper, enforced in normal AND emergency
|
|
11
|
+
* shedding, and a carried resolution deeper than the cap is un-folded to it;
|
|
12
|
+
* - level clamps to the deepest produced level (can't render at a missing L_k);
|
|
13
|
+
* - the KvStableStrategy walks the picker to a pin-at-k target.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=pin-at-level.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-at-level.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/pin-at-level.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for V2 dynamic pins — pin-at-level-k and pin-max-level
|
|
3
|
+
* (`docs/best-fit-frontier-resolution.md` §7). These extend the classic
|
|
4
|
+
* pin-as-raw with a fold-depth bound honored by the KV-stable controller:
|
|
5
|
+
*
|
|
6
|
+
* - `fixedLevels` (ProtectedRange.level): the chunk is fixed at EXACTLY level k
|
|
7
|
+
* — neither un-folded shallower (even under generous budget) nor folded
|
|
8
|
+
* deeper (even under budget pressure);
|
|
9
|
+
* - `pinCaps` (ProtectedRange.maxLevel): a HARD cap on fold depth — the chunk
|
|
10
|
+
* may render raw..k but never deeper, enforced in normal AND emergency
|
|
11
|
+
* shedding, and a carried resolution deeper than the cap is un-folded to it;
|
|
12
|
+
* - level clamps to the deepest produced level (can't render at a missing L_k);
|
|
13
|
+
* - the KvStableStrategy walks the picker to a pin-at-k target.
|
|
14
|
+
*/
|
|
15
|
+
import { test } from 'node:test';
|
|
16
|
+
import assert from 'node:assert/strict';
|
|
17
|
+
import { SummaryTree } from '../../src/adaptive/summary-tree.js';
|
|
18
|
+
import { planControlledFrontier } from '../../src/adaptive/kv-control.js';
|
|
19
|
+
import { Picker } from '../../src/adaptive/picker.js';
|
|
20
|
+
import { KvStableStrategy } from '../../src/adaptive/strategies/kv-stable.js';
|
|
21
|
+
import { buildChronicleWithChain } from './harness.js';
|
|
22
|
+
function inputsOf(ch) {
|
|
23
|
+
return {
|
|
24
|
+
chunks: ch.chunks, summaries: ch.summaries, recallPairTokens: ch.recallPairTokens,
|
|
25
|
+
headTokens: 0, tailTokens: 0, headChunkIds: new Set(), tailChunkIds: new Set(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// 90 chunks × 1000t = 90k raw; mergeThreshold 6 → 15 L1s → 2 L2s (max level 2).
|
|
29
|
+
// c-0000 has an L1 and an L2 ancestor.
|
|
30
|
+
function session() {
|
|
31
|
+
const ch = buildChronicleWithChain({ chunkCount: 90, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200 });
|
|
32
|
+
const inputs = inputsOf(ch);
|
|
33
|
+
return { ch, inputs, tree: new SummaryTree(inputs) };
|
|
34
|
+
}
|
|
35
|
+
const NOW = 89;
|
|
36
|
+
test('pin-at-level-k: fixed at exactly k under BOTH generous and tight budgets', () => {
|
|
37
|
+
const { tree, inputs } = session();
|
|
38
|
+
const pinned = 'c-0000';
|
|
39
|
+
// Group-consistent pin: the whole L2 node covering c-0000 is fixed at L2 (an
|
|
40
|
+
// L2 recall pair is atomic over its range — this is what the strategy expands
|
|
41
|
+
// a single-chunk pin-at-k into).
|
|
42
|
+
const node = tree.ancestorAt(pinned, 2);
|
|
43
|
+
const fixedLevels = new Map(node.leafChunkIds.map((id) => [id, 2]));
|
|
44
|
+
const outside = 'c-0040'; // in the OTHER L2 group → free to move
|
|
45
|
+
assert.ok(!node.leafChunkIds.includes(outside), 'neighbor is outside the pinned node');
|
|
46
|
+
// Generous budget → the controller un-folds everything toward raw to use
|
|
47
|
+
// headroom. The pinned node must stay at L2 (NOT un-folded to raw).
|
|
48
|
+
const loose = planControlledFrontier(inputs, tree, {
|
|
49
|
+
previous: new Map(fixedLevels),
|
|
50
|
+
foldAtTokens: 200_000, expandAtTokens: 200_000, targetTokens: 200_000,
|
|
51
|
+
windowTokens: 200_000, rawZone: new Set(), fixedLevels, now: NOW, mergeThreshold: 6,
|
|
52
|
+
});
|
|
53
|
+
assert.equal(loose.resolutions.get(pinned), 2, 'pinned held at L2 under generous budget (not un-folded)');
|
|
54
|
+
assert.equal(loose.resolutions.get(outside), 0, 'un-pinned old chunk un-folds to raw');
|
|
55
|
+
// Tight budget → the controller folds the middle deep. The pinned node must
|
|
56
|
+
// still be EXACTLY L2 (not folded deeper — no L3 exists — and not shallower).
|
|
57
|
+
const tight = planControlledFrontier(inputs, tree, {
|
|
58
|
+
previous: new Map(),
|
|
59
|
+
foldAtTokens: 12_000, expandAtTokens: 0, targetTokens: 12_000,
|
|
60
|
+
windowTokens: 90_000, rawZone: new Set(), fixedLevels, now: NOW, mergeThreshold: 6,
|
|
61
|
+
});
|
|
62
|
+
assert.equal(tight.resolutions.get(pinned), 2, 'pinned fixed at L2 under tight budget');
|
|
63
|
+
});
|
|
64
|
+
test('pin-at-level clamps to the deepest produced level (no phantom L_k)', () => {
|
|
65
|
+
const { tree, inputs } = session();
|
|
66
|
+
const pinned = 'c-0000';
|
|
67
|
+
// Ask for L5, which does not exist (max is L2) → clamp to L2.
|
|
68
|
+
const r = planControlledFrontier(inputs, tree, {
|
|
69
|
+
previous: new Map(),
|
|
70
|
+
foldAtTokens: 12_000, expandAtTokens: 0, targetTokens: 12_000,
|
|
71
|
+
windowTokens: 90_000, rawZone: new Set(), fixedLevels: new Map([[pinned, 5]]),
|
|
72
|
+
now: NOW, mergeThreshold: 6,
|
|
73
|
+
});
|
|
74
|
+
assert.equal(r.resolutions.get(pinned), 2, 'clamped to the deepest available level (L2)');
|
|
75
|
+
});
|
|
76
|
+
test('pin-max-level: a HARD fold-depth cap, enforced even under the W emergency', () => {
|
|
77
|
+
const { tree, inputs } = session();
|
|
78
|
+
const capped = 'c-0006'; // in the second L1 group (L1-1 → L2-0)
|
|
79
|
+
const pinCaps = new Map([[capped, 1]]); // never fold deeper than L1
|
|
80
|
+
// Very tight target: folding everything to L1 (15 groups × 200 = 3000t) does
|
|
81
|
+
// NOT fit, so the controller must fold to L2 — forcing the deepest available
|
|
82
|
+
// level. The capped chunk must still never exceed L1 (a pin is hard-protected).
|
|
83
|
+
const r = planControlledFrontier(inputs, tree, {
|
|
84
|
+
previous: new Map(),
|
|
85
|
+
foldAtTokens: 1_500, expandAtTokens: 0, targetTokens: 1_200,
|
|
86
|
+
windowTokens: 2_000, rawZone: new Set(), pinCaps, now: NOW, mergeThreshold: 6,
|
|
87
|
+
});
|
|
88
|
+
assert.ok((r.resolutions.get(capped) ?? 0) <= 1, `capped chunk never folds past L1 (got L${r.resolutions.get(capped)})`);
|
|
89
|
+
// An un-capped neighbor in a DIFFERENT L2 group is free to fold to L2 under
|
|
90
|
+
// the same pressure (a chunk sharing the capped chunk's L2 node would be held
|
|
91
|
+
// by group-consistency, which is correct — so pick one outside it).
|
|
92
|
+
assert.ok((r.resolutions.get('c-0040') ?? 0) >= 2, 'un-capped neighbor folds deeper');
|
|
93
|
+
});
|
|
94
|
+
test('pin-max-level un-folds a carried resolution deeper than the cap (on pin-add)', () => {
|
|
95
|
+
const { tree, inputs } = session();
|
|
96
|
+
const capped = 'c-0006';
|
|
97
|
+
// F_prev has it deep at L2; adding maxLevel:1 must un-fold it to L1 immediately,
|
|
98
|
+
// even though the render is comfortably under budget (dead band).
|
|
99
|
+
const r = planControlledFrontier(inputs, tree, {
|
|
100
|
+
previous: new Map([[capped, 2]]),
|
|
101
|
+
foldAtTokens: 200_000, expandAtTokens: 0, targetTokens: 200_000,
|
|
102
|
+
windowTokens: 200_000, rawZone: new Set(), pinCaps: new Map([[capped, 1]]),
|
|
103
|
+
now: NOW, mergeThreshold: 6,
|
|
104
|
+
});
|
|
105
|
+
assert.equal(r.resolutions.get(capped), 1, 'carried L2 clamped down to the L1 cap at plan start');
|
|
106
|
+
});
|
|
107
|
+
test('KvStableStrategy walks the picker to a pin-at-level-k target', () => {
|
|
108
|
+
const { ch, inputs } = session();
|
|
109
|
+
// Pin c-0000 at L2 via the PickerChunk field the strategy reads.
|
|
110
|
+
const target = ch.chunks.find((c) => c.id === 'c-0000');
|
|
111
|
+
target.pinLevel = 2;
|
|
112
|
+
target.currentResolution = 0; // starts raw → must be raised to L2
|
|
113
|
+
const picker = new Picker(new KvStableStrategy(inputs));
|
|
114
|
+
const result = picker.run(inputs, { totalBudget: 12_000, targetBudget: 10_000, slack: 0 });
|
|
115
|
+
assert.equal(result.finalResolutions.get('c-0000'), 2, 'picker walked the pinned chunk to exactly L2');
|
|
116
|
+
});
|
|
117
|
+
test('pinned-at-level render survives a full budget sweep (no oscillation)', () => {
|
|
118
|
+
const { tree, inputs } = session();
|
|
119
|
+
const pinned = 'c-0000';
|
|
120
|
+
const l1 = tree.ancestorAt(pinned, 1);
|
|
121
|
+
const fixedLevels = new Map(l1.leafChunkIds.map((id) => [id, 1]));
|
|
122
|
+
let prev = new Map();
|
|
123
|
+
for (const budget of [12_000, 40_000, 90_000, 40_000, 12_000]) {
|
|
124
|
+
const r = planControlledFrontier(inputs, tree, {
|
|
125
|
+
previous: prev,
|
|
126
|
+
foldAtTokens: budget, expandAtTokens: budget * 0.9, targetTokens: budget * 0.9,
|
|
127
|
+
windowTokens: 90_000, rawZone: new Set(), fixedLevels, now: NOW, mergeThreshold: 6,
|
|
128
|
+
});
|
|
129
|
+
assert.equal(r.resolutions.get(pinned), 1, `pinned held at L1 at budget ${budget}`);
|
|
130
|
+
prev = r.resolutions;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
//# sourceMappingURL=pin-at-level.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-at-level.test.js","sourceRoot":"","sources":["../../../test/adaptive/pin-at-level.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for render-offsets — the rendered-unit layout + KV-cost accounting.
|
|
3
|
+
*
|
|
4
|
+
* Two things matter:
|
|
5
|
+
* 1. `renderLayout().totalTokens` must equal the picker's own computeTokens
|
|
6
|
+
* model (parity), so the KV term is computed over the real render.
|
|
7
|
+
* 2. KV cost must be position-aware: a change deep in the old prefix costs far
|
|
8
|
+
* more than a change near the tail — the whole reason for the term.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=render-offsets.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-offsets.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/render-offsets.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for render-offsets — the rendered-unit layout + KV-cost accounting.
|
|
3
|
+
*
|
|
4
|
+
* Two things matter:
|
|
5
|
+
* 1. `renderLayout().totalTokens` must equal the picker's own computeTokens
|
|
6
|
+
* model (parity), so the KV term is computed over the real render.
|
|
7
|
+
* 2. KV cost must be position-aware: a change deep in the old prefix costs far
|
|
8
|
+
* more than a change near the tail — the whole reason for the term.
|
|
9
|
+
*/
|
|
10
|
+
import { test } from 'node:test';
|
|
11
|
+
import assert from 'node:assert/strict';
|
|
12
|
+
import { Picker } from '../../src/adaptive/picker.js';
|
|
13
|
+
import { FlatProfileStrategy } from '../../src/adaptive/strategies/flat-profile.js';
|
|
14
|
+
import { SummaryTree } from '../../src/adaptive/summary-tree.js';
|
|
15
|
+
import { renderLayout, kvCost, earliestDivergenceIndex, } from '../../src/adaptive/render-offsets.js';
|
|
16
|
+
import { buildChronicleWithChain } from './harness.js';
|
|
17
|
+
const BUDGET = (totalBudget, slack = 0.1) => ({
|
|
18
|
+
totalBudget,
|
|
19
|
+
targetBudget: totalBudget * (1 - slack),
|
|
20
|
+
slack,
|
|
21
|
+
});
|
|
22
|
+
function inputsOf(ch) {
|
|
23
|
+
// Explicit empty head/tail. (computeHeadTail force-includes one tail chunk
|
|
24
|
+
// even at tailTokens:0, which would add the tail block to the recomputed
|
|
25
|
+
// suffix and muddy the position-aware assertion below.)
|
|
26
|
+
return {
|
|
27
|
+
chunks: ch.chunks,
|
|
28
|
+
summaries: ch.summaries,
|
|
29
|
+
recallPairTokens: ch.recallPairTokens,
|
|
30
|
+
headTokens: 0,
|
|
31
|
+
tailTokens: 0,
|
|
32
|
+
headChunkIds: new Set(),
|
|
33
|
+
tailChunkIds: new Set(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function frontier(ids, level) {
|
|
37
|
+
return new Map(ids.map((id) => [id, level]));
|
|
38
|
+
}
|
|
39
|
+
test('render-offsets: totalTokens matches the picker computeTokens model (parity)', () => {
|
|
40
|
+
const ch = buildChronicleWithChain({
|
|
41
|
+
chunkCount: 36, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
42
|
+
});
|
|
43
|
+
const inputs = inputsOf(ch);
|
|
44
|
+
const result = new Picker(new FlatProfileStrategy()).run(inputs, BUDGET(10_000));
|
|
45
|
+
assert.ok(result.applied.length > 0, 'expected the picker to fold something');
|
|
46
|
+
const tree = new SummaryTree(inputs);
|
|
47
|
+
const layout = renderLayout(inputs, tree, result.finalResolutions);
|
|
48
|
+
assert.equal(layout.totalTokens, result.finalTokens);
|
|
49
|
+
});
|
|
50
|
+
test('render-offsets: kvCost is zero for an unchanged frontier', () => {
|
|
51
|
+
const ch = buildChronicleWithChain({
|
|
52
|
+
chunkCount: 36, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
53
|
+
});
|
|
54
|
+
const inputs = inputsOf(ch);
|
|
55
|
+
const tree = new SummaryTree(inputs);
|
|
56
|
+
const layout = renderLayout(inputs, tree, new Map());
|
|
57
|
+
assert.equal(kvCost(layout, layout), 0);
|
|
58
|
+
});
|
|
59
|
+
test('render-offsets: deep-old change costs more than a near-tail change (position-aware)', () => {
|
|
60
|
+
const ch = buildChronicleWithChain({
|
|
61
|
+
chunkCount: 36, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
62
|
+
});
|
|
63
|
+
const inputs = inputsOf(ch);
|
|
64
|
+
const tree = new SummaryTree(inputs);
|
|
65
|
+
const allRaw = renderLayout(inputs, tree, new Map());
|
|
66
|
+
const foldOldest = renderLayout(inputs, tree, frontier(['c-0000', 'c-0001', 'c-0002', 'c-0003', 'c-0004', 'c-0005'], 1));
|
|
67
|
+
const foldNewest = renderLayout(inputs, tree, frontier(['c-0030', 'c-0031', 'c-0032', 'c-0033', 'c-0034', 'c-0035'], 1));
|
|
68
|
+
const costOld = kvCost(allRaw, foldOldest);
|
|
69
|
+
const costNew = kvCost(allRaw, foldNewest);
|
|
70
|
+
assert.ok(costNew < costOld, `near-tail (${costNew}) must cost less than deep-old (${costOld})`);
|
|
71
|
+
assert.equal(costNew, 200, 'near-tail fold recomputes only the new recall pair');
|
|
72
|
+
});
|
|
73
|
+
test('render-offsets: earliestDivergenceIndex + kvCost prefix semantics', () => {
|
|
74
|
+
const u = (kind, key, tokens, offset) => ({ kind, key, tokens, offset });
|
|
75
|
+
const a = [u('head', 'head', 10, 0), u('raw', 'x', 5, 10), u('raw', 'y', 5, 15)];
|
|
76
|
+
const same = [u('head', 'head', 10, 0), u('raw', 'x', 5, 10), u('raw', 'y', 5, 15)];
|
|
77
|
+
assert.equal(earliestDivergenceIndex(a, same), -1);
|
|
78
|
+
const prefix = [u('head', 'head', 10, 0), u('raw', 'x', 5, 10)]; // strict prefix of a
|
|
79
|
+
assert.equal(earliestDivergenceIndex(a, prefix), 2);
|
|
80
|
+
assert.equal(kvCost({ units: a, totalTokens: 20 }, { units: prefix, totalTokens: 15 }), 0);
|
|
81
|
+
const diff = [u('head', 'head', 10, 0), u('raw', 'z', 5, 10), u('raw', 'y', 5, 15)];
|
|
82
|
+
assert.equal(earliestDivergenceIndex(a, diff), 1);
|
|
83
|
+
assert.equal(kvCost({ units: a, totalTokens: 20 }, { units: diff, totalTokens: 20 }), 10); // 20 − offset@1
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=render-offsets.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-offsets.test.js","sourceRoot":"","sources":["../../../test/adaptive/render-offsets.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AAGpF,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EACL,YAAY,EACZ,MAAM,EACN,uBAAuB,GAExB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAiB,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEtE,MAAM,MAAM,GAAG,CAAC,WAAmB,EAAE,KAAK,GAAG,GAAG,EAAiB,EAAE,CAAC,CAAC;IACnE,WAAW;IACX,YAAY,EAAE,WAAW,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;IACvC,KAAK;CACN,CAAC,CAAC;AAEH,SAAS,QAAQ,CAAC,EAAiB;IACjC,2EAA2E;IAC3E,yEAAyE;IACzE,wDAAwD;IACxD,OAAO;QACL,MAAM,EAAE,EAAE,CAAC,MAAM;QACjB,SAAS,EAAE,EAAE,CAAC,SAAS;QACvB,gBAAgB,EAAE,EAAE,CAAC,gBAAgB;QACrC,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,YAAY,EAAE,IAAI,GAAG,EAAE;KACxB,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,GAAc,EAAE,KAAa;IAC7C,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAqB,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,IAAI,CAAC,6EAA6E,EAAE,GAAG,EAAE;IACvF,MAAM,EAAE,GAAG,uBAAuB,CAAC;QACjC,UAAU,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,gBAAgB,EAAE,GAAG;KAC/E,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,mBAAmB,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACjF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,uCAAuC,CAAC,CAAC;IAE9E,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACnE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACpE,MAAM,EAAE,GAAG,uBAAuB,CAAC;QACjC,UAAU,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,gBAAgB,EAAE,GAAG;KAC/E,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;IACrD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qFAAqF,EAAE,GAAG,EAAE;IAC/F,MAAM,EAAE,GAAG,uBAAuB,CAAC;QACjC,UAAU,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,gBAAgB,EAAE,GAAG;KAC/E,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IAErC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,YAAY,CAC7B,MAAM,EAAE,IAAI,EACZ,QAAQ,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAC1E,CAAC;IACF,MAAM,UAAU,GAAG,YAAY,CAC7B,MAAM,EAAE,IAAI,EACZ,QAAQ,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAC1E,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,CAAC,OAAO,GAAG,OAAO,EAAE,cAAc,OAAO,mCAAmC,OAAO,GAAG,CAAC,CAAC;IACjG,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,oDAAoD,CAAC,CAAC;AACnF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mEAAmE,EAAE,GAAG,EAAE;IAC7E,MAAM,CAAC,GAAG,CAAC,IAA0B,EAAE,GAAW,EAAE,MAAc,EAAE,MAAc,EAAgB,EAAE,CAClG,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAElC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACjF,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,qBAAqB;IACtF,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAE3F,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB;AAC7G,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-tree.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/summary-tree.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for SummaryTree — the structural view the V2 best-fit DP walks.
|
|
3
|
+
*/
|
|
4
|
+
import { test } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { SummaryTree, nodeTokens } from '../../src/adaptive/summary-tree.js';
|
|
7
|
+
import { MockChronicle, buildChronicleWithChain } from './harness.js';
|
|
8
|
+
function inputsOf(chronicle) {
|
|
9
|
+
const ht = chronicle.computeHeadTail({ headTokens: 0, tailTokens: 0 });
|
|
10
|
+
return {
|
|
11
|
+
chunks: chronicle.chunks,
|
|
12
|
+
summaries: chronicle.summaries,
|
|
13
|
+
recallPairTokens: chronicle.recallPairTokens,
|
|
14
|
+
...ht,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
test('summary-tree: two L1s, no L2 — roots are the L1 summaries', () => {
|
|
18
|
+
const ch = buildChronicleWithChain({
|
|
19
|
+
chunkCount: 12,
|
|
20
|
+
tokensPerChunk: 100,
|
|
21
|
+
mergeThreshold: 6,
|
|
22
|
+
recallPairTokens: 200,
|
|
23
|
+
});
|
|
24
|
+
const tree = new SummaryTree(inputsOf(ch));
|
|
25
|
+
assert.equal(tree.ancestorAt('c-0000', 1)?.id, 'L1-0');
|
|
26
|
+
assert.equal(tree.ancestorAt('c-0000', 2), null, 'no L2 produced');
|
|
27
|
+
assert.equal(tree.maxLevel('c-0000'), 1);
|
|
28
|
+
assert.deepEqual(tree.leavesUnder('L1-0'), [
|
|
29
|
+
'c-0000', 'c-0001', 'c-0002', 'c-0003', 'c-0004', 'c-0005',
|
|
30
|
+
]);
|
|
31
|
+
assert.equal(tree.recallTokens('L1-0'), 200);
|
|
32
|
+
const roots = tree.roots();
|
|
33
|
+
assert.deepEqual(roots.map((r) => (r.kind === 'summary' ? r.id : r.chunkId)), ['L1-0', 'L1-1']);
|
|
34
|
+
const l1 = tree.summary('L1-0');
|
|
35
|
+
const kids = tree.children(l1);
|
|
36
|
+
assert.equal(kids.length, 6);
|
|
37
|
+
assert.ok(kids.every((k) => k.kind === 'leaf'));
|
|
38
|
+
assert.equal(nodeTokens(l1), 200);
|
|
39
|
+
assert.equal(nodeTokens(kids[0]), 100);
|
|
40
|
+
});
|
|
41
|
+
test('summary-tree: full L1→L2 chain — single L2 root with summary children', () => {
|
|
42
|
+
const ch = buildChronicleWithChain({
|
|
43
|
+
chunkCount: 36,
|
|
44
|
+
tokensPerChunk: 100,
|
|
45
|
+
mergeThreshold: 6,
|
|
46
|
+
recallPairTokens: 200,
|
|
47
|
+
});
|
|
48
|
+
const tree = new SummaryTree(inputsOf(ch));
|
|
49
|
+
assert.equal(tree.ancestorAt('c-0000', 1)?.id, 'L1-0');
|
|
50
|
+
assert.equal(tree.ancestorAt('c-0000', 2)?.id, 'L2-0');
|
|
51
|
+
assert.equal(tree.maxLevel('c-0000'), 2);
|
|
52
|
+
const roots = tree.roots();
|
|
53
|
+
assert.equal(roots.length, 1);
|
|
54
|
+
assert.equal(roots[0].kind, 'summary');
|
|
55
|
+
assert.equal(roots[0].id, 'L2-0');
|
|
56
|
+
assert.equal(tree.leavesUnder('L2-0').length, 36);
|
|
57
|
+
const kids = tree.children(tree.summary('L2-0'));
|
|
58
|
+
assert.equal(kids.length, 6);
|
|
59
|
+
assert.ok(kids.every((k) => k.kind === 'summary'));
|
|
60
|
+
});
|
|
61
|
+
test('summary-tree: an uncovered leaf is its own root at level 0', () => {
|
|
62
|
+
const ch = new MockChronicle({ mergeThreshold: 6, recallPairTokens: 200 });
|
|
63
|
+
ch.addChunk({ id: 'a', rawTokens: 100 });
|
|
64
|
+
ch.addChunk({ id: 'b', rawTokens: 100 });
|
|
65
|
+
ch.produceL1(['a']); // only 'a' is covered
|
|
66
|
+
const tree = new SummaryTree(inputsOf(ch));
|
|
67
|
+
assert.equal(tree.maxLevel('b'), 0);
|
|
68
|
+
assert.equal(tree.ancestorAt('b', 1), null);
|
|
69
|
+
const rootKeys = tree.roots().map((r) => (r.kind === 'summary' ? r.id : r.chunkId));
|
|
70
|
+
assert.deepEqual(rootKeys, ['L1-0', 'b']); // ordered by source sequence
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=summary-tree.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-tree.test.js","sourceRoot":"","sources":["../../../test/adaptive/summary-tree.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-strip-render-stats.test.d.ts","sourceRoot":"","sources":["../../test/image-strip-render-stats.test.ts"],"names":[],"mappings":""}
|