genai-lite 0.8.2 → 0.9.0

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.
Files changed (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +210 -207
  3. package/dist/adapters/image/GenaiElectronImageAdapter.d.ts +1 -0
  4. package/dist/adapters/image/GenaiElectronImageAdapter.js +5 -5
  5. package/dist/adapters/image/OpenAIImageAdapter.d.ts +1 -0
  6. package/dist/adapters/image/OpenAIImageAdapter.js +4 -4
  7. package/dist/config/llm-presets.json +24 -0
  8. package/dist/image/ImageService.js +2 -0
  9. package/dist/index.d.ts +4 -2
  10. package/dist/index.js +6 -1
  11. package/dist/llm/LLMService.d.ts +30 -1
  12. package/dist/llm/LLMService.js +43 -5
  13. package/dist/llm/clients/AnthropicClientAdapter.d.ts +6 -2
  14. package/dist/llm/clients/AnthropicClientAdapter.js +26 -11
  15. package/dist/llm/clients/GeminiClientAdapter.d.ts +6 -2
  16. package/dist/llm/clients/GeminiClientAdapter.js +21 -9
  17. package/dist/llm/clients/LlamaCppClientAdapter.d.ts +8 -4
  18. package/dist/llm/clients/LlamaCppClientAdapter.js +131 -28
  19. package/dist/llm/clients/MistralClientAdapter.d.ts +6 -2
  20. package/dist/llm/clients/MistralClientAdapter.js +22 -11
  21. package/dist/llm/clients/MockClientAdapter.d.ts +2 -2
  22. package/dist/llm/clients/MockClientAdapter.js +36 -19
  23. package/dist/llm/clients/OpenAIClientAdapter.d.ts +6 -2
  24. package/dist/llm/clients/OpenAIClientAdapter.js +33 -9
  25. package/dist/llm/clients/OpenRouterClientAdapter.d.ts +6 -2
  26. package/dist/llm/clients/OpenRouterClientAdapter.js +87 -16
  27. package/dist/llm/clients/types.d.ts +18 -1
  28. package/dist/llm/clients/types.js +4 -0
  29. package/dist/llm/config.d.ts +9 -2
  30. package/dist/llm/config.js +697 -33
  31. package/dist/llm/services/ModelResolver.d.ts +6 -0
  32. package/dist/llm/services/ModelResolver.js +57 -17
  33. package/dist/llm/services/RequestValidator.d.ts +8 -0
  34. package/dist/llm/services/RequestValidator.js +9 -2
  35. package/dist/llm/services/SettingsManager.d.ts +1 -1
  36. package/dist/llm/services/SettingsManager.js +92 -4
  37. package/dist/llm/types.d.ts +127 -0
  38. package/dist/prompting/index.d.ts +1 -1
  39. package/dist/prompting/index.js +2 -1
  40. package/dist/prompting/parser.d.ts +23 -0
  41. package/dist/prompting/parser.js +33 -0
  42. package/dist/shared/adapters/errorUtils.d.ts +15 -0
  43. package/dist/shared/adapters/errorUtils.js +69 -1
  44. package/dist/shared/adapters/logprobsUtils.d.ts +13 -0
  45. package/dist/shared/adapters/logprobsUtils.js +33 -0
  46. package/dist/shared/services/AdapterRegistry.js +3 -1
  47. package/dist/shared/services/withRetry.d.ts +50 -0
  48. package/dist/shared/services/withRetry.js +78 -0
  49. package/dist/types/image.d.ts +2 -0
  50. package/package.json +59 -59
  51. package/src/config/image-presets.json +212 -212
  52. package/src/config/llm-presets.json +623 -599
package/dist/index.js CHANGED
@@ -14,10 +14,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.silentLogger = exports.DEFAULT_LOG_LEVEL = exports.createDefaultLogger = exports.KNOWN_GGUF_MODELS = exports.detectGgufCapabilities = exports.createFallbackModelInfo = exports.parseTemplateWithMetadata = exports.extractInitialTaggedContent = exports.parseRoleTags = exports.parseStructuredContent = exports.extractRandomVariables = exports.getSmartPreview = exports.countTokens = exports.renderTemplate = exports.ImageService = exports.MistralClientAdapter = exports.OpenRouterClientAdapter = exports.LlamaCppServerClient = exports.LlamaCppClientAdapter = exports.fromEnvironment = exports.LLMService = void 0;
17
+ exports.silentLogger = exports.DEFAULT_LOG_LEVEL = exports.createDefaultLogger = exports.KNOWN_GGUF_MODELS = exports.detectGgufCapabilities = exports.createFallbackModelInfo = exports.parseTemplateWithMetadata = exports.extractMarkerDelimitedContent = exports.extractInitialTaggedContent = exports.parseRoleTags = exports.parseStructuredContent = exports.extractRandomVariables = exports.getSmartPreview = exports.countTokens = exports.renderTemplate = exports.ImageService = exports.MistralClientAdapter = exports.OpenRouterClientAdapter = exports.LlamaCppServerClient = exports.LlamaCppClientAdapter = exports.fromEnvironment = exports.DEFAULT_RETRY_POLICY = exports.withRetry = exports.LLMService = void 0;
18
18
  // --- LLM Service ---
19
19
  var LLMService_1 = require("./llm/LLMService");
20
20
  Object.defineProperty(exports, "LLMService", { enumerable: true, get: function () { return LLMService_1.LLMService; } });
21
+ // --- Retry Utilities ---
22
+ var withRetry_1 = require("./shared/services/withRetry");
23
+ Object.defineProperty(exports, "withRetry", { enumerable: true, get: function () { return withRetry_1.withRetry; } });
24
+ Object.defineProperty(exports, "DEFAULT_RETRY_POLICY", { enumerable: true, get: function () { return withRetry_1.DEFAULT_RETRY_POLICY; } });
21
25
  // Export all core request/response/config types from the LLM module
22
26
  __exportStar(require("./llm/types"), exports);
23
27
  // Export all client adapter types
@@ -51,6 +55,7 @@ var parser_1 = require("./prompting/parser");
51
55
  Object.defineProperty(exports, "parseStructuredContent", { enumerable: true, get: function () { return parser_1.parseStructuredContent; } });
52
56
  Object.defineProperty(exports, "parseRoleTags", { enumerable: true, get: function () { return parser_1.parseRoleTags; } });
53
57
  Object.defineProperty(exports, "extractInitialTaggedContent", { enumerable: true, get: function () { return parser_1.extractInitialTaggedContent; } });
58
+ Object.defineProperty(exports, "extractMarkerDelimitedContent", { enumerable: true, get: function () { return parser_1.extractMarkerDelimitedContent; } });
54
59
  Object.defineProperty(exports, "parseTemplateWithMetadata", { enumerable: true, get: function () { return parser_1.parseTemplateWithMetadata; } });
55
60
  var config_1 = require("./llm/config");
56
61
  Object.defineProperty(exports, "createFallbackModelInfo", { enumerable: true, get: function () { return config_1.createFallbackModelInfo; } });
@@ -2,6 +2,7 @@ import type { ApiKeyProvider, PresetMode } from '../types';
2
2
  import type { Logger, LogLevel } from '../logging/types';
3
3
  import type { LLMChatRequest, LLMChatRequestWithPreset, LLMResponse, LLMFailureResponse, ProviderInfo, ModelInfo, ApiProviderId, LLMSettings, ModelContext, LLMMessage } from "./types";
4
4
  import type { ModelPreset } from "../types/presets";
5
+ import { type RetryPolicy } from "../shared/services/withRetry";
5
6
  export type { PresetMode };
6
7
  /**
7
8
  * Options for configuring the LLMService
@@ -15,6 +16,32 @@ export interface LLMServiceOptions {
15
16
  logLevel?: LogLevel;
16
17
  /** Custom logger implementation. If provided, logLevel is ignored. */
17
18
  logger?: Logger;
19
+ /**
20
+ * Retry policy for transient failures (rate limits, 5xx, network errors, timeouts).
21
+ * Defaults: maxRetries 2, initialDelayMs 500, maxDelayMs 10000, backoffFactor 2,
22
+ * retryOnTimeout true. Provider SDK-internal retries are disabled — this layer
23
+ * is the single owner of retry behavior. Set `maxRetries: 0` to disable.
24
+ */
25
+ retry?: Partial<RetryPolicy> & {
26
+ /** Whether REQUEST_TIMEOUT failures are retryable (default true) */
27
+ retryOnTimeout?: boolean;
28
+ };
29
+ /** Default per-request timeout in ms (overridable per call). SDK defaults apply when unset. */
30
+ timeoutMs?: number;
31
+ }
32
+ /**
33
+ * Per-call options for LLMService.sendMessage
34
+ */
35
+ export interface SendMessageOptions {
36
+ /**
37
+ * Abort signal to cancel the request (client-side — the provider may still
38
+ * process and bill an already-dispatched request). Aborts are never retried.
39
+ */
40
+ signal?: AbortSignal;
41
+ /** Per-request timeout in ms (overrides the service-level timeoutMs) */
42
+ timeoutMs?: number;
43
+ /** Per-request retry cap (overrides the service-level retry.maxRetries) */
44
+ maxRetries?: number;
18
45
  }
19
46
  /**
20
47
  * Result from createMessages method
@@ -46,6 +73,8 @@ export declare class LLMService {
46
73
  private requestValidator;
47
74
  private settingsManager;
48
75
  private modelResolver;
76
+ private retryOptions;
77
+ private defaultTimeoutMs?;
49
78
  constructor(getApiKey: ApiKeyProvider, options?: LLMServiceOptions);
50
79
  /**
51
80
  * Gets list of supported LLM providers
@@ -66,7 +95,7 @@ export declare class LLMService {
66
95
  * @param request - The LLM chat request
67
96
  * @returns Promise resolving to either success or failure response
68
97
  */
69
- sendMessage(request: LLMChatRequest | LLMChatRequestWithPreset): Promise<LLMResponse | LLMFailureResponse>;
98
+ sendMessage(request: LLMChatRequest | LLMChatRequestWithPreset, callOptions?: SendMessageOptions): Promise<LLMResponse | LLMFailureResponse>;
70
99
  /**
71
100
  * Gets all configured model presets
72
101
  *
@@ -18,6 +18,8 @@ const AdapterRegistry_1 = require("../shared/services/AdapterRegistry");
18
18
  const RequestValidator_1 = require("./services/RequestValidator");
19
19
  const SettingsManager_1 = require("./services/SettingsManager");
20
20
  const ModelResolver_1 = require("./services/ModelResolver");
21
+ const withRetry_1 = require("../shared/services/withRetry");
22
+ const types_1 = require("./clients/types");
21
23
  /**
22
24
  * Main process service for LLM operations
23
25
  *
@@ -32,6 +34,8 @@ const ModelResolver_1 = require("./services/ModelResolver");
32
34
  class LLMService {
33
35
  constructor(getApiKey, options = {}) {
34
36
  this.getApiKey = getApiKey;
37
+ this.retryOptions = options.retry;
38
+ this.defaultTimeoutMs = options.timeoutMs;
35
39
  // Initialize logger - custom logger takes precedence over logLevel
36
40
  this.logger = options.logger ?? (0, defaultLogger_1.createDefaultLogger)(options.logLevel);
37
41
  // Initialize services with logger
@@ -42,7 +46,7 @@ class LLMService {
42
46
  adapterConstructors: config_1.ADAPTER_CONSTRUCTORS,
43
47
  adapterConfigs: config_1.ADAPTER_CONFIGS,
44
48
  }, this.logger);
45
- this.requestValidator = new RequestValidator_1.RequestValidator();
49
+ this.requestValidator = new RequestValidator_1.RequestValidator(this.logger);
46
50
  this.settingsManager = new SettingsManager_1.SettingsManager(this.logger);
47
51
  this.modelResolver = new ModelResolver_1.ModelResolver(this.presetManager, this.adapterRegistry, this.logger);
48
52
  }
@@ -78,7 +82,7 @@ class LLMService {
78
82
  * @param request - The LLM chat request
79
83
  * @returns Promise resolving to either success or failure response
80
84
  */
81
- async sendMessage(request) {
85
+ async sendMessage(request, callOptions) {
82
86
  this.logger.info(`LLMService.sendMessage called with presetId: ${request.presetId}, provider: ${request.providerId}, model: ${request.modelId}`);
83
87
  try {
84
88
  // Resolve model information from preset or direct IDs
@@ -107,7 +111,7 @@ class LLMService {
107
111
  }
108
112
  }
109
113
  // Apply model-specific defaults and merge with user settings
110
- const finalSettings = this.settingsManager.mergeSettingsForModel(modelId, providerId, combinedSettings);
114
+ const finalSettings = this.settingsManager.mergeSettingsForModel(modelId, providerId, combinedSettings, modelInfo);
111
115
  // Validate reasoning settings for model capabilities
112
116
  const reasoningValidation = this.requestValidator.validateReasoningSettings(modelInfo, finalSettings.reasoning, resolvedRequest);
113
117
  if (reasoningValidation) {
@@ -175,7 +179,41 @@ class LLMService {
175
179
  };
176
180
  }
177
181
  this.logger.info(`Making LLM request with ${clientAdapter.constructor.name} for provider: ${providerId}`);
178
- const result = await clientAdapter.sendMessage(internalRequest, apiKey);
182
+ // Unified retry layer: adapters never throw, so retry decisions are made on
183
+ // RETURNED failure responses. SDK-internal retries are disabled (maxRetries: 0
184
+ // at client construction) — this loop is the single owner of retry behavior.
185
+ const adapterOptions = {
186
+ ...(callOptions?.signal && { signal: callOptions.signal }),
187
+ ...((callOptions?.timeoutMs ?? this.defaultTimeoutMs) !== undefined && {
188
+ timeoutMs: callOptions?.timeoutMs ?? this.defaultTimeoutMs,
189
+ }),
190
+ };
191
+ const retryOnTimeout = this.retryOptions?.retryOnTimeout ?? true;
192
+ const retryableCodes = new Set([
193
+ types_1.ADAPTER_ERROR_CODES.RATE_LIMIT_EXCEEDED,
194
+ types_1.ADAPTER_ERROR_CODES.NETWORK_ERROR,
195
+ ...(retryOnTimeout ? [types_1.ADAPTER_ERROR_CODES.REQUEST_TIMEOUT] : []),
196
+ ]);
197
+ const result = await (0, withRetry_1.withRetry)(() => clientAdapter.sendMessage(internalRequest, apiKey, adapterOptions), (res) => {
198
+ if (res.object !== 'error') {
199
+ return { retry: false };
200
+ }
201
+ const code = String(res.error.code);
202
+ const status = res.error.status;
203
+ const retryable = retryableCodes.has(code) ||
204
+ (code === types_1.ADAPTER_ERROR_CODES.PROVIDER_ERROR &&
205
+ typeof status === 'number' &&
206
+ (status === 408 || status === 409 || status >= 500));
207
+ return { retry: retryable, retryAfterMs: res.error.retryAfterMs };
208
+ }, {
209
+ ...this.retryOptions,
210
+ ...(callOptions?.maxRetries !== undefined && {
211
+ maxRetries: callOptions.maxRetries,
212
+ }),
213
+ signal: callOptions?.signal,
214
+ logger: this.logger,
215
+ label: `${providerId}/${modelId}`,
216
+ });
179
217
  // Post-process for thinking tag fallback
180
218
  // This feature extracts reasoning from XML tags when native reasoning is not active.
181
219
  // It's a fallback mechanism for models without native reasoning or when native is disabled.
@@ -375,7 +413,7 @@ class LLMService {
375
413
  else {
376
414
  const { providerId, modelId, modelInfo, settings } = resolved;
377
415
  // Merge settings with model defaults
378
- const mergedSettings = this.settingsManager.mergeSettingsForModel(modelId, providerId, settings || {});
416
+ const mergedSettings = this.settingsManager.mergeSettingsForModel(modelId, providerId, settings || {}, modelInfo);
379
417
  // Calculate native reasoning status
380
418
  const nativeReasoningActive = !!(modelInfo.reasoning?.supported &&
381
419
  (mergedSettings.reasoning?.enabled === true ||
@@ -1,5 +1,6 @@
1
1
  import type { LLMResponse, LLMFailureResponse } from "../types";
2
- import type { ILLMClientAdapter, InternalLLMChatRequest } from "./types";
2
+ import type { ILLMClientAdapter, InternalLLMChatRequest, AdapterRequestOptions } from "./types";
3
+ import type { Logger } from "../../logging/types";
3
4
  /**
4
5
  * Client adapter for Anthropic API integration
5
6
  *
@@ -12,14 +13,17 @@ import type { ILLMClientAdapter, InternalLLMChatRequest } from "./types";
12
13
  */
13
14
  export declare class AnthropicClientAdapter implements ILLMClientAdapter {
14
15
  private baseURL?;
16
+ private logger;
15
17
  /**
16
18
  * Creates a new Anthropic client adapter
17
19
  *
18
20
  * @param config Optional configuration for the adapter
19
21
  * @param config.baseURL Custom base URL for Anthropic-compatible APIs
22
+ * @param config.logger Custom logger instance
20
23
  */
21
24
  constructor(config?: {
22
25
  baseURL?: string;
26
+ logger?: Logger;
23
27
  });
24
28
  /**
25
29
  * Sends a chat message to Anthropic's API
@@ -28,7 +32,7 @@ export declare class AnthropicClientAdapter implements ILLMClientAdapter {
28
32
  * @param apiKey - The decrypted Anthropic API key
29
33
  * @returns Promise resolving to success or failure response
30
34
  */
31
- sendMessage(request: InternalLLMChatRequest, apiKey: string): Promise<LLMResponse | LLMFailureResponse>;
35
+ sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
32
36
  /**
33
37
  * Validates Anthropic API key format
34
38
  *
@@ -11,7 +11,6 @@ const types_1 = require("./types");
11
11
  const errorUtils_1 = require("../../shared/adapters/errorUtils");
12
12
  const systemMessageUtils_1 = require("../../shared/adapters/systemMessageUtils");
13
13
  const defaultLogger_1 = require("../../logging/defaultLogger");
14
- const logger = (0, defaultLogger_1.createDefaultLogger)();
15
14
  /**
16
15
  * Client adapter for Anthropic API integration
17
16
  *
@@ -28,9 +27,11 @@ class AnthropicClientAdapter {
28
27
  *
29
28
  * @param config Optional configuration for the adapter
30
29
  * @param config.baseURL Custom base URL for Anthropic-compatible APIs
30
+ * @param config.logger Custom logger instance
31
31
  */
32
32
  constructor(config) {
33
33
  this.baseURL = config?.baseURL;
34
+ this.logger = config?.logger ?? (0, defaultLogger_1.createDefaultLogger)();
34
35
  }
35
36
  /**
36
37
  * Sends a chat message to Anthropic's API
@@ -39,7 +40,7 @@ class AnthropicClientAdapter {
39
40
  * @param apiKey - The decrypted Anthropic API key
40
41
  * @returns Promise resolving to success or failure response
41
42
  */
42
- async sendMessage(request, apiKey) {
43
+ async sendMessage(request, apiKey, options) {
43
44
  try {
44
45
  // Check if structured output is requested - need beta API
45
46
  const useStructuredOutput = request.settings.structuredOutput?.schema &&
@@ -48,7 +49,13 @@ class AnthropicClientAdapter {
48
49
  const anthropic = new sdk_1.default({
49
50
  apiKey,
50
51
  ...(this.baseURL && { baseURL: this.baseURL }),
52
+ maxRetries: 0, // retries are owned by the unified LLMService retry layer
51
53
  });
54
+ // Per-request transport options (abort signal, timeout)
55
+ const requestTransportOptions = {
56
+ ...(options?.signal && { signal: options.signal }),
57
+ ...(options?.timeoutMs !== undefined && { timeout: options.timeoutMs }),
58
+ };
52
59
  // Format messages for Anthropic API (Claude has specific requirements)
53
60
  const { messages, systemMessage } = this.formatMessagesForAnthropic(request);
54
61
  // Prepare API call parameters
@@ -58,6 +65,9 @@ class AnthropicClientAdapter {
58
65
  max_tokens: request.settings.maxTokens,
59
66
  temperature: request.settings.temperature,
60
67
  top_p: request.settings.topP,
68
+ ...(request.settings.topK !== undefined && {
69
+ top_k: request.settings.topK,
70
+ }),
61
71
  ...(systemMessage && { system: systemMessage }),
62
72
  ...(request.settings.stopSequences.length > 0 && {
63
73
  stop_sequences: request.settings.stopSequences,
@@ -115,8 +125,8 @@ class AnthropicClientAdapter {
115
125
  };
116
126
  }
117
127
  }
118
- logger.info(`Making Anthropic API call for model: ${request.modelId}`);
119
- logger.debug(`Anthropic API parameters:`, {
128
+ this.logger.info(`Making Anthropic API call for model: ${request.modelId}`);
129
+ this.logger.debug(`Anthropic API parameters:`, {
120
130
  model: messageParams.model,
121
131
  temperature: messageParams.temperature,
122
132
  max_tokens: messageParams.max_tokens,
@@ -134,18 +144,22 @@ class AnthropicClientAdapter {
134
144
  headers: {
135
145
  'anthropic-beta': 'structured-outputs-2025-11-13',
136
146
  },
147
+ ...requestTransportOptions,
137
148
  });
138
149
  }
139
150
  else {
140
- completion = await anthropic.messages.create(messageParams);
151
+ completion =
152
+ Object.keys(requestTransportOptions).length > 0
153
+ ? await anthropic.messages.create(messageParams, requestTransportOptions)
154
+ : await anthropic.messages.create(messageParams);
141
155
  }
142
- logger.info(`Anthropic API call successful, response ID: ${completion.id}`);
156
+ this.logger.info(`Anthropic API call successful, response ID: ${completion.id}`);
143
157
  // Convert to standardized response format
144
158
  // Cast to any to handle beta response type differences
145
159
  return this.createSuccessResponse(completion, request);
146
160
  }
147
161
  catch (error) {
148
- logger.error("Anthropic API error:", error);
162
+ this.logger.error("Anthropic API error:", error);
149
163
  return this.createErrorResponse(error, request);
150
164
  }
151
165
  }
@@ -245,7 +259,7 @@ class AnthropicClientAdapter {
245
259
  const modifiedIndex = (0, systemMessageUtils_1.prependSystemToFirstUserMessage)(simpleMessages, combinedSystemContent, request.settings.systemMessageFallback);
246
260
  if (modifiedIndex !== -1) {
247
261
  messages[modifiedIndex].content = simpleMessages[modifiedIndex].content;
248
- logger.debug(`Model ${request.modelId} doesn't support system messages - prepended to first user message`);
262
+ this.logger.debug(`Model ${request.modelId} doesn't support system messages - prepended to first user message`);
249
263
  }
250
264
  // Don't set systemMessage - it stays undefined
251
265
  }
@@ -253,7 +267,7 @@ class AnthropicClientAdapter {
253
267
  // Anthropic requires messages to start with 'user' role
254
268
  // If the first message is not from user, we need to handle this
255
269
  if (messages.length > 0 && messages[0].role !== "user") {
256
- logger.warn("Anthropic API requires first message to be from user. Adjusting message order.");
270
+ this.logger.warn("Anthropic API requires first message to be from user. Adjusting message order.");
257
271
  // Find the first user message and move it to the front, or create a default one
258
272
  const firstUserIndex = messages.findIndex((msg) => msg.role === "user");
259
273
  if (firstUserIndex > 0) {
@@ -295,7 +309,7 @@ class AnthropicClientAdapter {
295
309
  // If roles don't alternate properly, we might need to combine messages
296
310
  // or insert a placeholder. For now, we'll skip non-alternating messages
297
311
  // and log a warning.
298
- logger.warn(`Skipping message with unexpected role: expected ${expectedRole}, got ${message.role}`);
312
+ this.logger.warn(`Skipping message with unexpected role: expected ${expectedRole}, got ${message.role}`);
299
313
  }
300
314
  }
301
315
  return cleanedMessages;
@@ -386,7 +400,7 @@ class AnthropicClientAdapter {
386
400
  createErrorResponse(error, request) {
387
401
  // Use shared error mapping utility for common error patterns
388
402
  const initialProviderMessage = error instanceof sdk_1.default.APIError ? error.message : undefined;
389
- let { errorCode, errorMessage, errorType, status } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
403
+ let { errorCode, errorMessage, errorType, status, retryAfterMs } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
390
404
  // Apply Anthropic-specific refinements for 400 errors based on message content
391
405
  if (error instanceof sdk_1.default.APIError && status === 400) {
392
406
  if (error.message.toLowerCase().includes("context length") ||
@@ -408,6 +422,7 @@ class AnthropicClientAdapter {
408
422
  code: errorCode,
409
423
  type: errorType,
410
424
  ...(status && { status }),
425
+ ...(retryAfterMs !== undefined && { retryAfterMs }),
411
426
  providerError: error,
412
427
  },
413
428
  object: "error",
@@ -1,5 +1,6 @@
1
1
  import type { LLMResponse, LLMFailureResponse } from "../types";
2
- import type { ILLMClientAdapter, InternalLLMChatRequest } from "./types";
2
+ import type { ILLMClientAdapter, InternalLLMChatRequest, AdapterRequestOptions } from "./types";
3
+ import type { Logger } from "../../logging/types";
3
4
  /**
4
5
  * Client adapter for Google Gemini API integration
5
6
  *
@@ -12,14 +13,17 @@ import type { ILLMClientAdapter, InternalLLMChatRequest } from "./types";
12
13
  */
13
14
  export declare class GeminiClientAdapter implements ILLMClientAdapter {
14
15
  private baseURL?;
16
+ private logger;
15
17
  /**
16
18
  * Creates a new Gemini client adapter
17
19
  *
18
20
  * @param config Optional configuration for the adapter
19
21
  * @param config.baseURL Custom base URL (unused for Gemini but kept for consistency)
22
+ * @param config.logger Custom logger instance
20
23
  */
21
24
  constructor(config?: {
22
25
  baseURL?: string;
26
+ logger?: Logger;
23
27
  });
24
28
  /**
25
29
  * Sends a chat message to Gemini's API
@@ -28,7 +32,7 @@ export declare class GeminiClientAdapter implements ILLMClientAdapter {
28
32
  * @param apiKey - The decrypted Gemini API key
29
33
  * @returns Promise resolving to success or failure response
30
34
  */
31
- sendMessage(request: InternalLLMChatRequest, apiKey: string): Promise<LLMResponse | LLMFailureResponse>;
35
+ sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
32
36
  /**
33
37
  * Validates Gemini API key format
34
38
  *
@@ -8,7 +8,6 @@ const types_1 = require("./types");
8
8
  const errorUtils_1 = require("../../shared/adapters/errorUtils");
9
9
  const systemMessageUtils_1 = require("../../shared/adapters/systemMessageUtils");
10
10
  const defaultLogger_1 = require("../../logging/defaultLogger");
11
- const logger = (0, defaultLogger_1.createDefaultLogger)();
12
11
  /**
13
12
  * Client adapter for Google Gemini API integration
14
13
  *
@@ -25,9 +24,11 @@ class GeminiClientAdapter {
25
24
  *
26
25
  * @param config Optional configuration for the adapter
27
26
  * @param config.baseURL Custom base URL (unused for Gemini but kept for consistency)
27
+ * @param config.logger Custom logger instance
28
28
  */
29
29
  constructor(config) {
30
30
  this.baseURL = config?.baseURL;
31
+ this.logger = config?.logger ?? (0, defaultLogger_1.createDefaultLogger)();
31
32
  }
32
33
  /**
33
34
  * Sends a chat message to Gemini's API
@@ -36,14 +37,14 @@ class GeminiClientAdapter {
36
37
  * @param apiKey - The decrypted Gemini API key
37
38
  * @returns Promise resolving to success or failure response
38
39
  */
39
- async sendMessage(request, apiKey) {
40
+ async sendMessage(request, apiKey, options) {
40
41
  try {
41
- // Initialize Gemini client
42
+ // Initialize Gemini client (note: @google/genai performs no automatic retries)
42
43
  const genAI = new genai_1.GoogleGenAI({ apiKey });
43
44
  // Format the request for Gemini API
44
45
  const { contents, generationConfig, safetySettings, systemInstruction } = this.formatInternalRequestToGemini(request);
45
- logger.info(`Making Gemini API call for model: ${request.modelId}`);
46
- logger.debug(`Gemini API parameters:`, {
46
+ this.logger.info(`Making Gemini API call for model: ${request.modelId}`);
47
+ this.logger.debug(`Gemini API parameters:`, {
47
48
  model: request.modelId,
48
49
  temperature: generationConfig.temperature,
49
50
  maxOutputTokens: generationConfig.maxOutputTokens,
@@ -59,14 +60,18 @@ class GeminiClientAdapter {
59
60
  ...generationConfig,
60
61
  safetySettings: safetySettings,
61
62
  ...(systemInstruction && { systemInstruction: systemInstruction }),
63
+ ...(options?.signal && { abortSignal: options.signal }),
64
+ ...(options?.timeoutMs !== undefined && {
65
+ httpOptions: { timeout: options.timeoutMs },
66
+ }),
62
67
  },
63
68
  });
64
- logger.info(`Gemini API call successful, processing response`);
69
+ this.logger.info(`Gemini API call successful, processing response`);
65
70
  // Convert to standardized response format
66
71
  return this.createSuccessResponse(result, request);
67
72
  }
68
73
  catch (error) {
69
- logger.error("Gemini API error:", error);
74
+ this.logger.error("Gemini API error:", error);
70
75
  return this.createErrorResponse(error, request);
71
76
  }
72
77
  }
@@ -142,7 +147,7 @@ class GeminiClientAdapter {
142
147
  if (modifiedIndex !== -1) {
143
148
  // Update the actual contents array
144
149
  contents[modifiedIndex].parts[0].text = simpleContents[modifiedIndex].content;
145
- logger.debug(`Model ${request.modelId} doesn't support system instructions - prepended to first user message`);
150
+ this.logger.debug(`Model ${request.modelId} doesn't support system instructions - prepended to first user message`);
146
151
  }
147
152
  // Don't set systemInstruction - it stays undefined
148
153
  }
@@ -152,6 +157,12 @@ class GeminiClientAdapter {
152
157
  maxOutputTokens: request.settings.maxTokens,
153
158
  temperature: request.settings.temperature,
154
159
  ...(request.settings.topP && { topP: request.settings.topP }),
160
+ ...(request.settings.topK !== undefined && {
161
+ topK: request.settings.topK,
162
+ }),
163
+ ...(request.settings.seed !== undefined && {
164
+ seed: request.settings.seed,
165
+ }),
155
166
  ...(request.settings.stopSequences &&
156
167
  request.settings.stopSequences.length > 0 && {
157
168
  stopSequences: request.settings.stopSequences,
@@ -306,7 +317,7 @@ class GeminiClientAdapter {
306
317
  createErrorResponse(error, request) {
307
318
  // Use shared error mapping utility for common error patterns
308
319
  const initialProviderMessage = error?.message;
309
- let { errorCode, errorMessage, errorType, status } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
320
+ let { errorCode, errorMessage, errorType, status, retryAfterMs } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
310
321
  // Apply Gemini-specific refinements for certain error types
311
322
  if (error && error.message) {
312
323
  const message = error.message.toLowerCase();
@@ -336,6 +347,7 @@ class GeminiClientAdapter {
336
347
  code: errorCode,
337
348
  type: errorType,
338
349
  ...(status && { status }),
350
+ ...(retryAfterMs !== undefined && { retryAfterMs }),
339
351
  providerError: error,
340
352
  },
341
353
  object: "error",
@@ -1,14 +1,17 @@
1
1
  import type { LLMResponse, LLMFailureResponse, ModelInfo } from "../types";
2
- import type { ILLMClientAdapter, InternalLLMChatRequest } from "./types";
2
+ import type { ILLMClientAdapter, InternalLLMChatRequest, AdapterRequestOptions } from "./types";
3
3
  import { LlamaCppServerClient } from "./LlamaCppServerClient";
4
+ import type { Logger } from "../../logging/types";
4
5
  /**
5
6
  * Configuration options for LlamaCppClientAdapter
6
7
  */
7
8
  export interface LlamaCppClientConfig {
8
- /** Base URL of the llama.cpp server (default: http://localhost:8080) */
9
+ /** Base URL of the llama.cpp server (default: http://127.0.0.1:8080) */
9
10
  baseURL?: string;
10
11
  /** Whether to check server health before sending requests (default: false) */
11
12
  checkHealth?: boolean;
13
+ /** Logger instance for adapter logging */
14
+ logger?: Logger;
12
15
  }
13
16
  /**
14
17
  * Client adapter for llama.cpp server integration
@@ -30,7 +33,7 @@ export interface LlamaCppClientConfig {
30
33
  * ```typescript
31
34
  * // Create adapter for local server
32
35
  * const adapter = new LlamaCppClientAdapter({
33
- * baseURL: 'http://localhost:8080',
36
+ * baseURL: 'http://127.0.0.1:8080',
34
37
  * checkHealth: true
35
38
  * });
36
39
  *
@@ -51,6 +54,7 @@ export declare class LlamaCppClientAdapter implements ILLMClientAdapter {
51
54
  private serverClient;
52
55
  private cachedModelCapabilities;
53
56
  private detectionAttempted;
57
+ private logger;
54
58
  /**
55
59
  * Creates a new llama.cpp client adapter
56
60
  *
@@ -80,7 +84,7 @@ export declare class LlamaCppClientAdapter implements ILLMClientAdapter {
80
84
  * @param apiKey - Not used for llama.cpp (local server), but kept for interface compatibility
81
85
  * @returns Promise resolving to success or failure response
82
86
  */
83
- sendMessage(request: InternalLLMChatRequest, apiKey: string): Promise<LLMResponse | LLMFailureResponse>;
87
+ sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
84
88
  /**
85
89
  * Validates API key format
86
90
  *