diff options
Diffstat (limited to 'libpanto/src/agent.zig')
| -rw-r--r-- | libpanto/src/agent.zig | 35 |
1 files changed, 23 insertions, 12 deletions
diff --git a/libpanto/src/agent.zig b/libpanto/src/agent.zig index 378205a..0252af6 100644 --- a/libpanto/src/agent.zig +++ b/libpanto/src/agent.zig @@ -55,6 +55,17 @@ pub const Config = config_mod.Config; /// usage per message, used for retention sizing). pub const conversation_Usage = @import("session.zig").Usage; +/// Append a single-text user message. `Conversation.addUserMessage` now +/// takes a block slice (symmetric with `addAssistantMessage`); this wraps +/// the common plain-text case used by the agent's interactive turn and the +/// compaction prompt. +fn addUserText(conv: *conversation.Conversation, text: []const u8) !void { + const tb = try conversation.textualBlockFromSlice(conv.allocator, text); + var block: conversation.ContentBlock = .{ .Text = tb }; + errdefer block.deinit(conv.allocator); + try conv.addUserMessage(&.{block}); +} + /// Deep-copy a message (role + all content blocks) into fresh owned /// allocations. Used when rebuilding the conversation after compaction. fn cloneMessage(alloc: Allocator, msg: conversation.Message) !conversation.Message { @@ -373,7 +384,7 @@ pub const Agent = struct { // Append + persist the user prompt up front (the dangling-prompt // recovery guarantee). const user_start = self.conversation.messages.items.len; - try self.conversation.addUserMessage(message.text); + try addUserText(&self.conversation, message.text); try turn_persist.persistTurn( self._allocator, &self._session, @@ -816,7 +827,7 @@ pub const Agent = struct { var conv = conversation.Conversation.init(alloc); defer conv.deinit(); try conv.addSystemMessage(system_prompt); - try conv.addUserMessage(body); + try addUserText(&conv, body); // Drive one provider response to completion, ignoring every event. // Compaction doesn't need incremental output — the assembled message @@ -2681,11 +2692,11 @@ test "compact: summarizes prefix, keeps suffix, system survives" { const conv = &agent.conversation; try conv.addSystemMessage("you are helpful"); - try conv.addUserMessage("first question here with several words"); + try addUserText(conv, "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 addUserText(conv, "second recent question"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "second recent answer") }, }, null); @@ -2746,21 +2757,21 @@ test "compact: restated suffix usage reconstructs a fresh cumulative chain" { // Prefix turn (will be summarized). Cumulative footprint = 500+40+10+50 // = 600. Its real usage (with cache buckets) is irrelevant // post-compaction. - try conv.addUserMessage("first question here with several words"); + try addUserText(conv, "first question here with several words"); 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 addUserText(conv, "kept question"); // 2 words => ceil(2*1.3)=3 tokens 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 addUserText(conv, "two more words here"); // 4 words => ceil(4*1.3)=6 try conv.addAssistantMessage( &.{.{ .Text = try conversation.textualBlockFromSlice(allocator, "final answer") }}, .{ .input = 600, .output = 4, .cache_read = 8, .cache_write = 2 }, @@ -2818,7 +2829,7 @@ test "compact: no-op when conversation already fits the budget" { const conv = &agent.conversation; try conv.addSystemMessage("sys"); - try conv.addUserMessage("hi"); + try addUserText(conv, "hi"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "hello") }, }, null); @@ -2855,11 +2866,11 @@ test "compact: extra instructions are appended to the system prompt" { agent._open_stream_fn = stub.install(); const conv = &agent.conversation; - try conv.addUserMessage("question one two three"); + try addUserText(conv, "question one two three"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "answer one two three") }, }, null); - try conv.addUserMessage("question two"); + try addUserText(conv, "question two"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "answer two") }, }, null); @@ -2897,7 +2908,7 @@ test "runStep: auto-compacts on context overflow and retries once" { const conv = &agent.conversation; try conv.addSystemMessage("you are helpful"); - try conv.addUserMessage("first question with several words here"); + try addUserText(conv, "first question with several words here"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "first answer with several words") }, }, null); @@ -3277,7 +3288,7 @@ test "runStep: context-overflow compaction fires a compaction retry notification const conv = &agent.conversation; try conv.addSystemMessage("you are helpful"); - try conv.addUserMessage("first question with several words here"); + try addUserText(conv, "first question with several words here"); try conv.addAssistantMessage(&.{ .{ .Text = try conversation.textualBlockFromSlice(allocator, "first answer with several words") }, }, null); |
