From 17a985cb41727b8a1bca4d95f334106c09386040 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 7 Jun 2026 14:57:58 -0600 Subject: Alias Conversation instead of facading it; merge addAssistantMessage A facade that forwards every method 1:1 is worse than paring the real type down to the intended surface and aliasing it. conversation.Conversation's interface is already the public surface we want (init/deinit, the add*/replace* builders, and messages/allocator as plain data fields), so public.zig now aliases it straight through and Agent.conversation() returns a borrowed *Conversation for in-place surgery. Drops the ConversationData alias and the pointer-wrapper. Merged addAssistantMessageWithUsage into addAssistantMessage(blocks, ?usage) on the real type (separate method deleted), so the alias has the intended one-method shape; all call sites updated. Only Agent and Stream remain true facades -- they have genuinely-internal fields plus API-shaping renames (Stream.phase->state, Phase->State) an alias can't express. --- libpanto/src/agent.zig | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'libpanto/src/agent.zig') diff --git a/libpanto/src/agent.zig b/libpanto/src/agent.zig index 6d62b8a..45347ea 100644 --- a/libpanto/src/agent.zig +++ b/libpanto/src/agent.zig @@ -1552,7 +1552,7 @@ const StubResponse = struct { } const moved = try blocks.toOwnedSlice(self.allocator); defer self.allocator.free(moved); - try self.conv.addAssistantMessageWithUsage(moved, self.turn.usage); + try self.conv.addAssistantMessage(moved, self.turn.usage); const msg = self.conv.messages.items[self.conv.messages.items.len - 1]; try out.push(.{ .message_complete = .{ .message = msg, .usage = self.turn.usage } }); @@ -2651,11 +2651,11 @@ test "compact: summarizes prefix, keeps suffix, system survives" { try conv.addUserMessage("first question here with several words"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "first answer with several words") }, - }); + }, null); try conv.addUserMessage("second recent question"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "second recent answer") }, - }); + }, null); const res = try agent.compact("Summarize the conversation.", null); try testing.expect(res.compacted); @@ -2714,21 +2714,21 @@ test "compact: restated suffix usage reconstructs a fresh cumulative chain" { // = 600. Its real usage (with cache buckets) is irrelevant // post-compaction. try conv.addUserMessage("first question here with several words"); - try conv.addAssistantMessageWithUsage( + try conv.addAssistantMessage( &.{.{ .Text = try conversation.textualBlockFromSlice(allocator, "first answer with words") }}, .{ .input = 500, .output = 50, .cache_read = 40, .cache_write = 10 }, ); // Kept turn 1: cumulative 608 (delta 8 over prefix). Reasoning is a // subset of output. try conv.addUserMessage("kept question"); // 2 words => ceil(2*1.3)=3 tokens - try conv.addAssistantMessageWithUsage( + try conv.addAssistantMessage( &.{.{ .Text = try conversation.textualBlockFromSlice(allocator, "kept answer") }}, .{ .input = 600, .output = 8, .reasoning = 5 }, ); // Kept turn 2: cumulative 614 (delta 6). Cache buckets present to prove // they collapse into `input` on restatement. try conv.addUserMessage("two more words here"); // 4 words => ceil(4*1.3)=6 - try conv.addAssistantMessageWithUsage( + try conv.addAssistantMessage( &.{.{ .Text = try conversation.textualBlockFromSlice(allocator, "final answer") }}, .{ .input = 600, .output = 4, .cache_read = 8, .cache_write = 2 }, ); @@ -2788,7 +2788,7 @@ test "compact: no-op when conversation already fits the budget" { try conv.addUserMessage("hi"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "hello") }, - }); + }, null); const res = try agent.compact("Summarize.", null); try testing.expect(!res.compacted); @@ -2825,11 +2825,11 @@ test "compact: extra instructions are appended to the system prompt" { try conv.addUserMessage("question one two three"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "answer one two three") }, - }); + }, null); try conv.addUserMessage("question two"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "answer two") }, - }); + }, null); const res = try agent.compact("Base prompt.", "keep bug #3 details"); try testing.expect(res.compacted); @@ -2867,7 +2867,7 @@ test "runStep: auto-compacts on context overflow and retries once" { try conv.addUserMessage("first question with several words here"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "first answer with several words") }, - }); + }, null); try drainTurn(&agent, "second recent question"); @@ -3247,7 +3247,7 @@ test "runStep: context-overflow compaction fires a compaction retry notification try conv.addUserMessage("first question with several words here"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "first answer with several words") }, - }); + }, null); var rr = RetryRecorder{ .allocator = allocator }; defer rr.deinit(); -- cgit v1.3