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.
- package/LICENSE +21 -21
- package/README.md +210 -207
- package/dist/config/llm-presets.json +24 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -1
- package/dist/llm/LLMService.d.ts +30 -1
- package/dist/llm/LLMService.js +42 -4
- package/dist/llm/clients/AnthropicClientAdapter.d.ts +2 -2
- package/dist/llm/clients/AnthropicClientAdapter.js +17 -3
- package/dist/llm/clients/GeminiClientAdapter.d.ts +2 -2
- package/dist/llm/clients/GeminiClientAdapter.js +14 -3
- package/dist/llm/clients/LlamaCppClientAdapter.d.ts +4 -4
- package/dist/llm/clients/LlamaCppClientAdapter.js +118 -15
- package/dist/llm/clients/MistralClientAdapter.d.ts +2 -2
- package/dist/llm/clients/MistralClientAdapter.js +15 -4
- package/dist/llm/clients/MockClientAdapter.d.ts +2 -2
- package/dist/llm/clients/MockClientAdapter.js +36 -19
- package/dist/llm/clients/OpenAIClientAdapter.d.ts +2 -2
- package/dist/llm/clients/OpenAIClientAdapter.js +26 -3
- package/dist/llm/clients/OpenRouterClientAdapter.d.ts +2 -2
- package/dist/llm/clients/OpenRouterClientAdapter.js +81 -10
- package/dist/llm/clients/types.d.ts +18 -1
- package/dist/llm/clients/types.js +4 -0
- package/dist/llm/config.d.ts +9 -2
- package/dist/llm/config.js +695 -33
- package/dist/llm/services/ModelResolver.d.ts +6 -0
- package/dist/llm/services/ModelResolver.js +57 -17
- package/dist/llm/services/SettingsManager.d.ts +1 -1
- package/dist/llm/services/SettingsManager.js +92 -4
- package/dist/llm/types.d.ts +127 -0
- package/dist/prompting/index.d.ts +1 -1
- package/dist/prompting/index.js +2 -1
- package/dist/prompting/parser.d.ts +23 -0
- package/dist/prompting/parser.js +33 -0
- package/dist/shared/adapters/errorUtils.d.ts +15 -0
- package/dist/shared/adapters/errorUtils.js +69 -1
- package/dist/shared/adapters/logprobsUtils.d.ts +13 -0
- package/dist/shared/adapters/logprobsUtils.js +33 -0
- package/dist/shared/services/withRetry.d.ts +50 -0
- package/dist/shared/services/withRetry.js +78 -0
- package/package.json +59 -59
- package/src/config/image-presets.json +212 -212
- 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
|
|
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
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
};
|
package/dist/llm/config.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
*
|