summaryrefslogtreecommitdiff
path: root/libpanto/src/tool.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-07-07 11:29:23 -0600
committert <t@tjp.lol>2026-07-07 12:15:28 -0600
commit621d7fee0ace729f8d44126032d2c6e13f72ee7f (patch)
treea2938b0a9e5c0930e8644721abbe94875df9ff08 /libpanto/src/tool.zig
parent0fa6d4209ff9b4a95e7d1955887aa4c73ee3423c (diff)
Move libpanto projects to libpantograph dependencyHEADmain
Remove the in-repo libpanto sources and binding projects from pantograph. Consume libpantograph through the Zig package URL at code.tjp.lol/libpantograph.git, including the Lua module artifact used by CLI extensions.
Diffstat (limited to 'libpanto/src/tool.zig')
-rw-r--r--libpanto/src/tool.zig149
1 files changed, 0 insertions, 149 deletions
diff --git a/libpanto/src/tool.zig b/libpanto/src/tool.zig
deleted file mode 100644
index c96dfee..0000000
--- a/libpanto/src/tool.zig
+++ /dev/null
@@ -1,149 +0,0 @@
-//! Native tool extension API.
-//!
-//! A `Tool` is the boundary between the agent loop and any extension runtime
-//! — native Zig code, a Lua bridge, a future Python or Go bridge. libpanto
-//! itself does not parse tool inputs or outputs; it just dispatches.
-
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-
-/// Tool metadata: everything the LLM-facing wire needs (name,
-/// description, schema) without an invocation vtable. This is the more
-/// atomic type, so it lives here; `tool_source.zig` imports it.
-pub const ToolDecl = struct {
- name: []const u8,
- description: []const u8,
- schema_json: []const u8,
-};
-
-/// A binary attachment a tool may return alongside (or instead of) text:
-/// an image or a document (PDF).
-///
-/// `data` is the **raw, un-encoded file bytes** — tools do no encoding.
-/// libpanto owns the heavy lifting at tool-result assembly: it
-/// magic-byte-detects the type when `media_type` is null, resizes large
-/// rasters, and base64-encodes for storage/serialization.
-pub const MediaPart = struct {
- /// Optional MIME hint, e.g. "image/png". When null, libpanto detects
- /// the type from `data`'s leading bytes (magic numbers).
- media_type: ?[]const u8 = null,
- /// Raw (un-encoded) file bytes.
- data: []const u8,
-};
-
-/// 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,
-
- /// Free the bytes this part owns, using `allocator`.
- pub fn deinit(self: ResultPart, allocator: Allocator) void {
- switch (self) {
- .text => |t| allocator.free(t),
- .media => |m| {
- if (m.media_type) |mt| allocator.free(mt);
- allocator.free(m.data);
- },
- }
- }
-};
-
-/// 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
- /// lifetime of every string in `decl` is owned by whoever
- /// constructs the `Tool`. Typically the same owner that backs
- /// `ctx` (e.g. an adapter for an out-of-process runtime, or a
- /// `comptime` static in a native tool).
- decl: ToolDecl,
-
- /// Opaque context pointer passed back to every vtable call.
- ctx: *anyopaque,
-
- vtable: *const VTable,
-
- pub const VTable = struct {
- /// Invoke the tool. MUST be thread-safe — the agent may call
- /// `invoke` concurrently from multiple threads when the LLM emits
- /// multiple ToolUse blocks in a single response.
- ///
- /// `input` is the raw JSON bytes the provider sent. The tool is
- /// responsible for parsing them if it cares about their structure.
- ///
- /// 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
- /// call (and keeps the matching `ToolResult` for every other call
- /// in the batch), then lets the model continue so it can correct
- /// arguments, try another tool, or explain the failure. Only hard
- /// host failures (`error.Canceled`, `error.OutOfMemory`) abort the
- /// whole turn and propagate to the embedder.
- ///
- /// Native tool implementations are responsible for catching their
- /// own panics — a panic in `invoke` will crash the process.
- /// Adapters that bridge to safer languages (Lua, Python, Go) should
- /// convert panics/exceptions into errors.
- invoke: *const fn (
- ctx: *anyopaque,
- input: []const u8,
- allocator: Allocator,
- ) anyerror!ResultParts,
-
- /// Called when the tool is unregistered or the registry is torn
- /// down. Frees any resources owned by `ctx`, including `ctx`
- /// itself if it was heap-allocated.
- ///
- /// The strings inside `decl` are also typically owned by the
- /// same allocation as `ctx` — the tool's deinit hook is
- /// responsible for freeing them.
- deinit: *const fn (ctx: *anyopaque, allocator: Allocator) void,
- };
-};
-