summaryrefslogtreecommitdiff
path: root/libpanto/src/provider.zig
diff options
context:
space:
mode:
authorT <t@tjp.lol>2026-05-25 13:24:02 -0700
committerT <t@tjp.lol>2026-05-25 15:27:11 -0700
commitdf2edee86eec2a8deb0ad57b5d20552199c12b65 (patch)
treec1e962e9b0bdf25572929e3efedc3b1915be853f /libpanto/src/provider.zig
parente8272efd9f71ad27a0e62b6b3fa3d13e99a6736f (diff)
phase 1 done
Diffstat (limited to 'libpanto/src/provider.zig')
-rw-r--r--libpanto/src/provider.zig47
1 files changed, 32 insertions, 15 deletions
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);
}
};