From b22904f6bb21d4dc67e69a3145e7fc9193b298a9 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Fri, 24 Apr 2026 03:40:34 -0700 Subject: [PATCH] AIX: Gemini Interactions: Cancel + Delete Also see: googleapis/python-genai#1971 --- .../chatGenerate/chatGenerate.dispatch.ts | 15 +++++++++++++++ .../wiretypes/gemini.interactions.wiretypes.ts | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/src/modules/aix/server/dispatch/chatGenerate/chatGenerate.dispatch.ts b/src/modules/aix/server/dispatch/chatGenerate/chatGenerate.dispatch.ts index 11ae8b216..fd592de4f 100644 --- a/src/modules/aix/server/dispatch/chatGenerate/chatGenerate.dispatch.ts +++ b/src/modules/aix/server/dispatch/chatGenerate/chatGenerate.dispatch.ts @@ -398,6 +398,21 @@ export async function executeChatGenerateDelete(access: AixAPI_Access, handle: A case 'gemini': if (handle.uht !== 'vnd.gem.interactions') throw new Error(`Delete handle mismatch for gemini: expected 'vnd.gem.interactions', got '${handle.uht}'`); + + // Gemini: cancel the background run first (stops token generation), then DELETE the stored record. + // The DELETE endpoint only removes the resource; it does NOT cancel an in-flight run. + // Cancel may 404 "Method not found" on the Developer API (API-key mode, googleapis/python-genai#1971) - + // we log the outcome and proceed to DELETE so local cleanup still happens. + const { url: cancelUrl, headers: cancelHeaders } = geminiAccess(access, null, GeminiInteractionsWire_API_Interactions.cancelPath(handle.runId), false); + try { + const cancelResp = await fetchResponseOrTRPCThrow({ url: cancelUrl, method: 'POST', body: {}, headers: cancelHeaders, signal: abortSignal, name: 'Aix.Gemini.Interactions.cancel', throwWithoutName: true }); + console.log(`[AIX] Gemini.Interactions.cancel: ok=${cancelResp.ok} status=${cancelResp.status}`); + } catch (error: any) { + if (abortSignal.aborted) throw error; + const status = error instanceof TRPCFetcherError ? error.httpStatus : undefined; + console.log(`[AIX] Gemini.Interactions.cancel: failed status=${status ?? '?'} msg=${error?.message ?? 'unknown'}`); + } + ({ url, headers } = geminiAccess(access, null, GeminiInteractionsWire_API_Interactions.deletePath(handle.runId), false)); name = 'Aix.Gemini.Interactions.delete'; break; diff --git a/src/modules/aix/server/dispatch/wiretypes/gemini.interactions.wiretypes.ts b/src/modules/aix/server/dispatch/wiretypes/gemini.interactions.wiretypes.ts index 2ecd87e1c..b039f078e 100644 --- a/src/modules/aix/server/dispatch/wiretypes/gemini.interactions.wiretypes.ts +++ b/src/modules/aix/server/dispatch/wiretypes/gemini.interactions.wiretypes.ts @@ -23,8 +23,12 @@ export namespace GeminiInteractionsWire_API_Interactions { export const getPath = (id: string) => `/v1beta/interactions/${encodeURIComponent(id)}`; + // DELETE. Removes the stored record. Orthogonal to cancel; when removed the original connection may still be running and streaming export const deletePath = (id: string) => `/v1beta/interactions/${encodeURIComponent(id)}`; + // POST. Only cancels background interactions that are still running + export const cancelPath = (id: string) => `/v1beta/interactions/${encodeURIComponent(id)}/cancel`; + // -- Request Body (POST /v1beta/interactions) --