topic-memory 0.1.0 → 0.1.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/CHANGELOG.md +13 -0
- package/README.md +74 -264
- package/README.zh-CN.md +72 -327
- package/docs/EVALUATION.md +65 -0
- package/docs/TRYOUT.md +24 -0
- package/docs/USAGE.md +330 -325
- package/docs/USAGE.zh-CN.md +348 -343
- package/docs/evaluation/scripted.json +103 -0
- package/examples/chat.mjs +44 -0
- package/examples/minimal-node.mjs +15 -0
- package/examples/provider.mjs +46 -0
- package/examples/scenario.mjs +63 -0
- package/package.json +18 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- Make npm installation the primary entry point and refresh English and Chinese onboarding.
|
|
6
|
+
- Add a browser walkthrough using real SDK retrieval with explicitly scripted model responses.
|
|
7
|
+
- Add an executable live-model chat example with recent-context injection and a seeded conversation.
|
|
8
|
+
- Add transparent scripted checks and a real-model comparison harness with raw answers, failures, latency and reported token usage.
|
|
9
|
+
- Keep theoretical capacity calculations separate from measured claims.
|
|
10
|
+
- Fix fresh-consumer validation on Windows and include runnable examples in the package.
|
|
11
|
+
- Add first-use feedback and a small user-testing guide.
|
|
12
|
+
|
|
13
|
+
The SDK's core retrieval algorithm is unchanged. No real-model performance improvement is claimed by this release.
|
package/README.md
CHANGED
|
@@ -1,326 +1,136 @@
|
|
|
1
1
|
# Topic Memory
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Find the topic. Reopen the original conversation.**
|
|
4
4
|
|
|
5
|
-
[简体中文](./README.zh-CN.md) · [Integration guide](./docs/USAGE.md) · [
|
|
5
|
+
[简体中文](./README.zh-CN.md) · [Integration guide](./docs/USAGE.md) · [Evaluation](./docs/EVALUATION.md) · [Architecture](./docs/ARCHITECTURE.md)
|
|
6
6
|
|
|
7
|
-
Topic Memory
|
|
7
|
+
Topic Memory is a TypeScript SDK for chat apps and agents that need older conversation details. It groups history into topics, selects relevant topics for a new question, and returns the original exchanges as `memoryContext` for your existing model.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
For example, a user chooses **Ueno, 14,000 yen per night** for a Tokyo hotel. After the conversation moves on to food, a project release and astronomy, the SDK can reopen the hotel discussion when that plan becomes relevant again. The source text stays available for inspection.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
> **What it is not:** a chatbot, a model provider, or a replacement for your Main LLM.
|
|
13
|
-
|
|
14
|
-
## What can it do?
|
|
15
|
-
|
|
16
|
-
Topic Memory is useful when your AI needs to remember things that happened far earlier in a conversation, for example:
|
|
17
|
-
|
|
18
|
-
- user preferences, recurring habits, names, places, and personal context;
|
|
19
|
-
- decisions made hundreds or thousands of exchanges ago;
|
|
20
|
-
- project history, requirements, previous attempts, and unresolved tasks;
|
|
21
|
-
- earlier events where the exact wording or timing may matter;
|
|
22
|
-
- long-running conversations where replaying the full transcript would become expensive or exceed the model context window.
|
|
23
|
-
|
|
24
|
-
Unlike a single rolling summary, Topic Memory keeps the canonical transcript. A retrieved topic can therefore reopen the original exchanges behind that topic instead of relying only on a compressed summary.
|
|
25
|
-
|
|
26
|
-
## The model roles — important
|
|
27
|
-
|
|
28
|
-
There are two layers, and they should not be confused:
|
|
29
|
-
|
|
30
|
-
### Memory LLM
|
|
31
|
-
|
|
32
|
-
The **Memory LLM** powers the memory system itself.
|
|
33
|
-
|
|
34
|
-
By default, the same Memory LLM instance performs two jobs:
|
|
35
|
-
|
|
36
|
-
1. **Topic Worker** — organizes completed conversation exchanges into topic instances and writes a lightweight topic index.
|
|
37
|
-
2. **Memory Selector** — reads the current user message, recent context, and Topic Directory, then chooses up to three older topics to reopen.
|
|
38
|
-
|
|
39
|
-
You may configure separate models for these two jobs, but most integrations can use one Memory LLM for both.
|
|
40
|
-
|
|
41
|
-
### Your Main LLM
|
|
42
|
-
|
|
43
|
-
Your **Main LLM** is still your own user-facing chat model.
|
|
44
|
-
|
|
45
|
-
Topic Memory never generates the final reply and never takes ownership of your Main LLM. It returns `memoryContext`; your application decides how to inject that context into the Main LLM prompt.
|
|
46
|
-
|
|
47
|
-
```text
|
|
48
|
-
User message
|
|
49
|
-
│
|
|
50
|
-
├─→ Topic Memory retrieves relevant old context
|
|
51
|
-
│ ├─ Recent 5 completed exchanges
|
|
52
|
-
│ ├─ Topic Directory
|
|
53
|
-
│ └─ Opened Topic Packets
|
|
54
|
-
│
|
|
55
|
-
└─→ Your Main LLM receives memoryContext and writes the reply
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## How it works
|
|
59
|
-
|
|
60
|
-
The v0.1 pipeline is intentionally simple:
|
|
61
|
-
|
|
62
|
-
1. **Canonical Transcript**
|
|
63
|
-
Every exchange is stored as `pending`, `completed`, or `failed`. The full transcript remains the source of truth.
|
|
64
|
-
|
|
65
|
-
2. **Topic Worker**
|
|
66
|
-
After at least six completed exchanges exist, the Topic Worker processes the active tail of the conversation. It groups related exchanges into topic instances and stores:
|
|
67
|
-
- topic keywords;
|
|
68
|
-
- retrieval terms;
|
|
69
|
-
- exact transcript spans;
|
|
70
|
-
- topic status and timing metadata.
|
|
71
|
-
|
|
72
|
-
3. **Topic Directory**
|
|
73
|
-
The SDK builds a compact index of available topics. The Main LLM does not need the entire historical transcript just to decide what to remember.
|
|
74
|
-
|
|
75
|
-
4. **Memory Selector**
|
|
76
|
-
Before a new reply, the selector sees the current message, the latest five completed exchanges, and the Topic Directory. It selects at most three relevant topic IDs.
|
|
77
|
-
|
|
78
|
-
5. **Opened Topic Packets**
|
|
79
|
-
Selected topics are reopened from their exact Canonical Transcript spans. When timing matters, exchange timestamps can be included.
|
|
80
|
-
|
|
81
|
-
6. **Main LLM**
|
|
82
|
-
Topic Memory returns the restored material as `memoryContext`. Your own Main LLM uses it only when relevant to the current message.
|
|
11
|
+
## Install
|
|
83
12
|
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
│
|
|
87
|
-
▼
|
|
88
|
-
Topic Worker
|
|
89
|
-
│
|
|
90
|
-
▼
|
|
91
|
-
Topic Store ─────→ Topic Directory
|
|
92
|
-
│
|
|
93
|
-
Current message ─────────┤
|
|
94
|
-
Recent 5 exchanges ──────┤
|
|
95
|
-
▼
|
|
96
|
-
Memory Selector
|
|
97
|
-
│
|
|
98
|
-
up to 3 topic IDs
|
|
99
|
-
│
|
|
100
|
-
▼
|
|
101
|
-
Open Topic Packets
|
|
102
|
-
│
|
|
103
|
-
▼
|
|
104
|
-
memoryContext
|
|
105
|
-
│
|
|
106
|
-
▼
|
|
107
|
-
Your Main LLM
|
|
13
|
+
```bash
|
|
14
|
+
npm install topic-memory
|
|
108
15
|
```
|
|
109
16
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
## Why not just keep appending the entire transcript?
|
|
113
|
-
|
|
114
|
-
A large context window is still a finite prompt budget, and long-context models do not always use information uniformly well across very long inputs. Topic Memory instead separates **how much history you store** from **how much history you send on one request**.
|
|
115
|
-
|
|
116
|
-
Under one illustrative 128k-context workload, a raw-history design with about 200 tokens per completed exchange reaches roughly 600 exchanges when ~120k tokens are reserved for conversation history. With Topic Memory, a 5,000-exchange archive grouped at roughly eight exchanges per topic can be represented by a Topic Directory plus at most three reopened topics in roughly 43k–45k memory-related tokens under the assumptions documented in the appendix.
|
|
17
|
+
ES modules; SDK requires Node.js 18+. The live example commands below require **Node.js 20.6+** for `--env-file`. The published `topic-memory@0.1.0` was installed and exercised in a fresh Node 24 project on 2026-09-13.
|
|
117
18
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
**This is a theoretical sizing example, not a guaranteed hard limit or benchmark result.** Actual capacity depends on message length, tokenization, topic size, model context window, and how many topics accumulate. v0.1's main scaling constraint is that the Topic Directory grows with the number of topics.
|
|
121
|
-
|
|
122
|
-
See [Architecture & capacity notes](./docs/ARCHITECTURE.md) for the formula, assumptions, caveats, and related research.
|
|
123
|
-
|
|
124
|
-
## Install
|
|
125
|
-
|
|
126
|
-
This repository is currently distributed as source code. Clone it directly or build a tarball with `npm pack`.
|
|
19
|
+
## Try the walkthrough — no API key
|
|
127
20
|
|
|
128
21
|
```bash
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
npm
|
|
22
|
+
git clone https://github.com/ziningshu-code/memory-system-mvp.git
|
|
23
|
+
cd memory-system-mvp
|
|
24
|
+
npm ci
|
|
25
|
+
npm run demo
|
|
132
26
|
```
|
|
133
27
|
|
|
134
|
-
|
|
28
|
+
The demo loads **24 synthetic exchanges**, retrieves the old hotel topic, and prints the exact restored conversation. It uses the real SDK with **scripted worker and selector responses**. This is a mechanics demo, not evidence of real-model retrieval quality.
|
|
135
29
|
|
|
136
|
-
|
|
30
|
+
To open the interactive, English/Chinese browser version:
|
|
137
31
|
|
|
138
32
|
```bash
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
MEMORY_LLM_MODEL=your-memory-model
|
|
33
|
+
npm run build:site
|
|
34
|
+
npm run preview
|
|
142
35
|
```
|
|
143
36
|
|
|
144
|
-
|
|
37
|
+
Visit `http://127.0.0.1:4173`. Switch between hotel, food, project and missing-information questions; inspect the topic selection and original source text. Everything in this walkthrough runs locally in the browser, with no credentials or model requests.
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
145
40
|
|
|
146
|
-
|
|
41
|
+
Your application supplies a memory model and keeps its existing user-facing model. Pass **both** recent conversation and retrieved older evidence to that model:
|
|
147
42
|
|
|
148
43
|
```ts
|
|
149
|
-
import {
|
|
150
|
-
createMemory,
|
|
151
|
-
createOpenAICompatibleMemoryLlm,
|
|
152
|
-
InMemoryStorage,
|
|
153
|
-
} from 'topic-memory';
|
|
154
|
-
|
|
155
|
-
const memoryLlm = createOpenAICompatibleMemoryLlm({
|
|
156
|
-
baseUrl: process.env.MEMORY_LLM_BASE_URL!,
|
|
157
|
-
apiKey: process.env.MEMORY_LLM_API_KEY,
|
|
158
|
-
model: process.env.MEMORY_LLM_MODEL!,
|
|
159
|
-
});
|
|
44
|
+
import { createMemory, createOpenAICompatibleMemoryLlm, InMemoryStorage } from 'topic-memory';
|
|
160
45
|
|
|
161
46
|
const memory = createMemory({
|
|
162
47
|
storage: new InMemoryStorage(),
|
|
163
|
-
llm:
|
|
48
|
+
llm: createOpenAICompatibleMemoryLlm({
|
|
49
|
+
baseUrl: process.env.MEMORY_LLM_BASE_URL!,
|
|
50
|
+
apiKey: process.env.MEMORY_LLM_API_KEY,
|
|
51
|
+
model: process.env.MEMORY_LLM_MODEL!,
|
|
52
|
+
}),
|
|
164
53
|
});
|
|
165
54
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
const retrieved = await memory.retrieve({ userMessage });
|
|
171
|
-
|
|
172
|
-
// This is YOUR existing user-facing model call.
|
|
173
|
-
const assistantReply = await myOwnMainLlm({
|
|
174
|
-
userMessage,
|
|
175
|
-
memoryContext: retrieved.memoryContext,
|
|
176
|
-
recentContext: retrieved.recentContext,
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
await memory.completeExchange({
|
|
180
|
-
exchangeId: pending.id,
|
|
181
|
-
assistantText: assistantReply,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
await memory.maybeRunTopicWorker();
|
|
185
|
-
return assistantReply;
|
|
186
|
-
} catch (error) {
|
|
187
|
-
await memory.failExchange({
|
|
188
|
-
exchangeId: pending.id,
|
|
189
|
-
failureReason: error instanceof Error ? error.message : String(error),
|
|
190
|
-
});
|
|
191
|
-
throw error;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
```
|
|
55
|
+
const pending = await memory.begin(userMessage);
|
|
56
|
+
const context = await memory.retrieve({ userMessage });
|
|
195
57
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
```text
|
|
203
|
-
User message
|
|
204
|
-
→ memory.begin()
|
|
205
|
-
→ memory.retrieve()
|
|
206
|
-
→ Your Main LLM
|
|
207
|
-
→ memory.completeExchange()
|
|
208
|
-
→ memory.maybeRunTopicWorker()
|
|
58
|
+
// Call your existing model with current input, context.recentContext,
|
|
59
|
+
// and context.memoryContext. The complete runnable integration is linked below.
|
|
60
|
+
const assistantReply = await yourMainModel(userMessage, context);
|
|
61
|
+
await memory.completeExchange({ exchangeId: pending.id, assistantText: assistantReply });
|
|
62
|
+
await memory.maybeRunTopicWorker();
|
|
209
63
|
```
|
|
210
64
|
|
|
211
|
-
|
|
65
|
+
`yourMainModel` is application-owned. For a **complete executable integration**, use [examples/chat.mjs](./examples/chat.mjs) and its [provider helper](./examples/provider.mjs); they include the actual model request, recent-context injection, errors, and the pending/completed/failed lifecycle.
|
|
212
66
|
|
|
213
|
-
##
|
|
67
|
+
## Try it with a real model
|
|
214
68
|
|
|
215
|
-
|
|
69
|
+
In the cloned repository, copy `.env.example` to `.env` and set your OpenAI-compatible endpoint, model and API key. Keep this file local. The examples send your input to that provider and may use paid credits.
|
|
216
70
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
- `memoryContext` may be empty because no topic has been created yet.
|
|
220
|
-
|
|
221
|
-
This is expected behavior.
|
|
222
|
-
|
|
223
|
-
## Public API
|
|
224
|
-
|
|
225
|
-
```ts
|
|
226
|
-
createMemory
|
|
227
|
-
MemoryEngine
|
|
228
|
-
InMemoryStorage
|
|
229
|
-
IndexedDbMemoryStorage
|
|
230
|
-
createOpenAICompatibleMemoryLlm
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
Main engine methods:
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
begin
|
|
237
|
-
beginExchange
|
|
238
|
-
completeExchange
|
|
239
|
-
failExchange
|
|
240
|
-
maybeRunTopicWorker
|
|
241
|
-
retrieve
|
|
242
|
-
listExchanges
|
|
243
|
-
listTopics
|
|
244
|
-
getLatestTopicWorkerRun
|
|
245
|
-
clear
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
`retrieve()` returns:
|
|
249
|
-
|
|
250
|
-
```ts
|
|
251
|
-
{
|
|
252
|
-
recentContext,
|
|
253
|
-
topicDirectory,
|
|
254
|
-
selectedTopicIds,
|
|
255
|
-
openedTopicPackets,
|
|
256
|
-
memoryContext,
|
|
257
|
-
needsTimeMetadata,
|
|
258
|
-
trace,
|
|
259
|
-
}
|
|
71
|
+
```bash
|
|
72
|
+
npm run demo:chat
|
|
260
73
|
```
|
|
261
74
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
For demos and tests:
|
|
75
|
+
Or preload the public synthetic conversation, then ask about the hotel plan:
|
|
265
76
|
|
|
266
|
-
```
|
|
267
|
-
|
|
77
|
+
```bash
|
|
78
|
+
npm run demo:chat -- --seed
|
|
268
79
|
```
|
|
269
80
|
|
|
270
|
-
|
|
81
|
+
The seeded conversation is synthetic; topic organization, selection and final answers use the **real configured model**. Type `/exit` to leave. Example storage is in-memory and resets when the process exits.
|
|
271
82
|
|
|
272
|
-
|
|
273
|
-
new IndexedDbMemoryStorage()
|
|
274
|
-
```
|
|
83
|
+
## What is verified?
|
|
275
84
|
|
|
276
|
-
|
|
85
|
+
| Check | Status / meaning |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| Published npm package | Fresh installation and old-text recovery checked on Node 24 |
|
|
88
|
+
| SDK lifecycle, storage, adapter and retrieval tests | Automated checks in CI |
|
|
89
|
+
| Four scripted walkthrough cases | Old-text recovery and empty memory for a missing fact; [recorded output](./docs/evaluation/scripted.json) |
|
|
90
|
+
| Real-model comparison | Runnable harness provided; **no real-model performance result claimed** |
|
|
277
91
|
|
|
278
|
-
|
|
92
|
+
Run `npm run evaluate` for the scripted checks or `npm run evaluate:live` for a small, real-model comparison of **recent five exchanges**, **full transcript**, and **topic retrieval**. The live report includes raw answers, literal fact-check scores, request latency, and provider token counts when available. [Read the method and limitations](./docs/EVALUATION.md).
|
|
279
93
|
|
|
280
|
-
|
|
94
|
+
The earlier **8.3×** figure is an illustrative capacity calculation, **not a measured improvement in accuracy or usable memory**. Its assumptions remain in [Architecture & capacity notes](./docs/ARCHITECTURE.md).
|
|
281
95
|
|
|
282
|
-
|
|
283
|
-
createMemory({ storage, llm: memoryLlm })
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
Advanced: split the two memory jobs.
|
|
96
|
+
## How it works
|
|
287
97
|
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
selector: selectorLlm,
|
|
293
|
-
});
|
|
98
|
+
```text
|
|
99
|
+
Saved conversation → Topic Worker → Topic directory
|
|
100
|
+
New question + recent conversation + directory → Selector
|
|
101
|
+
Selected topic IDs → Original transcript spans → Your model
|
|
294
102
|
```
|
|
295
103
|
|
|
296
|
-
|
|
104
|
+
One memory LLM can handle both worker and selector; separate models are also supported. Topic Memory does not generate the final answer or replace your main model. It requires no embeddings or vector database.
|
|
297
105
|
|
|
298
|
-
##
|
|
106
|
+
## Current boundaries
|
|
299
107
|
|
|
300
|
-
- **
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
304
|
-
-
|
|
108
|
+
- Topic creation starts after **six completed exchanges**. Before that, recent context still works and long-term memory can be empty.
|
|
109
|
+
- The selector opens **up to three topics**. Selection can miss evidence; inspect `retrieve().trace`.
|
|
110
|
+
- A selector error falls back to empty long-term memory. Your app must still handle request timeouts and storage failures.
|
|
111
|
+
- `InMemoryStorage` is temporary. `IndexedDbMemoryStorage` persists in a browser. Backend persistence and user/conversation isolation require your own `MemoryStorage` implementation.
|
|
112
|
+
- Serialize turns and worker runs for a given memory store; concurrent writers are not coordinated by the SDK.
|
|
113
|
+
- The full topic directory grows with the archive. v0.1 does not enforce a total token budget.
|
|
114
|
+
- Treat historical text as untrusted evidence, not system instructions.
|
|
305
115
|
|
|
306
|
-
|
|
116
|
+
[Integration guide](./docs/USAGE.md) · [Public API and architecture](./docs/ARCHITECTURE.md)
|
|
307
117
|
|
|
308
|
-
|
|
118
|
+
## Development checks
|
|
309
119
|
|
|
310
120
|
```bash
|
|
121
|
+
npm ci
|
|
311
122
|
npm run build
|
|
312
123
|
npm run typecheck
|
|
313
124
|
npm test
|
|
314
|
-
npm pack --dry-run
|
|
315
125
|
npm run smoke:consumer
|
|
126
|
+
npm run evaluate
|
|
127
|
+
npm run build:site
|
|
316
128
|
```
|
|
317
129
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
## Non-goals
|
|
130
|
+
## Help test it
|
|
321
131
|
|
|
322
|
-
|
|
132
|
+
Try one real conversation from your own development workflow, then [report what happened](https://github.com/ziningshu-code/memory-system-mvp/issues/new?template=try-it.yml): what you asked it to remember, whether you could install it, and the first point where retrieval helped or failed. Remove credentials and private conversation details from public reports.
|
|
323
133
|
|
|
324
134
|
## License
|
|
325
135
|
|
|
326
|
-
MIT
|
|
136
|
+
MIT
|