@animalabs/context-manager 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/adaptive/chunker.d.ts +70 -0
- package/dist/src/adaptive/chunker.d.ts.map +1 -0
- package/dist/src/adaptive/chunker.js +233 -0
- package/dist/src/adaptive/chunker.js.map +1 -0
- package/dist/src/adaptive/folding-strategy.d.ts +118 -0
- package/dist/src/adaptive/folding-strategy.d.ts.map +1 -0
- package/dist/src/adaptive/folding-strategy.js +12 -0
- package/dist/src/adaptive/folding-strategy.js.map +1 -0
- package/dist/src/adaptive/index.d.ts +17 -0
- package/dist/src/adaptive/index.d.ts.map +1 -0
- package/dist/src/adaptive/index.js +23 -0
- package/dist/src/adaptive/index.js.map +1 -0
- package/dist/src/adaptive/picker.d.ts +103 -0
- package/dist/src/adaptive/picker.d.ts.map +1 -0
- package/dist/src/adaptive/picker.js +357 -0
- package/dist/src/adaptive/picker.js.map +1 -0
- package/dist/src/adaptive/render.d.ts +39 -0
- package/dist/src/adaptive/render.d.ts.map +1 -0
- package/dist/src/adaptive/render.js +104 -0
- package/dist/src/adaptive/render.js.map +1 -0
- package/dist/src/adaptive/strategies/flat-profile.d.ts +19 -0
- package/dist/src/adaptive/strategies/flat-profile.d.ts.map +1 -0
- package/dist/src/adaptive/strategies/flat-profile.js +86 -0
- package/dist/src/adaptive/strategies/flat-profile.js.map +1 -0
- package/dist/src/adaptive/strategies/oldest-first.d.ts +19 -0
- package/dist/src/adaptive/strategies/oldest-first.d.ts.map +1 -0
- package/dist/src/adaptive/strategies/oldest-first.js +39 -0
- package/dist/src/adaptive/strategies/oldest-first.js.map +1 -0
- package/dist/src/context-manager.d.ts +5 -0
- package/dist/src/context-manager.d.ts.map +1 -1
- package/dist/src/context-manager.js +22 -0
- package/dist/src/context-manager.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/message-store.d.ts +36 -1
- package/dist/src/message-store.d.ts.map +1 -1
- package/dist/src/message-store.js +134 -8
- package/dist/src/message-store.js.map +1 -1
- package/dist/src/strategies/autobiographical.d.ts +217 -2
- package/dist/src/strategies/autobiographical.d.ts.map +1 -1
- package/dist/src/strategies/autobiographical.js +1525 -37
- package/dist/src/strategies/autobiographical.js.map +1 -1
- package/dist/src/strategies/index.d.ts +1 -1
- package/dist/src/strategies/index.d.ts.map +1 -1
- package/dist/src/strategies/index.js.map +1 -1
- package/dist/src/types/message.d.ts +38 -0
- package/dist/src/types/message.d.ts.map +1 -1
- package/dist/src/types/strategy.d.ts +106 -10
- package/dist/src/types/strategy.d.ts.map +1 -1
- package/dist/src/types/strategy.js +7 -0
- package/dist/src/types/strategy.js.map +1 -1
- package/dist/test/adaptive/branching.test.d.ts +20 -0
- package/dist/test/adaptive/branching.test.d.ts.map +1 -0
- package/dist/test/adaptive/branching.test.js +176 -0
- package/dist/test/adaptive/branching.test.js.map +1 -0
- package/dist/test/adaptive/chunker.test.d.ts +2 -0
- package/dist/test/adaptive/chunker.test.d.ts.map +1 -0
- package/dist/test/adaptive/chunker.test.js +120 -0
- package/dist/test/adaptive/chunker.test.js.map +1 -0
- package/dist/test/adaptive/deep-levels.test.d.ts +10 -0
- package/dist/test/adaptive/deep-levels.test.d.ts.map +1 -0
- package/dist/test/adaptive/deep-levels.test.js +167 -0
- package/dist/test/adaptive/deep-levels.test.js.map +1 -0
- package/dist/test/adaptive/doc-plus-chat.test.d.ts +10 -0
- package/dist/test/adaptive/doc-plus-chat.test.d.ts.map +1 -0
- package/dist/test/adaptive/doc-plus-chat.test.js +194 -0
- package/dist/test/adaptive/doc-plus-chat.test.js.map +1 -0
- package/dist/test/adaptive/hard-fail.test.d.ts +9 -0
- package/dist/test/adaptive/hard-fail.test.d.ts.map +1 -0
- package/dist/test/adaptive/hard-fail.test.js +154 -0
- package/dist/test/adaptive/hard-fail.test.js.map +1 -0
- package/dist/test/adaptive/harness.d.ts +109 -0
- package/dist/test/adaptive/harness.d.ts.map +1 -0
- package/dist/test/adaptive/harness.js +263 -0
- package/dist/test/adaptive/harness.js.map +1 -0
- package/dist/test/adaptive/ingestion.test.d.ts +13 -0
- package/dist/test/adaptive/ingestion.test.d.ts.map +1 -0
- package/dist/test/adaptive/ingestion.test.js +306 -0
- package/dist/test/adaptive/ingestion.test.js.map +1 -0
- package/dist/test/adaptive/integration.test.d.ts +12 -0
- package/dist/test/adaptive/integration.test.d.ts.map +1 -0
- package/dist/test/adaptive/integration.test.js +247 -0
- package/dist/test/adaptive/integration.test.js.map +1 -0
- package/dist/test/adaptive/long-chronicle.test.d.ts +13 -0
- package/dist/test/adaptive/long-chronicle.test.d.ts.map +1 -0
- package/dist/test/adaptive/long-chronicle.test.js +186 -0
- package/dist/test/adaptive/long-chronicle.test.js.map +1 -0
- package/dist/test/adaptive/persistence.test.d.ts +11 -0
- package/dist/test/adaptive/persistence.test.d.ts.map +1 -0
- package/dist/test/adaptive/persistence.test.js +200 -0
- package/dist/test/adaptive/persistence.test.js.map +1 -0
- package/dist/test/adaptive/picker.test.d.ts +2 -0
- package/dist/test/adaptive/picker.test.d.ts.map +1 -0
- package/dist/test/adaptive/picker.test.js +333 -0
- package/dist/test/adaptive/picker.test.js.map +1 -0
- package/dist/test/adaptive/render.test.d.ts +2 -0
- package/dist/test/adaptive/render.test.d.ts.map +1 -0
- package/dist/test/adaptive/render.test.js +109 -0
- package/dist/test/adaptive/render.test.js.map +1 -0
- package/dist/test/adaptive/shard-immutability.test.d.ts +14 -0
- package/dist/test/adaptive/shard-immutability.test.d.ts.map +1 -0
- package/dist/test/adaptive/shard-immutability.test.js +184 -0
- package/dist/test/adaptive/shard-immutability.test.js.map +1 -0
- package/dist/test/adaptive/strategy-integration.test.d.ts +9 -0
- package/dist/test/adaptive/strategy-integration.test.d.ts.map +1 -0
- package/dist/test/adaptive/strategy-integration.test.js +293 -0
- package/dist/test/adaptive/strategy-integration.test.js.map +1 -0
- package/dist/test/message-store-sequence.test.d.ts +25 -0
- package/dist/test/message-store-sequence.test.d.ts.map +1 -0
- package/dist/test/message-store-sequence.test.js +157 -0
- package/dist/test/message-store-sequence.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/adaptive/chunker.ts +323 -0
- package/src/adaptive/folding-strategy.ts +129 -0
- package/src/adaptive/index.ts +51 -0
- package/src/adaptive/picker.ts +442 -0
- package/src/adaptive/render.ts +115 -0
- package/src/adaptive/strategies/flat-profile.ts +96 -0
- package/src/adaptive/strategies/oldest-first.ts +48 -0
- package/src/context-manager.ts +32 -0
- package/src/index.ts +1 -1
- package/src/message-store.ts +147 -8
- package/src/strategies/autobiographical.ts +1635 -38
- package/src/strategies/index.ts +1 -1
- package/src/types/message.ts +43 -0
- package/src/types/strategy.ts +122 -10
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that the bottom-up speculative pre-producer (config.speculativeProduction)
|
|
3
|
+
* builds the full L_n chain — not just L2/L3 like the legacy threshold path.
|
|
4
|
+
*
|
|
5
|
+
* Drives enough conversation to produce ≥ N^2 L1s, then verifies the chronicle
|
|
6
|
+
* ends up with L2, L3, L4, ... summaries via the same checkMergeThreshold
|
|
7
|
+
* mechanism (now recursive) that previously stopped at L3.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, before, after, beforeEach } from 'node:test';
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
12
|
+
import { ContextManager, AutobiographicalStrategy } from '../../src/index.js';
|
|
13
|
+
const TEST_STORE_PATH = './test-adaptive-deep-levels-store';
|
|
14
|
+
function cleanup() {
|
|
15
|
+
if (existsSync(TEST_STORE_PATH)) {
|
|
16
|
+
rmSync(TEST_STORE_PATH, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function makeMockMembrane() {
|
|
20
|
+
let callCount = 0;
|
|
21
|
+
return {
|
|
22
|
+
complete: async () => {
|
|
23
|
+
callCount++;
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: 'text', text: `[mock summary #${callCount}] short summary content here.` }],
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
get callCount() {
|
|
29
|
+
return callCount;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function summariesByLevel(strategy) {
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const s of strategy.summaries) {
|
|
36
|
+
out[s.level] = (out[s.level] ?? 0) + 1;
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
describe('AutobiographicalStrategy — deep-level pre-production', () => {
|
|
41
|
+
before(() => cleanup());
|
|
42
|
+
after(() => cleanup());
|
|
43
|
+
beforeEach(() => cleanup());
|
|
44
|
+
it('speculativeProduction off: only L1/L2/L3 (legacy behavior)', async () => {
|
|
45
|
+
const mock = makeMockMembrane();
|
|
46
|
+
const strategy = new AutobiographicalStrategy({
|
|
47
|
+
adaptiveResolution: false,
|
|
48
|
+
targetChunkTokens: 100,
|
|
49
|
+
mergeThreshold: 3, // small so we get to deep levels quickly
|
|
50
|
+
recentWindowTokens: 50,
|
|
51
|
+
});
|
|
52
|
+
const manager = await ContextManager.open({
|
|
53
|
+
path: TEST_STORE_PATH,
|
|
54
|
+
strategy,
|
|
55
|
+
membrane: mock,
|
|
56
|
+
});
|
|
57
|
+
// Need enough chunks for L1 → L2 → L3 to fire under threshold=3.
|
|
58
|
+
for (let i = 0; i < 30; i++) {
|
|
59
|
+
manager.addMessage('User', [{ type: 'text', text: `Turn ${i}. ` + 'word '.repeat(30) }]);
|
|
60
|
+
}
|
|
61
|
+
while (!manager.isReady()) {
|
|
62
|
+
await manager.tick();
|
|
63
|
+
}
|
|
64
|
+
const counts = summariesByLevel(strategy);
|
|
65
|
+
// Legacy path caps at L3 — even with many L3s, no L4 is built.
|
|
66
|
+
assert.ok(!counts[4], `legacy path should not produce L4; got counts: ${JSON.stringify(counts)}`);
|
|
67
|
+
manager.close();
|
|
68
|
+
});
|
|
69
|
+
it('speculativeProduction on: produces L4+ when source counts warrant it', async () => {
|
|
70
|
+
const mock = makeMockMembrane();
|
|
71
|
+
const strategy = new AutobiographicalStrategy({
|
|
72
|
+
adaptiveResolution: true, // turns on speculativeProduction
|
|
73
|
+
targetChunkTokens: 80,
|
|
74
|
+
mergeThreshold: 2, // smallest possible — forces the chain to climb fast
|
|
75
|
+
recentWindowTokens: 50,
|
|
76
|
+
});
|
|
77
|
+
const manager = await ContextManager.open({
|
|
78
|
+
path: TEST_STORE_PATH,
|
|
79
|
+
strategy,
|
|
80
|
+
membrane: mock,
|
|
81
|
+
});
|
|
82
|
+
// With mergeThreshold=2, each level halves the count. So if we end up
|
|
83
|
+
// with N L1s after compression, we get:
|
|
84
|
+
// ceil(N/2) L2s, ceil(N/4) L3s, ceil(N/8) L4s, ...
|
|
85
|
+
// Need N ≥ 16 for L4 (2 L3s required to merge → L4).
|
|
86
|
+
for (let i = 0; i < 120; i++) {
|
|
87
|
+
manager.addMessage('User', [{ type: 'text', text: `T${i}. ` + 'word '.repeat(20) }]);
|
|
88
|
+
}
|
|
89
|
+
while (!manager.isReady()) {
|
|
90
|
+
await manager.tick();
|
|
91
|
+
}
|
|
92
|
+
const counts = summariesByLevel(strategy);
|
|
93
|
+
// Should see L1, L2, L3 — and L4 with threshold=2.
|
|
94
|
+
assert.ok(counts[1] > 0, `expected L1 summaries; got ${JSON.stringify(counts)}`);
|
|
95
|
+
assert.ok(counts[2] > 0, `expected L2 summaries; got ${JSON.stringify(counts)}`);
|
|
96
|
+
assert.ok(counts[3] > 0, `expected L3 summaries; got ${JSON.stringify(counts)}`);
|
|
97
|
+
assert.ok(counts[4] >= 1, `with mergeThreshold=2 and 120 chunks, expected at least one L4; got ${JSON.stringify(counts)}`);
|
|
98
|
+
manager.close();
|
|
99
|
+
});
|
|
100
|
+
it('parentId set correctly across deep levels', async () => {
|
|
101
|
+
const mock = makeMockMembrane();
|
|
102
|
+
const strategy = new AutobiographicalStrategy({
|
|
103
|
+
adaptiveResolution: true,
|
|
104
|
+
targetChunkTokens: 80,
|
|
105
|
+
mergeThreshold: 2,
|
|
106
|
+
recentWindowTokens: 50,
|
|
107
|
+
});
|
|
108
|
+
const manager = await ContextManager.open({
|
|
109
|
+
path: TEST_STORE_PATH,
|
|
110
|
+
strategy,
|
|
111
|
+
membrane: mock,
|
|
112
|
+
});
|
|
113
|
+
for (let i = 0; i < 120; i++) {
|
|
114
|
+
manager.addMessage('User', [{ type: 'text', text: `T${i}. ` + 'word '.repeat(20) }]);
|
|
115
|
+
}
|
|
116
|
+
while (!manager.isReady()) {
|
|
117
|
+
await manager.tick();
|
|
118
|
+
}
|
|
119
|
+
// Walk the summary tree. Every L_n summary (except the topmost) should
|
|
120
|
+
// have a parentId pointing to an L_{n+1} that exists.
|
|
121
|
+
const summaries = strategy.summaries;
|
|
122
|
+
const byId = new Map(summaries.map((s) => [s.id, s]));
|
|
123
|
+
let maxLevel = 0;
|
|
124
|
+
for (const s of summaries)
|
|
125
|
+
if (s.level > maxLevel)
|
|
126
|
+
maxLevel = s.level;
|
|
127
|
+
for (const s of summaries) {
|
|
128
|
+
const parentId = s.parentId ?? s.mergedInto;
|
|
129
|
+
if (s.level === maxLevel)
|
|
130
|
+
continue; // top level has no parent
|
|
131
|
+
if (!parentId)
|
|
132
|
+
continue; // legitimately may be the newest at its level, no parent yet
|
|
133
|
+
const parent = byId.get(parentId);
|
|
134
|
+
assert.ok(parent, `summary ${s.id} (L${s.level}) has parentId ${parentId} but parent not found`);
|
|
135
|
+
assert.equal(parent.level, s.level + 1, `parent of L${s.level} should be L${s.level + 1}`);
|
|
136
|
+
}
|
|
137
|
+
manager.close();
|
|
138
|
+
});
|
|
139
|
+
it('picker uses L4+ summaries when budget pressure requires it', async () => {
|
|
140
|
+
const mock = makeMockMembrane();
|
|
141
|
+
const strategy = new AutobiographicalStrategy({
|
|
142
|
+
adaptiveResolution: true,
|
|
143
|
+
targetChunkTokens: 80,
|
|
144
|
+
mergeThreshold: 2,
|
|
145
|
+
recentWindowTokens: 50,
|
|
146
|
+
});
|
|
147
|
+
const manager = await ContextManager.open({
|
|
148
|
+
path: TEST_STORE_PATH,
|
|
149
|
+
strategy,
|
|
150
|
+
membrane: mock,
|
|
151
|
+
});
|
|
152
|
+
for (let i = 0; i < 120; i++) {
|
|
153
|
+
manager.addMessage('User', [{ type: 'text', text: `T${i}. ` + 'word '.repeat(20) }]);
|
|
154
|
+
}
|
|
155
|
+
while (!manager.isReady()) {
|
|
156
|
+
await manager.tick();
|
|
157
|
+
}
|
|
158
|
+
// Aggressive compression to force the picker to reach L4+.
|
|
159
|
+
await manager.compile({ maxTokens: 600, reserveForResponse: 100 });
|
|
160
|
+
// Inspect strategy.resolutions for any value >= 3 (L3 or deeper).
|
|
161
|
+
const resolutions = strategy.resolutions;
|
|
162
|
+
const maxResolution = Math.max(0, ...Array.from(resolutions.values()));
|
|
163
|
+
assert.ok(maxResolution >= 3, `with tight budget and deep tree, picker should reach L3+; max resolution was L${maxResolution}`);
|
|
164
|
+
manager.close();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
//# sourceMappingURL=deep-levels.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deep-levels.test.js","sourceRoot":"","sources":["../../../test/adaptive/deep-levels.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realistic Lena-migration scenario: a chronicle that starts with a large doc,
|
|
3
|
+
* then has many turns of chat about/with that doc.
|
|
4
|
+
*
|
|
5
|
+
* Verifies the doc gets sharded at ingestion, the chat turns coexist with
|
|
6
|
+
* the doc shards, the picker handles both correctly, and the compiled
|
|
7
|
+
* output structure makes sense throughout.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=doc-plus-chat.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-plus-chat.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/doc-plus-chat.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realistic Lena-migration scenario: a chronicle that starts with a large doc,
|
|
3
|
+
* then has many turns of chat about/with that doc.
|
|
4
|
+
*
|
|
5
|
+
* Verifies the doc gets sharded at ingestion, the chat turns coexist with
|
|
6
|
+
* the doc shards, the picker handles both correctly, and the compiled
|
|
7
|
+
* output structure makes sense throughout.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, before, after, beforeEach } from 'node:test';
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
12
|
+
import { ContextManager, AutobiographicalStrategy } from '../../src/index.js';
|
|
13
|
+
const TEST_STORE_PATH = './test-adaptive-doc-plus-chat-store';
|
|
14
|
+
function cleanup() {
|
|
15
|
+
if (existsSync(TEST_STORE_PATH)) {
|
|
16
|
+
rmSync(TEST_STORE_PATH, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function makeMockMembrane() {
|
|
20
|
+
let callCount = 0;
|
|
21
|
+
return {
|
|
22
|
+
complete: async () => {
|
|
23
|
+
callCount++;
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: 'text', text: `[mock summary #${callCount}] short content.` }],
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
get callCount() {
|
|
29
|
+
return callCount;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function compiledTokens(compiled) {
|
|
34
|
+
let total = 0;
|
|
35
|
+
for (const m of compiled.messages) {
|
|
36
|
+
for (const block of m.content) {
|
|
37
|
+
if (block.type === 'text')
|
|
38
|
+
total += Math.ceil(block.text.length / 4);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return total;
|
|
42
|
+
}
|
|
43
|
+
describe('AutobiographicalStrategy — doc + chat workload (Lena-like)', () => {
|
|
44
|
+
before(() => cleanup());
|
|
45
|
+
after(() => cleanup());
|
|
46
|
+
beforeEach(() => cleanup());
|
|
47
|
+
it('500K-equivalent doc + 50 chat turns: budget honored throughout', async () => {
|
|
48
|
+
const mock = makeMockMembrane();
|
|
49
|
+
const BUDGET = 30000;
|
|
50
|
+
const strategy = new AutobiographicalStrategy({
|
|
51
|
+
adaptiveResolution: true,
|
|
52
|
+
targetChunkTokens: 1000, // ~4K chars per shard
|
|
53
|
+
recentWindowTokens: 2000,
|
|
54
|
+
mergeThreshold: 6,
|
|
55
|
+
compressionSlackRatio: 0.1,
|
|
56
|
+
});
|
|
57
|
+
const manager = await ContextManager.open({
|
|
58
|
+
path: TEST_STORE_PATH,
|
|
59
|
+
strategy,
|
|
60
|
+
membrane: mock,
|
|
61
|
+
});
|
|
62
|
+
// Synthetic ~500K-equivalent doc. Use 50K chars ≈ 12.5K tokens for test
|
|
63
|
+
// speed; chunker treats ~1K tokens per shard. ~12 shards.
|
|
64
|
+
const sections = [];
|
|
65
|
+
for (let s = 0; s < 12; s++) {
|
|
66
|
+
sections.push(`# Section ${s}\n\n` + 'paragraph content with some words. '.repeat(40));
|
|
67
|
+
}
|
|
68
|
+
const doc = sections.join('\n\n');
|
|
69
|
+
manager.addMessage('User', [{ type: 'text', text: doc }]);
|
|
70
|
+
// Verify sharding happened at ingestion
|
|
71
|
+
const allMessages = manager.getAllMessages();
|
|
72
|
+
assert.ok(allMessages.length > 1, `expected doc to shard; got ${allMessages.length} messages`);
|
|
73
|
+
const shardCount = allMessages.length;
|
|
74
|
+
const groupId = allMessages[0].bodyGroupId;
|
|
75
|
+
assert.ok(groupId, 'first shard should have bodyGroupId');
|
|
76
|
+
for (const m of allMessages) {
|
|
77
|
+
assert.equal(m.bodyGroupId, groupId, 'all shards share bodyGroupId');
|
|
78
|
+
}
|
|
79
|
+
while (!manager.isReady()) {
|
|
80
|
+
await manager.tick();
|
|
81
|
+
}
|
|
82
|
+
// First compile after doc — verify budget honored
|
|
83
|
+
const compiledAfterDoc = await manager.compile({ maxTokens: BUDGET, reserveForResponse: 1000 });
|
|
84
|
+
assert.ok(compiledTokens(compiledAfterDoc) <= BUDGET - 1000, `after-doc tokens exceed budget: ${compiledTokens(compiledAfterDoc)}`);
|
|
85
|
+
// Now 50 chat turns about the doc
|
|
86
|
+
for (let i = 0; i < 50; i++) {
|
|
87
|
+
manager.addMessage('User', [{ type: 'text', text: `Question ${i} about section ${i % 12}: ` + 'word '.repeat(20) }]);
|
|
88
|
+
manager.addMessage('Claude', [{ type: 'text', text: `Answer ${i}: ` + 'word '.repeat(25) }]);
|
|
89
|
+
while (!manager.isReady()) {
|
|
90
|
+
await manager.tick();
|
|
91
|
+
}
|
|
92
|
+
const compiled = await manager.compile({ maxTokens: BUDGET, reserveForResponse: 1000 });
|
|
93
|
+
const tokens = compiledTokens(compiled);
|
|
94
|
+
assert.ok(tokens <= BUDGET - 1000, `turn ${i}: ${tokens} tokens exceeds budget ${BUDGET - 1000}`);
|
|
95
|
+
}
|
|
96
|
+
// The compiled output should still reflect the doc somewhere
|
|
97
|
+
const finalCompile = await manager.compile({ maxTokens: BUDGET, reserveForResponse: 1000 });
|
|
98
|
+
const finalText = finalCompile.messages
|
|
99
|
+
.flatMap((m) => m.content)
|
|
100
|
+
.filter((b) => b.type === 'text')
|
|
101
|
+
.map((b) => b.text)
|
|
102
|
+
.join('\n');
|
|
103
|
+
// Either the doc is still raw-rendered, OR a summary referring to it appears
|
|
104
|
+
assert.ok(finalText.includes('Section') || finalText.includes('summary'), 'final compile should reference doc content via either raw or summary');
|
|
105
|
+
manager.close();
|
|
106
|
+
});
|
|
107
|
+
it('agent can lock a critical doc section and it survives folding pressure', async () => {
|
|
108
|
+
const mock = makeMockMembrane();
|
|
109
|
+
const strategy = new AutobiographicalStrategy({
|
|
110
|
+
adaptiveResolution: true,
|
|
111
|
+
targetChunkTokens: 500,
|
|
112
|
+
recentWindowTokens: 500,
|
|
113
|
+
});
|
|
114
|
+
const manager = await ContextManager.open({
|
|
115
|
+
path: TEST_STORE_PATH,
|
|
116
|
+
strategy,
|
|
117
|
+
membrane: mock,
|
|
118
|
+
});
|
|
119
|
+
// Doc with distinct sections — big enough to shard at targetChunkTokens=500
|
|
120
|
+
// (chunker threshold = 2x targetChunkTokens = 1000 tokens; we need ~10 sections
|
|
121
|
+
// × 200 tokens = 2000+ tokens to trigger sharding).
|
|
122
|
+
const sections = Array.from({ length: 10 }, (_, i) => `# Section ${i}\n\nCRITICAL-MARKER-${i} ` + 'distinctive word here. '.repeat(40));
|
|
123
|
+
manager.addMessage('User', [{ type: 'text', text: sections.join('\n\n') }]);
|
|
124
|
+
const shards = manager.getAllMessages();
|
|
125
|
+
assert.ok(shards.length > 1, `doc should shard; got ${shards.length} messages`);
|
|
126
|
+
// Lock shard 3 specifically
|
|
127
|
+
const criticalShard = shards[3];
|
|
128
|
+
strategy.lockChunk(criticalShard.id);
|
|
129
|
+
// Add chat to push pressure
|
|
130
|
+
for (let i = 0; i < 30; i++) {
|
|
131
|
+
manager.addMessage('User', [{ type: 'text', text: `T${i}` }]);
|
|
132
|
+
}
|
|
133
|
+
while (!manager.isReady()) {
|
|
134
|
+
await manager.tick();
|
|
135
|
+
}
|
|
136
|
+
// Tight compile
|
|
137
|
+
const compiled = await manager.compile({ maxTokens: 3500, reserveForResponse: 500 });
|
|
138
|
+
// The locked shard's resolution should remain 0 in strategy.resolutions
|
|
139
|
+
const resolutions = strategy.resolutions;
|
|
140
|
+
const lockedRes = resolutions.get(criticalShard.id) ?? 0;
|
|
141
|
+
assert.equal(lockedRes, 0, `locked shard should remain L0, got L${lockedRes}`);
|
|
142
|
+
// The compiled output should contain the locked shard's content verbatim
|
|
143
|
+
// (or at least the CRITICAL-MARKER-3 substring from its raw text).
|
|
144
|
+
const allText = compiled.messages
|
|
145
|
+
.flatMap((m) => m.content)
|
|
146
|
+
.filter((b) => b.type === 'text')
|
|
147
|
+
.map((b) => b.text)
|
|
148
|
+
.join('\n');
|
|
149
|
+
assert.ok(allText.includes('CRITICAL-MARKER-3'), 'compiled output should contain the locked shard\'s content verbatim');
|
|
150
|
+
manager.close();
|
|
151
|
+
});
|
|
152
|
+
it('doc + many turns: shards in different regions render correctly', async () => {
|
|
153
|
+
const mock = makeMockMembrane();
|
|
154
|
+
const strategy = new AutobiographicalStrategy({
|
|
155
|
+
adaptiveResolution: true,
|
|
156
|
+
targetChunkTokens: 400,
|
|
157
|
+
recentWindowTokens: 800,
|
|
158
|
+
mergeThreshold: 3,
|
|
159
|
+
});
|
|
160
|
+
const manager = await ContextManager.open({
|
|
161
|
+
path: TEST_STORE_PATH,
|
|
162
|
+
strategy,
|
|
163
|
+
membrane: mock,
|
|
164
|
+
});
|
|
165
|
+
// Doc → some shards in middle, some in tail
|
|
166
|
+
const doc = Array.from({ length: 15 }, (_, i) => `Section ${i}: ` + 'word '.repeat(60)).join('\n\n');
|
|
167
|
+
manager.addMessage('User', [{ type: 'text', text: doc }]);
|
|
168
|
+
// Trailing chat so the doc isn't all-tail
|
|
169
|
+
for (let i = 0; i < 8; i++) {
|
|
170
|
+
manager.addMessage('User', [{ type: 'text', text: `Followup ${i}` }]);
|
|
171
|
+
}
|
|
172
|
+
while (!manager.isReady()) {
|
|
173
|
+
await manager.tick();
|
|
174
|
+
}
|
|
175
|
+
const compiled = await manager.compile({ maxTokens: 5000, reserveForResponse: 500 });
|
|
176
|
+
// The compile should have produced output containing both doc content
|
|
177
|
+
// (some sections) and chat content (followups). The doc should be a
|
|
178
|
+
// combined entry (or entries) representing its shards.
|
|
179
|
+
const userMessages = compiled.messages.filter((m) => m.participant.toLowerCase() === 'user');
|
|
180
|
+
assert.ok(userMessages.length >= 2, 'should have multiple user messages (doc + chat)');
|
|
181
|
+
// Find a user message containing "Section" (from the doc)
|
|
182
|
+
const docEntry = userMessages.find((m) => m.content
|
|
183
|
+
.filter((b) => b.type === 'text')
|
|
184
|
+
.some((b) => b.text.includes('Section ')));
|
|
185
|
+
assert.ok(docEntry, 'should have a user entry with doc section content');
|
|
186
|
+
// Find a user message containing "Followup" (from chat)
|
|
187
|
+
const chatEntry = userMessages.find((m) => m.content
|
|
188
|
+
.filter((b) => b.type === 'text')
|
|
189
|
+
.some((b) => b.text.includes('Followup')));
|
|
190
|
+
assert.ok(chatEntry, 'should have a user entry with chat followup content');
|
|
191
|
+
manager.close();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=doc-plus-chat.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-plus-chat.test.js","sourceRoot":"","sources":["../../../test/adaptive/doc-plus-chat.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that selectAdaptive throws OverBudgetError when the picker has
|
|
3
|
+
* folded everything it can but the result still exceeds the hard budget.
|
|
4
|
+
*
|
|
5
|
+
* The strategy's philosophy: surface the overage rather than silently
|
|
6
|
+
* dropping entries. The host decides how to recover.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=hard-fail.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hard-fail.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/hard-fail.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that selectAdaptive throws OverBudgetError when the picker has
|
|
3
|
+
* folded everything it can but the result still exceeds the hard budget.
|
|
4
|
+
*
|
|
5
|
+
* The strategy's philosophy: surface the overage rather than silently
|
|
6
|
+
* dropping entries. The host decides how to recover.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, before, after, beforeEach } from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
11
|
+
import { ContextManager, AutobiographicalStrategy } from '../../src/index.js';
|
|
12
|
+
import { OverBudgetError } from '../../src/adaptive/index.js';
|
|
13
|
+
const TEST_STORE_PATH = './test-adaptive-hard-fail-store';
|
|
14
|
+
function cleanup() {
|
|
15
|
+
if (existsSync(TEST_STORE_PATH)) {
|
|
16
|
+
rmSync(TEST_STORE_PATH, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function makeMockMembrane() {
|
|
20
|
+
let callCount = 0;
|
|
21
|
+
return {
|
|
22
|
+
complete: async () => {
|
|
23
|
+
callCount++;
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: 'text', text: `[mock summary #${callCount}]` }],
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
get callCount() {
|
|
29
|
+
return callCount;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
describe('AutobiographicalStrategy — adaptive OverBudgetError', () => {
|
|
34
|
+
before(() => cleanup());
|
|
35
|
+
after(() => cleanup());
|
|
36
|
+
beforeEach(() => cleanup());
|
|
37
|
+
it('throws OverBudgetError when tail alone exceeds budget', async () => {
|
|
38
|
+
const mock = makeMockMembrane();
|
|
39
|
+
const strategy = new AutobiographicalStrategy({
|
|
40
|
+
adaptiveResolution: true,
|
|
41
|
+
targetChunkTokens: 200,
|
|
42
|
+
// Large tail window: everything we add will be tail-resident, NOT
|
|
43
|
+
// foldable. The picker has nothing to do with it.
|
|
44
|
+
recentWindowTokens: 100_000,
|
|
45
|
+
});
|
|
46
|
+
const manager = await ContextManager.open({
|
|
47
|
+
path: TEST_STORE_PATH,
|
|
48
|
+
strategy,
|
|
49
|
+
membrane: mock,
|
|
50
|
+
});
|
|
51
|
+
// Add enough content that the tail alone is huge.
|
|
52
|
+
for (let i = 0; i < 30; i++) {
|
|
53
|
+
manager.addMessage('User', [{ type: 'text', text: `Msg ${i}. ` + 'word '.repeat(80) }]);
|
|
54
|
+
}
|
|
55
|
+
while (!manager.isReady()) {
|
|
56
|
+
await manager.tick();
|
|
57
|
+
}
|
|
58
|
+
// Budget so tight that even the tail can't fit. Picker has nothing it
|
|
59
|
+
// can fold (tail is treated as pinned).
|
|
60
|
+
await assert.rejects(() => manager.compile({ maxTokens: 500, reserveForResponse: 100 }), (err) => {
|
|
61
|
+
assert.ok(err instanceof OverBudgetError, `expected OverBudgetError, got ${err?.constructor?.name}`);
|
|
62
|
+
const e = err;
|
|
63
|
+
assert.ok(e.actual > e.budget, `actual ${e.actual} should exceed budget ${e.budget}`);
|
|
64
|
+
assert.ok(e.diagnostics.tailTokens > 0, 'diagnostics should include tail tokens');
|
|
65
|
+
return true;
|
|
66
|
+
});
|
|
67
|
+
manager.close();
|
|
68
|
+
});
|
|
69
|
+
it('does NOT throw when picker can fold its way under budget', async () => {
|
|
70
|
+
const mock = makeMockMembrane();
|
|
71
|
+
const strategy = new AutobiographicalStrategy({
|
|
72
|
+
adaptiveResolution: true,
|
|
73
|
+
targetChunkTokens: 100,
|
|
74
|
+
recentWindowTokens: 200,
|
|
75
|
+
});
|
|
76
|
+
const manager = await ContextManager.open({
|
|
77
|
+
path: TEST_STORE_PATH,
|
|
78
|
+
strategy,
|
|
79
|
+
membrane: mock,
|
|
80
|
+
});
|
|
81
|
+
for (let i = 0; i < 30; i++) {
|
|
82
|
+
manager.addMessage('User', [{ type: 'text', text: `Msg ${i}. ` + 'word '.repeat(40) }]);
|
|
83
|
+
}
|
|
84
|
+
while (!manager.isReady()) {
|
|
85
|
+
await manager.tick();
|
|
86
|
+
}
|
|
87
|
+
// Tight but reachable budget — picker should fold middle content to fit.
|
|
88
|
+
const compiled = await manager.compile({ maxTokens: 3000, reserveForResponse: 200 });
|
|
89
|
+
assert.ok(compiled.messages.length > 0);
|
|
90
|
+
manager.close();
|
|
91
|
+
});
|
|
92
|
+
it('does NOT throw under generous budget', async () => {
|
|
93
|
+
const mock = makeMockMembrane();
|
|
94
|
+
const strategy = new AutobiographicalStrategy({
|
|
95
|
+
adaptiveResolution: true,
|
|
96
|
+
targetChunkTokens: 100,
|
|
97
|
+
recentWindowTokens: 200,
|
|
98
|
+
});
|
|
99
|
+
const manager = await ContextManager.open({
|
|
100
|
+
path: TEST_STORE_PATH,
|
|
101
|
+
strategy,
|
|
102
|
+
membrane: mock,
|
|
103
|
+
});
|
|
104
|
+
for (let i = 0; i < 10; i++) {
|
|
105
|
+
manager.addMessage('User', [{ type: 'text', text: `Msg ${i}` }]);
|
|
106
|
+
}
|
|
107
|
+
const compiled = await manager.compile({ maxTokens: 100_000, reserveForResponse: 200 });
|
|
108
|
+
assert.ok(compiled.messages.length > 0);
|
|
109
|
+
manager.close();
|
|
110
|
+
});
|
|
111
|
+
it('error diagnostics include useful state info', async () => {
|
|
112
|
+
const mock = makeMockMembrane();
|
|
113
|
+
const strategy = new AutobiographicalStrategy({
|
|
114
|
+
adaptiveResolution: true,
|
|
115
|
+
targetChunkTokens: 200,
|
|
116
|
+
recentWindowTokens: 100_000,
|
|
117
|
+
});
|
|
118
|
+
const manager = await ContextManager.open({
|
|
119
|
+
path: TEST_STORE_PATH,
|
|
120
|
+
strategy,
|
|
121
|
+
membrane: mock,
|
|
122
|
+
});
|
|
123
|
+
for (let i = 0; i < 20; i++) {
|
|
124
|
+
manager.addMessage('User', [{ type: 'text', text: `Msg ${i}. ` + 'word '.repeat(80) }]);
|
|
125
|
+
}
|
|
126
|
+
while (!manager.isReady()) {
|
|
127
|
+
await manager.tick();
|
|
128
|
+
}
|
|
129
|
+
let caught = null;
|
|
130
|
+
try {
|
|
131
|
+
await manager.compile({ maxTokens: 400, reserveForResponse: 100 });
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (err instanceof OverBudgetError)
|
|
135
|
+
caught = err;
|
|
136
|
+
}
|
|
137
|
+
assert.ok(caught, 'should have caught OverBudgetError');
|
|
138
|
+
if (!caught) {
|
|
139
|
+
manager.close();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
assert.equal(typeof caught.budget, 'number');
|
|
143
|
+
assert.equal(typeof caught.actual, 'number');
|
|
144
|
+
assert.equal(typeof caught.diagnostics.headTokens, 'number');
|
|
145
|
+
assert.equal(typeof caught.diagnostics.tailTokens, 'number');
|
|
146
|
+
assert.equal(typeof caught.diagnostics.middleTokens, 'number');
|
|
147
|
+
assert.equal(typeof caught.diagnostics.deepestLevel, 'number');
|
|
148
|
+
// The error message should be informative
|
|
149
|
+
assert.ok(caught.message.includes('exhausted'));
|
|
150
|
+
assert.ok(caught.message.includes('hard budget'));
|
|
151
|
+
manager.close();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=hard-fail.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hard-fail.test.js","sourceRoot":"","sources":["../../../test/adaptive/hard-fail.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,MAAM,eAAe,GAAG,iCAAiC,CAAC;AAE1D,SAAS,OAAO;IACd,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB;IACvB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,OAAO;QACL,QAAQ,EAAE,KAAK,IAAI,EAAE;YACnB,SAAS,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,SAAS,GAAG,EAAE,CAAC;aAClE,CAAC;QACJ,CAAC;QACD,IAAI,SAAS;YACX,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACnE,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACxB,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACvB,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAE5B,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,wBAAwB,CAAC;YAC5C,kBAAkB,EAAE,IAAI;YACxB,iBAAiB,EAAE,GAAG;YACtB,kEAAkE;YAClE,kDAAkD;YAClD,kBAAkB,EAAE,OAAO;SAC5B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;YACxC,IAAI,EAAE,eAAe;YACrB,QAAQ;YACR,QAAQ,EAAE,IAAW;SACtB,CAAC,CAAC;QAEH,kDAAkD;QAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC;QAED,sEAAsE;QACtE,wCAAwC;QACxC,MAAM,MAAM,CAAC,OAAO,CAClB,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,EAClE,CAAC,GAAY,EAAE,EAAE;YACf,MAAM,CAAC,EAAE,CAAC,GAAG,YAAY,eAAe,EAAE,iCAAkC,GAAa,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;YAChH,MAAM,CAAC,GAAG,GAAsB,CAAC;YACjC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,MAAM,yBAAyB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YACtF,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,GAAG,CAAC,EAAE,wCAAwC,CAAC,CAAC;YAClF,OAAO,IAAI,CAAC;QACd,CAAC,CACF,CAAC;QACF,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,wBAAwB,CAAC;YAC5C,kBAAkB,EAAE,IAAI;YACxB,iBAAiB,EAAE,GAAG;YACtB,kBAAkB,EAAE,GAAG;SACxB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;YACxC,IAAI,EAAE,eAAe;YACrB,QAAQ;YACR,QAAQ,EAAE,IAAW;SACtB,CAAC,CAAC;QAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC;QAED,yEAAyE;QACzE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;QACrF,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,wBAAwB,CAAC;YAC5C,kBAAkB,EAAE,IAAI;YACxB,iBAAiB,EAAE,GAAG;YACtB,kBAAkB,EAAE,GAAG;SACxB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;YACxC,IAAI,EAAE,eAAe;YACrB,QAAQ;YACR,QAAQ,EAAE,IAAW;SACtB,CAAC,CAAC;QAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,wBAAwB,CAAC;YAC5C,kBAAkB,EAAE,IAAI;YACxB,iBAAiB,EAAE,GAAG;YACtB,kBAAkB,EAAE,OAAO;SAC5B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;YACxC,IAAI,EAAE,eAAe;YACrB,QAAQ;YACR,QAAQ,EAAE,IAAW;SACtB,CAAC,CAAC;QACH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC;QAED,IAAI,MAAM,GAA2B,IAAI,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;QACrE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,eAAe;gBAAE,MAAM,GAAG,GAAG,CAAC;QACnD,CAAC;QACD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,oCAAoC,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC7D,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC7D,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC/D,0CAA0C;QAC1C,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|