summaryrefslogtreecommitdiff
path: root/libpanto/src/tool.zig
blob: 1d8d113f9a7b540bce4112b3dff00a5325131116 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//! 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;

pub const Tool = struct {
    /// Tool name. Borrowed — lifetime is owned by whoever constructs the
    /// `Tool`. Typically the same owner that backs `ctx` (e.g. a LuaTool
    /// adapter, or a static const in a native tool).
    name: []const u8,

    /// Human-readable purpose of the tool. Emitted to the LLM alongside the
    /// schema. Borrowed; same lifetime contract as `name`.
    description: []const u8,

    /// JSON Schema for the tool's input, as raw JSON bytes. Emitted verbatim
    /// into provider request bodies. Borrowed; same lifetime contract.
    schema_json: []const u8,

    /// 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 owned bytes allocated with `allocator`. These bytes
        /// become the `content` of the ToolResult block sent back to the
        /// LLM. The agent takes ownership and frees them.
        ///
        /// Returning an error aborts the current turn. The agent surfaces
        /// the error to the user. 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![]u8,

        /// 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.
        ///
        /// `name`, `description`, and `schema_json` 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,
    };
};