agent: abort in-flight LLM requests on cancel

Updates the zenai dependency and integrates `zenai.http.Interrupt`
to immediately abort active LLM requests when cancellation is triggered.
This commit is contained in:
Adrià Arrufat
2026-06-04 13:07:06 +02:00
parent a24a949028
commit 0bb87330a7
2 changed files with 8 additions and 2 deletions

View File

@@ -35,8 +35,8 @@
.hash = "sqlite3-3.51.0-DMxLWssOAABZ8cAvU_LfBIbp0kZjm824PU8sSLXpEDdr",
},
.zenai = .{
.url = "git+https://github.com/lightpanda-io/zenai.git#a84812e6d37beccdc43ee79f43b0af8c1ae2e5e8",
.hash = "zenai-0.0.0-iOY_VErUAwA06Ygd91R3fP6jmUhScXsvIfceno4lA9Po",
.url = "git+https://github.com/lightpanda-io/zenai.git#35bec07f40f1493362ba4b3dc3bb9b8e51a98fd0",
.hash = "zenai-0.0.0-iOY_VOvxAwDY9Gj7AsJhFb8CBf-QuQjJsJgUJb2mGjC_",
},
.isocline = .{
.url = "git+https://github.com/arrufat/isocline#94433ba3afa8e1ebb0187af17838a8d853a3829c",

View File

@@ -113,6 +113,9 @@ script_file: ?[]const u8,
one_shot_task: ?[]const u8,
one_shot_attachments: ?[]const []const u8,
cancel_requested: std.atomic.Value(bool) = .init(false),
/// Shuts down the in-flight LLM socket on Ctrl-C so an agent turn aborts
/// mid-request instead of blocking until the model's full response arrives.
http_interrupt: zenai.http.Interrupt = .{},
synthetic_tool_call_id: u32 = 0,
/// Aggregate Anthropic/OpenAI/Gemini token usage across every model call
/// this Agent has made. Printed as a structured `$usage ...` line on stderr
@@ -249,6 +252,7 @@ pub fn init(allocator: std.mem.Allocator, app: *App, opts: Config.Agent) !*Agent
self.ai_client = if (llm) |l| try zenai.provider.Client.init(allocator, l, .{ .base_url = opts.base_url, .retry_policy = .long_running }) else null;
errdefer if (self.ai_client) |c| c.deinit(allocator);
if (self.ai_client) |c| c.setInterrupt(&self.http_interrupt);
if (will_repl) {
self.terminal.attachCompleter();
@@ -305,6 +309,7 @@ fn globalTools() []const ProviderTool {
/// touches from this context.
pub fn requestCancel(self: *Agent) void {
self.cancel_requested.store(true, .release);
self.http_interrupt.fire();
{
self.script_runtime_mutex.lock();
defer self.script_runtime_mutex.unlock();
@@ -671,6 +676,7 @@ fn setProvider(self: *Agent, credentials: Credentials) !void {
const new_model = try self.allocator.dupe(u8, zenai.provider.defaultModel(credentials.provider));
if (self.ai_client) |client| client.deinit(self.allocator);
new_client.setInterrupt(&self.http_interrupt);
self.ai_client = new_client;
self.model_credentials = credentials;
self.model_completions = null;