summaryrefslogtreecommitdiff
path: root/libpanto-c/src
diff options
context:
space:
mode:
Diffstat (limited to 'libpanto-c/src')
-rw-r--r--libpanto-c/src/lib.zig102
1 files changed, 102 insertions, 0 deletions
diff --git a/libpanto-c/src/lib.zig b/libpanto-c/src/lib.zig
index 81ce0ce..9b55bee 100644
--- a/libpanto-c/src/lib.zig
+++ b/libpanto-c/src/lib.zig
@@ -18,6 +18,8 @@ const SessionHandle = struct { session: panto.Session, owns_info: bool = true };
const StoreHandle = struct { ctx: *anyopaque, vtable: *const PantoSessionStoreVTable };
const NullStoreHandle = struct { store: panto.NullStore };
const FileSystemJSONLStoreHandle = struct { store: panto.FileSystemJSONLStore };
+const ToolHandle = struct { ctx: *anyopaque, invoke: PantoToolInvokeFn, destroy: ?PantoToolDestroyFn, decl: panto.ToolDecl };
+const ToolSourceHandle = struct { ctx: *anyopaque, invoke_batch: PantoToolSourceInvokeBatchFn, destroy: ?PantoToolDestroyFn, name: []u8, decls: []panto.ToolDecl };
pub const PantoSlice = extern struct { ptr: ?[*]const u8, len: usize };
pub const PantoStatus = enum(c_int) { ok = 0, err = 1 };
@@ -63,6 +65,16 @@ pub const PantoSessionStoreVTable = extern struct {
append_messages: *const fn (*anyopaque, PantoSlice, [*]const PantoPersistentMessage, usize) callconv(.c) PantoStatus,
destroy: ?*const fn (*anyopaque) callconv(.c) void,
};
+pub const PantoToolDecl = extern struct { name: PantoSlice, description: PantoSlice, schema_json: PantoSlice };
+pub const PantoMediaPart = extern struct { media_type: PantoSlice, has_media_type: bool, data: PantoSlice };
+pub const PantoResultPartTag = enum(c_int) { text = 0, media = 1 };
+pub const PantoResultPart = extern struct { tag: PantoResultPartTag, data: extern union { text: PantoSlice, media: PantoMediaPart } };
+pub const PantoResultParts = extern struct { ptr: ?[*]PantoResultPart, len: usize };
+pub const PantoToolCall = extern struct { tool_name: PantoSlice, input: PantoSlice };
+pub const PantoToolCallResult = extern struct { ok: bool, parts: PantoResultParts, err: PantoSlice };
+pub const PantoToolInvokeFn = *const fn (*anyopaque, PantoSlice, *PantoResultParts) callconv(.c) PantoStatus;
+pub const PantoToolSourceInvokeBatchFn = *const fn (*anyopaque, [*]const PantoToolCall, [*]PantoToolCallResult, usize) callconv(.c) PantoStatus;
+pub const PantoToolDestroyFn = *const fn (*anyopaque) callconv(.c) void;
fn setErr(comptime fmt: []const u8, args: anytype) PantoStatus {
setErrVoid(fmt, args);
@@ -637,3 +649,93 @@ fn zigUsage(u: PantoUsage) panto.Usage {
test "c abi exports compile" {
try std.testing.expect(true);
}
+
+fn cloneToolDecl(d: PantoToolDecl) !panto.ToolDecl {
+ return .{ .name = try dupSlice(d.name), .description = try dupSlice(d.description), .schema_json = try dupSlice(d.schema_json) };
+}
+fn freeToolDecl(d: panto.ToolDecl) void {
+ allocator.free(d.name);
+ allocator.free(d.description);
+ allocator.free(d.schema_json);
+}
+fn toolInvoke(ctx: *anyopaque, input: []const u8, alloc: std.mem.Allocator) anyerror!panto.ResultParts {
+ _ = alloc;
+ const h: *ToolHandle = @ptrCast(@alignCast(ctx));
+ var out: PantoResultParts = .{ .ptr = null, .len = 0 };
+ if (h.invoke(h.ctx, outSlice(input), &out) != .ok) return error.ToolCallbackFailed;
+ return convertResultParts(out);
+}
+fn toolDeinit(ctx: *anyopaque, alloc: std.mem.Allocator) void {
+ _ = alloc;
+ const h: *ToolHandle = @ptrCast(@alignCast(ctx));
+ if (h.destroy) |d| d(h.ctx);
+ freeToolDecl(h.decl);
+ allocator.destroy(h);
+}
+fn sourceInvokeBatch(ctx: *anyopaque, calls: []const panto.ToolCall, results: []panto.ToolCallResult, alloc: std.mem.Allocator) anyerror!void {
+ _ = alloc;
+ const h: *ToolSourceHandle = @ptrCast(@alignCast(ctx));
+ const ccalls = try allocator.alloc(PantoToolCall, calls.len);
+ defer allocator.free(ccalls);
+ const cresults = try allocator.alloc(PantoToolCallResult, results.len);
+ defer allocator.free(cresults);
+ for (calls, 0..) |c, i| ccalls[i] = .{ .tool_name = outSlice(c.tool_name), .input = outSlice(c.input) };
+ for (cresults) |*r| r.* = .{ .ok = false, .parts = .{ .ptr = null, .len = 0 }, .err = .{ .ptr = null, .len = 0 } };
+ if (h.invoke_batch(h.ctx, ccalls.ptr, cresults.ptr, cresults.len) != .ok) return error.ToolSourceCallbackFailed;
+ for (cresults, 0..) |r, i| {
+ if (r.ok) results[i] = .{ .ok = try convertResultParts(r.parts) } else results[i] = .{ .err = error.ToolCallbackFailed };
+ }
+}
+fn sourceDeinit(ctx: *anyopaque, alloc: std.mem.Allocator) void {
+ _ = alloc;
+ const h: *ToolSourceHandle = @ptrCast(@alignCast(ctx));
+ if (h.destroy) |d| d(h.ctx);
+ allocator.free(h.name);
+ for (h.decls) |d| freeToolDecl(d);
+ allocator.free(h.decls);
+ allocator.destroy(h);
+}
+fn convertResultParts(parts: PantoResultParts) !panto.ResultParts {
+ const out = try allocator.alloc(panto.ResultPart, parts.len);
+ errdefer allocator.free(out);
+ if (parts.ptr) |p| {
+ for (p[0..parts.len], 0..) |part, i| {
+ out[i] = switch (part.tag) {
+ .text => .{ .text = try dupSlice(part.data.text) },
+ .media => .{ .media = .{ .media_type = if (part.data.media.has_media_type) try dupSlice(part.data.media.media_type) else null, .data = try dupSlice(part.data.media.data) } },
+ };
+ }
+ }
+ return .{ .items = out };
+}
+export fn panto_agent_register_tool(a: *PantoAgent, decl: PantoToolDecl, ctx: *anyopaque, invoke: PantoToolInvokeFn, destroy: ?PantoToolDestroyFn) PantoStatus {
+ const ah: *AgentHandle = @ptrCast(@alignCast(a));
+ const h = allocator.create(ToolHandle) catch return setErr("OutOfMemory", .{});
+ h.* = .{ .ctx = ctx, .invoke = invoke, .destroy = destroy, .decl = cloneToolDecl(decl) catch |e| { allocator.destroy(h); return setErr("{t}", .{e}); } };
+ ah.agent.registerTool(.{ .decl = h.decl, .ctx = h, .vtable = &.{ .invoke = toolInvoke, .deinit = toolDeinit } }) catch |e| return setErr("{t}", .{e});
+ return .ok;
+}
+export fn panto_agent_register_tool_source(a: *PantoAgent, name: PantoSlice, decls: [*]const PantoToolDecl, len: usize, ctx: *anyopaque, invoke_batch: PantoToolSourceInvokeBatchFn, destroy: ?PantoToolDestroyFn) PantoStatus {
+ const ah: *AgentHandle = @ptrCast(@alignCast(a));
+ const h = allocator.create(ToolSourceHandle) catch return setErr("OutOfMemory", .{});
+ h.name = dupSlice(name) catch |e| { allocator.destroy(h); return setErr("{t}", .{e}); };
+ h.decls = allocator.alloc(panto.ToolDecl, len) catch |e| { allocator.free(h.name); allocator.destroy(h); return setErr("{t}", .{e}); };
+ for (decls[0..len], 0..) |d, i| h.decls[i] = cloneToolDecl(d) catch |e| return setErr("{t}", .{e});
+ h.ctx = ctx; h.invoke_batch = invoke_batch; h.destroy = destroy;
+ ah.agent.registerToolSource(.{ .name = h.name, .tools = h.decls, .ctx = h, .vtable = &.{ .invoke_batch = sourceInvokeBatch, .deinit = sourceDeinit } }) catch |e| return setErr("{t}", .{e});
+ return .ok;
+}
+export fn panto_result_parts_text(text: ?[*:0]const u8) PantoResultParts {
+ const part = allocator.alloc(PantoResultPart, 1) catch return .{ .ptr = null, .len = 0 };
+ part[0] = .{ .tag = .text, .data = .{ .text = dupOut(cstr(text)) catch .{ .ptr = null, .len = 0 } } };
+ return .{ .ptr = part.ptr, .len = 1 };
+}
+export fn panto_result_parts_destroy(parts: PantoResultParts) void {
+ if (parts.ptr) |p| {
+ for (p[0..parts.len]) |part| switch (part.tag) {
+ .text => freePS(part.data.text),
+ .media => { if (part.data.media.has_media_type) freePS(part.data.media.media_type); freePS(part.data.media.data); },
+ };
+ allocator.free(p[0..parts.len]);
+ }
+}