summaryrefslogtreecommitdiff
path: root/libpanto/src/openai_chat_json.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-06-02 15:00:44 -0600
committert <t@tjp.lol>2026-06-02 16:37:32 -0600
commit8b88b886346460b1ab29edb03ad85bc3bb565a45 (patch)
tree13b9ab56061a722641389b5fc8ac1113d09b66e5 /libpanto/src/openai_chat_json.zig
parent7268c0b8e8bcf4b325581fabe7476a394f167738 (diff)
compaction
Diffstat (limited to 'libpanto/src/openai_chat_json.zig')
-rw-r--r--libpanto/src/openai_chat_json.zig48
1 files changed, 46 insertions, 2 deletions
diff --git a/libpanto/src/openai_chat_json.zig b/libpanto/src/openai_chat_json.zig
index 8e6c9be..8035338 100644
--- a/libpanto/src/openai_chat_json.zig
+++ b/libpanto/src/openai_chat_json.zig
@@ -158,8 +158,11 @@ pub fn serializeRequest(
try s.write(text);
try s.endObject();
}
- // Then every non-system message, in its original order.
- for (conv.messages.items) |msg| {
+ // Then every non-system message, in its original order. If the
+ // conversation has been compacted, only the latest compaction summary
+ // and the messages after it are active; the superseded prefix is
+ // dropped.
+ for (conversation.activeMessageWindow(conv.messages.items)) |msg| {
if (msg.role == .system) continue;
try writeMessage(&s, msg, allocator);
}
@@ -309,6 +312,9 @@ fn concatTextBlocks(
for (blocks) |block| {
switch (block) {
.Text => |tb| try out.appendSlice(allocator, tb.items),
+ // A compaction summary is the synthetic seed text standing in
+ // for a compacted prefix; emit it as ordinary message text.
+ .CompactionSummary => |cs| try out.appendSlice(allocator, cs.text.items),
// Thinking, ToolUse, ToolResult: handled elsewhere or dropped.
else => {},
}
@@ -945,3 +951,41 @@ test "parseStreamEvent - bare-string error" {
try testing.expectEqualStrings("something went wrong", pd.delta.error_message.?);
try testing.expect(pd.delta.error_type == null);
}
+
+test "serializeRequest - compaction summary trims superseded prefix" {
+ const allocator = testing.allocator;
+
+ var conv = conversation.Conversation.init(allocator);
+ defer conv.deinit();
+
+ // System prompt survives compaction.
+ try conv.addSystemMessage("you are helpful");
+ // Old prefix that must be dropped.
+ try conv.addUserMessage("ancient question");
+ try conv.addAssistantMessage(&.{
+ .{ .Text = try conversation.textualBlockFromSlice(allocator, "ancient answer") },
+ });
+ // Compaction summary resets conversation context.
+ try conv.addCompactionSummary("summary of the ancient exchange");
+ // Kept-verbatim suffix replayed after the summary.
+ try conv.addUserMessage("recent question");
+
+ var tools = emptyTools();
+ defer tools.deinit();
+ const cfg = testConfig("gpt-4o");
+ const body = try serializeRequest(allocator, &cfg, &conv, &tools);
+ defer allocator.free(body);
+
+ var parsed = try std.json.parseFromSlice(std.json.Value, allocator, body, .{});
+ defer parsed.deinit();
+
+ const msgs = parsed.value.object.get("messages").?.array.items;
+ // system + compaction summary (user) + recent question (user) = 3.
+ try testing.expectEqual(@as(usize, 3), msgs.len);
+ try testing.expectEqualStrings("system", msgs[0].object.get("role").?.string);
+ try testing.expectEqualStrings("you are helpful", msgs[0].object.get("content").?.string);
+ try testing.expectEqualStrings("user", msgs[1].object.get("role").?.string);
+ try testing.expectEqualStrings("summary of the ancient exchange", msgs[1].object.get("content").?.string);
+ try testing.expectEqualStrings("user", msgs[2].object.get("role").?.string);
+ try testing.expectEqualStrings("recent question", msgs[2].object.get("content").?.string);
+}