recursive-llm-ts 4.9.0 → 5.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.
@@ -0,0 +1,428 @@
1
+ package rlm
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ "time"
7
+ )
8
+
9
+ func testStoreMessage(id string, role MessageRole, content string, tokens int, ts time.Time, fileIDs []string) *StoreMessage {
10
+ return &StoreMessage{
11
+ ID: id,
12
+ Role: role,
13
+ Content: content,
14
+ Tokens: tokens,
15
+ Timestamp: ts,
16
+ FileIDs: fileIDs,
17
+ Metadata: map[string]string{
18
+ "source": "test",
19
+ },
20
+ }
21
+ }
22
+
23
+ func testSummaryNode(id string, kind SummaryKind, content string, tokens int, level int, createdAt time.Time) *SummaryNode {
24
+ return &SummaryNode{
25
+ ID: id,
26
+ Kind: kind,
27
+ Content: content,
28
+ Tokens: tokens,
29
+ Level: level,
30
+ CreatedAt: createdAt,
31
+ MessageIDs: []string{"msg_1", "msg_2"},
32
+ ChildIDs: []string{"sum_child_1"},
33
+ ParentID: "",
34
+ FileIDs: []string{"file_a.go", "file_b.go"},
35
+ }
36
+ }
37
+
38
+ func TestMemoryBackend_PersistAndGetMessage(t *testing.T) {
39
+ backend := NewMemoryBackend()
40
+
41
+ ts := time.Now().UTC()
42
+ msg := testStoreMessage("msg_1", RoleUser, "hello memory backend", 5, ts, []string{"a.txt"})
43
+ if err := backend.PersistMessage(msg); err != nil {
44
+ t.Fatalf("PersistMessage() error = %v", err)
45
+ }
46
+
47
+ got, err := backend.GetMessage("msg_1")
48
+ if err != nil {
49
+ t.Fatalf("GetMessage() error = %v", err)
50
+ }
51
+ if got == nil {
52
+ t.Fatal("GetMessage() = nil, want message")
53
+ }
54
+ if got.ID != msg.ID || got.Role != msg.Role || got.Content != msg.Content || got.Tokens != msg.Tokens {
55
+ t.Fatalf("GetMessage() mismatch: got %+v, want %+v", got, msg)
56
+ }
57
+ if !got.Timestamp.Equal(msg.Timestamp) {
58
+ t.Fatalf("GetMessage().Timestamp = %v, want %v", got.Timestamp, msg.Timestamp)
59
+ }
60
+ if len(got.FileIDs) != 1 || got.FileIDs[0] != "a.txt" {
61
+ t.Fatalf("GetMessage().FileIDs = %v, want [a.txt]", got.FileIDs)
62
+ }
63
+ }
64
+
65
+ func TestMemoryBackend_GetAllMessages(t *testing.T) {
66
+ backend := NewMemoryBackend()
67
+ base := time.Now().UTC()
68
+
69
+ m1 := testStoreMessage("msg_1", RoleUser, "first", 1, base, []string{"1.txt"})
70
+ m2 := testStoreMessage("msg_2", RoleAssistant, "second", 2, base.Add(time.Second), []string{"2.txt"})
71
+ m3 := testStoreMessage("msg_3", RoleTool, "third", 3, base.Add(2*time.Second), []string{"3.txt"})
72
+
73
+ for _, m := range []*StoreMessage{m1, m2, m3} {
74
+ if err := backend.PersistMessage(m); err != nil {
75
+ t.Fatalf("PersistMessage(%s) error = %v", m.ID, err)
76
+ }
77
+ }
78
+
79
+ all, err := backend.GetAllMessages()
80
+ if err != nil {
81
+ t.Fatalf("GetAllMessages() error = %v", err)
82
+ }
83
+ if len(all) != 3 {
84
+ t.Fatalf("GetAllMessages() len = %d, want 3", len(all))
85
+ }
86
+ if all[0].ID != "msg_1" || all[1].ID != "msg_2" || all[2].ID != "msg_3" {
87
+ t.Fatalf("GetAllMessages() order = [%s %s %s], want [msg_1 msg_2 msg_3]", all[0].ID, all[1].ID, all[2].ID)
88
+ }
89
+ }
90
+
91
+ func TestMemoryBackend_PersistAndGetSummary(t *testing.T) {
92
+ backend := NewMemoryBackend()
93
+
94
+ node := testSummaryNode("sum_1", SummaryLeaf, "summary content", 7, 1, time.Now().UTC())
95
+ if err := backend.PersistSummary(node); err != nil {
96
+ t.Fatalf("PersistSummary() error = %v", err)
97
+ }
98
+
99
+ got, err := backend.GetSummary("sum_1")
100
+ if err != nil {
101
+ t.Fatalf("GetSummary() error = %v", err)
102
+ }
103
+ if got == nil {
104
+ t.Fatal("GetSummary() = nil, want summary")
105
+ }
106
+ if got.ID != node.ID || got.Kind != node.Kind || got.Content != node.Content || got.Tokens != node.Tokens || got.Level != node.Level {
107
+ t.Fatalf("GetSummary() mismatch: got %+v, want %+v", got, node)
108
+ }
109
+ if got.ParentID != "" {
110
+ t.Fatalf("GetSummary().ParentID = %q, want empty", got.ParentID)
111
+ }
112
+ }
113
+
114
+ func TestMemoryBackend_MessageCount(t *testing.T) {
115
+ backend := NewMemoryBackend()
116
+
117
+ for i := 1; i <= 4; i++ {
118
+ msg := testStoreMessage(
119
+ "msg_"+string(rune('0'+i)),
120
+ RoleUser,
121
+ "count test",
122
+ i,
123
+ time.Now().UTC().Add(time.Duration(i)*time.Second),
124
+ []string{"count.txt"},
125
+ )
126
+ if err := backend.PersistMessage(msg); err != nil {
127
+ t.Fatalf("PersistMessage(%s) error = %v", msg.ID, err)
128
+ }
129
+ }
130
+
131
+ count, err := backend.MessageCount()
132
+ if err != nil {
133
+ t.Fatalf("MessageCount() error = %v", err)
134
+ }
135
+ if count != 4 {
136
+ t.Fatalf("MessageCount() = %d, want 4", count)
137
+ }
138
+ }
139
+
140
+ func TestMemoryBackend_UpdateSummaryParent(t *testing.T) {
141
+ backend := NewMemoryBackend()
142
+
143
+ node := testSummaryNode("sum_1", SummaryLeaf, "summary", 5, 1, time.Now().UTC())
144
+ if err := backend.PersistSummary(node); err != nil {
145
+ t.Fatalf("PersistSummary() error = %v", err)
146
+ }
147
+
148
+ if err := backend.UpdateSummaryParent("sum_1", "sum_parent"); err != nil {
149
+ t.Fatalf("UpdateSummaryParent() error = %v", err)
150
+ }
151
+
152
+ got, err := backend.GetSummary("sum_1")
153
+ if err != nil {
154
+ t.Fatalf("GetSummary() error = %v", err)
155
+ }
156
+ if got == nil {
157
+ t.Fatal("GetSummary() = nil, want summary")
158
+ }
159
+ if got.ParentID != "sum_parent" {
160
+ t.Fatalf("GetSummary().ParentID = %q, want %q", got.ParentID, "sum_parent")
161
+ }
162
+ }
163
+
164
+ func TestSQLiteBackend_CreateAndClose(t *testing.T) {
165
+ backend, err := NewSQLiteBackend(":memory:")
166
+ if err != nil {
167
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
168
+ }
169
+ if backend == nil {
170
+ t.Fatal("NewSQLiteBackend(:memory:) = nil, want backend")
171
+ }
172
+
173
+ if err := backend.Close(); err != nil {
174
+ t.Fatalf("Close() error = %v", err)
175
+ }
176
+ }
177
+
178
+ func TestSQLiteBackend_PersistAndGetMessage(t *testing.T) {
179
+ backend, err := NewSQLiteBackend(":memory:")
180
+ if err != nil {
181
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
182
+ }
183
+ defer backend.Close()
184
+
185
+ ts := time.Now().UTC()
186
+ msg := testStoreMessage("msg_1", RoleAssistant, "sqlite hello", 11, ts, []string{"sqlite.txt"})
187
+ if err := backend.PersistMessage(msg); err != nil {
188
+ t.Fatalf("PersistMessage() error = %v", err)
189
+ }
190
+
191
+ got, err := backend.GetMessage("msg_1")
192
+ if err != nil {
193
+ t.Fatalf("GetMessage() error = %v", err)
194
+ }
195
+ if got == nil {
196
+ t.Fatal("GetMessage() = nil, want message")
197
+ }
198
+ if got.ID != msg.ID || got.Role != msg.Role || got.Content != msg.Content || got.Tokens != msg.Tokens {
199
+ t.Fatalf("GetMessage() mismatch: got %+v, want %+v", got, msg)
200
+ }
201
+ if !got.Timestamp.Equal(msg.Timestamp) {
202
+ t.Fatalf("GetMessage().Timestamp = %v, want %v", got.Timestamp, msg.Timestamp)
203
+ }
204
+ if len(got.FileIDs) != 1 || got.FileIDs[0] != "sqlite.txt" {
205
+ t.Fatalf("GetMessage().FileIDs = %v, want [sqlite.txt]", got.FileIDs)
206
+ }
207
+ }
208
+
209
+ func TestSQLiteBackend_GetAllMessages(t *testing.T) {
210
+ backend, err := NewSQLiteBackend(":memory:")
211
+ if err != nil {
212
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
213
+ }
214
+ defer backend.Close()
215
+
216
+ base := time.Now().UTC()
217
+ m1 := testStoreMessage("msg_1", RoleUser, "first sqlite", 1, base, []string{"1.txt"})
218
+ m2 := testStoreMessage("msg_2", RoleAssistant, "second sqlite", 2, base.Add(time.Second), []string{"2.txt"})
219
+ m3 := testStoreMessage("msg_3", RoleTool, "third sqlite", 3, base.Add(2*time.Second), []string{"3.txt"})
220
+
221
+ for _, m := range []*StoreMessage{m1, m2, m3} {
222
+ if err := backend.PersistMessage(m); err != nil {
223
+ t.Fatalf("PersistMessage(%s) error = %v", m.ID, err)
224
+ }
225
+ }
226
+
227
+ all, err := backend.GetAllMessages()
228
+ if err != nil {
229
+ t.Fatalf("GetAllMessages() error = %v", err)
230
+ }
231
+ if len(all) != 3 {
232
+ t.Fatalf("GetAllMessages() len = %d, want 3", len(all))
233
+ }
234
+ if all[0].ID != "msg_1" || all[1].ID != "msg_2" || all[2].ID != "msg_3" {
235
+ t.Fatalf("GetAllMessages() order = [%s %s %s], want [msg_1 msg_2 msg_3]", all[0].ID, all[1].ID, all[2].ID)
236
+ }
237
+ }
238
+
239
+ func TestSQLiteBackend_MessageCount(t *testing.T) {
240
+ backend, err := NewSQLiteBackend(":memory:")
241
+ if err != nil {
242
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
243
+ }
244
+ defer backend.Close()
245
+
246
+ for i := 1; i <= 5; i++ {
247
+ msg := testStoreMessage(
248
+ "msg_"+string(rune('0'+i)),
249
+ RoleUser,
250
+ "sqlite count",
251
+ i,
252
+ time.Now().UTC().Add(time.Duration(i)*time.Second),
253
+ []string{"count_sqlite.txt"},
254
+ )
255
+ if err := backend.PersistMessage(msg); err != nil {
256
+ t.Fatalf("PersistMessage(%s) error = %v", msg.ID, err)
257
+ }
258
+ }
259
+
260
+ count, err := backend.MessageCount()
261
+ if err != nil {
262
+ t.Fatalf("MessageCount() error = %v", err)
263
+ }
264
+ if count != 5 {
265
+ t.Fatalf("MessageCount() = %d, want 5", count)
266
+ }
267
+ }
268
+
269
+ func TestSQLiteBackend_PersistAndGetSummary(t *testing.T) {
270
+ backend, err := NewSQLiteBackend(":memory:")
271
+ if err != nil {
272
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
273
+ }
274
+ defer backend.Close()
275
+
276
+ node := testSummaryNode("sum_1", SummaryCondensed, "sqlite summary", 13, 2, time.Now().UTC())
277
+ node.ParentID = "sum_parent"
278
+ if err := backend.PersistSummary(node); err != nil {
279
+ t.Fatalf("PersistSummary() error = %v", err)
280
+ }
281
+
282
+ got, err := backend.GetSummary("sum_1")
283
+ if err != nil {
284
+ t.Fatalf("GetSummary() error = %v", err)
285
+ }
286
+ if got == nil {
287
+ t.Fatal("GetSummary() = nil, want summary")
288
+ }
289
+ if got.ID != node.ID || got.Kind != node.Kind || got.Content != node.Content || got.Tokens != node.Tokens || got.Level != node.Level {
290
+ t.Fatalf("GetSummary() mismatch: got %+v, want %+v", got, node)
291
+ }
292
+ if got.ParentID != "sum_parent" {
293
+ t.Fatalf("GetSummary().ParentID = %q, want %q", got.ParentID, "sum_parent")
294
+ }
295
+ }
296
+
297
+ func TestSQLiteBackend_UpdateSummaryParent(t *testing.T) {
298
+ backend, err := NewSQLiteBackend(":memory:")
299
+ if err != nil {
300
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
301
+ }
302
+ defer backend.Close()
303
+
304
+ node := testSummaryNode("sum_1", SummaryLeaf, "sqlite summary", 5, 1, time.Now().UTC())
305
+ if err := backend.PersistSummary(node); err != nil {
306
+ t.Fatalf("PersistSummary() error = %v", err)
307
+ }
308
+
309
+ if err := backend.UpdateSummaryParent("sum_1", "sum_parent_2"); err != nil {
310
+ t.Fatalf("UpdateSummaryParent() error = %v", err)
311
+ }
312
+
313
+ got, err := backend.GetSummary("sum_1")
314
+ if err != nil {
315
+ t.Fatalf("GetSummary() error = %v", err)
316
+ }
317
+ if got == nil {
318
+ t.Fatal("GetSummary() = nil, want summary")
319
+ }
320
+ if got.ParentID != "sum_parent_2" {
321
+ t.Fatalf("GetSummary().ParentID = %q, want %q", got.ParentID, "sum_parent_2")
322
+ }
323
+ }
324
+
325
+ func TestSQLiteBackend_GrepMessages_Simple(t *testing.T) {
326
+ backend, err := NewSQLiteBackend(":memory:")
327
+ if err != nil {
328
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
329
+ }
330
+ defer backend.Close()
331
+
332
+ base := time.Now().UTC()
333
+ msgs := []*StoreMessage{
334
+ testStoreMessage("msg_1", RoleUser, "alpha beta gamma", 3, base, []string{"a.txt"}),
335
+ testStoreMessage("msg_2", RoleAssistant, "delta epsilon", 2, base.Add(time.Second), []string{"b.txt"}),
336
+ testStoreMessage("msg_3", RoleTool, "gamma zeta", 2, base.Add(2*time.Second), []string{"c.txt"}),
337
+ }
338
+ for _, m := range msgs {
339
+ if err := backend.PersistMessage(m); err != nil {
340
+ t.Fatalf("PersistMessage(%s) error = %v", m.ID, err)
341
+ }
342
+ }
343
+
344
+ results, err := backend.GrepMessages("gamma", nil, 10)
345
+ if err != nil {
346
+ t.Fatalf("GrepMessages() error = %v", err)
347
+ }
348
+ if len(results) != 2 {
349
+ t.Fatalf("GrepMessages() len = %d, want 2", len(results))
350
+ }
351
+ for _, r := range results {
352
+ if !strings.Contains(r.Content, "gamma") {
353
+ t.Fatalf("result content %q does not contain %q", r.Content, "gamma")
354
+ }
355
+ }
356
+ }
357
+
358
+ func TestSQLiteBackend_GrepMessages_Regex(t *testing.T) {
359
+ backend, err := NewSQLiteBackend(":memory:")
360
+ if err != nil {
361
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
362
+ }
363
+ defer backend.Close()
364
+
365
+ base := time.Now().UTC()
366
+ msgs := []*StoreMessage{
367
+ testStoreMessage("msg_1", RoleUser, "hello world", 2, base, []string{"a.txt"}),
368
+ testStoreMessage("msg_2", RoleAssistant, "hallo world", 2, base.Add(time.Second), []string{"b.txt"}),
369
+ testStoreMessage("msg_3", RoleTool, "goodbye world", 2, base.Add(2*time.Second), []string{"c.txt"}),
370
+ }
371
+ for _, m := range msgs {
372
+ if err := backend.PersistMessage(m); err != nil {
373
+ t.Fatalf("PersistMessage(%s) error = %v", m.ID, err)
374
+ }
375
+ }
376
+
377
+ results, err := backend.GrepMessages("h.llo\\s+world", nil, 10)
378
+ if err != nil {
379
+ t.Fatalf("GrepMessages() error = %v", err)
380
+ }
381
+ if len(results) != 2 {
382
+ t.Fatalf("GrepMessages() len = %d, want 2", len(results))
383
+ }
384
+ }
385
+
386
+ func TestSQLiteBackend_GetMessage_NotFound(t *testing.T) {
387
+ backend, err := NewSQLiteBackend(":memory:")
388
+ if err != nil {
389
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
390
+ }
391
+ defer backend.Close()
392
+
393
+ got, err := backend.GetMessage("does_not_exist")
394
+ if err != nil {
395
+ t.Fatalf("GetMessage() error = %v", err)
396
+ }
397
+ if got != nil {
398
+ t.Fatalf("GetMessage() = %+v, want nil", got)
399
+ }
400
+ }
401
+
402
+ func TestSQLiteBackend_GetSummary_NotFound(t *testing.T) {
403
+ backend, err := NewSQLiteBackend(":memory:")
404
+ if err != nil {
405
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
406
+ }
407
+ defer backend.Close()
408
+
409
+ got, err := backend.GetSummary("does_not_exist")
410
+ if err != nil {
411
+ t.Fatalf("GetSummary() error = %v", err)
412
+ }
413
+ if got != nil {
414
+ t.Fatalf("GetSummary() = %+v, want nil", got)
415
+ }
416
+ }
417
+
418
+ func TestStoreBackend_InterfaceCompliance(t *testing.T) {
419
+ var _ StoreBackend = NewMemoryBackend()
420
+
421
+ sqliteBackend, err := NewSQLiteBackend(":memory:")
422
+ if err != nil {
423
+ t.Fatalf("NewSQLiteBackend(:memory:) error = %v", err)
424
+ }
425
+ defer sqliteBackend.Close()
426
+
427
+ var _ StoreBackend = sqliteBackend
428
+ }