From 75dedacfbeab1fea281d1bce124b920dcf878844 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 7 Jun 2026 21:56:51 -0600 Subject: further libpanto public API name cleanup --- libpanto/src/tool.zig | 84 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 35 deletions(-) (limited to 'libpanto/src/tool.zig') diff --git a/libpanto/src/tool.zig b/libpanto/src/tool.zig index 60a7736..c96dfee 100644 --- a/libpanto/src/tool.zig +++ b/libpanto/src/tool.zig @@ -31,10 +31,11 @@ pub const MediaPart = struct { data: []const u8, }; -/// One element of a tool's result. A tool returns a `[]ResultPart`; the -/// agent assembles these into a `ToolResultBlock`. Bytes referenced by a -/// part are owned by the allocator passed to `invoke` / `invoke_batch`; -/// ownership transfers to the agent, which frees them. +/// One element of a tool's result. A tool returns a `ResultParts` (a thin +/// wrapper around `[]ResultPart`); the agent assembles these into a +/// `ToolResultBlock`. Bytes referenced by a part are owned by the allocator +/// passed to `invoke` / `invoke_batch`; ownership transfers to the agent, +/// which frees them. pub const ResultPart = union(enum) { text: []const u8, media: MediaPart, @@ -51,11 +52,42 @@ pub const ResultPart = union(enum) { } }; -/// Free a `[]ResultPart` and every part it owns. -pub fn freeResultParts(allocator: Allocator, parts: []ResultPart) void { - for (parts) |p| p.deinit(allocator); - allocator.free(parts); -} +/// A tool's full result: an owned slice of `ResultPart`s. The value the +/// `Tool`/`ToolSource` vtable returns and the agent loop assembles — a thin +/// wrapper around `[]ResultPart` that carries the construction/teardown +/// ergonomics a bare slice alias can't. Build one with +/// `fromText`/`fromTextOwned` (or wrap a hand-built slice as +/// `.{ .items = slice }`); release it (slice + every part's bytes) with +/// `deinit`. +pub const ResultParts = struct { + items: []ResultPart, + + /// A single text part that owns `text` (duped from the input slice). + pub fn fromText(allocator: Allocator, text: []const u8) !ResultParts { + const owned = try allocator.dupe(u8, text); + errdefer allocator.free(owned); + const parts = try allocator.alloc(ResultPart, 1); + parts[0] = .{ .text = owned }; + return .{ .items = parts }; + } + + /// A single text part wrapping an already-owned `text` slice. Takes + /// ownership of `text` (frees it if the allocation below fails). + pub fn fromTextOwned(allocator: Allocator, text: []u8) !ResultParts { + const parts = allocator.alloc(ResultPart, 1) catch |e| { + allocator.free(text); + return e; + }; + parts[0] = .{ .text = text }; + return .{ .items = parts }; + } + + /// Free the slice and every part it owns. + pub fn deinit(self: ResultParts, allocator: Allocator) void { + for (self.items) |p| p.deinit(allocator); + allocator.free(self.items); + } +}; pub const Tool = struct { /// Metadata: `name`, `description`, `schema_json`. Borrowed — the @@ -78,11 +110,13 @@ pub const Tool = struct { /// `input` is the raw JSON bytes the provider sent. The tool is /// responsible for parsing them if it cares about their structure. /// - /// Returns an owned slice of `ResultPart`s allocated with - /// `allocator`; each part's bytes are likewise owned. These become - /// the parts of the ToolResult block sent back to the LLM. The - /// agent takes ownership and frees the slice and every part (see - /// `freeResultParts`). + /// Returns a `ResultParts` allocated with `allocator`; each part's + /// bytes are likewise owned. These become the parts of the + /// ToolResult block sent back to the LLM. The agent takes ownership + /// and frees the slice and every part (see `ResultParts.deinit`). + /// Build the return value with `ResultParts.fromText` / + /// `.fromTextOwned` for the common single-text case, or wrap a + /// hand-built slice as `.{ .items = slice }`. /// /// Returning an error normally becomes a model-visible error /// `ToolResult`: the agent synthesizes an error result for this @@ -100,7 +134,7 @@ pub const Tool = struct { ctx: *anyopaque, input: []const u8, allocator: Allocator, - ) anyerror![]ResultPart, + ) anyerror!ResultParts, /// Called when the tool is unregistered or the registry is torn /// down. Frees any resources owned by `ctx`, including `ctx` @@ -113,23 +147,3 @@ pub const Tool = struct { }; }; -/// Convenience: allocate a single-element `[]ResultPart` holding one text -/// part that owns `text` (duped from the input slice). -pub fn textResult(allocator: Allocator, text: []const u8) ![]ResultPart { - const owned = try allocator.dupe(u8, text); - errdefer allocator.free(owned); - const parts = try allocator.alloc(ResultPart, 1); - parts[0] = .{ .text = owned }; - return parts; -} - -/// Convenience: wrap an already-owned `text` slice as a single-element -/// `[]ResultPart`. Takes ownership of `text`. -pub fn ownedTextResult(allocator: Allocator, text: []u8) ![]ResultPart { - const parts = allocator.alloc(ResultPart, 1) catch |e| { - allocator.free(text); - return e; - }; - parts[0] = .{ .text = text }; - return parts; -} -- cgit v1.3