Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156const std = @import("std");
pub fn parseToolArgsObject(alloc: std.mem.Allocator, args_json: []const u8) !std.json.ObjectMap { const parsed = try std.json.parseFromSlice(std.json.Value, alloc, args_json, .{}); // The returned object map is backed by the parse allocation; callers should // use an arena or allocator lifetime that outlives all borrowed values. if (parsed.value != .object) return error.InvalidToolArguments; return parsed.value.object;}
pub fn normalizeCompositeObjectValue( alloc: std.mem.Allocator, value: *std.json.Value,) !void { if (value.* != .string) return; const decoded = try std.json.parseFromSliceLeaky( std.json.Value, alloc, value.string, .{ .allocate = .alloc_always }, ); if (decoded != .object) return error.InvalidCompositeArgument; value.* = decoded;}
pub fn requiredStringArg(args: std.json.ObjectMap, key: []const u8) ![]const u8 { const value = args.get(key) orelse return error.InvalidToolArguments; if (value != .string) return error.InvalidToolArguments; return value.string;}
pub fn optionalStringArg(args: std.json.ObjectMap, key: []const u8) ?[]const u8 { const value = args.get(key) orelse return null; if (value != .string) return null; return value.string;}
/// Tool schemas that require every field tell the model to send null for the/// fields its selected action does not use. Models routinely serialize that null/// as the literal text "null", so readers treat it as the absence it expresses.pub fn isNullPlaceholderText(text: []const u8) bool { return std.ascii.eqlIgnoreCase(std.mem.trim(u8, text, &std.ascii.whitespace), "null");}
/// Reads an optional string argument from a schema whose unused fields arrive as/// nulls, so textual null placeholders read as absent instead of as a value.pub fn nullablePlaceholderStringArg(args: std.json.ObjectMap, key: []const u8) ?[]const u8 { const text = optionalStringArg(args, key) orelse return null; if (isNullPlaceholderText(text)) return null; return text;}
pub fn optionalBoolArg(args: std.json.ObjectMap, key: []const u8) ?bool { const value = args.get(key) orelse return null; if (value != .bool) return null; return value.bool;}
pub fn optionalIntArg(args: std.json.ObjectMap, key: []const u8) ?i64 { const value = args.get(key) orelse return null; if (value != .integer) return null; return value.integer;}
test "parseToolArgsObject parses object roots" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
const args = try parseToolArgsObject(arena, "{\"path\":\"src/main.zig\"}");
try std.testing.expectEqualStrings("src/main.zig", args.get("path").?.string);}
test "parseToolArgsObject rejects non-object roots" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
try std.testing.expectError(error.InvalidToolArguments, parseToolArgsObject(arena, "[]"));}
test "requiredStringArg rejects missing and wrong-type values" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
const args = try parseToolArgsObject(arena, "{\"path\":\"src/main.zig\",\"count\":3}");
try std.testing.expectEqualStrings("src/main.zig", try requiredStringArg(args, "path")); try std.testing.expectError(error.InvalidToolArguments, requiredStringArg(args, "missing")); try std.testing.expectError(error.InvalidToolArguments, requiredStringArg(args, "count"));}
test "optional typed args return payloads only for matching tags" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
const args = try parseToolArgsObject(arena, "{\"name\":\"fx\",\"enabled\":true,\"count\":3,\"other\":1.25}");
try std.testing.expectEqualStrings("fx", optionalStringArg(args, "name").?); try std.testing.expect(optionalStringArg(args, "missing") == null); try std.testing.expect(optionalStringArg(args, "enabled") == null);
try std.testing.expectEqual(true, optionalBoolArg(args, "enabled").?); try std.testing.expect(optionalBoolArg(args, "missing") == null); try std.testing.expect(optionalBoolArg(args, "name") == null);
try std.testing.expectEqual(@as(i64, 3), optionalIntArg(args, "count").?); try std.testing.expect(optionalIntArg(args, "missing") == null); try std.testing.expect(optionalIntArg(args, "other") == null);}
test "null placeholder reads treat textual nulls as absent" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
const args = try parseToolArgsObject( arena, "{\"cwd\":\"null\",\"profile\":\" NULL \",\"shell\":null,\"command\":\"echo null\",\"dir\":\"nullify\"}", );
try std.testing.expect(nullablePlaceholderStringArg(args, "cwd") == null); try std.testing.expect(nullablePlaceholderStringArg(args, "profile") == null); try std.testing.expect(nullablePlaceholderStringArg(args, "shell") == null); try std.testing.expect(nullablePlaceholderStringArg(args, "missing") == null); try std.testing.expectEqualStrings("echo null", nullablePlaceholderStringArg(args, "command").?); try std.testing.expectEqualStrings("nullify", nullablePlaceholderStringArg(args, "dir").?); try std.testing.expectEqualStrings("null", optionalStringArg(args, "cwd").?);}
test "composite object normalization preserves objects and decodes JSON strings" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
var object = std.json.Value{ .object = try parseToolArgsObject( arena, "{\"kind\":\"executable\",\"path\":\"/bin/bash\"}", ) }; try normalizeCompositeObjectValue(arena, &object); try std.testing.expectEqualStrings("/bin/bash", object.object.get("path").?.string);
var encoded = std.json.Value{ .string = "{\"kind\":\"executable\",\"path\":\"/bin/zsh\"}" }; try normalizeCompositeObjectValue(arena, &encoded); try std.testing.expectEqualStrings("/bin/zsh", encoded.object.get("path").?.string);
var invalid = std.json.Value{ .string = "[]" }; try std.testing.expectError( error.InvalidCompositeArgument, normalizeCompositeObjectValue(arena, &invalid), );}