From 16ea45b6854232541fb45f7d2e5b767118cccf43 Mon Sep 17 00:00:00 2001 From: t Date: Tue, 2 Jun 2026 10:50:05 -0600 Subject: tool call resiliency --- src/main.zig | 70 +++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 22 deletions(-) (limited to 'src/main.zig') diff --git a/src/main.zig b/src/main.zig index 20686eb..28f1144 100644 --- a/src/main.zig +++ b/src/main.zig @@ -704,6 +704,13 @@ fn persistTurn( model: []const u8, per_message_usage: []const ?panto.session_manager.Usage, ) !void { + var batch_messages: std.ArrayList(panto.session_manager.DiskMessage) = .empty; + defer batch_messages.deinit(alloc); + var batch_providers: std.ArrayList(?[]const u8) = .empty; + defer batch_providers.deinit(alloc); + var batch_models: std.ArrayList(?[]const u8) = .empty; + defer batch_models.deinit(alloc); + var i = start_index; var assistant_seen: usize = 0; while (i < conv.messages.items.len) : (i += 1) { @@ -718,22 +725,23 @@ fn persistTurn( blocks[allocated] = try panto.session.contentBlockToDisk(alloc, block); allocated += 1; } + if (msg.role == .assistant and hasToolUseWithoutFollowingResults(conv, i)) { + for (blocks[0..allocated]) |b| b.deinit(alloc); + alloc.free(blocks); + continue; + } switch (msg.role) { .system => { // Mid-turn system messages aren't a thing in pantograph today. // Treat as harmless and persist verbatim, sans stamps. - _ = try mgr.appendMessage( - .{ .role = .system, .content = blocks }, - null, - null, - ); + try batch_messages.append(alloc, .{ .role = .system, .content = blocks }); + try batch_providers.append(alloc, null); + try batch_models.append(alloc, null); }, .user => { - _ = try mgr.appendMessage( - .{ .role = .user, .content = blocks }, - provider, - model, - ); + try batch_messages.append(alloc, .{ .role = .user, .content = blocks }); + try batch_providers.append(alloc, provider); + try batch_models.append(alloc, model); }, .assistant => { // Pair this assistant message with its usage, if the @@ -745,19 +753,37 @@ fn persistTurn( else null; assistant_seen += 1; - _ = try mgr.appendMessage( - .{ - .role = .assistant, - .content = blocks, - .provider = try alloc.dupe(u8, provider), - .model = try alloc.dupe(u8, model), - .stop_reason = try alloc.dupe(u8, "stop"), - .usage = usage, - }, - null, - null, - ); + try batch_messages.append(alloc, .{ + .role = .assistant, + .content = blocks, + .provider = try alloc.dupe(u8, provider), + .model = try alloc.dupe(u8, model), + .stop_reason = try alloc.dupe(u8, "stop"), + .usage = usage, + }); + try batch_providers.append(alloc, null); + try batch_models.append(alloc, null); }, } } + try mgr.appendMessagesAtomic(batch_messages.items, batch_providers.items, batch_models.items); +} + +fn hasToolUseWithoutFollowingResults(conv: *const panto.conversation.Conversation, index: usize) bool { + const msg = conv.messages.items[index]; + for (msg.content.items) |block| { + if (block != .ToolUse) continue; + if (index + 1 >= conv.messages.items.len) return true; + const next = conv.messages.items[index + 1]; + if (next.role != .user) return true; + var found = false; + for (next.content.items) |next_block| { + if (next_block == .ToolResult and std.mem.eql(u8, next_block.ToolResult.tool_use_id, block.ToolUse.id)) { + found = true; + break; + } + } + if (!found) return true; + } + return false; } -- cgit v1.3