@diyor28/context 1.0.0
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/README.md +270 -0
- package/dist/__tests__/attachment-selector.test.d.ts +11 -0
- package/dist/__tests__/attachment-selector.test.d.ts.map +1 -0
- package/dist/__tests__/attachment-selector.test.js +449 -0
- package/dist/__tests__/attachment-selector.test.js.map +1 -0
- package/dist/__tests__/cache-breakpoints.test.d.ts +11 -0
- package/dist/__tests__/cache-breakpoints.test.d.ts.map +1 -0
- package/dist/__tests__/cache-breakpoints.test.js +398 -0
- package/dist/__tests__/cache-breakpoints.test.js.map +1 -0
- package/dist/__tests__/codecs.test.d.ts +7 -0
- package/dist/__tests__/codecs.test.d.ts.map +1 -0
- package/dist/__tests__/codecs.test.js +331 -0
- package/dist/__tests__/codecs.test.js.map +1 -0
- package/dist/__tests__/compactor.test.d.ts +11 -0
- package/dist/__tests__/compactor.test.d.ts.map +1 -0
- package/dist/__tests__/compactor.test.js +519 -0
- package/dist/__tests__/compactor.test.js.map +1 -0
- package/dist/__tests__/context-graph.test.d.ts +7 -0
- package/dist/__tests__/context-graph.test.d.ts.map +1 -0
- package/dist/__tests__/context-graph.test.js +262 -0
- package/dist/__tests__/context-graph.test.js.map +1 -0
- package/dist/__tests__/hash.test.d.ts +7 -0
- package/dist/__tests__/hash.test.d.ts.map +1 -0
- package/dist/__tests__/hash.test.js +228 -0
- package/dist/__tests__/hash.test.js.map +1 -0
- package/dist/__tests__/integration.test.d.ts +15 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.js +728 -0
- package/dist/__tests__/integration.test.js.map +1 -0
- package/dist/__tests__/kind-order.test.d.ts +7 -0
- package/dist/__tests__/kind-order.test.d.ts.map +1 -0
- package/dist/__tests__/kind-order.test.js +243 -0
- package/dist/__tests__/kind-order.test.js.map +1 -0
- package/dist/__tests__/phase2-integration.test.d.ts +5 -0
- package/dist/__tests__/phase2-integration.test.d.ts.map +1 -0
- package/dist/__tests__/phase2-integration.test.js +222 -0
- package/dist/__tests__/phase2-integration.test.js.map +1 -0
- package/dist/__tests__/queries.test.d.ts +7 -0
- package/dist/__tests__/queries.test.d.ts.map +1 -0
- package/dist/__tests__/queries.test.js +254 -0
- package/dist/__tests__/queries.test.js.map +1 -0
- package/dist/__tests__/token-estimator.test.d.ts +7 -0
- package/dist/__tests__/token-estimator.test.d.ts.map +1 -0
- package/dist/__tests__/token-estimator.test.js +267 -0
- package/dist/__tests__/token-estimator.test.js.map +1 -0
- package/dist/adapters/anthropic-estimator.d.ts +38 -0
- package/dist/adapters/anthropic-estimator.d.ts.map +1 -0
- package/dist/adapters/anthropic-estimator.js +108 -0
- package/dist/adapters/anthropic-estimator.js.map +1 -0
- package/dist/adapters/attachment-resolver.d.ts +96 -0
- package/dist/adapters/attachment-resolver.d.ts.map +1 -0
- package/dist/adapters/attachment-resolver.js +176 -0
- package/dist/adapters/attachment-resolver.js.map +1 -0
- package/dist/adapters/attachment-selector.d.ts +59 -0
- package/dist/adapters/attachment-selector.d.ts.map +1 -0
- package/dist/adapters/attachment-selector.js +163 -0
- package/dist/adapters/attachment-selector.js.map +1 -0
- package/dist/adapters/gemini-estimator.d.ts +27 -0
- package/dist/adapters/gemini-estimator.d.ts.map +1 -0
- package/dist/adapters/gemini-estimator.js +80 -0
- package/dist/adapters/gemini-estimator.js.map +1 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/memory-store.d.ts +139 -0
- package/dist/adapters/memory-store.d.ts.map +1 -0
- package/dist/adapters/memory-store.js +187 -0
- package/dist/adapters/memory-store.js.map +1 -0
- package/dist/adapters/openai-estimator.d.ts +35 -0
- package/dist/adapters/openai-estimator.d.ts.map +1 -0
- package/dist/adapters/openai-estimator.js +89 -0
- package/dist/adapters/openai-estimator.js.map +1 -0
- package/dist/adapters/summarizer.d.ts +121 -0
- package/dist/adapters/summarizer.d.ts.map +1 -0
- package/dist/adapters/summarizer.js +121 -0
- package/dist/adapters/summarizer.js.map +1 -0
- package/dist/adapters/token-estimator.d.ts +63 -0
- package/dist/adapters/token-estimator.d.ts.map +1 -0
- package/dist/adapters/token-estimator.js +37 -0
- package/dist/adapters/token-estimator.js.map +1 -0
- package/dist/builder/context-builder.d.ts +186 -0
- package/dist/builder/context-builder.d.ts.map +1 -0
- package/dist/builder/context-builder.js +305 -0
- package/dist/builder/context-builder.js.map +1 -0
- package/dist/builder/context-fork.d.ts +166 -0
- package/dist/builder/context-fork.d.ts.map +1 -0
- package/dist/builder/context-fork.js +282 -0
- package/dist/builder/context-fork.js.map +1 -0
- package/dist/builder/index.d.ts +6 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +22 -0
- package/dist/builder/index.js.map +1 -0
- package/dist/codecs/base.d.ts +18 -0
- package/dist/codecs/base.d.ts.map +1 -0
- package/dist/codecs/base.js +39 -0
- package/dist/codecs/base.js.map +1 -0
- package/dist/codecs/conversation-history.codec.d.ts +81 -0
- package/dist/codecs/conversation-history.codec.d.ts.map +1 -0
- package/dist/codecs/conversation-history.codec.js +89 -0
- package/dist/codecs/conversation-history.codec.js.map +1 -0
- package/dist/codecs/index.d.ts +31 -0
- package/dist/codecs/index.d.ts.map +1 -0
- package/dist/codecs/index.js +71 -0
- package/dist/codecs/index.js.map +1 -0
- package/dist/codecs/redacted-stub.codec.d.ts +32 -0
- package/dist/codecs/redacted-stub.codec.d.ts.map +1 -0
- package/dist/codecs/redacted-stub.codec.js +64 -0
- package/dist/codecs/redacted-stub.codec.js.map +1 -0
- package/dist/codecs/structured-reference.codec.d.ts +40 -0
- package/dist/codecs/structured-reference.codec.d.ts.map +1 -0
- package/dist/codecs/structured-reference.codec.js +81 -0
- package/dist/codecs/structured-reference.codec.js.map +1 -0
- package/dist/codecs/system-rules.codec.d.ts +32 -0
- package/dist/codecs/system-rules.codec.d.ts.map +1 -0
- package/dist/codecs/system-rules.codec.js +62 -0
- package/dist/codecs/system-rules.codec.js.map +1 -0
- package/dist/codecs/tool-output.codec.d.ts +66 -0
- package/dist/codecs/tool-output.codec.d.ts.map +1 -0
- package/dist/codecs/tool-output.codec.js +95 -0
- package/dist/codecs/tool-output.codec.js.map +1 -0
- package/dist/codecs/tool-schema.codec.d.ts +36 -0
- package/dist/codecs/tool-schema.codec.d.ts.map +1 -0
- package/dist/codecs/tool-schema.codec.js +74 -0
- package/dist/codecs/tool-schema.codec.js.map +1 -0
- package/dist/codecs/unsafe-text.codec.d.ts +28 -0
- package/dist/codecs/unsafe-text.codec.d.ts.map +1 -0
- package/dist/codecs/unsafe-text.codec.js +63 -0
- package/dist/codecs/unsafe-text.codec.js.map +1 -0
- package/dist/graph/context-graph.d.ts +121 -0
- package/dist/graph/context-graph.d.ts.map +1 -0
- package/dist/graph/context-graph.js +166 -0
- package/dist/graph/context-graph.js.map +1 -0
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +24 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/kind-order.d.ts +60 -0
- package/dist/graph/kind-order.d.ts.map +1 -0
- package/dist/graph/kind-order.js +113 -0
- package/dist/graph/kind-order.js.map +1 -0
- package/dist/graph/queries.d.ts +68 -0
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +240 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/views.d.ts +90 -0
- package/dist/graph/views.d.ts.map +1 -0
- package/dist/graph/views.js +173 -0
- package/dist/graph/views.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/compactor.d.ts +128 -0
- package/dist/pipeline/compactor.d.ts.map +1 -0
- package/dist/pipeline/compactor.js +346 -0
- package/dist/pipeline/compactor.js.map +1 -0
- package/dist/pipeline/index.d.ts +6 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +22 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/summarizer.d.ts +18 -0
- package/dist/pipeline/summarizer.d.ts.map +1 -0
- package/dist/pipeline/summarizer.js +68 -0
- package/dist/pipeline/summarizer.js.map +1 -0
- package/dist/policies/default-policy.d.ts +29 -0
- package/dist/policies/default-policy.d.ts.map +1 -0
- package/dist/policies/default-policy.js +58 -0
- package/dist/policies/default-policy.js.map +1 -0
- package/dist/policies/index.d.ts +5 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +21 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/providers/anthropic-compiler.d.ts +58 -0
- package/dist/providers/anthropic-compiler.d.ts.map +1 -0
- package/dist/providers/anthropic-compiler.js +182 -0
- package/dist/providers/anthropic-compiler.js.map +1 -0
- package/dist/providers/capabilities.d.ts +54 -0
- package/dist/providers/capabilities.d.ts.map +1 -0
- package/dist/providers/capabilities.js +87 -0
- package/dist/providers/capabilities.js.map +1 -0
- package/dist/providers/gemini-compiler.d.ts +51 -0
- package/dist/providers/gemini-compiler.d.ts.map +1 -0
- package/dist/providers/gemini-compiler.js +206 -0
- package/dist/providers/gemini-compiler.js.map +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +24 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-compiler.d.ts +46 -0
- package/dist/providers/openai-compiler.d.ts.map +1 -0
- package/dist/providers/openai-compiler.js +149 -0
- package/dist/providers/openai-compiler.js.map +1 -0
- package/dist/types/attachment.d.ts +62 -0
- package/dist/types/attachment.d.ts.map +1 -0
- package/dist/types/attachment.js +6 -0
- package/dist/types/attachment.js.map +1 -0
- package/dist/types/block.d.ts +61 -0
- package/dist/types/block.d.ts.map +1 -0
- package/dist/types/block.js +8 -0
- package/dist/types/block.js.map +1 -0
- package/dist/types/codec.d.ts +58 -0
- package/dist/types/codec.d.ts.map +1 -0
- package/dist/types/codec.js +6 -0
- package/dist/types/codec.js.map +1 -0
- package/dist/types/compiled.d.ts +91 -0
- package/dist/types/compiled.d.ts.map +1 -0
- package/dist/types/compiled.js +6 -0
- package/dist/types/compiled.js.map +1 -0
- package/dist/types/hash.d.ts +24 -0
- package/dist/types/hash.d.ts.map +1 -0
- package/dist/types/hash.js +49 -0
- package/dist/types/hash.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +26 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/policy.d.ts +128 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/policy.js +55 -0
- package/dist/types/policy.js.map +1 -0
- package/package.json +55 -0
- package/postcss.config.js +4 -0
- package/src/__tests__/attachment-selector.test.ts +559 -0
- package/src/__tests__/cache-breakpoints.test.ts +566 -0
- package/src/__tests__/codecs.test.ts +417 -0
- package/src/__tests__/compactor.test.ts +608 -0
- package/src/__tests__/context-graph.test.ts +383 -0
- package/src/__tests__/hash.test.ts +274 -0
- package/src/__tests__/integration.test.ts +866 -0
- package/src/__tests__/kind-order.test.ts +312 -0
- package/src/__tests__/phase2-integration.test.ts +253 -0
- package/src/__tests__/queries.test.ts +387 -0
- package/src/__tests__/token-estimator.test.ts +326 -0
- package/src/adapters/anthropic-estimator.ts +125 -0
- package/src/adapters/attachment-resolver.ts +295 -0
- package/src/adapters/attachment-selector.ts +218 -0
- package/src/adapters/gemini-estimator.ts +93 -0
- package/src/adapters/index.ts +12 -0
- package/src/adapters/memory-store.ts +299 -0
- package/src/adapters/openai-estimator.ts +105 -0
- package/src/adapters/summarizer.ts +250 -0
- package/src/adapters/token-estimator.ts +74 -0
- package/src/builder/context-builder.ts +467 -0
- package/src/builder/context-fork.ts +471 -0
- package/src/builder/index.ts +6 -0
- package/src/codecs/base.ts +36 -0
- package/src/codecs/conversation-history.codec.ts +108 -0
- package/src/codecs/index.ts +57 -0
- package/src/codecs/redacted-stub.codec.ts +76 -0
- package/src/codecs/structured-reference.codec.ts +96 -0
- package/src/codecs/system-rules.codec.ts +74 -0
- package/src/codecs/tool-output.codec.ts +109 -0
- package/src/codecs/tool-schema.codec.ts +87 -0
- package/src/codecs/unsafe-text.codec.ts +74 -0
- package/src/graph/context-graph.ts +205 -0
- package/src/graph/index.ts +8 -0
- package/src/graph/kind-order.ts +125 -0
- package/src/graph/queries.ts +306 -0
- package/src/graph/views.ts +255 -0
- package/src/index.ts +31 -0
- package/src/pipeline/compactor.ts +563 -0
- package/src/pipeline/index.ts +6 -0
- package/src/pipeline/summarizer.ts +76 -0
- package/src/policies/default-policy.ts +69 -0
- package/src/policies/index.ts +5 -0
- package/src/providers/anthropic-compiler.ts +294 -0
- package/src/providers/capabilities.ts +144 -0
- package/src/providers/gemini-compiler.ts +272 -0
- package/src/providers/index.ts +8 -0
- package/src/providers/openai-compiler.ts +191 -0
- package/src/types/attachment.ts +86 -0
- package/src/types/block.ts +84 -0
- package/src/types/codec.ts +68 -0
- package/src/types/compiled.ts +109 -0
- package/src/types/hash.ts +58 -0
- package/src/types/index.ts +10 -0
- package/src/types/policy.ts +194 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +21 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for BlockQuery filtering logic.
|
|
3
|
+
*
|
|
4
|
+
* Tests matchesQuery, compareSensitivity, mergeQueries, and emptyQuery.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
import {
|
|
9
|
+
matchesQuery,
|
|
10
|
+
compareSensitivity,
|
|
11
|
+
mergeQueries,
|
|
12
|
+
emptyQuery,
|
|
13
|
+
} from '../graph/queries.js';
|
|
14
|
+
import { ContextGraph } from '../graph/context-graph.js';
|
|
15
|
+
import type { ContextBlock, BlockKind } from '../types/block.js';
|
|
16
|
+
import type { BlockQuery } from '../graph/queries.js';
|
|
17
|
+
|
|
18
|
+
describe('BlockQuery', () => {
|
|
19
|
+
describe('compareSensitivity', () => {
|
|
20
|
+
it('should order public < internal < restricted', () => {
|
|
21
|
+
expect(compareSensitivity('public', 'internal')).toBeLessThan(0);
|
|
22
|
+
expect(compareSensitivity('internal', 'restricted')).toBeLessThan(0);
|
|
23
|
+
expect(compareSensitivity('public', 'restricted')).toBeLessThan(0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should be symmetric', () => {
|
|
27
|
+
expect(compareSensitivity('internal', 'public')).toBeGreaterThan(0);
|
|
28
|
+
expect(compareSensitivity('restricted', 'internal')).toBeGreaterThan(0);
|
|
29
|
+
expect(compareSensitivity('restricted', 'public')).toBeGreaterThan(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should return zero for equal levels', () => {
|
|
33
|
+
expect(compareSensitivity('public', 'public')).toBe(0);
|
|
34
|
+
expect(compareSensitivity('internal', 'internal')).toBe(0);
|
|
35
|
+
expect(compareSensitivity('restricted', 'restricted')).toBe(0);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('matchesQuery', () => {
|
|
40
|
+
const graph = new ContextGraph();
|
|
41
|
+
|
|
42
|
+
describe('Kind filtering', () => {
|
|
43
|
+
it('should match blocks with specified kinds', () => {
|
|
44
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
45
|
+
|
|
46
|
+
expect(matchesQuery(block, { kinds: ['pinned'] }, graph)).toBe(true);
|
|
47
|
+
expect(matchesQuery(block, { kinds: ['memory'] }, graph)).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should use OR logic for multiple kinds', () => {
|
|
51
|
+
const block = createBlock('hash1', 'memory', 'test');
|
|
52
|
+
|
|
53
|
+
expect(
|
|
54
|
+
matchesQuery(block, { kinds: ['pinned', 'memory'] }, graph)
|
|
55
|
+
).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should match all blocks when kinds is empty', () => {
|
|
59
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
60
|
+
|
|
61
|
+
expect(matchesQuery(block, { kinds: [] }, graph)).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('Tag filtering', () => {
|
|
66
|
+
it('should match blocks with all specified tags', () => {
|
|
67
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
68
|
+
block.meta.tags = ['important', 'cached'];
|
|
69
|
+
|
|
70
|
+
expect(
|
|
71
|
+
matchesQuery(block, { tags: ['important'] }, graph)
|
|
72
|
+
).toBe(true);
|
|
73
|
+
expect(
|
|
74
|
+
matchesQuery(block, { tags: ['important', 'cached'] }, graph)
|
|
75
|
+
).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should use AND logic for multiple tags', () => {
|
|
79
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
80
|
+
block.meta.tags = ['important'];
|
|
81
|
+
|
|
82
|
+
expect(
|
|
83
|
+
matchesQuery(block, { tags: ['important', 'cached'] }, graph)
|
|
84
|
+
).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should match blocks without tags when tags filter is empty', () => {
|
|
88
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
89
|
+
|
|
90
|
+
expect(matchesQuery(block, { tags: [] }, graph)).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('Sensitivity filtering', () => {
|
|
95
|
+
it('should filter by maximum sensitivity', () => {
|
|
96
|
+
const publicBlock = createBlock('hash1', 'pinned', 'test');
|
|
97
|
+
publicBlock.meta.sensitivity = 'public';
|
|
98
|
+
|
|
99
|
+
const internalBlock = createBlock('hash2', 'memory', 'test');
|
|
100
|
+
internalBlock.meta.sensitivity = 'internal';
|
|
101
|
+
|
|
102
|
+
expect(
|
|
103
|
+
matchesQuery(publicBlock, { maxSensitivity: 'public' }, graph)
|
|
104
|
+
).toBe(true);
|
|
105
|
+
expect(
|
|
106
|
+
matchesQuery(internalBlock, { maxSensitivity: 'public' }, graph)
|
|
107
|
+
).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should filter by minimum sensitivity', () => {
|
|
111
|
+
const publicBlock = createBlock('hash1', 'pinned', 'test');
|
|
112
|
+
publicBlock.meta.sensitivity = 'public';
|
|
113
|
+
|
|
114
|
+
const internalBlock = createBlock('hash2', 'memory', 'test');
|
|
115
|
+
internalBlock.meta.sensitivity = 'internal';
|
|
116
|
+
|
|
117
|
+
expect(
|
|
118
|
+
matchesQuery(publicBlock, { minSensitivity: 'internal' }, graph)
|
|
119
|
+
).toBe(false);
|
|
120
|
+
expect(
|
|
121
|
+
matchesQuery(internalBlock, { minSensitivity: 'internal' }, graph)
|
|
122
|
+
).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('Source filtering', () => {
|
|
127
|
+
it('should filter by source', () => {
|
|
128
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
129
|
+
block.meta.source = 'test-source';
|
|
130
|
+
|
|
131
|
+
expect(
|
|
132
|
+
matchesQuery(block, { source: 'test-source' }, graph)
|
|
133
|
+
).toBe(true);
|
|
134
|
+
expect(
|
|
135
|
+
matchesQuery(block, { source: 'other-source' }, graph)
|
|
136
|
+
).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('Timestamp filtering', () => {
|
|
141
|
+
it('should filter by minimum creation timestamp', () => {
|
|
142
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
143
|
+
block.meta.createdAt = 1000;
|
|
144
|
+
|
|
145
|
+
expect(
|
|
146
|
+
matchesQuery(block, { minCreatedAt: 900 }, graph)
|
|
147
|
+
).toBe(true);
|
|
148
|
+
expect(
|
|
149
|
+
matchesQuery(block, { minCreatedAt: 1100 }, graph)
|
|
150
|
+
).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should filter by maximum creation timestamp', () => {
|
|
154
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
155
|
+
block.meta.createdAt = 1000;
|
|
156
|
+
|
|
157
|
+
expect(
|
|
158
|
+
matchesQuery(block, { maxCreatedAt: 1100 }, graph)
|
|
159
|
+
).toBe(true);
|
|
160
|
+
expect(
|
|
161
|
+
matchesQuery(block, { maxCreatedAt: 900 }, graph)
|
|
162
|
+
).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('Provenance filtering', () => {
|
|
167
|
+
it('should filter by derivedFromAny', () => {
|
|
168
|
+
const parent = createBlock('parent', 'pinned', 'parent');
|
|
169
|
+
const child = createBlock('child', 'memory', 'child');
|
|
170
|
+
|
|
171
|
+
const testGraph = new ContextGraph();
|
|
172
|
+
testGraph.addBlock(parent);
|
|
173
|
+
testGraph.addBlock(child, [{ blockHash: 'parent' }]);
|
|
174
|
+
|
|
175
|
+
expect(
|
|
176
|
+
matchesQuery(child, { derivedFromAny: ['parent'] }, testGraph)
|
|
177
|
+
).toBe(true);
|
|
178
|
+
expect(
|
|
179
|
+
matchesQuery(child, { derivedFromAny: ['other'] }, testGraph)
|
|
180
|
+
).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should filter by notDerivedFromAny', () => {
|
|
184
|
+
const parent = createBlock('parent', 'pinned', 'parent');
|
|
185
|
+
const child = createBlock('child', 'memory', 'child');
|
|
186
|
+
|
|
187
|
+
const testGraph = new ContextGraph();
|
|
188
|
+
testGraph.addBlock(parent);
|
|
189
|
+
testGraph.addBlock(child, [{ blockHash: 'parent' }]);
|
|
190
|
+
|
|
191
|
+
expect(
|
|
192
|
+
matchesQuery(child, { notDerivedFromAny: ['parent'] }, testGraph)
|
|
193
|
+
).toBe(false);
|
|
194
|
+
expect(
|
|
195
|
+
matchesQuery(child, { notDerivedFromAny: ['other'] }, testGraph)
|
|
196
|
+
).toBe(true);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('Reference filtering', () => {
|
|
201
|
+
it('should filter by referencesAny', () => {
|
|
202
|
+
const ref = createBlock('ref', 'reference', 'ref');
|
|
203
|
+
const block = createBlock('block', 'memory', 'block');
|
|
204
|
+
|
|
205
|
+
const testGraph = new ContextGraph();
|
|
206
|
+
testGraph.addBlock(ref);
|
|
207
|
+
testGraph.addBlock(block, undefined, ['ref']);
|
|
208
|
+
|
|
209
|
+
expect(
|
|
210
|
+
matchesQuery(block, { referencesAny: ['ref'] }, testGraph)
|
|
211
|
+
).toBe(true);
|
|
212
|
+
expect(
|
|
213
|
+
matchesQuery(block, { referencesAny: ['other'] }, testGraph)
|
|
214
|
+
).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('Exclude hashes', () => {
|
|
219
|
+
it('should exclude specific block hashes', () => {
|
|
220
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
221
|
+
|
|
222
|
+
expect(
|
|
223
|
+
matchesQuery(block, { excludeHashes: ['hash2'] }, graph)
|
|
224
|
+
).toBe(true);
|
|
225
|
+
expect(
|
|
226
|
+
matchesQuery(block, { excludeHashes: ['hash1'] }, graph)
|
|
227
|
+
).toBe(false);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('Combined filters', () => {
|
|
232
|
+
it('should apply multiple filters with AND logic', () => {
|
|
233
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
234
|
+
block.meta.tags = ['important'];
|
|
235
|
+
block.meta.sensitivity = 'public';
|
|
236
|
+
|
|
237
|
+
const query: BlockQuery = {
|
|
238
|
+
kinds: ['pinned'],
|
|
239
|
+
tags: ['important'],
|
|
240
|
+
maxSensitivity: 'public',
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
expect(matchesQuery(block, query, graph)).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should reject if any filter fails', () => {
|
|
247
|
+
const block = createBlock('hash1', 'pinned', 'test');
|
|
248
|
+
block.meta.tags = ['important'];
|
|
249
|
+
block.meta.sensitivity = 'internal';
|
|
250
|
+
|
|
251
|
+
const query: BlockQuery = {
|
|
252
|
+
kinds: ['pinned'],
|
|
253
|
+
tags: ['important'],
|
|
254
|
+
maxSensitivity: 'public', // Fails this filter
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
expect(matchesQuery(block, query, graph)).toBe(false);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('emptyQuery', () => {
|
|
263
|
+
it('should create empty query object', () => {
|
|
264
|
+
const query = emptyQuery();
|
|
265
|
+
|
|
266
|
+
expect(query).toEqual({});
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe('mergeQueries', () => {
|
|
271
|
+
it('should merge kinds with intersection', () => {
|
|
272
|
+
const q1: BlockQuery = { kinds: ['pinned', 'memory'] };
|
|
273
|
+
const q2: BlockQuery = { kinds: ['memory', 'history'] };
|
|
274
|
+
|
|
275
|
+
const merged = mergeQueries(q1, q2);
|
|
276
|
+
|
|
277
|
+
expect(merged.kinds).toEqual(['memory']);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should merge tags with union', () => {
|
|
281
|
+
const q1: BlockQuery = { tags: ['tag1'] };
|
|
282
|
+
const q2: BlockQuery = { tags: ['tag2'] };
|
|
283
|
+
|
|
284
|
+
const merged = mergeQueries(q1, q2);
|
|
285
|
+
|
|
286
|
+
expect(merged.tags).toEqual(['tag1', 'tag2']);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should merge sensitivity with most restrictive', () => {
|
|
290
|
+
const q1: BlockQuery = { minSensitivity: 'public' };
|
|
291
|
+
const q2: BlockQuery = { minSensitivity: 'internal' };
|
|
292
|
+
|
|
293
|
+
const merged = mergeQueries(q1, q2);
|
|
294
|
+
|
|
295
|
+
expect(merged.minSensitivity).toBe('internal'); // Higher minimum
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should merge maxSensitivity with lower maximum', () => {
|
|
299
|
+
const q1: BlockQuery = { maxSensitivity: 'internal' };
|
|
300
|
+
const q2: BlockQuery = { maxSensitivity: 'public' };
|
|
301
|
+
|
|
302
|
+
const merged = mergeQueries(q1, q2);
|
|
303
|
+
|
|
304
|
+
expect(merged.maxSensitivity).toBe('public'); // Lower maximum
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should merge timestamps with most restrictive range', () => {
|
|
308
|
+
const q1: BlockQuery = { minCreatedAt: 100, maxCreatedAt: 500 };
|
|
309
|
+
const q2: BlockQuery = { minCreatedAt: 200, maxCreatedAt: 400 };
|
|
310
|
+
|
|
311
|
+
const merged = mergeQueries(q1, q2);
|
|
312
|
+
|
|
313
|
+
expect(merged.minCreatedAt).toBe(200); // Max of minimums
|
|
314
|
+
expect(merged.maxCreatedAt).toBe(400); // Min of maximums
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should merge provenance with union', () => {
|
|
318
|
+
const q1: BlockQuery = { derivedFromAny: ['hash1'] };
|
|
319
|
+
const q2: BlockQuery = { derivedFromAny: ['hash2'] };
|
|
320
|
+
|
|
321
|
+
const merged = mergeQueries(q1, q2);
|
|
322
|
+
|
|
323
|
+
expect(merged.derivedFromAny).toEqual(['hash1', 'hash2']);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should merge excludeHashes with union', () => {
|
|
327
|
+
const q1: BlockQuery = { excludeHashes: ['hash1'] };
|
|
328
|
+
const q2: BlockQuery = { excludeHashes: ['hash2'] };
|
|
329
|
+
|
|
330
|
+
const merged = mergeQueries(q1, q2);
|
|
331
|
+
|
|
332
|
+
expect(merged.excludeHashes).toEqual(['hash1', 'hash2']);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should merge maxTokens with minimum', () => {
|
|
336
|
+
const q1: BlockQuery = { maxTokens: 1000 };
|
|
337
|
+
const q2: BlockQuery = { maxTokens: 500 };
|
|
338
|
+
|
|
339
|
+
const merged = mergeQueries(q1, q2);
|
|
340
|
+
|
|
341
|
+
expect(merged.maxTokens).toBe(500);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should handle conflicting sources', () => {
|
|
345
|
+
const q1: BlockQuery = { source: 'source1' };
|
|
346
|
+
const q2: BlockQuery = { source: 'source2' };
|
|
347
|
+
|
|
348
|
+
const merged = mergeQueries(q1, q2);
|
|
349
|
+
|
|
350
|
+
// Conflicting sources = impossible query (empty kinds array)
|
|
351
|
+
expect(merged.kinds).toEqual([]);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should merge empty queries', () => {
|
|
355
|
+
const merged = mergeQueries({}, {});
|
|
356
|
+
|
|
357
|
+
expect(merged).toEqual({});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should handle single query', () => {
|
|
361
|
+
const q1: BlockQuery = { kinds: ['pinned'] };
|
|
362
|
+
|
|
363
|
+
const merged = mergeQueries(q1);
|
|
364
|
+
|
|
365
|
+
expect(merged).toEqual({ kinds: ['pinned'] });
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Helper to create test blocks
|
|
371
|
+
function createBlock(
|
|
372
|
+
hash: string,
|
|
373
|
+
kind: BlockKind,
|
|
374
|
+
payload: string
|
|
375
|
+
): ContextBlock<string> {
|
|
376
|
+
return {
|
|
377
|
+
blockHash: hash,
|
|
378
|
+
meta: {
|
|
379
|
+
kind,
|
|
380
|
+
sensitivity: 'public',
|
|
381
|
+
codecId: 'test',
|
|
382
|
+
codecVersion: '1.0.0',
|
|
383
|
+
createdAt: Date.now(),
|
|
384
|
+
},
|
|
385
|
+
payload,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for token estimators.
|
|
3
|
+
*
|
|
4
|
+
* Tests AnthropicTokenEstimator, OpenAITokenEstimator, GeminiTokenEstimator.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
8
|
+
import {
|
|
9
|
+
heuristicTokenCount,
|
|
10
|
+
serializeBlockForEstimation,
|
|
11
|
+
LOW_CONFIDENCE_MULTIPLIER,
|
|
12
|
+
} from '../adapters/token-estimator.js';
|
|
13
|
+
import { OpenAITokenEstimator } from '../adapters/openai-estimator.js';
|
|
14
|
+
import { GeminiTokenEstimator } from '../adapters/gemini-estimator.js';
|
|
15
|
+
import type { ContextBlock } from '../types/block.js';
|
|
16
|
+
|
|
17
|
+
describe('Token Estimator', () => {
|
|
18
|
+
describe('heuristicTokenCount', () => {
|
|
19
|
+
it('should estimate tokens using chars/4 with multiplier', () => {
|
|
20
|
+
const text = 'a'.repeat(100); // 100 chars
|
|
21
|
+
const tokens = heuristicTokenCount(text);
|
|
22
|
+
|
|
23
|
+
// Expected: (100 / 4) * 1.2 = 30
|
|
24
|
+
expect(tokens).toBe(30);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should round up fractional tokens', () => {
|
|
28
|
+
const text = 'a'.repeat(7); // 7 chars
|
|
29
|
+
const tokens = heuristicTokenCount(text);
|
|
30
|
+
|
|
31
|
+
// Expected: ceil((7 / 4) * 1.2) = ceil(2.1) = 3
|
|
32
|
+
expect(tokens).toBe(3);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should apply safety multiplier', () => {
|
|
36
|
+
const text = 'a'.repeat(40); // 40 chars
|
|
37
|
+
|
|
38
|
+
const tokens = heuristicTokenCount(text);
|
|
39
|
+
|
|
40
|
+
// Without multiplier: 40 / 4 = 10
|
|
41
|
+
// With multiplier: 10 * 1.2 = 12
|
|
42
|
+
expect(tokens).toBe(12);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle empty string', () => {
|
|
46
|
+
expect(heuristicTokenCount('')).toBe(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should handle long text', () => {
|
|
50
|
+
const text = 'a'.repeat(10000);
|
|
51
|
+
const tokens = heuristicTokenCount(text);
|
|
52
|
+
|
|
53
|
+
// Expected: (10000 / 4) * 1.2 = 3000
|
|
54
|
+
expect(tokens).toBe(3000);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('serializeBlockForEstimation', () => {
|
|
59
|
+
it('should serialize payload as JSON', () => {
|
|
60
|
+
const block: ContextBlock<any> = {
|
|
61
|
+
blockHash: 'test',
|
|
62
|
+
meta: {
|
|
63
|
+
kind: 'pinned',
|
|
64
|
+
sensitivity: 'public',
|
|
65
|
+
codecId: 'test',
|
|
66
|
+
codecVersion: '1.0.0',
|
|
67
|
+
createdAt: Date.now(),
|
|
68
|
+
},
|
|
69
|
+
payload: { text: 'Hello, world!' },
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const serialized = serializeBlockForEstimation(block);
|
|
73
|
+
|
|
74
|
+
expect(serialized).toBe('{"text":"Hello, world!"}');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle complex payloads', () => {
|
|
78
|
+
const block: ContextBlock<any> = {
|
|
79
|
+
blockHash: 'test',
|
|
80
|
+
meta: {
|
|
81
|
+
kind: 'memory',
|
|
82
|
+
sensitivity: 'public',
|
|
83
|
+
codecId: 'test',
|
|
84
|
+
codecVersion: '1.0.0',
|
|
85
|
+
createdAt: Date.now(),
|
|
86
|
+
},
|
|
87
|
+
payload: {
|
|
88
|
+
nested: {
|
|
89
|
+
array: [1, 2, 3],
|
|
90
|
+
text: 'test',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const serialized = serializeBlockForEstimation(block);
|
|
96
|
+
|
|
97
|
+
expect(serialized).toContain('"nested"');
|
|
98
|
+
expect(serialized).toContain('"array"');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('OpenAITokenEstimator', () => {
|
|
103
|
+
it('should create estimator with model', () => {
|
|
104
|
+
const estimator = new OpenAITokenEstimator('gpt-4');
|
|
105
|
+
|
|
106
|
+
expect(estimator).toBeDefined();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should estimate block tokens with high confidence', async () => {
|
|
110
|
+
const estimator = new OpenAITokenEstimator('gpt-4');
|
|
111
|
+
|
|
112
|
+
const block: ContextBlock<any> = {
|
|
113
|
+
blockHash: 'test',
|
|
114
|
+
meta: {
|
|
115
|
+
kind: 'pinned',
|
|
116
|
+
sensitivity: 'public',
|
|
117
|
+
codecId: 'test',
|
|
118
|
+
codecVersion: '1.0.0',
|
|
119
|
+
createdAt: Date.now(),
|
|
120
|
+
},
|
|
121
|
+
payload: { text: 'Hello, world!' },
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const estimate = await estimator.estimateBlock(block);
|
|
125
|
+
|
|
126
|
+
expect(estimate.tokens).toBeGreaterThan(0);
|
|
127
|
+
expect(estimate.confidence).toBe('high'); // tiktoken-based
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should estimate multiple blocks', async () => {
|
|
131
|
+
const estimator = new OpenAITokenEstimator('gpt-4');
|
|
132
|
+
|
|
133
|
+
const blocks: ContextBlock<any>[] = [
|
|
134
|
+
{
|
|
135
|
+
blockHash: 'test1',
|
|
136
|
+
meta: {
|
|
137
|
+
kind: 'pinned',
|
|
138
|
+
sensitivity: 'public',
|
|
139
|
+
codecId: 'test',
|
|
140
|
+
codecVersion: '1.0.0',
|
|
141
|
+
createdAt: Date.now(),
|
|
142
|
+
},
|
|
143
|
+
payload: { text: 'Block 1' },
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
blockHash: 'test2',
|
|
147
|
+
meta: {
|
|
148
|
+
kind: 'memory',
|
|
149
|
+
sensitivity: 'public',
|
|
150
|
+
codecId: 'test',
|
|
151
|
+
codecVersion: '1.0.0',
|
|
152
|
+
createdAt: Date.now(),
|
|
153
|
+
},
|
|
154
|
+
payload: { text: 'Block 2' },
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
const estimate = await estimator.estimate(blocks);
|
|
159
|
+
|
|
160
|
+
expect(estimate.tokens).toBeGreaterThan(0);
|
|
161
|
+
expect(estimate.confidence).toBe('high');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should return zero tokens for empty array', async () => {
|
|
165
|
+
const estimator = new OpenAITokenEstimator('gpt-4');
|
|
166
|
+
|
|
167
|
+
const estimate = await estimator.estimate([]);
|
|
168
|
+
|
|
169
|
+
expect(estimate.tokens).toBe(0);
|
|
170
|
+
expect(estimate.confidence).toBe('high');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should estimate different token counts for different content', async () => {
|
|
174
|
+
const estimator = new OpenAITokenEstimator('gpt-4');
|
|
175
|
+
|
|
176
|
+
const block1: ContextBlock<any> = {
|
|
177
|
+
blockHash: 'test1',
|
|
178
|
+
meta: {
|
|
179
|
+
kind: 'pinned',
|
|
180
|
+
sensitivity: 'public',
|
|
181
|
+
codecId: 'test',
|
|
182
|
+
codecVersion: '1.0.0',
|
|
183
|
+
createdAt: Date.now(),
|
|
184
|
+
},
|
|
185
|
+
payload: { text: 'Short' },
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const block2: ContextBlock<any> = {
|
|
189
|
+
blockHash: 'test2',
|
|
190
|
+
meta: {
|
|
191
|
+
kind: 'pinned',
|
|
192
|
+
sensitivity: 'public',
|
|
193
|
+
codecId: 'test',
|
|
194
|
+
codecVersion: '1.0.0',
|
|
195
|
+
createdAt: Date.now(),
|
|
196
|
+
},
|
|
197
|
+
payload: { text: 'This is a much longer text with many more tokens' },
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const estimate1 = await estimator.estimateBlock(block1);
|
|
201
|
+
const estimate2 = await estimator.estimateBlock(block2);
|
|
202
|
+
|
|
203
|
+
expect(estimate2.tokens).toBeGreaterThan(estimate1.tokens);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('GeminiTokenEstimator', () => {
|
|
208
|
+
it('should create estimator', () => {
|
|
209
|
+
const estimator = new GeminiTokenEstimator();
|
|
210
|
+
|
|
211
|
+
expect(estimator).toBeDefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should estimate block tokens with high confidence', async () => {
|
|
215
|
+
const estimator = new GeminiTokenEstimator();
|
|
216
|
+
|
|
217
|
+
const block: ContextBlock<any> = {
|
|
218
|
+
blockHash: 'test',
|
|
219
|
+
meta: {
|
|
220
|
+
kind: 'pinned',
|
|
221
|
+
sensitivity: 'public',
|
|
222
|
+
codecId: 'test',
|
|
223
|
+
codecVersion: '1.0.0',
|
|
224
|
+
createdAt: Date.now(),
|
|
225
|
+
},
|
|
226
|
+
payload: { text: 'Hello, Gemini!' },
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const estimate = await estimator.estimateBlock(block);
|
|
230
|
+
|
|
231
|
+
expect(estimate.tokens).toBeGreaterThan(0);
|
|
232
|
+
expect(estimate.confidence).toBe('high'); // tiktoken-based
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should estimate multiple blocks', async () => {
|
|
236
|
+
const estimator = new GeminiTokenEstimator();
|
|
237
|
+
|
|
238
|
+
const blocks: ContextBlock<any>[] = [
|
|
239
|
+
{
|
|
240
|
+
blockHash: 'test1',
|
|
241
|
+
meta: {
|
|
242
|
+
kind: 'pinned',
|
|
243
|
+
sensitivity: 'public',
|
|
244
|
+
codecId: 'test',
|
|
245
|
+
codecVersion: '1.0.0',
|
|
246
|
+
createdAt: Date.now(),
|
|
247
|
+
},
|
|
248
|
+
payload: { text: 'Block 1' },
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
blockHash: 'test2',
|
|
252
|
+
meta: {
|
|
253
|
+
kind: 'memory',
|
|
254
|
+
sensitivity: 'public',
|
|
255
|
+
codecId: 'test',
|
|
256
|
+
codecVersion: '1.0.0',
|
|
257
|
+
createdAt: Date.now(),
|
|
258
|
+
},
|
|
259
|
+
payload: { text: 'Block 2' },
|
|
260
|
+
},
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
const estimate = await estimator.estimate(blocks);
|
|
264
|
+
|
|
265
|
+
expect(estimate.tokens).toBeGreaterThan(0);
|
|
266
|
+
expect(estimate.confidence).toBe('high');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should return zero tokens for empty array', async () => {
|
|
270
|
+
const estimator = new GeminiTokenEstimator();
|
|
271
|
+
|
|
272
|
+
const estimate = await estimator.estimate([]);
|
|
273
|
+
|
|
274
|
+
expect(estimate.tokens).toBe(0);
|
|
275
|
+
expect(estimate.confidence).toBe('high');
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('Confidence levels', () => {
|
|
280
|
+
it('should use correct confidence for OpenAI tiktoken', async () => {
|
|
281
|
+
const estimator = new OpenAITokenEstimator('gpt-4');
|
|
282
|
+
|
|
283
|
+
const block: ContextBlock<any> = {
|
|
284
|
+
blockHash: 'test',
|
|
285
|
+
meta: {
|
|
286
|
+
kind: 'pinned',
|
|
287
|
+
sensitivity: 'public',
|
|
288
|
+
codecId: 'test',
|
|
289
|
+
codecVersion: '1.0.0',
|
|
290
|
+
createdAt: Date.now(),
|
|
291
|
+
},
|
|
292
|
+
payload: { text: 'Test' },
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const estimate = await estimator.estimateBlock(block);
|
|
296
|
+
|
|
297
|
+
expect(estimate.confidence).toBe('high');
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('should use correct confidence for Gemini tiktoken', async () => {
|
|
301
|
+
const estimator = new GeminiTokenEstimator();
|
|
302
|
+
|
|
303
|
+
const block: ContextBlock<any> = {
|
|
304
|
+
blockHash: 'test',
|
|
305
|
+
meta: {
|
|
306
|
+
kind: 'pinned',
|
|
307
|
+
sensitivity: 'public',
|
|
308
|
+
codecId: 'test',
|
|
309
|
+
codecVersion: '1.0.0',
|
|
310
|
+
createdAt: Date.now(),
|
|
311
|
+
},
|
|
312
|
+
payload: { text: 'Test' },
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const estimate = await estimator.estimateBlock(block);
|
|
316
|
+
|
|
317
|
+
expect(estimate.confidence).toBe('high');
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe('LOW_CONFIDENCE_MULTIPLIER', () => {
|
|
322
|
+
it('should be 1.2 for safety', () => {
|
|
323
|
+
expect(LOW_CONFIDENCE_MULTIPLIER).toBe(1.2);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
});
|