genai-lite 0.9.0 → 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 === 'SERVER_BUSY') {
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 = mapped.errorCode;
262
- enhancedError.type = mapped.errorType;
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
- ...(options?.signal && { abortSignal: options.signal }),
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);
@@ -517,7 +517,7 @@ exports.KNOWN_GGUF_MODELS = [
517
517
  description: "Gemma 4 E2B (2.3B effective) hybrid-thinking model",
518
518
  capabilities: {
519
519
  maxTokens: 8192,
520
- contextWindow: 32768,
520
+ contextWindow: 131072,
521
521
  supportsImages: false,
522
522
  supportsPromptCache: false,
523
523
  supportsSystemMessage: true,
@@ -532,7 +532,22 @@ exports.KNOWN_GGUF_MODELS = [
532
532
  description: "Gemma 4 E4B (4.5B effective) hybrid-thinking model",
533
533
  capabilities: {
534
534
  maxTokens: 8192,
535
- contextWindow: 32768,
535
+ contextWindow: 131072,
536
+ supportsImages: false,
537
+ supportsPromptCache: false,
538
+ supportsSystemMessage: true,
539
+ reasoning: { ...HYBRID_REASONING },
540
+ localReasoning: GEMMA4_LOCAL_REASONING,
541
+ defaultSettings: GEMMA_SAMPLING,
542
+ },
543
+ },
544
+ {
545
+ pattern: "gemma-4-12b",
546
+ name: "Gemma 4 12B",
547
+ description: "Gemma 4 12B dense hybrid-thinking model (256K context)",
548
+ capabilities: {
549
+ maxTokens: 16384,
550
+ contextWindow: 262144,
536
551
  supportsImages: false,
537
552
  supportsPromptCache: false,
538
553
  supportsSystemMessage: true,
@@ -544,10 +559,10 @@ exports.KNOWN_GGUF_MODELS = [
544
559
  {
545
560
  pattern: "gemma-4-26b-a4b",
546
561
  name: "Gemma 4 26B-A4B",
547
- description: "Gemma 4 26B MoE (~4B active) hybrid-thinking model",
562
+ description: "Gemma 4 26B MoE (~4B active) hybrid-thinking model (256K context)",
548
563
  capabilities: {
549
564
  maxTokens: 16384,
550
- contextWindow: 131072,
565
+ contextWindow: 262144,
551
566
  supportsImages: false,
552
567
  supportsPromptCache: false,
553
568
  supportsSystemMessage: true,
@@ -577,7 +592,7 @@ exports.KNOWN_GGUF_MODELS = [
577
592
  description: "Gemma 4 hybrid-thinking model (size not recognized)",
578
593
  capabilities: {
579
594
  maxTokens: 8192,
580
- contextWindow: 32768,
595
+ contextWindow: 131072,
581
596
  supportsImages: false,
582
597
  supportsPromptCache: false,
583
598
  supportsSystemMessage: true,
@@ -1170,31 +1185,14 @@ exports.SUPPORTED_MODELS = [
1170
1185
  // Note: Unlike Gemma 3, Gemma 4 supports a native system role
1171
1186
  // Note: Reasoning left unsupported on the cloud entries — the Gemini API exposes
1172
1187
  // no thinking toggle for Gemma (the <|think|> system-token mechanism is not modeled)
1173
- {
1174
- id: "gemma-4-4b-it",
1175
- name: "Gemma 4 4B",
1176
- providerId: "gemini",
1177
- contextWindow: 32768,
1178
- inputPrice: 0.0,
1179
- outputPrice: 0.0,
1180
- description: "Google's Gemma 4 4B open model with system-message support (free via Gemini API)",
1181
- maxTokens: 8192,
1182
- supportsImages: false,
1183
- supportsPromptCache: false,
1184
- supportsSystemMessage: true,
1185
- structuredOutput: {
1186
- supported: false,
1187
- notes: "Gemma models do not support JSON mode via Google's API",
1188
- },
1189
- },
1190
1188
  {
1191
1189
  id: "gemma-4-26b-a4b-it",
1192
1190
  name: "Gemma 4 26B-A4B",
1193
1191
  providerId: "gemini",
1194
- contextWindow: 131072,
1192
+ contextWindow: 262144,
1195
1193
  inputPrice: 0.0,
1196
1194
  outputPrice: 0.0,
1197
- description: "Google's Gemma 4 26B MoE (~4B active) open model (free via Gemini API)",
1195
+ description: "Google's Gemma 4 26B MoE (~4B active) open model with 256K context (free via Gemini API)",
1198
1196
  maxTokens: 8192,
1199
1197
  supportsImages: false,
1200
1198
  supportsPromptCache: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genai-lite",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "A lightweight, portable toolkit for interacting with various Generative AI APIs.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",