diff options
Diffstat (limited to 'libpanto/src/agent.zig')
| -rw-r--r-- | libpanto/src/agent.zig | 33 |
1 files changed, 31 insertions, 2 deletions
diff --git a/libpanto/src/agent.zig b/libpanto/src/agent.zig index 08cc91c..a42c16c 100644 --- a/libpanto/src/agent.zig +++ b/libpanto/src/agent.zig @@ -43,6 +43,23 @@ const Entry = tool_registry_mod.Entry; pub const Config = config_mod.Config; +fn isValidToolInput(input: []const u8) bool { + if (input.len == 0) return true; + if (input[0] != '{') return true; // legacy tests/tools may use opaque bytes + var parsed = std.json.parseFromSlice(std.json.Value, std.heap.page_allocator, input, .{}) catch return false; + defer parsed.deinit(); + return parsed.value == .object; +} + +fn invalidInputResult(allocator: Allocator, input: []const u8) ![]u8 { + return std.fmt.allocPrint( + allocator, + "Tool call was not executed: tool input was incomplete or invalid JSON. Partial input: {s}", + .{input}, + ); +} + + pub const Agent = struct { allocator: Allocator, io: Io, @@ -132,6 +149,17 @@ pub const Agent = struct { for (assistant_msg.content.items) |block| { if (block != .ToolUse) continue; const tu = block.ToolUse; + if (!isValidToolInput(tu.input.items)) { + try calls.append(.{ + .tool_use_id = tu.id, + .tool_name = tu.name, + .input = tu.input.items, + .entry = null, + .result = try invalidInputResult(self.allocator, tu.input.items), + .err = null, + }); + continue; + } const entry = self.config.registry.lookup(tu.name) orelse { // Unknown tool: abort the turn with a clear error. return error.UnknownTool; @@ -236,7 +264,7 @@ const FlatCall = struct { tool_use_id: []const u8, // borrowed from assistant_msg tool_name: []const u8, // borrowed from assistant_msg input: []const u8, // borrowed from assistant_msg - entry: Entry, + entry: ?Entry, /// Owned result bytes from `Tool.invoke` or `ToolSource.invoke_batch`. /// Allocated with the agent's allocator. Transferred into a @@ -292,7 +320,8 @@ fn buildGroups( } for (calls, 0..) |c, i| { - switch (c.entry) { + const ent = c.entry orelse continue; + switch (ent) { .single => |t| try out.append(.{ .single = .{ .tool = t, .call_index = i } }), .source => |sr| { const gop = try pending.getOrPut(sr.source); |
