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.
- package/LICENSE +21 -21
- package/README.md +210 -207
- package/dist/adapters/image/GenaiElectronImageAdapter.d.ts +1 -0
- package/dist/adapters/image/GenaiElectronImageAdapter.js +5 -5
- package/dist/adapters/image/OpenAIImageAdapter.d.ts +1 -0
- package/dist/adapters/image/OpenAIImageAdapter.js +4 -4
- package/dist/config/llm-presets.json +24 -0
- package/dist/image/ImageService.js +2 -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 +43 -5
- package/dist/llm/clients/AnthropicClientAdapter.d.ts +6 -2
- package/dist/llm/clients/AnthropicClientAdapter.js +26 -11
- package/dist/llm/clients/GeminiClientAdapter.d.ts +6 -2
- package/dist/llm/clients/GeminiClientAdapter.js +21 -9
- package/dist/llm/clients/LlamaCppClientAdapter.d.ts +8 -4
- package/dist/llm/clients/LlamaCppClientAdapter.js +131 -28
- package/dist/llm/clients/MistralClientAdapter.d.ts +6 -2
- package/dist/llm/clients/MistralClientAdapter.js +22 -11
- package/dist/llm/clients/MockClientAdapter.d.ts +2 -2
- package/dist/llm/clients/MockClientAdapter.js +36 -19
- package/dist/llm/clients/OpenAIClientAdapter.d.ts +6 -2
- package/dist/llm/clients/OpenAIClientAdapter.js +33 -9
- package/dist/llm/clients/OpenRouterClientAdapter.d.ts +6 -2
- package/dist/llm/clients/OpenRouterClientAdapter.js +87 -16
- 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 +697 -33
- package/dist/llm/services/ModelResolver.d.ts +6 -0
- package/dist/llm/services/ModelResolver.js +57 -17
- package/dist/llm/services/RequestValidator.d.ts +8 -0
- package/dist/llm/services/RequestValidator.js +9 -2
- 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/AdapterRegistry.js +3 -1
- package/dist/shared/services/withRetry.d.ts +50 -0
- package/dist/shared/services/withRetry.js +78 -0
- package/dist/types/image.d.ts +2 -0
- package/package.json +59 -59
- package/src/config/image-presets.json +212 -212
- package/src/config/llm-presets.json +623 -599
|
@@ -9,9 +9,9 @@ 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
|
-
const logger = (0, defaultLogger_1.createDefaultLogger)();
|
|
15
15
|
/**
|
|
16
16
|
* Client adapter for OpenAI API integration
|
|
17
17
|
*
|
|
@@ -27,9 +27,11 @@ class OpenAIClientAdapter {
|
|
|
27
27
|
*
|
|
28
28
|
* @param config Optional configuration for the adapter
|
|
29
29
|
* @param config.baseURL Custom base URL for OpenAI-compatible APIs
|
|
30
|
+
* @param config.logger Custom logger instance
|
|
30
31
|
*/
|
|
31
32
|
constructor(config) {
|
|
32
33
|
this.baseURL = config?.baseURL;
|
|
34
|
+
this.logger = config?.logger ?? (0, defaultLogger_1.createDefaultLogger)();
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* Sends a chat message to OpenAI's API
|
|
@@ -38,12 +40,13 @@ class OpenAIClientAdapter {
|
|
|
38
40
|
* @param apiKey - The decrypted OpenAI API key
|
|
39
41
|
* @returns Promise resolving to success or failure response
|
|
40
42
|
*/
|
|
41
|
-
async sendMessage(request, apiKey) {
|
|
43
|
+
async sendMessage(request, apiKey, options) {
|
|
42
44
|
try {
|
|
43
45
|
// Initialize OpenAI client
|
|
44
46
|
const openai = new openai_1.default({
|
|
45
47
|
apiKey,
|
|
46
48
|
...(this.baseURL && { baseURL: this.baseURL }),
|
|
49
|
+
maxRetries: 0, // retries are owned by the unified LLMService retry layer
|
|
47
50
|
});
|
|
48
51
|
// Format messages for OpenAI API
|
|
49
52
|
const messages = this.formatMessages(request);
|
|
@@ -63,6 +66,15 @@ class OpenAIClientAdapter {
|
|
|
63
66
|
...(request.settings.presencePenalty !== 0 && {
|
|
64
67
|
presence_penalty: request.settings.presencePenalty,
|
|
65
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
|
+
}),
|
|
66
78
|
...(request.settings.user && {
|
|
67
79
|
user: request.settings.user,
|
|
68
80
|
}),
|
|
@@ -95,7 +107,7 @@ class OpenAIClientAdapter {
|
|
|
95
107
|
}
|
|
96
108
|
};
|
|
97
109
|
}
|
|
98
|
-
logger.debug(`OpenAI API parameters:`, {
|
|
110
|
+
this.logger.debug(`OpenAI API parameters:`, {
|
|
99
111
|
model: completionParams.model,
|
|
100
112
|
temperature: completionParams.temperature,
|
|
101
113
|
max_completion_tokens: completionParams.max_completion_tokens,
|
|
@@ -105,12 +117,18 @@ class OpenAIClientAdapter {
|
|
|
105
117
|
presence_penalty: completionParams.presence_penalty,
|
|
106
118
|
hasUser: !!completionParams.user,
|
|
107
119
|
});
|
|
108
|
-
logger.info(`Making OpenAI API call for model: ${request.modelId}`);
|
|
120
|
+
this.logger.info(`Making OpenAI API call for model: ${request.modelId}`);
|
|
109
121
|
// Make the API call
|
|
110
|
-
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);
|
|
111
129
|
// Type guard to ensure we have a non-streaming response
|
|
112
130
|
if ('id' in completion && 'choices' in completion) {
|
|
113
|
-
logger.info(`OpenAI API call successful, response ID: ${completion.id}`);
|
|
131
|
+
this.logger.info(`OpenAI API call successful, response ID: ${completion.id}`);
|
|
114
132
|
// Convert to standardized response format
|
|
115
133
|
return this.createSuccessResponse(completion, request);
|
|
116
134
|
}
|
|
@@ -119,7 +137,7 @@ class OpenAIClientAdapter {
|
|
|
119
137
|
}
|
|
120
138
|
}
|
|
121
139
|
catch (error) {
|
|
122
|
-
logger.error("OpenAI API error:", error);
|
|
140
|
+
this.logger.error("OpenAI API error:", error);
|
|
123
141
|
return this.createErrorResponse(error, request);
|
|
124
142
|
}
|
|
125
143
|
}
|
|
@@ -224,7 +242,7 @@ class OpenAIClientAdapter {
|
|
|
224
242
|
const modifiedIndex = (0, systemMessageUtils_1.prependSystemToFirstUserMessage)(simpleMessages, combinedSystemContent, request.settings.systemMessageFallback);
|
|
225
243
|
if (modifiedIndex !== -1) {
|
|
226
244
|
messages[modifiedIndex].content = simpleMessages[modifiedIndex].content;
|
|
227
|
-
logger.debug(`Model ${request.modelId} doesn't support system messages - prepended to first user message`);
|
|
245
|
+
this.logger.debug(`Model ${request.modelId} doesn't support system messages - prepended to first user message`);
|
|
228
246
|
}
|
|
229
247
|
}
|
|
230
248
|
}
|
|
@@ -255,6 +273,11 @@ class OpenAIClientAdapter {
|
|
|
255
273
|
if (choice.reasoning && request.settings.reasoning && !request.settings.reasoning.exclude) {
|
|
256
274
|
responseChoice.reasoning = choice.reasoning;
|
|
257
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
|
+
}
|
|
258
281
|
return {
|
|
259
282
|
id: completion.id,
|
|
260
283
|
provider: request.providerId,
|
|
@@ -281,7 +304,7 @@ class OpenAIClientAdapter {
|
|
|
281
304
|
createErrorResponse(error, request) {
|
|
282
305
|
// Use shared error mapping utility for common error patterns
|
|
283
306
|
const initialProviderMessage = error instanceof openai_1.default.APIError ? error.message : undefined;
|
|
284
|
-
let { errorCode, errorMessage, errorType, status } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
|
|
307
|
+
let { errorCode, errorMessage, errorType, status, retryAfterMs } = (0, errorUtils_1.getCommonMappedErrorDetails)(error, initialProviderMessage);
|
|
285
308
|
// Apply OpenAI-specific refinements for 400 errors based on message content
|
|
286
309
|
if (error instanceof openai_1.default.APIError && status === 400) {
|
|
287
310
|
if (error.message.toLowerCase().includes("context length")) {
|
|
@@ -301,6 +324,7 @@ class OpenAIClientAdapter {
|
|
|
301
324
|
code: errorCode,
|
|
302
325
|
type: errorType,
|
|
303
326
|
...(status && { status }),
|
|
327
|
+
...(retryAfterMs !== undefined && { retryAfterMs }),
|
|
304
328
|
providerError: error,
|
|
305
329
|
},
|
|
306
330
|
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
|
* Configuration options for OpenRouterClientAdapter
|
|
5
6
|
*/
|
|
@@ -10,6 +11,8 @@ export interface OpenRouterClientConfig {
|
|
|
10
11
|
httpReferer?: string;
|
|
11
12
|
/** Your app's display name for rankings (optional) */
|
|
12
13
|
siteTitle?: string;
|
|
14
|
+
/** Logger instance for adapter logging */
|
|
15
|
+
logger?: Logger;
|
|
13
16
|
}
|
|
14
17
|
/**
|
|
15
18
|
* Client adapter for OpenRouter API integration
|
|
@@ -45,6 +48,7 @@ export declare class OpenRouterClientAdapter implements ILLMClientAdapter {
|
|
|
45
48
|
private baseURL;
|
|
46
49
|
private httpReferer?;
|
|
47
50
|
private siteTitle?;
|
|
51
|
+
private logger;
|
|
48
52
|
/**
|
|
49
53
|
* Creates a new OpenRouter client adapter
|
|
50
54
|
*
|
|
@@ -58,7 +62,7 @@ export declare class OpenRouterClientAdapter implements ILLMClientAdapter {
|
|
|
58
62
|
* @param apiKey - The OpenRouter API key
|
|
59
63
|
* @returns Promise resolving to success or failure response
|
|
60
64
|
*/
|
|
61
|
-
sendMessage(request: InternalLLMChatRequest, apiKey: string): Promise<LLMResponse | LLMFailureResponse>;
|
|
65
|
+
sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
|
|
62
66
|
/**
|
|
63
67
|
* Validates OpenRouter API key format
|
|
64
68
|
*
|
|
@@ -9,9 +9,9 @@ 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
|
-
const logger = (0, defaultLogger_1.createDefaultLogger)();
|
|
15
15
|
/**
|
|
16
16
|
* Client adapter for OpenRouter API integration
|
|
17
17
|
*
|
|
@@ -52,6 +52,7 @@ class OpenRouterClientAdapter {
|
|
|
52
52
|
this.baseURL = config?.baseURL || 'https://openrouter.ai/api/v1';
|
|
53
53
|
this.httpReferer = config?.httpReferer || process.env.OPENROUTER_HTTP_REFERER;
|
|
54
54
|
this.siteTitle = config?.siteTitle || process.env.OPENROUTER_SITE_TITLE;
|
|
55
|
+
this.logger = config?.logger ?? (0, defaultLogger_1.createDefaultLogger)();
|
|
55
56
|
}
|
|
56
57
|
/**
|
|
57
58
|
* Sends a chat message to OpenRouter API
|
|
@@ -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,7 +148,31 @@ class OpenRouterClientAdapter {
|
|
|
126
148
|
}
|
|
127
149
|
};
|
|
128
150
|
}
|
|
129
|
-
|
|
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
|
+
}
|
|
175
|
+
this.logger.debug(`OpenRouter API parameters:`, {
|
|
130
176
|
baseURL: this.baseURL,
|
|
131
177
|
model: completionParams.model,
|
|
132
178
|
temperature: completionParams.temperature,
|
|
@@ -134,12 +180,18 @@ class OpenRouterClientAdapter {
|
|
|
134
180
|
top_p: completionParams.top_p,
|
|
135
181
|
hasProviderRouting: !!completionParams.provider,
|
|
136
182
|
});
|
|
137
|
-
logger.info(`Making OpenRouter API call for model: ${request.modelId}`);
|
|
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
|
-
logger.info(`OpenRouter API call successful, response ID: ${completion.id}`);
|
|
194
|
+
this.logger.info(`OpenRouter API call successful, response ID: ${completion.id}`);
|
|
143
195
|
return this.createSuccessResponse(completion, request);
|
|
144
196
|
}
|
|
145
197
|
else {
|
|
@@ -147,7 +199,7 @@ class OpenRouterClientAdapter {
|
|
|
147
199
|
}
|
|
148
200
|
}
|
|
149
201
|
catch (error) {
|
|
150
|
-
logger.error("OpenRouter API error:", error);
|
|
202
|
+
this.logger.error("OpenRouter API error:", error);
|
|
151
203
|
return this.createErrorResponse(error, request);
|
|
152
204
|
}
|
|
153
205
|
}
|
|
@@ -223,7 +275,7 @@ class OpenRouterClientAdapter {
|
|
|
223
275
|
const modifiedIndex = (0, systemMessageUtils_1.prependSystemToFirstUserMessage)(simpleMessages, combinedSystemContent, request.settings.systemMessageFallback);
|
|
224
276
|
if (modifiedIndex !== -1) {
|
|
225
277
|
messages[modifiedIndex].content = simpleMessages[modifiedIndex].content;
|
|
226
|
-
logger.debug(`Model ${request.modelId} doesn't support system messages - prepended to first user message`);
|
|
278
|
+
this.logger.debug(`Model ${request.modelId} doesn't support system messages - prepended to first user message`);
|
|
227
279
|
}
|
|
228
280
|
}
|
|
229
281
|
}
|
|
@@ -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
|
*
|