genai-lite 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.GenaiElectronImageAdapter = void 0;
|
|
20
20
|
const errorUtils_1 = require("../../shared/adapters/errorUtils");
|
|
21
|
+
const types_1 = require("../../llm/clients/types");
|
|
21
22
|
const defaultLogger_1 = require("../../logging/defaultLogger");
|
|
22
23
|
/**
|
|
23
24
|
* Adapter for genai-electron's local diffusion image generation
|
|
@@ -157,6 +158,12 @@ class GenaiElectronImageAdapter {
|
|
|
157
158
|
const error = state.error || { message: 'Unknown error', code: 'UNKNOWN_ERROR' };
|
|
158
159
|
throw this.createGenerationError(error.message, error.code);
|
|
159
160
|
}
|
|
161
|
+
// Handle out-of-band cancellation (terminal status since genai-electron 0.6.0)
|
|
162
|
+
if (state.status === 'cancelled') {
|
|
163
|
+
const cancelError = new Error(`Image generation was cancelled on the server (ID: ${generationId})`);
|
|
164
|
+
cancelError.code = 'GENERATION_CANCELLED';
|
|
165
|
+
throw cancelError;
|
|
166
|
+
}
|
|
160
167
|
// Wait before next poll
|
|
161
168
|
await this.sleep(this.pollInterval);
|
|
162
169
|
}
|
|
@@ -231,8 +238,15 @@ class GenaiElectronImageAdapter {
|
|
|
231
238
|
const mapped = (0, errorUtils_1.getCommonMappedErrorDetails)(error);
|
|
232
239
|
// Enhance error message with context
|
|
233
240
|
let errorMessage = mapped.errorMessage;
|
|
241
|
+
let errorCode = mapped.errorCode;
|
|
242
|
+
let errorType = mapped.errorType;
|
|
234
243
|
// Special handling for genai-electron specific errors
|
|
235
|
-
if (error.code === '
|
|
244
|
+
if (error.code === 'GENERATION_CANCELLED') {
|
|
245
|
+
errorMessage = error.message;
|
|
246
|
+
errorCode = types_1.ADAPTER_ERROR_CODES.REQUEST_ABORTED;
|
|
247
|
+
errorType = 'abort_error';
|
|
248
|
+
}
|
|
249
|
+
else if (error.code === 'SERVER_BUSY') {
|
|
236
250
|
errorMessage = 'Image generation server is busy. Wait for current generation to complete.';
|
|
237
251
|
error.type = 'rate_limit_error';
|
|
238
252
|
}
|
|
@@ -258,8 +272,8 @@ class GenaiElectronImageAdapter {
|
|
|
258
272
|
}
|
|
259
273
|
// Create enhanced error with all details
|
|
260
274
|
const enhancedError = new Error(errorMessage);
|
|
261
|
-
enhancedError.code =
|
|
262
|
-
enhancedError.type =
|
|
275
|
+
enhancedError.code = errorCode;
|
|
276
|
+
enhancedError.type = errorType;
|
|
263
277
|
enhancedError.status = mapped.status;
|
|
264
278
|
enhancedError.providerId = this.id;
|
|
265
279
|
enhancedError.modelId = request.modelId;
|
|
@@ -75,6 +75,7 @@ export declare class GeminiClientAdapter implements ILLMClientAdapter {
|
|
|
75
75
|
*
|
|
76
76
|
* @param error - The error from Gemini API
|
|
77
77
|
* @param request - Original request for context
|
|
78
|
+
* @param context - Adapter-side transport state captured at catch time
|
|
78
79
|
* @returns Standardized LLM failure response
|
|
79
80
|
*/
|
|
80
81
|
private createErrorResponse;
|
|
@@ -38,6 +38,12 @@ class GeminiClientAdapter {
|
|
|
38
38
|
* @returns Promise resolving to success or failure response
|
|
39
39
|
*/
|
|
40
40
|
async sendMessage(request, apiKey, options) {
|
|
41
|
+
// The SDK enforces httpOptions.timeout by aborting an internal controller and
|
|
42
|
+
// wraps any fetch rejection in a plain Error (no name/status/code), so timeouts
|
|
43
|
+
// and caller aborts cannot be classified from the error object. The adapter owns
|
|
44
|
+
// the timeout instead and classifies from its own state in the catch block.
|
|
45
|
+
let timedOut = false;
|
|
46
|
+
let timeoutHandle;
|
|
41
47
|
try {
|
|
42
48
|
// Initialize Gemini client (note: @google/genai performs no automatic retries)
|
|
43
49
|
const genAI = new genai_1.GoogleGenAI({ apiKey });
|
|
@@ -52,6 +58,26 @@ class GeminiClientAdapter {
|
|
|
52
58
|
contentsLength: contents.length,
|
|
53
59
|
safetySettingsCount: safetySettings?.length || 0,
|
|
54
60
|
});
|
|
61
|
+
let abortSignal = options?.signal;
|
|
62
|
+
if (options?.timeoutMs !== undefined) {
|
|
63
|
+
const controller = new AbortController();
|
|
64
|
+
if (options.signal) {
|
|
65
|
+
if (options.signal.aborted) {
|
|
66
|
+
controller.abort();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
options.signal.addEventListener("abort", () => controller.abort(), {
|
|
70
|
+
once: true,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
timeoutHandle = setTimeout(() => {
|
|
75
|
+
timedOut = true;
|
|
76
|
+
controller.abort();
|
|
77
|
+
}, options.timeoutMs);
|
|
78
|
+
timeoutHandle.unref?.();
|
|
79
|
+
abortSignal = controller.signal;
|
|
80
|
+
}
|
|
55
81
|
// Generate content using the modern API
|
|
56
82
|
const result = await genAI.models.generateContent({
|
|
57
83
|
model: request.modelId,
|
|
@@ -60,9 +86,11 @@ class GeminiClientAdapter {
|
|
|
60
86
|
...generationConfig,
|
|
61
87
|
safetySettings: safetySettings,
|
|
62
88
|
...(systemInstruction && { systemInstruction: systemInstruction }),
|
|
63
|
-
...(
|
|
89
|
+
...(abortSignal && { abortSignal }),
|
|
90
|
+
// Keep the SDK's server-side timeout hint (X-Server-Timeout header),
|
|
91
|
+
// padded so the adapter's local timer always fires first
|
|
64
92
|
...(options?.timeoutMs !== undefined && {
|
|
65
|
-
httpOptions: { timeout: options.timeoutMs },
|
|
93
|
+
httpOptions: { timeout: options.timeoutMs + 1000 },
|
|
66
94
|
}),
|
|
67
95
|
},
|
|
68
96
|
});
|
|
@@ -72,7 +100,15 @@ class GeminiClientAdapter {
|
|
|
72
100
|
}
|
|
73
101
|
catch (error) {
|
|
74
102
|
this.logger.error("Gemini API error:", error);
|
|
75
|
-
return this.createErrorResponse(error, request
|
|
103
|
+
return this.createErrorResponse(error, request, {
|
|
104
|
+
timedOut,
|
|
105
|
+
aborted: options?.signal?.aborted === true,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
if (timeoutHandle !== undefined) {
|
|
110
|
+
clearTimeout(timeoutHandle);
|
|
111
|
+
}
|
|
76
112
|
}
|
|
77
113
|
}
|
|
78
114
|
/**
|
|
@@ -312,9 +348,38 @@ class GeminiClientAdapter {
|
|
|
312
348
|
*
|
|
313
349
|
* @param error - The error from Gemini API
|
|
314
350
|
* @param request - Original request for context
|
|
351
|
+
* @param context - Adapter-side transport state captured at catch time
|
|
315
352
|
* @returns Standardized LLM failure response
|
|
316
353
|
*/
|
|
317
|
-
createErrorResponse(error, request) {
|
|
354
|
+
createErrorResponse(error, request, context) {
|
|
355
|
+
// Classify adapter-owned aborts/timeouts first — the SDK surfaces both as
|
|
356
|
+
// plain Errors the common mapping cannot recognize (see sendMessage)
|
|
357
|
+
if (context?.aborted) {
|
|
358
|
+
return {
|
|
359
|
+
provider: request.providerId,
|
|
360
|
+
model: request.modelId,
|
|
361
|
+
error: {
|
|
362
|
+
message: "Request was aborted",
|
|
363
|
+
code: types_1.ADAPTER_ERROR_CODES.REQUEST_ABORTED,
|
|
364
|
+
type: "abort_error",
|
|
365
|
+
providerError: error,
|
|
366
|
+
},
|
|
367
|
+
object: "error",
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (context?.timedOut) {
|
|
371
|
+
return {
|
|
372
|
+
provider: request.providerId,
|
|
373
|
+
model: request.modelId,
|
|
374
|
+
error: {
|
|
375
|
+
message: "Request timed out",
|
|
376
|
+
code: types_1.ADAPTER_ERROR_CODES.REQUEST_TIMEOUT,
|
|
377
|
+
type: "timeout_error",
|
|
378
|
+
providerError: error,
|
|
379
|
+
},
|
|
380
|
+
object: "error",
|
|
381
|
+
};
|
|
382
|
+
}
|
|
318
383
|
// Use shared error mapping utility for common error patterns
|
|
319
384
|
const initialProviderMessage = error?.message;
|
|
320
385
|
let { errorCode, errorMessage, errorType, status, retryAfterMs } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
|