summaryrefslogtreecommitdiff
path: root/libpanto/src/anthropic_messages_json.zig
diff options
context:
space:
mode:
Diffstat (limited to 'libpanto/src/anthropic_messages_json.zig')
-rw-r--r--libpanto/src/anthropic_messages_json.zig38
1 files changed, 38 insertions, 0 deletions
diff --git a/libpanto/src/anthropic_messages_json.zig b/libpanto/src/anthropic_messages_json.zig
index 8e33539..940d55e 100644
--- a/libpanto/src/anthropic_messages_json.zig
+++ b/libpanto/src/anthropic_messages_json.zig
@@ -232,6 +232,13 @@ fn writeBlock(s: *std.json.Stringify, block: conversation.ContentBlock) !void {
try s.write("tool_result");
try s.objectField("tool_use_id");
try s.write(tr.tool_use_id);
+ // Anthropic supports an `is_error` marker on tool results;
+ // emit it only when set (omitting it defaults to a success
+ // result on the wire).
+ if (tr.is_error) {
+ try s.objectField("is_error");
+ try s.write(true);
+ }
// Anthropic accepts `content` as an array of typed blocks:
// text, image (base64 source), and document (PDF).
try s.objectField("content");
@@ -1053,12 +1060,43 @@ test "serializeRequest - user ToolResult becomes tool_result content block" {
try testing.expectEqual(@as(usize, 1), arr.len);
try testing.expectEqualStrings("tool_result", arr[0].object.get("type").?.string);
try testing.expectEqualStrings("tu_1", arr[0].object.get("tool_use_id").?.string);
+ // A successful result omits the is_error marker entirely.
+ try testing.expect(arr[0].object.get("is_error") == null);
const tr_content = arr[0].object.get("content").?.array.items;
try testing.expectEqual(@as(usize, 1), tr_content.len);
try testing.expectEqualStrings("text", tr_content[0].object.get("type").?.string);
try testing.expectEqualStrings("the answer is 42", tr_content[0].object.get("text").?.string);
}
+test "serializeRequest - error ToolResult emits is_error: true" {
+ const allocator = testing.allocator;
+
+ var conv = conversation.Conversation.init(allocator);
+ defer conv.deinit();
+
+ const id = try allocator.dupe(u8, "tu_err");
+ var parts: std.ArrayList(conversation.ResultPartStored) = .empty;
+ try parts.append(allocator, .{ .text = try conversation.textualBlockFromSlice(allocator, "file not found") });
+
+ var blocks: std.ArrayList(conversation.ContentBlock) = .empty;
+ try blocks.append(allocator, .{ .ToolResult = .{ .tool_use_id = id, .parts = parts, .is_error = true } });
+ try conv.messages.append(allocator, .{ .role = .user, .content = blocks });
+
+ var empty_tools = tool_registry_mod.ToolRegistry.init(allocator);
+ defer empty_tools.deinit();
+ const cfg = testConfig("claude-x");
+ const body = try serializeRequest(allocator, &cfg, &conv, &empty_tools);
+ defer allocator.free(body);
+
+ var parsed = try std.json.parseFromSlice(std.json.Value, allocator, body, .{});
+ defer parsed.deinit();
+
+ const msg = parsed.value.object.get("messages").?.array.items[0].object;
+ const arr = msg.get("content").?.array.items;
+ try testing.expectEqualStrings("tool_result", arr[0].object.get("type").?.string);
+ try testing.expect(arr[0].object.get("is_error").?.bool);
+}
+
test "serializeRequest - tool result with image part emits image block" {
const allocator = testing.allocator;