From df2edee86eec2a8deb0ad57b5d20552199c12b65 Mon Sep 17 00:00:00 2001 From: T Date: Mon, 25 May 2026 13:24:02 -0700 Subject: phase 1 done --- libpanto/src/provider.zig | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) (limited to 'libpanto/src/provider.zig') diff --git a/libpanto/src/provider.zig b/libpanto/src/provider.zig index 8d6eea5..16c3dd6 100644 --- a/libpanto/src/provider.zig +++ b/libpanto/src/provider.zig @@ -14,36 +14,53 @@ pub const BlockMeta = struct { tool_name: ?[]const u8 = null, }; +/// Vtable for receiving streaming events from a Provider. +/// +/// The lifecycle callbacks (`onMessageStart` ... `onMessageComplete`) return +/// `anyerror!void`. Returning an error aborts the in-flight turn: the Provider +/// stops streaming, calls `onError(err)`, and propagates `err` out of +/// `streamStep`. No partial assistant message is appended to the conversation. +/// +/// `onError` is the receiver's cleanup hook. It fires exactly once per failed +/// turn, whether the error originated in the receiver itself (a write failure) +/// or in the Provider (HTTP/parse/stream failure). It is the last callback the +/// receiver will see for that turn. `onError` itself cannot fail; receivers +/// must swallow secondary failures during cleanup. pub const ReceiverVTable = struct { - onMessageStart: *const fn (*anyopaque, conversation.MessageRole) void, - onBlockStart: *const fn (*anyopaque, ContentBlockType, usize, ?BlockMeta) void, - onContentDelta: *const fn (*anyopaque, usize, []const u8) void, - onBlockComplete: *const fn (*anyopaque, usize, conversation.ContentBlock) void, - onMessageComplete: *const fn (*anyopaque, conversation.Message) void, + onMessageStart: *const fn (*anyopaque, conversation.MessageRole) anyerror!void, + onBlockStart: *const fn (*anyopaque, ContentBlockType, usize, ?BlockMeta) anyerror!void, + onContentDelta: *const fn (*anyopaque, usize, []const u8) anyerror!void, + onBlockComplete: *const fn (*anyopaque, usize, conversation.ContentBlock) anyerror!void, + onMessageComplete: *const fn (*anyopaque, conversation.Message) anyerror!void, + onError: *const fn (*anyopaque, anyerror) void, }; pub const Receiver = struct { ptr: *anyopaque, vtable: *const ReceiverVTable, - pub fn onMessageStart(self: Receiver, role: conversation.MessageRole) void { - self.vtable.onMessageStart(self.ptr, role); + pub fn onMessageStart(self: Receiver, role: conversation.MessageRole) !void { + try self.vtable.onMessageStart(self.ptr, role); } - pub fn onBlockStart(self: Receiver, block_type: ContentBlockType, index: usize, meta: ?BlockMeta) void { - self.vtable.onBlockStart(self.ptr, block_type, index, meta); + pub fn onBlockStart(self: Receiver, block_type: ContentBlockType, index: usize, meta: ?BlockMeta) !void { + try self.vtable.onBlockStart(self.ptr, block_type, index, meta); } - pub fn onContentDelta(self: Receiver, block_index: usize, delta: []const u8) void { - self.vtable.onContentDelta(self.ptr, block_index, delta); + pub fn onContentDelta(self: Receiver, block_index: usize, delta: []const u8) !void { + try self.vtable.onContentDelta(self.ptr, block_index, delta); } - pub fn onBlockComplete(self: Receiver, block_index: usize, block: conversation.ContentBlock) void { - self.vtable.onBlockComplete(self.ptr, block_index, block); + pub fn onBlockComplete(self: Receiver, block_index: usize, block: conversation.ContentBlock) !void { + try self.vtable.onBlockComplete(self.ptr, block_index, block); } - pub fn onMessageComplete(self: Receiver, message: conversation.Message) void { - self.vtable.onMessageComplete(self.ptr, message); + pub fn onMessageComplete(self: Receiver, message: conversation.Message) !void { + try self.vtable.onMessageComplete(self.ptr, message); + } + + pub fn onError(self: Receiver, err: anyerror) void { + self.vtable.onError(self.ptr, err); } }; -- cgit v1.3