diff options
Diffstat (limited to 'libpanto/src/anthropic_messages_json.zig')
| -rw-r--r-- | libpanto/src/anthropic_messages_json.zig | 47 |
1 files changed, 42 insertions, 5 deletions
diff --git a/libpanto/src/anthropic_messages_json.zig b/libpanto/src/anthropic_messages_json.zig index 46ec17c..5b65c2b 100644 --- a/libpanto/src/anthropic_messages_json.zig +++ b/libpanto/src/anthropic_messages_json.zig @@ -64,10 +64,11 @@ pub fn serializeRequest( if (tools.count() > 0) { try s.objectField("tools"); try s.beginArray(); - var it = tools.iterator(); + var it = tools.toolsForLLM(); while (it.next()) |t| { try s.beginObject(); try s.objectField("name"); + // `t.decl.name` is already wire-encoded by `toolsForLLM`. try s.write(t.decl.name); try s.objectField("description"); try s.write(t.decl.description); @@ -180,7 +181,9 @@ fn writeBlock(s: *std.json.Stringify, block: conversation.ContentBlock) !void { try s.objectField("id"); try s.write(tu.id); try s.objectField("name"); - try s.write(tu.name); + // Replayed assistant tool_use: encode the stored dotted name. + var name_buf: [tool_registry_mod.max_wire_name_len]u8 = undefined; + try s.write(tool_registry_mod.encodeName(&name_buf, tu.name)); try s.objectField("input"); // Anthropic expects `input` as a nested JSON object, not a // string. The block's input bytes were assembled from @@ -801,9 +804,7 @@ test "serializeRequest - emits tools array when registry non-empty" { var tools = tool_registry_mod.ToolRegistry.init(allocator); defer tools.deinit(); - try tools.register(makeStaticTool( - "echo", - "Echo a message back.", + try tools.register(makeStaticTool("echo", "Echo a message back.", \\{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]} )); @@ -824,6 +825,42 @@ test "serializeRequest - emits tools array when registry non-empty" { try testing.expect(schema.get("properties").? == .object); } +test "serializeRequest - dotted names are wire-encoded in tools and history" { + const allocator = testing.allocator; + + var conv = conversation.Conversation.init(allocator); + defer conv.deinit(); + // A prior assistant tool_use replayed from history (dotted internally). + const id = try allocator.dupe(u8, "tu_1"); + const name = try allocator.dupe(u8, "std.write"); + var input: conversation.TextualBlock = .empty; + try input.appendSlice(allocator, "{}"); + try conv.addAssistantMessage(&.{ + .{ .ToolUse = .{ .id = id, .name = name, .input = input } }, + }); + + var tools = tool_registry_mod.ToolRegistry.init(allocator); + defer tools.deinit(); + try tools.register(makeStaticTool("std.read", "Read.", "{\"type\":\"object\"}")); + + const cfg = testConfig("claude-x"); + 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(); + + // Tool list: `std.read` -> `std__read`. + const tool_name = parsed.value.object.get("tools").?.array.items[0] + .object.get("name").?.string; + try testing.expectEqualStrings("std__read", tool_name); + + // Replayed history tool_use: `std.write` -> `std__write`. + const hist_name = parsed.value.object.get("messages").?.array.items[0] + .object.get("content").?.array.items[0].object.get("name").?.string; + try testing.expectEqualStrings("std__write", hist_name); +} + test "serializeRequest - assistant ToolUse becomes tool_use content block" { const allocator = testing.allocator; |
