@limo-labs/deity 0.1.0-alpha.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 +519 -0
- package/dist/src/component.d.ts +19 -0
- package/dist/src/component.d.ts.map +1 -0
- package/dist/src/component.js +2 -0
- package/dist/src/component.js.map +1 -0
- package/dist/src/context/context-scope.d.ts +24 -0
- package/dist/src/context/context-scope.d.ts.map +1 -0
- package/dist/src/context/context-scope.js +19 -0
- package/dist/src/context/context-scope.js.map +1 -0
- package/dist/src/context/memory-view.d.ts +62 -0
- package/dist/src/context/memory-view.d.ts.map +1 -0
- package/dist/src/context/memory-view.js +104 -0
- package/dist/src/context/memory-view.js.map +1 -0
- package/dist/src/context/scoped-context.d.ts +51 -0
- package/dist/src/context/scoped-context.d.ts.map +1 -0
- package/dist/src/context/scoped-context.js +74 -0
- package/dist/src/context/scoped-context.js.map +1 -0
- package/dist/src/context/scoped-execution-context.d.ts +55 -0
- package/dist/src/context/scoped-execution-context.d.ts.map +1 -0
- package/dist/src/context/scoped-execution-context.js +78 -0
- package/dist/src/context/scoped-execution-context.js.map +1 -0
- package/dist/src/conversation/conversation-manager.d.ts +272 -0
- package/dist/src/conversation/conversation-manager.d.ts.map +1 -0
- package/dist/src/conversation/conversation-manager.js +11 -0
- package/dist/src/conversation/conversation-manager.js.map +1 -0
- package/dist/src/conversation/conversation-pruner.d.ts +190 -0
- package/dist/src/conversation/conversation-pruner.d.ts.map +1 -0
- package/dist/src/conversation/conversation-pruner.js +274 -0
- package/dist/src/conversation/conversation-pruner.js.map +1 -0
- package/dist/src/conversation/conversation-tree.d.ts +185 -0
- package/dist/src/conversation/conversation-tree.d.ts.map +1 -0
- package/dist/src/conversation/conversation-tree.js +288 -0
- package/dist/src/conversation/conversation-tree.js.map +1 -0
- package/dist/src/conversation/file-conversation-store.d.ts +93 -0
- package/dist/src/conversation/file-conversation-store.d.ts.map +1 -0
- package/dist/src/conversation/file-conversation-store.js +284 -0
- package/dist/src/conversation/file-conversation-store.js.map +1 -0
- package/dist/src/conversation/in-memory-conversation-store.d.ts +36 -0
- package/dist/src/conversation/in-memory-conversation-store.d.ts.map +1 -0
- package/dist/src/conversation/in-memory-conversation-store.js +146 -0
- package/dist/src/conversation/in-memory-conversation-store.js.map +1 -0
- package/dist/src/copilot-adapter.d.ts +33 -0
- package/dist/src/copilot-adapter.d.ts.map +1 -0
- package/dist/src/copilot-adapter.js +119 -0
- package/dist/src/copilot-adapter.js.map +1 -0
- package/dist/src/file-trace-enhanced.d.ts +123 -0
- package/dist/src/file-trace-enhanced.d.ts.map +1 -0
- package/dist/src/file-trace-enhanced.js +177 -0
- package/dist/src/file-trace-enhanced.js.map +1 -0
- package/dist/src/file-trace.d.ts +24 -0
- package/dist/src/file-trace.d.ts.map +1 -0
- package/dist/src/file-trace.js +60 -0
- package/dist/src/file-trace.js.map +1 -0
- package/dist/src/index.d.ts +63 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +40 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/json-store.d.ts +27 -0
- package/dist/src/json-store.d.ts.map +1 -0
- package/dist/src/json-store.js +93 -0
- package/dist/src/json-store.js.map +1 -0
- package/dist/src/llm.d.ts +35 -0
- package/dist/src/llm.d.ts.map +1 -0
- package/dist/src/llm.js +2 -0
- package/dist/src/llm.js.map +1 -0
- package/dist/src/memory/cold-storage.d.ts +60 -0
- package/dist/src/memory/cold-storage.d.ts.map +1 -0
- package/dist/src/memory/cold-storage.js +132 -0
- package/dist/src/memory/cold-storage.js.map +1 -0
- package/dist/src/memory/compression.d.ts +161 -0
- package/dist/src/memory/compression.d.ts.map +1 -0
- package/dist/src/memory/compression.js +193 -0
- package/dist/src/memory/compression.js.map +1 -0
- package/dist/src/memory/hot-memory.d.ts +69 -0
- package/dist/src/memory/hot-memory.d.ts.map +1 -0
- package/dist/src/memory/hot-memory.js +116 -0
- package/dist/src/memory/hot-memory.js.map +1 -0
- package/dist/src/memory/memory-budget.d.ts +162 -0
- package/dist/src/memory/memory-budget.d.ts.map +1 -0
- package/dist/src/memory/memory-budget.js +241 -0
- package/dist/src/memory/memory-budget.js.map +1 -0
- package/dist/src/memory/memory-config.d.ts +419 -0
- package/dist/src/memory/memory-config.d.ts.map +1 -0
- package/dist/src/memory/memory-config.js +297 -0
- package/dist/src/memory/memory-config.js.map +1 -0
- package/dist/src/memory/prefetcher.d.ts +137 -0
- package/dist/src/memory/prefetcher.d.ts.map +1 -0
- package/dist/src/memory/prefetcher.js +186 -0
- package/dist/src/memory/prefetcher.js.map +1 -0
- package/dist/src/memory/tiered-memory.d.ts +116 -0
- package/dist/src/memory/tiered-memory.d.ts.map +1 -0
- package/dist/src/memory/tiered-memory.js +215 -0
- package/dist/src/memory/tiered-memory.js.map +1 -0
- package/dist/src/memory/warm-storage.d.ts +74 -0
- package/dist/src/memory/warm-storage.d.ts.map +1 -0
- package/dist/src/memory/warm-storage.js +207 -0
- package/dist/src/memory/warm-storage.js.map +1 -0
- package/dist/src/openai-adapter.d.ts +20 -0
- package/dist/src/openai-adapter.d.ts.map +1 -0
- package/dist/src/openai-adapter.js +73 -0
- package/dist/src/openai-adapter.js.map +1 -0
- package/dist/src/parser.d.ts +27 -0
- package/dist/src/parser.d.ts.map +1 -0
- package/dist/src/parser.js +76 -0
- package/dist/src/parser.js.map +1 -0
- package/dist/src/runtime.d.ts +172 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +436 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/schema-utils.d.ts +7 -0
- package/dist/src/schema-utils.d.ts.map +1 -0
- package/dist/src/schema-utils.js +71 -0
- package/dist/src/schema-utils.js.map +1 -0
- package/dist/src/stage.d.ts +139 -0
- package/dist/src/stage.d.ts.map +1 -0
- package/dist/src/stage.js +2 -0
- package/dist/src/stage.js.map +1 -0
- package/dist/src/store.d.ts +51 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/store.js +2 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/tool.d.ts +12 -0
- package/dist/src/tool.d.ts.map +1 -0
- package/dist/src/tool.js +2 -0
- package/dist/src/tool.js.map +1 -0
- package/dist/src/trace.d.ts +60 -0
- package/dist/src/trace.d.ts.map +1 -0
- package/dist/src/trace.js +2 -0
- package/dist/src/trace.js.map +1 -0
- package/dist/src/validator.d.ts +17 -0
- package/dist/src/validator.d.ts.map +1 -0
- package/dist/src/validator.js +21 -0
- package/dist/src/validator.js.map +1 -0
- package/dist/src/workflow.d.ts +192 -0
- package/dist/src/workflow.d.ts.map +1 -0
- package/dist/src/workflow.js +50 -0
- package/dist/src/workflow.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation tree with branching support.
|
|
3
|
+
*
|
|
4
|
+
* Enables creating branches for retries, exploring alternatives,
|
|
5
|
+
* and managing complex conversation histories.
|
|
6
|
+
*
|
|
7
|
+
* @module conversation/conversation-tree
|
|
8
|
+
* @since 2.0.0
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
/**
|
|
12
|
+
* Conversation tree with branching support.
|
|
13
|
+
*
|
|
14
|
+
* Useful for:
|
|
15
|
+
* - Creating branches during retries
|
|
16
|
+
* - Exploring alternative conversation paths
|
|
17
|
+
* - Rolling back to previous states
|
|
18
|
+
* - A/B testing different prompts
|
|
19
|
+
*
|
|
20
|
+
* @since 2.0.0
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const tree = new ConversationTree();
|
|
25
|
+
*
|
|
26
|
+
* // Add messages to main branch
|
|
27
|
+
* await tree.addMessage({ role: "user", content: "Hello" });
|
|
28
|
+
* await tree.addMessage({ role: "assistant", content: "Hi!" });
|
|
29
|
+
*
|
|
30
|
+
* // Create a branch for retry
|
|
31
|
+
* const retryBranch = await tree.createBranch("retry-1");
|
|
32
|
+
* await tree.addMessage({ role: "user", content: "Try again" });
|
|
33
|
+
*
|
|
34
|
+
* // Switch back to main
|
|
35
|
+
* await tree.switchBranch("main");
|
|
36
|
+
*
|
|
37
|
+
* // Get full history for current branch
|
|
38
|
+
* const history = await tree.getHistory();
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export class ConversationTree {
|
|
42
|
+
branches = new Map();
|
|
43
|
+
currentBranchId = "main";
|
|
44
|
+
constructor() {
|
|
45
|
+
// Initialize with main branch
|
|
46
|
+
this.branches.set("main", {
|
|
47
|
+
id: "main",
|
|
48
|
+
parentId: undefined,
|
|
49
|
+
name: "main",
|
|
50
|
+
branchPoint: 0,
|
|
51
|
+
messages: [],
|
|
52
|
+
createdAt: Date.now(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the current branch ID.
|
|
57
|
+
*/
|
|
58
|
+
getCurrentBranchId() {
|
|
59
|
+
return this.currentBranchId;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get a branch by ID.
|
|
63
|
+
*/
|
|
64
|
+
getBranch(branchId) {
|
|
65
|
+
return this.branches.get(branchId);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get all branches.
|
|
69
|
+
*/
|
|
70
|
+
getAllBranches() {
|
|
71
|
+
return Array.from(this.branches.values());
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create a new branch from the current branch.
|
|
75
|
+
*
|
|
76
|
+
* The new branch starts at the current point in the conversation.
|
|
77
|
+
* All previous messages are inherited from the parent branch.
|
|
78
|
+
*
|
|
79
|
+
* @param name - Human-readable name for the branch
|
|
80
|
+
* @returns The new branch ID
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // Create a retry branch
|
|
85
|
+
* const retryBranch = await tree.createBranch("retry-1");
|
|
86
|
+
* tree.switchBranch(retryBranch);
|
|
87
|
+
* // Add messages to retry branch...
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
createBranch(name) {
|
|
91
|
+
const currentBranch = this.branches.get(this.currentBranchId);
|
|
92
|
+
if (!currentBranch) {
|
|
93
|
+
throw new Error(`Current branch "${this.currentBranchId}" not found`);
|
|
94
|
+
}
|
|
95
|
+
const branchId = randomUUID();
|
|
96
|
+
// Branch point is the total history length, not just current branch messages
|
|
97
|
+
const currentHistory = this.getBranchHistory(this.currentBranchId);
|
|
98
|
+
const newBranch = {
|
|
99
|
+
id: branchId,
|
|
100
|
+
parentId: this.currentBranchId,
|
|
101
|
+
name,
|
|
102
|
+
branchPoint: currentHistory.length,
|
|
103
|
+
messages: [],
|
|
104
|
+
createdAt: Date.now(),
|
|
105
|
+
};
|
|
106
|
+
this.branches.set(branchId, newBranch);
|
|
107
|
+
return branchId;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Switch to a different branch.
|
|
111
|
+
*
|
|
112
|
+
* Future addMessage calls will add to the specified branch.
|
|
113
|
+
*
|
|
114
|
+
* @param branchId - ID of the branch to switch to
|
|
115
|
+
*/
|
|
116
|
+
switchBranch(branchId) {
|
|
117
|
+
const branch = this.branches.get(branchId);
|
|
118
|
+
if (!branch) {
|
|
119
|
+
throw new Error(`Branch "${branchId}" not found`);
|
|
120
|
+
}
|
|
121
|
+
this.currentBranchId = branchId;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Add a message to the current branch.
|
|
125
|
+
*
|
|
126
|
+
* @param message - The message to add
|
|
127
|
+
* @param metadata - Optional metadata (id and timestamp will be auto-generated)
|
|
128
|
+
*/
|
|
129
|
+
addMessage(message, metadata) {
|
|
130
|
+
const currentBranch = this.branches.get(this.currentBranchId);
|
|
131
|
+
if (!currentBranch) {
|
|
132
|
+
throw new Error(`Current branch "${this.currentBranchId}" not found`);
|
|
133
|
+
}
|
|
134
|
+
const fullMetadata = {
|
|
135
|
+
id: randomUUID(),
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
...metadata,
|
|
138
|
+
};
|
|
139
|
+
currentBranch.messages.push({
|
|
140
|
+
message,
|
|
141
|
+
metadata: fullMetadata,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get the full history for the current branch.
|
|
146
|
+
*
|
|
147
|
+
* Includes all messages from parent branches up to the branch point,
|
|
148
|
+
* plus messages specific to this branch.
|
|
149
|
+
*
|
|
150
|
+
* @returns Array of messages in chronological order
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const history = tree.getHistory();
|
|
155
|
+
* console.log(`${history.length} messages in current branch`);
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
getHistory() {
|
|
159
|
+
return this.getBranchHistory(this.currentBranchId);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get the history for a specific branch.
|
|
163
|
+
*
|
|
164
|
+
* @param branchId - ID of the branch
|
|
165
|
+
* @returns Array of messages in chronological order
|
|
166
|
+
*/
|
|
167
|
+
getBranchHistory(branchId) {
|
|
168
|
+
const branch = this.branches.get(branchId);
|
|
169
|
+
if (!branch) {
|
|
170
|
+
throw new Error(`Branch "${branchId}" not found`);
|
|
171
|
+
}
|
|
172
|
+
const messages = [];
|
|
173
|
+
// Collect inherited messages from parent branches
|
|
174
|
+
if (branch.parentId) {
|
|
175
|
+
const parentHistory = this.getBranchHistory(branch.parentId);
|
|
176
|
+
// branchPoint is the index in the parent's total history where we diverged
|
|
177
|
+
messages.push(...parentHistory.slice(0, branch.branchPoint));
|
|
178
|
+
}
|
|
179
|
+
// Add messages specific to this branch
|
|
180
|
+
messages.push(...branch.messages);
|
|
181
|
+
return messages;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get child branches of a specific branch.
|
|
185
|
+
*
|
|
186
|
+
* @param branchId - ID of the parent branch
|
|
187
|
+
* @returns Array of child branches
|
|
188
|
+
*/
|
|
189
|
+
getChildBranches(branchId) {
|
|
190
|
+
return Array.from(this.branches.values()).filter((b) => b.parentId === branchId);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Delete a branch and all its children.
|
|
194
|
+
*
|
|
195
|
+
* Cannot delete the main branch.
|
|
196
|
+
*
|
|
197
|
+
* @param branchId - ID of the branch to delete
|
|
198
|
+
*/
|
|
199
|
+
deleteBranch(branchId) {
|
|
200
|
+
if (branchId === "main") {
|
|
201
|
+
throw new Error("Cannot delete main branch");
|
|
202
|
+
}
|
|
203
|
+
const branch = this.branches.get(branchId);
|
|
204
|
+
if (!branch) {
|
|
205
|
+
throw new Error(`Branch "${branchId}" not found`);
|
|
206
|
+
}
|
|
207
|
+
// Delete all child branches recursively
|
|
208
|
+
const children = this.getChildBranches(branchId);
|
|
209
|
+
for (const child of children) {
|
|
210
|
+
this.deleteBranch(child.id);
|
|
211
|
+
}
|
|
212
|
+
// If currently on this branch, switch to parent
|
|
213
|
+
if (this.currentBranchId === branchId) {
|
|
214
|
+
this.currentBranchId = branch.parentId || "main";
|
|
215
|
+
}
|
|
216
|
+
this.branches.delete(branchId);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Merge a branch into its parent.
|
|
220
|
+
*
|
|
221
|
+
* Copies all branch-specific messages to the parent branch
|
|
222
|
+
* and deletes the child branch.
|
|
223
|
+
*
|
|
224
|
+
* @param branchId - ID of the branch to merge
|
|
225
|
+
* @returns The parent branch ID
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* // Try an alternative approach
|
|
230
|
+
* const tryBranch = tree.createBranch("alternative");
|
|
231
|
+
* tree.switchBranch(tryBranch);
|
|
232
|
+
* tree.addMessage({ role: "user", content: "Alternative prompt" });
|
|
233
|
+
*
|
|
234
|
+
* // If successful, merge back to main
|
|
235
|
+
* tree.mergeBranch(tryBranch);
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
mergeBranch(branchId) {
|
|
239
|
+
if (branchId === "main") {
|
|
240
|
+
throw new Error("Cannot merge main branch");
|
|
241
|
+
}
|
|
242
|
+
const branch = this.branches.get(branchId);
|
|
243
|
+
if (!branch) {
|
|
244
|
+
throw new Error(`Branch "${branchId}" not found`);
|
|
245
|
+
}
|
|
246
|
+
if (!branch.parentId) {
|
|
247
|
+
throw new Error(`Branch "${branchId}" has no parent`);
|
|
248
|
+
}
|
|
249
|
+
const parentBranch = this.branches.get(branch.parentId);
|
|
250
|
+
if (!parentBranch) {
|
|
251
|
+
throw new Error(`Parent branch "${branch.parentId}" not found`);
|
|
252
|
+
}
|
|
253
|
+
// Copy messages from child to parent
|
|
254
|
+
parentBranch.messages.push(...branch.messages);
|
|
255
|
+
// Delete the child branch
|
|
256
|
+
this.deleteBranch(branchId);
|
|
257
|
+
return branch.parentId;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Export the tree structure.
|
|
261
|
+
*
|
|
262
|
+
* @returns Object containing all branches
|
|
263
|
+
*/
|
|
264
|
+
export() {
|
|
265
|
+
return {
|
|
266
|
+
branches: Array.from(this.branches.values()),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Import a tree structure.
|
|
271
|
+
*
|
|
272
|
+
* Replaces the current tree.
|
|
273
|
+
*
|
|
274
|
+
* @param data - Exported tree data
|
|
275
|
+
*/
|
|
276
|
+
import(data) {
|
|
277
|
+
this.branches.clear();
|
|
278
|
+
for (const branch of data.branches) {
|
|
279
|
+
this.branches.set(branch.id, branch);
|
|
280
|
+
}
|
|
281
|
+
// Ensure main branch exists
|
|
282
|
+
if (!this.branches.has("main")) {
|
|
283
|
+
throw new Error("Imported data must contain a main branch");
|
|
284
|
+
}
|
|
285
|
+
this.currentBranchId = "main";
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=conversation-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-tree.js","sourceRoot":"","sources":["../../../src/conversation/conversation-tree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based implementation of ConversationManager using JSONL format.
|
|
3
|
+
*
|
|
4
|
+
* This implementation stores conversation history in a JSONL (JSON Lines) file,
|
|
5
|
+
* where each message is stored as a separate line. This format is ideal for:
|
|
6
|
+
* - Incremental writes (append-only)
|
|
7
|
+
* - Streaming reads
|
|
8
|
+
* - Easy debugging (human-readable)
|
|
9
|
+
* - Efficient storage
|
|
10
|
+
*
|
|
11
|
+
* @module conversation/file-conversation-store
|
|
12
|
+
* @since 2.0.0
|
|
13
|
+
*/
|
|
14
|
+
import type { Message } from "../component.js";
|
|
15
|
+
import type { ConversationManager, ConversationMessage, ConversationQuery, ConversationSnapshot, ConversationStats, MessageMetadata, PruneStrategy } from "./conversation-manager.js";
|
|
16
|
+
/**
|
|
17
|
+
* File-based conversation storage using JSONL format.
|
|
18
|
+
*
|
|
19
|
+
* Each message is stored as a single line in JSON format, making it easy
|
|
20
|
+
* to append new messages without rewriting the entire file.
|
|
21
|
+
*
|
|
22
|
+
* @since 2.0.0
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const store = new FileConversationStore("./conversations/session1.jsonl");
|
|
27
|
+
* await store.addMessage({ role: "user", content: "Hello!" });
|
|
28
|
+
* const history = await store.getHistory();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class FileConversationStore implements ConversationManager {
|
|
32
|
+
private filePath;
|
|
33
|
+
private cache;
|
|
34
|
+
private cacheInvalidated;
|
|
35
|
+
/**
|
|
36
|
+
* Create a new file-based conversation store.
|
|
37
|
+
*
|
|
38
|
+
* @param filePath - Path to the JSONL file for storing messages
|
|
39
|
+
*/
|
|
40
|
+
constructor(filePath: string);
|
|
41
|
+
/**
|
|
42
|
+
* Add a new message to the conversation.
|
|
43
|
+
*
|
|
44
|
+
* The message is appended to the JSONL file immediately.
|
|
45
|
+
*/
|
|
46
|
+
addMessage(message: Message, metadata?: Partial<Omit<MessageMetadata, "id" | "timestamp">>): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Get the complete conversation history.
|
|
49
|
+
*/
|
|
50
|
+
getHistory(): Promise<ConversationMessage[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Get the most recent N messages.
|
|
53
|
+
*/
|
|
54
|
+
getRecent(count: number): Promise<ConversationMessage[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Search for messages matching the query criteria.
|
|
57
|
+
*/
|
|
58
|
+
search(query: ConversationQuery): Promise<ConversationMessage[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Prune old messages based on the strategy.
|
|
61
|
+
*
|
|
62
|
+
* Returns the number of messages removed.
|
|
63
|
+
*/
|
|
64
|
+
prune(strategy: PruneStrategy): Promise<number>;
|
|
65
|
+
/**
|
|
66
|
+
* Clear all messages from the conversation.
|
|
67
|
+
*/
|
|
68
|
+
clear(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Get statistics about the conversation.
|
|
71
|
+
*/
|
|
72
|
+
getStats(): Promise<ConversationStats>;
|
|
73
|
+
/**
|
|
74
|
+
* Export the conversation to a snapshot.
|
|
75
|
+
*/
|
|
76
|
+
export(): Promise<ConversationSnapshot>;
|
|
77
|
+
/**
|
|
78
|
+
* Import a conversation from a snapshot.
|
|
79
|
+
*
|
|
80
|
+
* This replaces the current conversation.
|
|
81
|
+
*/
|
|
82
|
+
import(snapshot: ConversationSnapshot): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Load messages from file into cache.
|
|
85
|
+
*/
|
|
86
|
+
private loadCache;
|
|
87
|
+
/**
|
|
88
|
+
* Rewrite the entire file with the given messages.
|
|
89
|
+
* Used for pruning and importing.
|
|
90
|
+
*/
|
|
91
|
+
private rewriteFile;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=file-conversation-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-conversation-store.d.ts","sourceRoot":"","sources":["../../../src/conversation/file-conversation-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACd,MAAM,2BAA2B,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qBAAsB,YAAW,mBAAmB;IAC/D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,gBAAgB,CAAS;IAEjC;;;;OAIG;gBACS,QAAQ,EAAE,MAAM;IAI5B;;;;OAIG;IACG,UAAU,CACd,OAAO,EAAE,OAAO,EAChB,QAAQ,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC,GAC5D,OAAO,CAAC,IAAI,CAAC;IAwBhB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAKlD;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAS9D;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAiDtE;;;;OAIG;IACG,KAAK,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAsDrD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAsC5C;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAW7C;;;;OAIG;IACG,MAAM,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3D;;OAEG;YACW,SAAS;IA0BvB;;;OAGG;YACW,WAAW;CAgB1B"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based implementation of ConversationManager using JSONL format.
|
|
3
|
+
*
|
|
4
|
+
* This implementation stores conversation history in a JSONL (JSON Lines) file,
|
|
5
|
+
* where each message is stored as a separate line. This format is ideal for:
|
|
6
|
+
* - Incremental writes (append-only)
|
|
7
|
+
* - Streaming reads
|
|
8
|
+
* - Easy debugging (human-readable)
|
|
9
|
+
* - Efficient storage
|
|
10
|
+
*
|
|
11
|
+
* @module conversation/file-conversation-store
|
|
12
|
+
* @since 2.0.0
|
|
13
|
+
*/
|
|
14
|
+
import { promises as fs } from "node:fs";
|
|
15
|
+
import { dirname } from "node:path";
|
|
16
|
+
import { randomUUID } from "node:crypto";
|
|
17
|
+
/**
|
|
18
|
+
* File-based conversation storage using JSONL format.
|
|
19
|
+
*
|
|
20
|
+
* Each message is stored as a single line in JSON format, making it easy
|
|
21
|
+
* to append new messages without rewriting the entire file.
|
|
22
|
+
*
|
|
23
|
+
* @since 2.0.0
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const store = new FileConversationStore("./conversations/session1.jsonl");
|
|
28
|
+
* await store.addMessage({ role: "user", content: "Hello!" });
|
|
29
|
+
* const history = await store.getHistory();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class FileConversationStore {
|
|
33
|
+
filePath;
|
|
34
|
+
cache = null;
|
|
35
|
+
cacheInvalidated = false;
|
|
36
|
+
/**
|
|
37
|
+
* Create a new file-based conversation store.
|
|
38
|
+
*
|
|
39
|
+
* @param filePath - Path to the JSONL file for storing messages
|
|
40
|
+
*/
|
|
41
|
+
constructor(filePath) {
|
|
42
|
+
this.filePath = filePath;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Add a new message to the conversation.
|
|
46
|
+
*
|
|
47
|
+
* The message is appended to the JSONL file immediately.
|
|
48
|
+
*/
|
|
49
|
+
async addMessage(message, metadata) {
|
|
50
|
+
// Ensure directory exists
|
|
51
|
+
await fs.mkdir(dirname(this.filePath), { recursive: true });
|
|
52
|
+
// Create full metadata
|
|
53
|
+
const fullMetadata = {
|
|
54
|
+
id: randomUUID(),
|
|
55
|
+
timestamp: Date.now(),
|
|
56
|
+
...metadata,
|
|
57
|
+
};
|
|
58
|
+
const conversationMessage = {
|
|
59
|
+
message,
|
|
60
|
+
metadata: fullMetadata,
|
|
61
|
+
};
|
|
62
|
+
// Append to file
|
|
63
|
+
const line = JSON.stringify(conversationMessage) + "\n";
|
|
64
|
+
await fs.appendFile(this.filePath, line, "utf-8");
|
|
65
|
+
// Invalidate cache
|
|
66
|
+
this.cacheInvalidated = true;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the complete conversation history.
|
|
70
|
+
*/
|
|
71
|
+
async getHistory() {
|
|
72
|
+
await this.loadCache();
|
|
73
|
+
return [...(this.cache || [])];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get the most recent N messages.
|
|
77
|
+
*/
|
|
78
|
+
async getRecent(count) {
|
|
79
|
+
await this.loadCache();
|
|
80
|
+
const messages = this.cache || [];
|
|
81
|
+
if (count === 0) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
return messages.slice(-count);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Search for messages matching the query criteria.
|
|
88
|
+
*/
|
|
89
|
+
async search(query) {
|
|
90
|
+
await this.loadCache();
|
|
91
|
+
let results = this.cache || [];
|
|
92
|
+
// Filter by role
|
|
93
|
+
if (query.role !== undefined) {
|
|
94
|
+
results = results.filter((msg) => msg.message.role === query.role);
|
|
95
|
+
}
|
|
96
|
+
// Filter by content (case-insensitive)
|
|
97
|
+
if (query.content !== undefined) {
|
|
98
|
+
const searchLower = query.content.toLowerCase();
|
|
99
|
+
results = results.filter((msg) => msg.message.content.toLowerCase().includes(searchLower));
|
|
100
|
+
}
|
|
101
|
+
// Filter by stageId
|
|
102
|
+
if (query.stageId !== undefined) {
|
|
103
|
+
results = results.filter((msg) => msg.metadata.stageId === query.stageId);
|
|
104
|
+
}
|
|
105
|
+
// Filter by timestamp range
|
|
106
|
+
if (query.after !== undefined) {
|
|
107
|
+
results = results.filter((msg) => msg.metadata.timestamp >= query.after);
|
|
108
|
+
}
|
|
109
|
+
if (query.before !== undefined) {
|
|
110
|
+
results = results.filter((msg) => msg.metadata.timestamp <= query.before);
|
|
111
|
+
}
|
|
112
|
+
// Filter by importance
|
|
113
|
+
if (query.important !== undefined) {
|
|
114
|
+
results = results.filter((msg) => msg.metadata.important === query.important);
|
|
115
|
+
}
|
|
116
|
+
// Limit results
|
|
117
|
+
if (query.limit !== undefined && query.limit > 0) {
|
|
118
|
+
results = results.slice(0, query.limit);
|
|
119
|
+
}
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Prune old messages based on the strategy.
|
|
124
|
+
*
|
|
125
|
+
* Returns the number of messages removed.
|
|
126
|
+
*/
|
|
127
|
+
async prune(strategy) {
|
|
128
|
+
await this.loadCache();
|
|
129
|
+
const messages = this.cache || [];
|
|
130
|
+
if (messages.length === 0) {
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
const toKeep = [];
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
for (let i = 0; i < messages.length; i++) {
|
|
136
|
+
const msg = messages[i];
|
|
137
|
+
let keep = false;
|
|
138
|
+
// Check if in recent N
|
|
139
|
+
if (strategy.keepRecent !== undefined &&
|
|
140
|
+
i >= messages.length - strategy.keepRecent) {
|
|
141
|
+
keep = true;
|
|
142
|
+
}
|
|
143
|
+
// Check if marked as important
|
|
144
|
+
if (strategy.keepImportant !== undefined &&
|
|
145
|
+
strategy.keepImportant(msg)) {
|
|
146
|
+
keep = true;
|
|
147
|
+
}
|
|
148
|
+
// Check age
|
|
149
|
+
if (strategy.maxAge !== undefined) {
|
|
150
|
+
const age = now - msg.metadata.timestamp;
|
|
151
|
+
if (age <= strategy.maxAge) {
|
|
152
|
+
keep = true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (keep) {
|
|
156
|
+
toKeep.push(msg);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const prunedCount = messages.length - toKeep.length;
|
|
160
|
+
if (prunedCount > 0) {
|
|
161
|
+
// Rewrite file with remaining messages
|
|
162
|
+
await this.rewriteFile(toKeep);
|
|
163
|
+
}
|
|
164
|
+
return prunedCount;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Clear all messages from the conversation.
|
|
168
|
+
*/
|
|
169
|
+
async clear() {
|
|
170
|
+
try {
|
|
171
|
+
await fs.unlink(this.filePath);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// File might not exist, which is fine
|
|
175
|
+
}
|
|
176
|
+
this.cache = [];
|
|
177
|
+
this.cacheInvalidated = false;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get statistics about the conversation.
|
|
181
|
+
*/
|
|
182
|
+
async getStats() {
|
|
183
|
+
await this.loadCache();
|
|
184
|
+
const messages = this.cache || [];
|
|
185
|
+
const byRole = {};
|
|
186
|
+
let totalTokens = 0;
|
|
187
|
+
let oldestMessage;
|
|
188
|
+
let newestMessage;
|
|
189
|
+
for (const msg of messages) {
|
|
190
|
+
// Count by role
|
|
191
|
+
const role = msg.message.role;
|
|
192
|
+
byRole[role] = (byRole[role] || 0) + 1;
|
|
193
|
+
// Sum tokens
|
|
194
|
+
if (msg.metadata.tokens !== undefined) {
|
|
195
|
+
totalTokens += msg.metadata.tokens;
|
|
196
|
+
}
|
|
197
|
+
// Track timestamps
|
|
198
|
+
const timestamp = msg.metadata.timestamp;
|
|
199
|
+
if (oldestMessage === undefined || timestamp < oldestMessage) {
|
|
200
|
+
oldestMessage = timestamp;
|
|
201
|
+
}
|
|
202
|
+
if (newestMessage === undefined || timestamp > newestMessage) {
|
|
203
|
+
newestMessage = timestamp;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
messageCount: messages.length,
|
|
208
|
+
byRole,
|
|
209
|
+
totalTokens: totalTokens > 0 ? totalTokens : undefined,
|
|
210
|
+
oldestMessage,
|
|
211
|
+
newestMessage,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Export the conversation to a snapshot.
|
|
216
|
+
*/
|
|
217
|
+
async export() {
|
|
218
|
+
await this.loadCache();
|
|
219
|
+
const messages = this.cache || [];
|
|
220
|
+
return {
|
|
221
|
+
version: "1.0.0",
|
|
222
|
+
createdAt: Date.now(),
|
|
223
|
+
messages,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Import a conversation from a snapshot.
|
|
228
|
+
*
|
|
229
|
+
* This replaces the current conversation.
|
|
230
|
+
*/
|
|
231
|
+
async import(snapshot) {
|
|
232
|
+
// Validate version (for future compatibility)
|
|
233
|
+
if (!snapshot.version.startsWith("1.")) {
|
|
234
|
+
throw new Error(`Unsupported snapshot version: ${snapshot.version}. Expected 1.x`);
|
|
235
|
+
}
|
|
236
|
+
// Rewrite file with imported messages
|
|
237
|
+
await this.rewriteFile(snapshot.messages);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Load messages from file into cache.
|
|
241
|
+
*/
|
|
242
|
+
async loadCache() {
|
|
243
|
+
// If cache is valid, use it
|
|
244
|
+
if (this.cache !== null && !this.cacheInvalidated) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const content = await fs.readFile(this.filePath, "utf-8");
|
|
249
|
+
const lines = content.trim().split("\n");
|
|
250
|
+
this.cache = lines
|
|
251
|
+
.filter((line) => line.length > 0)
|
|
252
|
+
.map((line) => JSON.parse(line));
|
|
253
|
+
this.cacheInvalidated = false;
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
// If file doesn't exist, start with empty cache
|
|
257
|
+
if (error.code === "ENOENT") {
|
|
258
|
+
this.cache = [];
|
|
259
|
+
this.cacheInvalidated = false;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Rewrite the entire file with the given messages.
|
|
268
|
+
* Used for pruning and importing.
|
|
269
|
+
*/
|
|
270
|
+
async rewriteFile(messages) {
|
|
271
|
+
// Ensure directory exists
|
|
272
|
+
await fs.mkdir(dirname(this.filePath), { recursive: true });
|
|
273
|
+
// Write to temp file first (atomic write)
|
|
274
|
+
const tempPath = `${this.filePath}.tmp`;
|
|
275
|
+
const lines = messages.map((msg) => JSON.stringify(msg) + "\n").join("");
|
|
276
|
+
await fs.writeFile(tempPath, lines, "utf-8");
|
|
277
|
+
// Atomic rename
|
|
278
|
+
await fs.rename(tempPath, this.filePath);
|
|
279
|
+
// Update cache
|
|
280
|
+
this.cache = messages;
|
|
281
|
+
this.cacheInvalidated = false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=file-conversation-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-conversation-store.js","sourceRoot":"","sources":["../../../src/conversation/file-conversation-store.ts"],"names":[],"mappings":"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"}
|