summaryrefslogtreecommitdiff
path: root/libpanto/src/agent.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-06-10 10:06:07 -0600
committert <t@tjp.lol>2026-06-10 12:17:27 -0600
commitbb85e867bc938138f29fb45230169af1ba60761c (patch)
tree5ad1cf50274c61ccfffab7048b4a4e28003d82b5 /libpanto/src/agent.zig
parentd26890bbc52e9f0050db40f208763a7f2b714ddd (diff)
Add addUserText helper; update all call sites
Conversation.addUserMessage now takes a []ContentBlock (symmetric with addAssistantMessage). Introduce a thin addUserText wrapper in agent.zig for the plain-text case and update every call site in agent.zig and anthropic_messages_json.zig accordingly.
Diffstat (limited to 'libpanto/src/agent.zig')
-rw-r--r--libpanto/src/agent.zig35
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);