diff options
Diffstat (limited to 'libpanto/src/provider_openai_chat.zig')
| -rw-r--r-- | libpanto/src/provider_openai_chat.zig | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/libpanto/src/provider_openai_chat.zig b/libpanto/src/provider_openai_chat.zig index 5152d3e..5713b5d 100644 --- a/libpanto/src/provider_openai_chat.zig +++ b/libpanto/src/provider_openai_chat.zig @@ -214,8 +214,14 @@ pub const ResumableResponse = struct { const vtable: provider_mod.ProviderStream.VTable = .{ .produce = produceVT, .deinit = deinitVT, + .last_error = lastErrorVT, }; + fn lastErrorVT(ptr: *anyopaque) ?[]const u8 { + const self: *ResumableResponse = @ptrCast(@alignCast(ptr)); + return self.state.stream_error_message; + } + fn produceVT(ptr: *anyopaque, out: *EventQueue) anyerror!ProduceStatus { const self: *ResumableResponse = @ptrCast(@alignCast(ptr)); return self.produce(out); @@ -339,6 +345,11 @@ const StreamState = struct { /// `stream_options.include_usage: true` AND the server honored it). usage: ?provider_mod.Usage = null, + /// Owned, human-readable description of a mid-stream error embedded in an + /// HTTP-200 SSE body, surfaced to the agent via `ProviderStream.lastError` + /// so the retry notice can explain *why*. + stream_error_message: ?[]u8 = null, + const ToolUseInProgress = struct { /// Block index emitted to the receiver for this tool call's /// onBlockStart / onContentDelta / onBlockComplete callbacks. @@ -385,6 +396,23 @@ const StreamState = struct { self.blocks.deinit(self.allocator); if (self.active_tool) |*tu| tu.deinit(self.allocator); self.closed_tool_indices.deinit(); + if (self.stream_error_message) |s| self.allocator.free(s); + } + + /// Record a readable description of an embedded stream error, combining + /// the error `type` and `message` into one owned string (either may be + /// absent). Replaces any previous value. + fn setStreamErrorMessage(self: *StreamState, kind: ?[]const u8, message: ?[]const u8) !void { + if (self.stream_error_message) |old| self.allocator.free(old); + self.stream_error_message = null; + self.stream_error_message = if (kind != null and message != null) + try std.fmt.allocPrint(self.allocator, "{s}: {s}", .{ kind.?, message.? }) + else if (kind) |k| + try self.allocator.dupe(u8, k) + else if (message) |m| + try self.allocator.dupe(u8, m) + else + null; } /// Close the active text/thinking block (if any) and emit @@ -683,6 +711,9 @@ fn handleEvent( d.error_type, d.error_message, }); } + // Stash a readable description so the agent's retry notice can + // explain *why* the stream failed. Owned by `state`. + state.setStreamErrorMessage(d.error_type, d.error_message) catch {}; return error.ProviderStreamMalformed; } |
