sliding-context 0.3.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.
Files changed (54) hide show
  1. package/README.md +245 -0
  2. package/dist/__tests__/context.test.d.ts +2 -0
  3. package/dist/__tests__/context.test.d.ts.map +1 -0
  4. package/dist/__tests__/context.test.js +441 -0
  5. package/dist/__tests__/context.test.js.map +1 -0
  6. package/dist/__tests__/fixtures/messages.d.ts +9 -0
  7. package/dist/__tests__/fixtures/messages.d.ts.map +1 -0
  8. package/dist/__tests__/fixtures/messages.js +44 -0
  9. package/dist/__tests__/fixtures/messages.js.map +1 -0
  10. package/dist/__tests__/token-counter.test.d.ts +2 -0
  11. package/dist/__tests__/token-counter.test.d.ts.map +1 -0
  12. package/dist/__tests__/token-counter.test.js +123 -0
  13. package/dist/__tests__/token-counter.test.js.map +1 -0
  14. package/dist/__tests__/types.test.d.ts +2 -0
  15. package/dist/__tests__/types.test.d.ts.map +1 -0
  16. package/dist/__tests__/types.test.js +92 -0
  17. package/dist/__tests__/types.test.js.map +1 -0
  18. package/dist/budget.d.ts +16 -0
  19. package/dist/budget.d.ts.map +1 -0
  20. package/dist/budget.js +31 -0
  21. package/dist/budget.js.map +1 -0
  22. package/dist/context.d.ts +16 -0
  23. package/dist/context.d.ts.map +1 -0
  24. package/dist/context.js +245 -0
  25. package/dist/context.js.map +1 -0
  26. package/dist/eviction.d.ts +14 -0
  27. package/dist/eviction.d.ts.map +1 -0
  28. package/dist/eviction.js +78 -0
  29. package/dist/eviction.js.map +1 -0
  30. package/dist/index.d.ts +9 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +23 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/prompt.d.ts +6 -0
  35. package/dist/prompt.d.ts.map +1 -0
  36. package/dist/prompt.js +28 -0
  37. package/dist/prompt.js.map +1 -0
  38. package/dist/serialization.d.ts +12 -0
  39. package/dist/serialization.d.ts.map +1 -0
  40. package/dist/serialization.js +33 -0
  41. package/dist/serialization.js.map +1 -0
  42. package/dist/summarization.d.ts +13 -0
  43. package/dist/summarization.d.ts.map +1 -0
  44. package/dist/summarization.js +23 -0
  45. package/dist/summarization.js.map +1 -0
  46. package/dist/token-counter.d.ts +7 -0
  47. package/dist/token-counter.d.ts.map +1 -0
  48. package/dist/token-counter.js +41 -0
  49. package/dist/token-counter.js.map +1 -0
  50. package/dist/types.d.ts +72 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +3 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,245 @@
1
+ # sliding-context
2
+
3
+ Provider-agnostic sliding window context manager for LLMs. Keeps your conversation within a token budget by evicting old messages, summarizing context, and preserving tool call atomicity.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install sliding-context
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```ts
14
+ import { createSlidingContext } from 'sliding-context';
15
+ import type { Message, SlidingContextOptions } from 'sliding-context';
16
+
17
+ const ctx = createSlidingContext({
18
+ tokenBudget: 8192,
19
+ systemPrompt: 'You are a helpful assistant.',
20
+ summarizer: async (messages, existingSummary) => {
21
+ // Call your LLM to summarize evicted messages
22
+ return 'Summary of the conversation so far...';
23
+ },
24
+ strategy: 'incremental',
25
+ });
26
+
27
+ // Add messages as the conversation progresses
28
+ await ctx.addMessage({ role: 'user', content: 'Hello!' });
29
+ await ctx.addMessage({ role: 'assistant', content: 'Hi there!' });
30
+
31
+ // Get the current context window (fits within tokenBudget)
32
+ const messages = ctx.getMessages();
33
+ // => [systemPrompt, ...recentMessages] or [systemPrompt, summary, ...recentMessages]
34
+
35
+ // Inspect token usage
36
+ const breakdown = ctx.getTokenBreakdown();
37
+ // => { system: 12, anchor: 0, summary: 0, recent: 10, total: 22 }
38
+ ```
39
+
40
+ ## API
41
+
42
+ ### `createSlidingContext(options): SlidingContext`
43
+
44
+ Creates a new sliding context manager.
45
+
46
+ ```ts
47
+ import { createSlidingContext } from 'sliding-context';
48
+
49
+ const ctx = createSlidingContext({ tokenBudget: 4096 });
50
+ ```
51
+
52
+ Throws `RangeError` if `tokenBudget < 100` or is not finite.
53
+
54
+ ### SlidingContext Methods
55
+
56
+ #### `addMessage(message: Message): Promise<void>`
57
+
58
+ Adds a message to the context. After each addition, checks if the token budget is exceeded and evicts oldest messages if needed. If a summarizer is configured and the pending eviction buffer reaches the summarization threshold, the summarizer is invoked automatically.
59
+
60
+ ```ts
61
+ await ctx.addMessage({ role: 'user', content: 'What time is it?' });
62
+ ```
63
+
64
+ #### `getMessages(): Message[]`
65
+
66
+ Returns the current message array in order:
67
+ 1. System prompt (if configured)
68
+ 2. Anchor messages (if set)
69
+ 3. Pending eviction buffer (messages evicted but not yet summarized)
70
+ 4. Summary message (if summarization has occurred)
71
+ 5. Recent messages (verbatim, newest last)
72
+
73
+ This array is always ready to send directly to any LLM API.
74
+
75
+ #### `getSummary(): string | undefined`
76
+
77
+ Returns the current rolling summary text, or `undefined` if summarization has not yet occurred.
78
+
79
+ #### `getTokenCount(): number`
80
+
81
+ Returns the total token count across all zones (system + anchor + summary + recent + pending).
82
+
83
+ #### `getTokenBreakdown(): { system, anchor, summary, recent, total }`
84
+
85
+ Returns a breakdown of token usage by zone.
86
+
87
+ ```ts
88
+ const bd = ctx.getTokenBreakdown();
89
+ // { system: 12, anchor: 0, summary: 150, recent: 800, total: 962 }
90
+ ```
91
+
92
+ #### `getRecentMessageCount(): number`
93
+
94
+ Returns the count of recent messages (including pending buffer, excluding system/anchor/summary).
95
+
96
+ #### `getTotalMessageCount(): number`
97
+
98
+ Returns the total count of all messages including system prompt, anchor, summary, pending buffer, and recent.
99
+
100
+ #### `setAnchor(messages: Message[]): void`
101
+
102
+ Replaces the anchor message set. Anchor messages are always included verbatim after the system prompt and are never evicted.
103
+
104
+ #### `setTokenBudget(budget: number): void`
105
+
106
+ Updates the token budget dynamically and immediately re-enforces eviction if needed. Throws `RangeError` if `budget < 100`.
107
+
108
+ #### `clear(): void`
109
+
110
+ Resets recent messages, pending buffer, and summary to empty. The system prompt and anchor messages are retained (anchor is reset to initial options value).
111
+
112
+ #### `serialize(): ContextState`
113
+
114
+ Returns a serializable snapshot of the current context state (version 1). Does not include function-valued options (summarizer, tokenCounter, hooks).
115
+
116
+ ### `serializeContext(ctx): string`
117
+
118
+ Serializes a `SlidingContext` to a JSON string suitable for persistence.
119
+
120
+ ```ts
121
+ import { serializeContext } from 'sliding-context';
122
+
123
+ const json = serializeContext(ctx);
124
+ await redis.set('ctx:user123', json);
125
+ ```
126
+
127
+ ### `restoreSlidingContext(data, options): SlidingContext`
128
+
129
+ Restores a `SlidingContext` from a JSON string. Re-supply function-valued options since they cannot be serialized.
130
+
131
+ ```ts
132
+ import { restoreSlidingContext } from 'sliding-context';
133
+
134
+ const json = await redis.get('ctx:user123');
135
+ const ctx = restoreSlidingContext(json, {
136
+ tokenBudget: 4096,
137
+ summarizer: myLLMSummarizer,
138
+ });
139
+ ```
140
+
141
+ ## Configuration Options
142
+
143
+ | Option | Type | Default | Description |
144
+ |--------|------|---------|-------------|
145
+ | `tokenBudget` | `number` | *required* | Maximum total tokens for the context window |
146
+ | `systemPrompt` | `string` | — | System prompt (never evicted or summarized) |
147
+ | `summarizer` | `Summarizer` | — | Async function to summarize evicted messages |
148
+ | `strategy` | `SummarizationStrategy` | `'incremental'` | Summarization strategy |
149
+ | `maxSummaryTokens` | `number` | `floor(budget * 0.3)` | Max tokens for the summary zone |
150
+ | `minRecentTokens` | `number` | `floor(budget * 0.3)` | Minimum tokens reserved for recent messages |
151
+ | `summarizeThresholdTokens` | `number` | `floor(budget * 0.1)` | Token count in pending buffer to trigger summarization |
152
+ | `summarizeThresholdMessages` | `number` | `6` | Message count in pending buffer to trigger summarization |
153
+ | `tokenCounter` | `TokenCounter` | `approximateTokenCounter` | Custom token counting function |
154
+ | `messageOverhead` | `number` | `4` | Per-message token overhead |
155
+ | `summaryRole` | `SummaryRole` | `'system'` | Role for injected summary messages |
156
+ | `maxSummaryRounds` | `number` | `5` | Max summarization rounds before stopping |
157
+ | `anchor` | `Message[]` | — | Initial anchor messages |
158
+ | `maxAnchorTokens` | `number` | `floor(maxSummaryTokens * 0.4)` | Max tokens for anchor section |
159
+ | `hooks` | `EventHooks` | — | Event callbacks for observability |
160
+
161
+ ## Token Counting
162
+
163
+ Built-in approximate token counter and message token counting utilities:
164
+
165
+ ```ts
166
+ import {
167
+ approximateTokenCounter,
168
+ countMessageTokens,
169
+ DEFAULT_MESSAGE_OVERHEAD,
170
+ } from 'sliding-context';
171
+ ```
172
+
173
+ ### `approximateTokenCounter(text: string): number`
174
+
175
+ Returns `Math.ceil(text.length / 4)`. Approximates GPT-style tokenization. Returns `0` for empty input.
176
+
177
+ ### `countMessageTokens(message, tokenCounter, messageOverhead): number`
178
+
179
+ Counts tokens for a single `Message`, including content, `tool_calls` JSON, `tool_call_id`, and per-message overhead.
180
+
181
+ ### `DEFAULT_MESSAGE_OVERHEAD`
182
+
183
+ The default per-message token overhead: `4`.
184
+
185
+ ## Event Hooks
186
+
187
+ ```ts
188
+ const ctx = createSlidingContext({
189
+ tokenBudget: 4096,
190
+ hooks: {
191
+ onEvict: (messages, reason) => console.log('Evicted', messages.length, 'messages:', reason),
192
+ onSummarize: (input, existing, summary, durationMs) =>
193
+ console.log('Summarized in', durationMs, 'ms'),
194
+ onBudgetExceeded: (total, budget) =>
195
+ console.log('Budget exceeded:', total, '/', budget),
196
+ onSummaryCompressed: (old, next) => console.log('Summary compressed'),
197
+ },
198
+ });
199
+ ```
200
+
201
+ ## Serialization / Deserialization
202
+
203
+ ```ts
204
+ import { serialize, deserialize } from 'sliding-context';
205
+
206
+ // Low-level: serialize a ContextState object to JSON string.
207
+ const json = serialize(ctx.serialize());
208
+
209
+ // Low-level: parse a JSON string back to ContextState.
210
+ const state = deserialize(json); // throws on version mismatch
211
+ ```
212
+
213
+ ## Types
214
+
215
+ ```ts
216
+ import type {
217
+ Message,
218
+ ToolCall,
219
+ TokenCounter,
220
+ Summarizer,
221
+ SummarizationStrategy,
222
+ SummaryRole,
223
+ EventHooks,
224
+ SlidingContextOptions,
225
+ ContextState,
226
+ SlidingContext,
227
+ } from 'sliding-context';
228
+ ```
229
+
230
+ | Type | Description |
231
+ |------|-------------|
232
+ | `Message` | Chat message with `role`, `content`, optional `tool_calls`, `tool_call_id`, `name` |
233
+ | `ToolCall` | Tool/function call descriptor with `id`, `type`, and `function` |
234
+ | `TokenCounter` | `(text: string) => number` — pluggable token counting function |
235
+ | `Summarizer` | `(messages: Message[], existingSummary?: string) => Promise<string>` |
236
+ | `SummarizationStrategy` | `'incremental' \| 'rolling' \| 'anchored'` |
237
+ | `SummaryRole` | `'system' \| 'user'` — role used for injected summary messages |
238
+ | `EventHooks` | Callbacks for eviction, summarization, budget, and compression events |
239
+ | `SlidingContextOptions` | Full configuration for the context manager |
240
+ | `ContextState` | Serializable snapshot of context state (version 1) |
241
+ | `SlidingContext` | Public interface for the context manager instance |
242
+
243
+ ## License
244
+
245
+ MIT
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/context.test.ts"],"names":[],"mappings":""}