@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,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GeminiTokenEstimator: High-confidence token counting via tiktoken.
|
|
3
|
+
*
|
|
4
|
+
* Uses tiktoken with gpt-4 encoding as a good approximation for Gemini.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { encoding_for_model } from 'tiktoken';
|
|
8
|
+
import type { ContextBlock } from '../types/block.js';
|
|
9
|
+
import type { TokenEstimator, TokenEstimate } from './token-estimator.js';
|
|
10
|
+
import {
|
|
11
|
+
heuristicTokenCount,
|
|
12
|
+
serializeBlockForEstimation,
|
|
13
|
+
} from './token-estimator.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* GeminiTokenEstimator using tiktoken (gpt-4 encoding as approximation).
|
|
17
|
+
*/
|
|
18
|
+
export class GeminiTokenEstimator implements TokenEstimator {
|
|
19
|
+
/**
|
|
20
|
+
* Estimate tokens for a single block using tiktoken.
|
|
21
|
+
*
|
|
22
|
+
* @param block - Block to estimate
|
|
23
|
+
* @returns Token estimate (high confidence)
|
|
24
|
+
*/
|
|
25
|
+
async estimateBlock(block: ContextBlock<unknown>): Promise<TokenEstimate> {
|
|
26
|
+
try {
|
|
27
|
+
const text = serializeBlockForEstimation(block);
|
|
28
|
+
const encoding = encoding_for_model('gpt-4');
|
|
29
|
+
const tokens = encoding.encode(text);
|
|
30
|
+
encoding.free(); // Important: free memory
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
tokens: tokens.length,
|
|
34
|
+
confidence: 'high',
|
|
35
|
+
};
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// Fallback to heuristic on error
|
|
38
|
+
console.warn(
|
|
39
|
+
'[GeminiTokenEstimator] tiktoken error, falling back to heuristic:',
|
|
40
|
+
error
|
|
41
|
+
);
|
|
42
|
+
const text = serializeBlockForEstimation(block);
|
|
43
|
+
return {
|
|
44
|
+
tokens: heuristicTokenCount(text),
|
|
45
|
+
confidence: 'low',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Estimate tokens for multiple blocks.
|
|
52
|
+
*
|
|
53
|
+
* @param blocks - Blocks to estimate
|
|
54
|
+
* @returns Token estimate (high confidence if all succeed)
|
|
55
|
+
*/
|
|
56
|
+
async estimate(blocks: ContextBlock<unknown>[]): Promise<TokenEstimate> {
|
|
57
|
+
if (blocks.length === 0) {
|
|
58
|
+
return { tokens: 0, confidence: 'high' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const combinedText = blocks
|
|
63
|
+
.map((block) => serializeBlockForEstimation(block))
|
|
64
|
+
.join('\n\n');
|
|
65
|
+
|
|
66
|
+
const encoding = encoding_for_model('gpt-4');
|
|
67
|
+
const tokens = encoding.encode(combinedText);
|
|
68
|
+
encoding.free(); // Important: free memory
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
tokens: tokens.length,
|
|
72
|
+
confidence: 'high',
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// Fallback: sum individual heuristic estimates
|
|
76
|
+
console.warn(
|
|
77
|
+
'[GeminiTokenEstimator] tiktoken error, falling back to heuristic:',
|
|
78
|
+
error
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
let totalTokens = 0;
|
|
82
|
+
for (const block of blocks) {
|
|
83
|
+
const text = serializeBlockForEstimation(block);
|
|
84
|
+
totalTokens += heuristicTokenCount(text);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
tokens: totalTokens,
|
|
89
|
+
confidence: 'low',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter exports (token estimators, attachment resolvers, etc.)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './token-estimator.js';
|
|
6
|
+
export * from './anthropic-estimator.js';
|
|
7
|
+
export * from './openai-estimator.js';
|
|
8
|
+
export * from './gemini-estimator.js';
|
|
9
|
+
export * from './attachment-resolver.js';
|
|
10
|
+
export * from './attachment-selector.js';
|
|
11
|
+
export * from './summarizer.js';
|
|
12
|
+
export * from './memory-store.js';
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryStore: Persistence interface for context blocks.
|
|
3
|
+
*
|
|
4
|
+
* Provides a common interface for storing and retrieving context blocks
|
|
5
|
+
* with support for querying and deletion.
|
|
6
|
+
*
|
|
7
|
+
* External implementations:
|
|
8
|
+
* - @foundry/context-store-postgres - PostgreSQL backend
|
|
9
|
+
* - @foundry/context-store-redis - Redis backend
|
|
10
|
+
* - @foundry/context-store-memory - In-memory backend (testing)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ContextBlock } from '../types/block.js';
|
|
14
|
+
import type { BlockQuery } from '../graph/queries.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Memory store query options.
|
|
18
|
+
*/
|
|
19
|
+
export interface MemoryQueryOptions {
|
|
20
|
+
/** Block query filter */
|
|
21
|
+
query?: BlockQuery;
|
|
22
|
+
|
|
23
|
+
/** Maximum number of results */
|
|
24
|
+
limit?: number;
|
|
25
|
+
|
|
26
|
+
/** Offset for pagination */
|
|
27
|
+
offset?: number;
|
|
28
|
+
|
|
29
|
+
/** Sort by field (default: createdAt descending) */
|
|
30
|
+
sortBy?: 'createdAt' | 'blockHash';
|
|
31
|
+
|
|
32
|
+
/** Sort order */
|
|
33
|
+
sortOrder?: 'asc' | 'desc';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Memory store save options.
|
|
38
|
+
*/
|
|
39
|
+
export interface MemorySaveOptions {
|
|
40
|
+
/** Time-to-live (seconds, optional) */
|
|
41
|
+
ttl?: number;
|
|
42
|
+
|
|
43
|
+
/** Overwrite existing block with same hash */
|
|
44
|
+
overwrite?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* MemoryStore interface for context block persistence.
|
|
49
|
+
*/
|
|
50
|
+
export interface MemoryStore {
|
|
51
|
+
/**
|
|
52
|
+
* Save a block to the store.
|
|
53
|
+
*
|
|
54
|
+
* @param block - Block to save
|
|
55
|
+
* @param options - Save options
|
|
56
|
+
* @returns Promise resolving when save is complete
|
|
57
|
+
*/
|
|
58
|
+
save<TPayload>(
|
|
59
|
+
block: ContextBlock<TPayload>,
|
|
60
|
+
options?: MemorySaveOptions
|
|
61
|
+
): Promise<void>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Load a block by hash.
|
|
65
|
+
*
|
|
66
|
+
* @param blockHash - Block hash to load
|
|
67
|
+
* @returns Block if found, undefined otherwise
|
|
68
|
+
*/
|
|
69
|
+
load<TPayload = unknown>(
|
|
70
|
+
blockHash: string
|
|
71
|
+
): Promise<ContextBlock<TPayload> | undefined>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Query blocks matching criteria.
|
|
75
|
+
*
|
|
76
|
+
* @param options - Query options
|
|
77
|
+
* @returns Matching blocks
|
|
78
|
+
*/
|
|
79
|
+
query(options?: MemoryQueryOptions): Promise<ContextBlock<unknown>[]>;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Delete a block by hash.
|
|
83
|
+
*
|
|
84
|
+
* @param blockHash - Block hash to delete
|
|
85
|
+
* @returns True if block was deleted, false if not found
|
|
86
|
+
*/
|
|
87
|
+
delete(blockHash: string): Promise<boolean>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Delete all blocks matching a query.
|
|
91
|
+
*
|
|
92
|
+
* @param query - Block query filter
|
|
93
|
+
* @returns Number of blocks deleted
|
|
94
|
+
*/
|
|
95
|
+
deleteMany(query: BlockQuery): Promise<number>;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if a block exists in the store.
|
|
99
|
+
*
|
|
100
|
+
* @param blockHash - Block hash to check
|
|
101
|
+
* @returns True if block exists
|
|
102
|
+
*/
|
|
103
|
+
exists(blockHash: string): Promise<boolean>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get store statistics.
|
|
107
|
+
*
|
|
108
|
+
* @returns Store stats
|
|
109
|
+
*/
|
|
110
|
+
getStats(): Promise<{
|
|
111
|
+
blockCount: number;
|
|
112
|
+
totalSizeBytes?: number;
|
|
113
|
+
}>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Clear all blocks from the store.
|
|
117
|
+
* Use with caution!
|
|
118
|
+
*
|
|
119
|
+
* @returns Promise resolving when clear is complete
|
|
120
|
+
*/
|
|
121
|
+
clear(): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* InMemoryStore: Simple in-memory implementation for testing.
|
|
126
|
+
*
|
|
127
|
+
* WARNING: Not suitable for production use. Data is lost on process restart.
|
|
128
|
+
*/
|
|
129
|
+
export class InMemoryStore implements MemoryStore {
|
|
130
|
+
private readonly blocks: Map<string, ContextBlock<unknown>>;
|
|
131
|
+
private readonly ttls: Map<string, number>; // blockHash -> expiresAt timestamp
|
|
132
|
+
|
|
133
|
+
constructor() {
|
|
134
|
+
this.blocks = new Map();
|
|
135
|
+
this.ttls = new Map();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Clean up expired blocks (called before operations).
|
|
140
|
+
*/
|
|
141
|
+
private cleanupExpired(): void {
|
|
142
|
+
const now = Math.floor(Date.now() / 1000);
|
|
143
|
+
const expiredHashes: string[] = [];
|
|
144
|
+
|
|
145
|
+
for (const [blockHash, expiresAt] of this.ttls.entries()) {
|
|
146
|
+
if (expiresAt <= now) {
|
|
147
|
+
expiredHashes.push(blockHash);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const hash of expiredHashes) {
|
|
152
|
+
this.blocks.delete(hash);
|
|
153
|
+
this.ttls.delete(hash);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async save<TPayload>(
|
|
158
|
+
block: ContextBlock<TPayload>,
|
|
159
|
+
options?: MemorySaveOptions
|
|
160
|
+
): Promise<void> {
|
|
161
|
+
this.cleanupExpired();
|
|
162
|
+
|
|
163
|
+
const { blockHash } = block;
|
|
164
|
+
|
|
165
|
+
// Check for existing block
|
|
166
|
+
if (this.blocks.has(blockHash) && !options?.overwrite) {
|
|
167
|
+
// Block already exists and overwrite is false - skip
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Save block
|
|
172
|
+
this.blocks.set(blockHash, block as ContextBlock<unknown>);
|
|
173
|
+
|
|
174
|
+
// Set TTL if provided
|
|
175
|
+
if (options?.ttl) {
|
|
176
|
+
const expiresAt = Math.floor(Date.now() / 1000) + options.ttl;
|
|
177
|
+
this.ttls.set(blockHash, expiresAt);
|
|
178
|
+
} else {
|
|
179
|
+
// Remove TTL if overwriting without TTL
|
|
180
|
+
this.ttls.delete(blockHash);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async load<TPayload = unknown>(
|
|
185
|
+
blockHash: string
|
|
186
|
+
): Promise<ContextBlock<TPayload> | undefined> {
|
|
187
|
+
this.cleanupExpired();
|
|
188
|
+
return this.blocks.get(blockHash) as ContextBlock<TPayload> | undefined;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async query(options?: MemoryQueryOptions): Promise<ContextBlock<unknown>[]> {
|
|
192
|
+
this.cleanupExpired();
|
|
193
|
+
|
|
194
|
+
let results = Array.from(this.blocks.values());
|
|
195
|
+
|
|
196
|
+
// Apply query filter if provided
|
|
197
|
+
if (options?.query) {
|
|
198
|
+
const { matchesQuery } = await import('../graph/queries.js');
|
|
199
|
+
results = results.filter((block) =>
|
|
200
|
+
matchesQuery(block, options.query!, {
|
|
201
|
+
select: () => [],
|
|
202
|
+
hasBlock: (hash: string) => this.blocks.has(hash),
|
|
203
|
+
} as any)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Sort results
|
|
208
|
+
const sortBy = options?.sortBy ?? 'createdAt';
|
|
209
|
+
const sortOrder = options?.sortOrder ?? 'desc';
|
|
210
|
+
|
|
211
|
+
results.sort((a, b) => {
|
|
212
|
+
let comparison = 0;
|
|
213
|
+
|
|
214
|
+
if (sortBy === 'createdAt') {
|
|
215
|
+
comparison = a.meta.createdAt - b.meta.createdAt;
|
|
216
|
+
} else if (sortBy === 'blockHash') {
|
|
217
|
+
comparison = a.blockHash.localeCompare(b.blockHash);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return sortOrder === 'asc' ? comparison : -comparison;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Apply limit and offset
|
|
224
|
+
const offset = options?.offset ?? 0;
|
|
225
|
+
const limit = options?.limit ?? results.length;
|
|
226
|
+
|
|
227
|
+
return results.slice(offset, offset + limit);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async delete(blockHash: string): Promise<boolean> {
|
|
231
|
+
this.cleanupExpired();
|
|
232
|
+
|
|
233
|
+
const existed = this.blocks.delete(blockHash);
|
|
234
|
+
this.ttls.delete(blockHash);
|
|
235
|
+
|
|
236
|
+
return existed;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async deleteMany(query: BlockQuery): Promise<number> {
|
|
240
|
+
this.cleanupExpired();
|
|
241
|
+
|
|
242
|
+
const matchingBlocks = await this.query({ query });
|
|
243
|
+
let deleted = 0;
|
|
244
|
+
|
|
245
|
+
for (const block of matchingBlocks) {
|
|
246
|
+
if (await this.delete(block.blockHash)) {
|
|
247
|
+
deleted++;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return deleted;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async exists(blockHash: string): Promise<boolean> {
|
|
255
|
+
this.cleanupExpired();
|
|
256
|
+
return this.blocks.has(blockHash);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async getStats(): Promise<{ blockCount: number; totalSizeBytes?: number }> {
|
|
260
|
+
this.cleanupExpired();
|
|
261
|
+
|
|
262
|
+
// Estimate total size in bytes
|
|
263
|
+
let totalSizeBytes = 0;
|
|
264
|
+
|
|
265
|
+
for (const block of this.blocks.values()) {
|
|
266
|
+
// Rough estimate: JSON.stringify length
|
|
267
|
+
totalSizeBytes += JSON.stringify(block).length;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
blockCount: this.blocks.size,
|
|
272
|
+
totalSizeBytes,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async clear(): Promise<void> {
|
|
277
|
+
this.blocks.clear();
|
|
278
|
+
this.ttls.clear();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get all blocks (testing only).
|
|
283
|
+
*
|
|
284
|
+
* @returns All blocks in the store
|
|
285
|
+
*/
|
|
286
|
+
getAllBlocks(): ContextBlock<unknown>[] {
|
|
287
|
+
this.cleanupExpired();
|
|
288
|
+
return Array.from(this.blocks.values());
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Create an in-memory store for testing.
|
|
294
|
+
*
|
|
295
|
+
* @returns InMemoryStore instance
|
|
296
|
+
*/
|
|
297
|
+
export function createInMemoryStore(): MemoryStore {
|
|
298
|
+
return new InMemoryStore();
|
|
299
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAITokenEstimator: High-confidence token counting via tiktoken.
|
|
3
|
+
*
|
|
4
|
+
* Uses tiktoken library for accurate token counts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { encoding_for_model } from 'tiktoken';
|
|
8
|
+
import type { TiktokenModel } from 'tiktoken';
|
|
9
|
+
import type { ContextBlock } from '../types/block.js';
|
|
10
|
+
import type { TokenEstimator, TokenEstimate } from './token-estimator.js';
|
|
11
|
+
import {
|
|
12
|
+
heuristicTokenCount,
|
|
13
|
+
serializeBlockForEstimation,
|
|
14
|
+
} from './token-estimator.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* OpenAITokenEstimator using tiktoken.
|
|
18
|
+
*/
|
|
19
|
+
export class OpenAITokenEstimator implements TokenEstimator {
|
|
20
|
+
private readonly model: TiktokenModel;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create an OpenAITokenEstimator.
|
|
24
|
+
*
|
|
25
|
+
* @param model - Model name (e.g., 'gpt-4', 'gpt-3.5-turbo')
|
|
26
|
+
*/
|
|
27
|
+
constructor(model: TiktokenModel = 'gpt-4') {
|
|
28
|
+
this.model = model;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Estimate tokens for a single block using tiktoken.
|
|
33
|
+
*
|
|
34
|
+
* @param block - Block to estimate
|
|
35
|
+
* @returns Token estimate (high confidence)
|
|
36
|
+
*/
|
|
37
|
+
async estimateBlock(block: ContextBlock<unknown>): Promise<TokenEstimate> {
|
|
38
|
+
try {
|
|
39
|
+
const text = serializeBlockForEstimation(block);
|
|
40
|
+
const encoding = encoding_for_model(this.model);
|
|
41
|
+
const tokens = encoding.encode(text);
|
|
42
|
+
encoding.free(); // Important: free memory
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
tokens: tokens.length,
|
|
46
|
+
confidence: 'high',
|
|
47
|
+
};
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// Fallback to heuristic on error
|
|
50
|
+
console.warn(
|
|
51
|
+
'[OpenAITokenEstimator] tiktoken error, falling back to heuristic:',
|
|
52
|
+
error
|
|
53
|
+
);
|
|
54
|
+
const text = serializeBlockForEstimation(block);
|
|
55
|
+
return {
|
|
56
|
+
tokens: heuristicTokenCount(text),
|
|
57
|
+
confidence: 'low',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Estimate tokens for multiple blocks.
|
|
64
|
+
*
|
|
65
|
+
* @param blocks - Blocks to estimate
|
|
66
|
+
* @returns Token estimate (high confidence if all succeed)
|
|
67
|
+
*/
|
|
68
|
+
async estimate(blocks: ContextBlock<unknown>[]): Promise<TokenEstimate> {
|
|
69
|
+
if (blocks.length === 0) {
|
|
70
|
+
return { tokens: 0, confidence: 'high' };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const combinedText = blocks
|
|
75
|
+
.map((block) => serializeBlockForEstimation(block))
|
|
76
|
+
.join('\n\n');
|
|
77
|
+
|
|
78
|
+
const encoding = encoding_for_model(this.model);
|
|
79
|
+
const tokens = encoding.encode(combinedText);
|
|
80
|
+
encoding.free(); // Important: free memory
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
tokens: tokens.length,
|
|
84
|
+
confidence: 'high',
|
|
85
|
+
};
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Fallback: sum individual heuristic estimates
|
|
88
|
+
console.warn(
|
|
89
|
+
'[OpenAITokenEstimator] tiktoken error, falling back to heuristic:',
|
|
90
|
+
error
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
let totalTokens = 0;
|
|
94
|
+
for (const block of blocks) {
|
|
95
|
+
const text = serializeBlockForEstimation(block);
|
|
96
|
+
totalTokens += heuristicTokenCount(text);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
tokens: totalTokens,
|
|
101
|
+
confidence: 'low',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|