summaryrefslogtreecommitdiff
path: root/libpanto/src/conversation.zig
diff options
context:
space:
mode:
Diffstat (limited to 'libpanto/src/conversation.zig')
-rw-r--r--libpanto/src/conversation.zig45
1 files changed, 34 insertions, 11 deletions
diff --git a/libpanto/src/conversation.zig b/libpanto/src/conversation.zig
index a436378..1942a0c 100644
--- a/libpanto/src/conversation.zig
+++ b/libpanto/src/conversation.zig
@@ -265,10 +265,24 @@ pub const Conversation = struct {
});
}
- pub fn addUserMessage(self: *Conversation, text: []const u8) !void {
- const tb = try textualBlockFromSlice(self.allocator, text);
+ /// Append a `user`-role message from a slice of content blocks.
+ /// Symmetric with `addAssistantMessage`: ownership of the blocks (and
+ /// every byte they reference) transfers to the conversation; the caller
+ /// must not deinit them after this call. This is the general user-side
+ /// builder — a user turn may carry plain text, multiple text blocks
+ /// (e.g. messages queued while the agent was mid-turn), one or more
+ /// `.ToolResult` blocks, or any mix. For the common single-text case,
+ /// build a one-element `.{ .Text = ... }` slice (see the `addUserText`
+ /// test helpers across the codebase).
+ ///
+ /// Blocks must use this conversation's allocator for any owned bytes,
+ /// since the conversation will free them with that allocator.
+ pub fn addUserMessage(self: *Conversation, blocks: []const ContentBlock) !void {
var content: std.ArrayList(ContentBlock) = .empty;
- try content.append(self.allocator, .{ .Text = tb });
+ try content.ensureTotalCapacity(self.allocator, blocks.len);
+ for (blocks) |block| {
+ content.appendAssumeCapacity(block);
+ }
try self.messages.append(self.allocator, .{
.role = .user,
.content = content,
@@ -401,6 +415,15 @@ pub fn activeMessageWindow(messages: []const Message) []const Message {
return messages;
}
+/// Test helper: append a single-text user message. `addUserMessage` takes
+/// a block slice; this wraps the plain-text case the tests below use.
+fn addUserText(conv: *Conversation, text: []const u8) !void {
+ const tb = try textualBlockFromSlice(conv.allocator, text);
+ var block: ContentBlock = .{ .Text = tb };
+ errdefer block.deinit(conv.allocator);
+ try conv.addUserMessage(&.{block});
+}
+
test "Conversation - add messages and verify content" {
const allocator = std.testing.allocator;
@@ -408,7 +431,7 @@ test "Conversation - add messages and verify content" {
defer conv.deinit();
try conv.addSystemMessage("You are a helpful assistant.");
- try conv.addUserMessage("Hello!");
+ try addUserText(&conv, "Hello!");
try conv.addAssistantMessage(&.{
.{ .Text = try textualBlockFromSlice(allocator, "Hi there!") },
}, null);
@@ -448,7 +471,7 @@ test "Conversation - deinit frees without leaks" {
var conv = Conversation.init(allocator);
try conv.addSystemMessage("system");
- try conv.addUserMessage("user message");
+ try addUserText(&conv, "user message");
try conv.addAssistantMessage(&.{
.{ .Text = try textualBlockFromSlice(allocator, "response") },
}, null);
@@ -494,7 +517,7 @@ test "effectiveSystemBlocks - append accumulates in order" {
try conv.addSystemMessage("a");
try conv.addSystemMessage("b");
- try conv.addUserMessage("hi");
+ try addUserText(&conv, "hi");
try conv.addSystemMessage("c");
var blocks = try effectiveSystemBlocks(allocator, conv.messages.items);
@@ -548,7 +571,7 @@ test "addCompactionSummary - sits alone in a user message" {
var conv = Conversation.init(allocator);
defer conv.deinit();
- try conv.addUserMessage("hi");
+ try addUserText(&conv, "hi");
try conv.addCompactionSummary("summary of earlier history");
const m = conv.messages.items[1];
@@ -567,7 +590,7 @@ test "latestCompactionIndex - null when never compacted" {
defer conv.deinit();
try conv.addSystemMessage("sys");
- try conv.addUserMessage("hi");
+ try addUserText(&conv, "hi");
try conv.addAssistantMessage(&.{
.{ .Text = try textualBlockFromSlice(allocator, "hello") },
}, null);
@@ -582,11 +605,11 @@ test "latestCompactionIndex - returns the latest summary anchor" {
defer conv.deinit();
try conv.addSystemMessage("sys");
- try conv.addUserMessage("old");
+ try addUserText(&conv, "old");
try conv.addCompactionSummary("S1");
- try conv.addUserMessage("mid");
+ try addUserText(&conv, "mid");
try conv.addCompactionSummary("S2"); // index 4
- try conv.addUserMessage("recent");
+ try addUserText(&conv, "recent");
try std.testing.expectEqual(@as(?usize, 4), latestCompactionIndex(conv.messages.items));
}