diff options
Diffstat (limited to 'libpanto/src/session_manager.zig')
| -rw-r--r-- | libpanto/src/session_manager.zig | 69 |
1 files changed, 68 insertions, 1 deletions
diff --git a/libpanto/src/session_manager.zig b/libpanto/src/session_manager.zig index 9e8e2c8..c6256e0 100644 --- a/libpanto/src/session_manager.zig +++ b/libpanto/src/session_manager.zig @@ -750,7 +750,13 @@ fn appendMessageToConv( .user => .user, .assistant => .assistant, }; - try conv.messages.append(allocator, .{ .role = role, .content = content }); + // Carry the recorded usage forward so compaction can size the retention + // window after a session is reopened (it's null for user/system). + try conv.messages.append(allocator, .{ + .role = role, + .content = content, + .usage = disk_msg.usage, + }); } // ============================================================================= @@ -1621,3 +1627,64 @@ test "resolveSessionId: unique prefix → match, ambiguous → error" { try testing.expectError(error.SessionNotFound, resolveSessionId(testing.allocator, io, sessions, "ffffffff")); } + +test "compaction summary round-trips through persist + resume + rebuild" { + const io = testing.io; + + var td = try TmpSessionDir.init(testing.allocator); + defer td.deinit(testing.allocator); + const sessions = try std.fs.path.join(testing.allocator, &.{ td.abs_path, "sessions" }); + defer testing.allocator.free(sessions); + + const session_file: []u8 = blk: { + var mgr = try SessionManager.init(testing.allocator, io, sessions, "/c"); + defer mgr.deinit(); + + // System + an old turn that will be superseded. + const sys = try testing.allocator.alloc(DiskContentBlock, 1); + sys[0] = .{ .text = .{ .text = try testing.allocator.dupe(u8, "you are helpful") } }; + _ = try mgr.appendMessage(.{ .role = .system, .content = sys }, null, null); + + const uo = try testing.allocator.alloc(DiskContentBlock, 1); + uo[0] = .{ .text = .{ .text = try testing.allocator.dupe(u8, "old q") } }; + _ = try mgr.appendMessage(.{ .role = .user, .content = uo }, "openai", "gpt-4o"); + const ao = try testing.allocator.alloc(DiskContentBlock, 1); + ao[0] = .{ .text = .{ .text = try testing.allocator.dupe(u8, "old a") } }; + _ = try mgr.appendMessage(.{ .role = .assistant, .content = ao }, null, null); + + // Compaction: summary message + duplicated kept suffix. + const cs = try testing.allocator.alloc(DiskContentBlock, 1); + cs[0] = .{ .compaction_summary = .{ .text = try testing.allocator.dupe(u8, "SUMMARY") } }; + _ = try mgr.appendMessage(.{ .role = .user, .content = cs }, "openai", "gpt-4o"); + + const ur = try testing.allocator.alloc(DiskContentBlock, 1); + ur[0] = .{ .text = .{ .text = try testing.allocator.dupe(u8, "recent q") } }; + _ = try mgr.appendMessage(.{ .role = .user, .content = ur }, "openai", "gpt-4o"); + + break :blk try testing.allocator.dupe(u8, mgr.getSessionFile()); + }; + defer testing.allocator.free(session_file); + + var resumed = try SessionManager.open(testing.allocator, io, session_file); + defer resumed.deinit(); + var conv = try resumed.rebuildConversation(); + defer conv.deinit(); + + // The compaction summary block survived as a CompactionSummary. + const anchor = conversation_mod.latestCompactionIndex(conv.messages.items).?; + try testing.expectEqualStrings( + "SUMMARY", + conv.messages.items[anchor].content.items[0].CompactionSummary.text.items, + ); + + // The active window is [summary, recent q]. + const window = conversation_mod.activeMessageWindow(conv.messages.items); + try testing.expectEqual(@as(usize, 2), window.len); + try testing.expectEqualStrings("recent q", window[1].content.items[0].Text.items); + + // System prompt survives (derived independently). + var sys_blocks = try conversation_mod.effectiveSystemBlocks(testing.allocator, conv.messages.items); + defer sys_blocks.deinit(testing.allocator); + try testing.expectEqual(@as(usize, 1), sys_blocks.items.len); + try testing.expectEqualStrings("you are helpful", sys_blocks.items[0]); +} |
