genai-lite 0.8.3 → 0.9.1

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 (43) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +210 -207
  3. package/dist/config/llm-presets.json +24 -0
  4. package/dist/index.d.ts +4 -2
  5. package/dist/index.js +6 -1
  6. package/dist/llm/LLMService.d.ts +30 -1
  7. package/dist/llm/LLMService.js +42 -4
  8. package/dist/llm/clients/AnthropicClientAdapter.d.ts +2 -2
  9. package/dist/llm/clients/AnthropicClientAdapter.js +17 -3
  10. package/dist/llm/clients/GeminiClientAdapter.d.ts +2 -2
  11. package/dist/llm/clients/GeminiClientAdapter.js +14 -3
  12. package/dist/llm/clients/LlamaCppClientAdapter.d.ts +4 -4
  13. package/dist/llm/clients/LlamaCppClientAdapter.js +118 -15
  14. package/dist/llm/clients/MistralClientAdapter.d.ts +2 -2
  15. package/dist/llm/clients/MistralClientAdapter.js +15 -4
  16. package/dist/llm/clients/MockClientAdapter.d.ts +2 -2
  17. package/dist/llm/clients/MockClientAdapter.js +36 -19
  18. package/dist/llm/clients/OpenAIClientAdapter.d.ts +2 -2
  19. package/dist/llm/clients/OpenAIClientAdapter.js +26 -3
  20. package/dist/llm/clients/OpenRouterClientAdapter.d.ts +2 -2
  21. package/dist/llm/clients/OpenRouterClientAdapter.js +81 -10
  22. package/dist/llm/clients/types.d.ts +18 -1
  23. package/dist/llm/clients/types.js +4 -0
  24. package/dist/llm/config.d.ts +9 -2
  25. package/dist/llm/config.js +695 -33
  26. package/dist/llm/services/ModelResolver.d.ts +6 -0
  27. package/dist/llm/services/ModelResolver.js +57 -17
  28. package/dist/llm/services/SettingsManager.d.ts +1 -1
  29. package/dist/llm/services/SettingsManager.js +92 -4
  30. package/dist/llm/types.d.ts +127 -0
  31. package/dist/prompting/index.d.ts +1 -1
  32. package/dist/prompting/index.js +2 -1
  33. package/dist/prompting/parser.d.ts +23 -0
  34. package/dist/prompting/parser.js +33 -0
  35. package/dist/shared/adapters/errorUtils.d.ts +15 -0
  36. package/dist/shared/adapters/errorUtils.js +69 -1
  37. package/dist/shared/adapters/logprobsUtils.d.ts +13 -0
  38. package/dist/shared/adapters/logprobsUtils.js +33 -0
  39. package/dist/shared/services/withRetry.d.ts +50 -0
  40. package/dist/shared/services/withRetry.js +78 -0
  41. package/package.json +59 -59
  42. package/src/config/image-presets.json +212 -212
  43. package/src/config/llm-presets.json +623 -599
@@ -25,7 +25,20 @@ class MockClientAdapter {
25
25
  * @param apiKey - The API key (ignored for mock)
26
26
  * @returns Promise resolving to mock response
27
27
  */
28
- async sendMessage(request, apiKey) {
28
+ async sendMessage(request, apiKey, options) {
29
+ // Honor abort signals like a real adapter would
30
+ if (options?.signal?.aborted) {
31
+ return {
32
+ provider: request.providerId,
33
+ model: request.modelId,
34
+ error: {
35
+ message: "Request was aborted",
36
+ code: types_1.ADAPTER_ERROR_CODES.REQUEST_ABORTED,
37
+ type: "abort_error",
38
+ },
39
+ object: "error",
40
+ };
41
+ }
29
42
  // Simulate network delay
30
43
  await this.simulateDelay(100, 500);
31
44
  try {
@@ -213,15 +226,19 @@ class MockClientAdapter {
213
226
  * Generates a response that shows current settings
214
227
  */
215
228
  generateSettingsTestResponse(settings) {
216
- return `Current mock settings:
217
- - Temperature: ${settings.temperature}
218
- - Max Tokens: ${settings.maxTokens}
219
- - Top P: ${settings.topP}
229
+ return `Current mock settings:
230
+ - Temperature: ${settings.temperature}
231
+ - Max Tokens: ${settings.maxTokens}
232
+ - Top P: ${settings.topP}
220
233
  - Stop Sequences: ${settings.stopSequences.length > 0
221
234
  ? settings.stopSequences.join(", ")
222
- : "none"}
223
- - Frequency Penalty: ${settings.frequencyPenalty}
224
- - Presence Penalty: ${settings.presencePenalty}
235
+ : "none"}
236
+ - Frequency Penalty: ${settings.frequencyPenalty}
237
+ - Presence Penalty: ${settings.presencePenalty}
238
+ - Top K: ${settings.topK ?? "not set"}
239
+ - Min P: ${settings.minP ?? "not set"}
240
+ - Repeat Penalty: ${settings.repeatPenalty ?? "not set"}
241
+ - Seed: ${settings.seed ?? "not set"}
225
242
  - User: ${settings.user || "not set"}`;
226
243
  }
227
244
  /**
@@ -285,17 +302,17 @@ class MockClientAdapter {
285
302
  * Generates a longer mock response for testing
286
303
  */
287
304
  generateLongResponse() {
288
- return `This is a detailed mock response from the ${this.providerId} adapter.
289
-
290
- I can simulate various types of responses based on your input. Here are some features:
291
-
292
- 1. **Error Simulation**: Include phrases like "error_rate_limit" to test error handling
293
- 2. **Variable Length**: Request "long" responses to test token limits
294
- 3. **Code Generation**: Ask about "programming" to get mock code snippets
295
- 4. **Conversational**: Simple greetings work too
296
-
297
- The mock adapter is useful for testing the LLM integration without making real API calls. It simulates realistic response times, token usage, and various error conditions that you might encounter with real LLM providers.
298
-
305
+ return `This is a detailed mock response from the ${this.providerId} adapter.
306
+
307
+ I can simulate various types of responses based on your input. Here are some features:
308
+
309
+ 1. **Error Simulation**: Include phrases like "error_rate_limit" to test error handling
310
+ 2. **Variable Length**: Request "long" responses to test token limits
311
+ 3. **Code Generation**: Ask about "programming" to get mock code snippets
312
+ 4. **Conversational**: Simple greetings work too
313
+
314
+ The mock adapter is useful for testing the LLM integration without making real API calls. It simulates realistic response times, token usage, and various error conditions that you might encounter with real LLM providers.
315
+
299
316
  This response demonstrates how the adapter can generate longer content while still respecting the maxTokens parameter if specified in the request settings.`;
300
317
  }
301
318
  /**
@@ -1,5 +1,5 @@
1
1
  import type { LLMResponse, LLMFailureResponse } from "../types";
2
- import type { ILLMClientAdapter, InternalLLMChatRequest } from "./types";
2
+ import type { ILLMClientAdapter, InternalLLMChatRequest, AdapterRequestOptions } from "./types";
3
3
  import type { Logger } from "../../logging/types";
4
4
  /**
5
5
  * Client adapter for OpenAI API integration
@@ -31,7 +31,7 @@ export declare class OpenAIClientAdapter implements ILLMClientAdapter {
31
31
  * @param apiKey - The decrypted OpenAI API key
32
32
  * @returns Promise resolving to success or failure response
33
33
  */
34
- sendMessage(request: InternalLLMChatRequest, apiKey: string): Promise<LLMResponse | LLMFailureResponse>;
34
+ sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
35
35
  /**
36
36
  * Validates OpenAI API key format
37
37
  *
@@ -9,6 +9,7 @@ exports.OpenAIClientAdapter = void 0;
9
9
  const openai_1 = __importDefault(require("openai"));
10
10
  const types_1 = require("./types");
11
11
  const errorUtils_1 = require("../../shared/adapters/errorUtils");
12
+ const logprobsUtils_1 = require("../../shared/adapters/logprobsUtils");
12
13
  const systemMessageUtils_1 = require("../../shared/adapters/systemMessageUtils");
13
14
  const defaultLogger_1 = require("../../logging/defaultLogger");
14
15
  /**
@@ -39,12 +40,13 @@ class OpenAIClientAdapter {
39
40
  * @param apiKey - The decrypted OpenAI 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
  // Initialize OpenAI client
45
46
  const openai = new openai_1.default({
46
47
  apiKey,
47
48
  ...(this.baseURL && { baseURL: this.baseURL }),
49
+ maxRetries: 0, // retries are owned by the unified LLMService retry layer
48
50
  });
49
51
  // Format messages for OpenAI API
50
52
  const messages = this.formatMessages(request);
@@ -64,6 +66,15 @@ class OpenAIClientAdapter {
64
66
  ...(request.settings.presencePenalty !== 0 && {
65
67
  presence_penalty: request.settings.presencePenalty,
66
68
  }),
69
+ ...(request.settings.seed !== undefined && {
70
+ seed: request.settings.seed,
71
+ }),
72
+ ...(request.settings.logprobs === true && {
73
+ logprobs: true,
74
+ ...(request.settings.topLogprobs !== undefined && {
75
+ top_logprobs: request.settings.topLogprobs,
76
+ }),
77
+ }),
67
78
  ...(request.settings.user && {
68
79
  user: request.settings.user,
69
80
  }),
@@ -108,7 +119,13 @@ class OpenAIClientAdapter {
108
119
  });
109
120
  this.logger.info(`Making OpenAI API call for model: ${request.modelId}`);
110
121
  // Make the API call
111
- const completion = await openai.chat.completions.create(completionParams);
122
+ const transportOptions = {
123
+ ...(options?.signal && { signal: options.signal }),
124
+ ...(options?.timeoutMs !== undefined && { timeout: options.timeoutMs }),
125
+ };
126
+ const completion = Object.keys(transportOptions).length > 0
127
+ ? await openai.chat.completions.create(completionParams, transportOptions)
128
+ : await openai.chat.completions.create(completionParams);
112
129
  // Type guard to ensure we have a non-streaming response
113
130
  if ('id' in completion && 'choices' in completion) {
114
131
  this.logger.info(`OpenAI API call successful, response ID: ${completion.id}`);
@@ -256,6 +273,11 @@ class OpenAIClientAdapter {
256
273
  if (choice.reasoning && request.settings.reasoning && !request.settings.reasoning.exclude) {
257
274
  responseChoice.reasoning = choice.reasoning;
258
275
  }
276
+ // Per-token log probabilities (when requested via settings.logprobs)
277
+ const logprobs = (0, logprobsUtils_1.mapOpenAIChatLogprobs)(choice.logprobs);
278
+ if (logprobs) {
279
+ responseChoice.logprobs = logprobs;
280
+ }
259
281
  return {
260
282
  id: completion.id,
261
283
  provider: request.providerId,
@@ -282,7 +304,7 @@ class OpenAIClientAdapter {
282
304
  createErrorResponse(error, request) {
283
305
  // Use shared error mapping utility for common error patterns
284
306
  const initialProviderMessage = error instanceof openai_1.default.APIError ? error.message : undefined;
285
- let { errorCode, errorMessage, errorType, status } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
307
+ let { errorCode, errorMessage, errorType, status, retryAfterMs } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
286
308
  // Apply OpenAI-specific refinements for 400 errors based on message content
287
309
  if (error instanceof openai_1.default.APIError && status === 400) {
288
310
  if (error.message.toLowerCase().includes("context length")) {
@@ -302,6 +324,7 @@ class OpenAIClientAdapter {
302
324
  code: errorCode,
303
325
  type: errorType,
304
326
  ...(status && { status }),
327
+ ...(retryAfterMs !== undefined && { retryAfterMs }),
305
328
  providerError: error,
306
329
  },
307
330
  object: "error",
@@ -1,5 +1,5 @@
1
1
  import type { LLMResponse, LLMFailureResponse } from "../types";
2
- import type { ILLMClientAdapter, InternalLLMChatRequest } from "./types";
2
+ import type { ILLMClientAdapter, InternalLLMChatRequest, AdapterRequestOptions } from "./types";
3
3
  import type { Logger } from "../../logging/types";
4
4
  /**
5
5
  * Configuration options for OpenRouterClientAdapter
@@ -62,7 +62,7 @@ export declare class OpenRouterClientAdapter implements ILLMClientAdapter {
62
62
  * @param apiKey - The OpenRouter API key
63
63
  * @returns Promise resolving to success or failure response
64
64
  */
65
- sendMessage(request: InternalLLMChatRequest, apiKey: string): Promise<LLMResponse | LLMFailureResponse>;
65
+ sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
66
66
  /**
67
67
  * Validates OpenRouter API key format
68
68
  *
@@ -9,6 +9,7 @@ exports.OpenRouterClientAdapter = void 0;
9
9
  const openai_1 = __importDefault(require("openai"));
10
10
  const types_1 = require("./types");
11
11
  const errorUtils_1 = require("../../shared/adapters/errorUtils");
12
+ const logprobsUtils_1 = require("../../shared/adapters/logprobsUtils");
12
13
  const systemMessageUtils_1 = require("../../shared/adapters/systemMessageUtils");
13
14
  const defaultLogger_1 = require("../../logging/defaultLogger");
14
15
  /**
@@ -60,11 +61,12 @@ class OpenRouterClientAdapter {
60
61
  * @param apiKey - The OpenRouter API key
61
62
  * @returns Promise resolving to success or failure response
62
63
  */
63
- async sendMessage(request, apiKey) {
64
+ async sendMessage(request, apiKey, options) {
64
65
  try {
65
66
  // Initialize OpenAI client with OpenRouter base URL and custom headers
66
67
  const openai = new openai_1.default({
67
68
  apiKey,
69
+ maxRetries: 0, // retries are owned by the unified LLMService retry layer
68
70
  baseURL: this.baseURL,
69
71
  defaultHeaders: {
70
72
  ...(this.httpReferer && { 'HTTP-Referer': this.httpReferer }),
@@ -89,6 +91,26 @@ class OpenRouterClientAdapter {
89
91
  ...(request.settings.presencePenalty !== 0 && {
90
92
  presence_penalty: request.settings.presencePenalty,
91
93
  }),
94
+ ...(request.settings.seed !== undefined && {
95
+ seed: request.settings.seed,
96
+ }),
97
+ // OpenRouter pass-through sampling params (not in the OpenAI SDK types;
98
+ // sent as-is — OpenRouter ignores them for models that don't support them)
99
+ ...(request.settings.topK !== undefined && {
100
+ top_k: request.settings.topK,
101
+ }),
102
+ ...(request.settings.minP !== undefined && {
103
+ min_p: request.settings.minP,
104
+ }),
105
+ ...(request.settings.repeatPenalty !== undefined && {
106
+ repetition_penalty: request.settings.repeatPenalty,
107
+ }),
108
+ ...(request.settings.logprobs === true && {
109
+ logprobs: true,
110
+ ...(request.settings.topLogprobs !== undefined && {
111
+ top_logprobs: request.settings.topLogprobs,
112
+ }),
113
+ }),
92
114
  };
93
115
  // Add OpenRouter-specific provider routing if configured
94
116
  const providerSettings = request.settings.openRouterProvider;
@@ -126,6 +148,30 @@ class OpenRouterClientAdapter {
126
148
  }
127
149
  };
128
150
  }
151
+ // Map unified reasoning settings to OpenRouter's reasoning body param.
152
+ // effort and max_tokens are mutually exclusive on OpenRouter; max_tokens wins
153
+ // when both are set (matching the Anthropic adapter's precedence). OpenRouter
154
+ // silently ignores the param for models without reasoning support.
155
+ const reasoningSettings = request.settings.reasoning;
156
+ if (reasoningSettings &&
157
+ (reasoningSettings.enabled === true ||
158
+ reasoningSettings.effort !== undefined ||
159
+ reasoningSettings.maxTokens !== undefined)) {
160
+ const reasoning = {};
161
+ if (reasoningSettings.maxTokens !== undefined) {
162
+ reasoning.max_tokens = reasoningSettings.maxTokens;
163
+ }
164
+ else if (reasoningSettings.effort) {
165
+ reasoning.effort = reasoningSettings.effort;
166
+ }
167
+ else {
168
+ reasoning.enabled = true;
169
+ }
170
+ if (reasoningSettings.exclude === true) {
171
+ reasoning.exclude = true;
172
+ }
173
+ completionParams.reasoning = reasoning;
174
+ }
129
175
  this.logger.debug(`OpenRouter API parameters:`, {
130
176
  baseURL: this.baseURL,
131
177
  model: completionParams.model,
@@ -136,7 +182,13 @@ class OpenRouterClientAdapter {
136
182
  });
137
183
  this.logger.info(`Making OpenRouter API call for model: ${request.modelId}`);
138
184
  // Make the API call
139
- const completion = await openai.chat.completions.create(completionParams);
185
+ const transportOptions = {
186
+ ...(options?.signal && { signal: options.signal }),
187
+ ...(options?.timeoutMs !== undefined && { timeout: options.timeoutMs }),
188
+ };
189
+ const completion = Object.keys(transportOptions).length > 0
190
+ ? await openai.chat.completions.create(completionParams, transportOptions)
191
+ : await openai.chat.completions.create(completionParams);
140
192
  // Type guard to ensure we have a non-streaming response
141
193
  if ('id' in completion && 'choices' in completion) {
142
194
  this.logger.info(`OpenRouter API call successful, response ID: ${completion.id}`);
@@ -246,14 +298,32 @@ class OpenRouterClientAdapter {
246
298
  provider: request.providerId,
247
299
  model: completion.model || request.modelId,
248
300
  created: completion.created,
249
- choices: completion.choices.map((c) => ({
250
- message: {
251
- role: "assistant",
252
- content: c.message.content || "",
253
- },
254
- finish_reason: c.finish_reason,
255
- index: c.index,
256
- })),
301
+ choices: completion.choices.map((c) => {
302
+ const mappedChoice = {
303
+ message: {
304
+ role: "assistant",
305
+ content: c.message.content || "",
306
+ },
307
+ finish_reason: c.finish_reason,
308
+ index: c.index,
309
+ };
310
+ // OpenRouter returns the reasoning trace on message.reasoning (plus
311
+ // structured reasoning_details) for models that expose it
312
+ const messageReasoning = c.message.reasoning;
313
+ if (messageReasoning && request.settings.reasoning?.exclude !== true) {
314
+ mappedChoice.reasoning = messageReasoning;
315
+ }
316
+ const reasoningDetails = c.message.reasoning_details;
317
+ if (reasoningDetails && request.settings.reasoning?.exclude !== true) {
318
+ mappedChoice.reasoning_details = reasoningDetails;
319
+ }
320
+ // Per-token log probabilities (OpenAI-compatible shape)
321
+ const logprobs = (0, logprobsUtils_1.mapOpenAIChatLogprobs)(c.logprobs);
322
+ if (logprobs) {
323
+ mappedChoice.logprobs = logprobs;
324
+ }
325
+ return mappedChoice;
326
+ }),
257
327
  usage: completion.usage
258
328
  ? {
259
329
  prompt_tokens: completion.usage.prompt_tokens,
@@ -289,6 +359,7 @@ class OpenRouterClientAdapter {
289
359
  code: mappedError.errorCode,
290
360
  type: mappedError.errorType,
291
361
  ...(mappedError.status && { status: mappedError.status }),
362
+ ...(mappedError.retryAfterMs !== undefined && { retryAfterMs: mappedError.retryAfterMs }),
292
363
  providerError: error,
293
364
  },
294
365
  object: "error",
@@ -6,6 +6,18 @@ import type { LLMChatRequest, LLMResponse, LLMFailureResponse, LLMSettings } fro
6
6
  export interface InternalLLMChatRequest extends Omit<LLMChatRequest, "settings"> {
7
7
  settings: Required<LLMSettings>;
8
8
  }
9
+ /**
10
+ * Per-request transport options passed from the service layer to adapters.
11
+ */
12
+ export interface AdapterRequestOptions {
13
+ /**
14
+ * Abort signal for cancelling the in-flight request. Note that aborting only
15
+ * cancels the client-side wait — the provider may still process (and bill) the request.
16
+ */
17
+ signal?: AbortSignal;
18
+ /** Request timeout in milliseconds (mapped to each SDK's timeout mechanism) */
19
+ timeoutMs?: number;
20
+ }
9
21
  /**
10
22
  * Interface that all LLM client adapters must implement
11
23
  *
@@ -22,11 +34,12 @@ export interface ILLMClientAdapter {
22
34
  *
23
35
  * @param request - The LLM request with applied default settings
24
36
  * @param apiKey - The decrypted API key for the provider
37
+ * @param options - Optional per-request transport options (abort signal, timeout)
25
38
  * @returns Promise resolving to either a successful response or failure response
26
39
  *
27
40
  * @throws Should not throw - all errors should be caught and returned as LLMFailureResponse
28
41
  */
29
- sendMessage(request: InternalLLMChatRequest, apiKey: string): Promise<LLMResponse | LLMFailureResponse>;
42
+ sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
30
43
  /**
31
44
  * Optional method to validate API key format before making requests
32
45
  *
@@ -58,6 +71,10 @@ export declare const ADAPTER_ERROR_CODES: {
58
71
  readonly NETWORK_ERROR: "NETWORK_ERROR";
59
72
  readonly PROVIDER_ERROR: "PROVIDER_ERROR";
60
73
  readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
74
+ /** The request exceeded its timeout (retryable by default) */
75
+ readonly REQUEST_TIMEOUT: "REQUEST_TIMEOUT";
76
+ /** The request was cancelled via AbortSignal (never retried) */
77
+ readonly REQUEST_ABORTED: "REQUEST_ABORTED";
61
78
  };
62
79
  /**
63
80
  * Helper type for adapter error codes
@@ -16,4 +16,8 @@ exports.ADAPTER_ERROR_CODES = {
16
16
  NETWORK_ERROR: "NETWORK_ERROR",
17
17
  PROVIDER_ERROR: "PROVIDER_ERROR",
18
18
  UNKNOWN_ERROR: "UNKNOWN_ERROR",
19
+ /** The request exceeded its timeout (retryable by default) */
20
+ REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
21
+ /** The request was cancelled via AbortSignal (never retried) */
22
+ REQUEST_ABORTED: "REQUEST_ABORTED",
19
23
  };
@@ -50,7 +50,11 @@ export interface GgufModelPattern {
50
50
  * Order matters: more specific patterns should come before generic ones.
51
51
  * First matching pattern wins.
52
52
  *
53
- * Example: "Qwen3-0.6B-0522" should be before "Qwen3-0.6B"
53
+ * Ordering rules (see docs/dev/adding-models-and-providers.md):
54
+ * - Specific before generic: "qwen3-4b-instruct-2507" before "qwen3-4b"
55
+ * - Newer family names before older substrings they contain: "qwen3.5" contains no
56
+ * "qwen3-" (the dot breaks the match) but is listed first anyway for clarity
57
+ * - Quantization agnostic: never embed Q4_K_M/Q8_0 in patterns
54
58
  */
55
59
  export declare const KNOWN_GGUF_MODELS: GgufModelPattern[];
56
60
  /**
@@ -131,9 +135,12 @@ export declare function createFallbackModelInfo(modelId: string, providerId: str
131
135
  *
132
136
  * @param modelId - The model ID
133
137
  * @param providerId - The provider ID
138
+ * @param resolvedModelInfo - Optional already-resolved ModelInfo (e.g. a detected GGUF
139
+ * model's fallback info). When provided it is used instead of a registry lookup, so
140
+ * capabilities and defaultSettings of dynamically-detected models flow into settings.
134
141
  * @returns Merged default settings with model-specific overrides applied
135
142
  */
136
- export declare function getDefaultSettingsForModel(modelId: string, providerId: ApiProviderId): Required<LLMSettings>;
143
+ export declare function getDefaultSettingsForModel(modelId: string, providerId: ApiProviderId, resolvedModelInfo?: ModelInfo): Required<LLMSettings>;
137
144
  /**
138
145
  * Validates LLM settings values
139
146
  *