diff options
Diffstat (limited to 'libpanto/src/provider.zig')
| -rw-r--r-- | libpanto/src/provider.zig | 21 |
1 files changed, 15 insertions, 6 deletions
diff --git a/libpanto/src/provider.zig b/libpanto/src/provider.zig index fd170f0..bc39026 100644 --- a/libpanto/src/provider.zig +++ b/libpanto/src/provider.zig @@ -19,12 +19,16 @@ pub const ContentBlockType = enum { /// stops streaming, calls `onError(err)`, and propagates `err` out of /// `streamStep`. No partial assistant message is appended to the conversation. /// -/// Identity fields (tool id/name for ToolUse blocks) are intentionally *not* -/// surfaced at `onBlockStart`. They cannot be reliably known at start time -/// across all wire protocols — OpenAI streaming may deliver `id` and `name` -/// in fragments across multiple deltas — so any "meta" we passed at start -/// would be a phantom guarantee. Receivers that need identity get it from -/// the fully-assembled `ContentBlock` delivered to `onBlockComplete`. +/// Tool-use identity (`id`, `name`) is delivered via `onToolDetails`, fired +/// once per ToolUse block at the earliest moment both fields are known. For +/// Anthropic this is immediately after `onBlockStart`, before any deltas. For +/// OpenAI Chat Completions this may be partway through the arg-deltas, since +/// the wire protocol can split `id` and `name` across multiple streaming +/// chunks. The only guarantees are: it fires strictly after the block's +/// `onBlockStart`, strictly before its `onBlockComplete`, and at most once +/// per ToolUse block. It never fires for non-ToolUse blocks. If a tool_use +/// block is dropped because identity never fully arrived, `onToolDetails` +/// (and `onBlockComplete`) never fire for it. /// /// `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) @@ -34,6 +38,7 @@ pub const ContentBlockType = enum { pub const ReceiverVTable = struct { onMessageStart: *const fn (*anyopaque, conversation.MessageRole) anyerror!void, onBlockStart: *const fn (*anyopaque, ContentBlockType, usize) anyerror!void, + onToolDetails: *const fn (*anyopaque, usize, []const u8, []const u8) 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, @@ -52,6 +57,10 @@ pub const Receiver = struct { try self.vtable.onBlockStart(self.ptr, block_type, index); } + pub fn onToolDetails(self: Receiver, block_index: usize, id: []const u8, name: []const u8) !void { + try self.vtable.onToolDetails(self.ptr, block_index, id, name); + } + pub fn onContentDelta(self: Receiver, block_index: usize, delta: []const u8) !void { try self.vtable.onContentDelta(self.ptr, block_index, delta); } |
