genai-lite 0.1.1 → 0.1.3
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/README.md +99 -1
- package/dist/config/presets.json +222 -0
- package/dist/index.d.ts +2 -0
- package/dist/llm/LLMService.d.ts +25 -1
- package/dist/llm/LLMService.js +34 -1
- package/dist/llm/LLMService.presets.test.d.ts +1 -0
- package/dist/llm/LLMService.presets.test.js +210 -0
- package/dist/llm/LLMService.test.d.ts +1 -0
- package/dist/llm/LLMService.test.js +279 -0
- package/dist/llm/clients/AnthropicClientAdapter.test.d.ts +1 -0
- package/dist/llm/clients/AnthropicClientAdapter.test.js +263 -0
- package/dist/llm/clients/GeminiClientAdapter.test.d.ts +1 -0
- package/dist/llm/clients/GeminiClientAdapter.test.js +281 -0
- package/dist/llm/clients/MockClientAdapter.test.d.ts +1 -0
- package/dist/llm/clients/MockClientAdapter.test.js +240 -0
- package/dist/llm/clients/OpenAIClientAdapter.test.d.ts +1 -0
- package/dist/llm/clients/OpenAIClientAdapter.test.js +248 -0
- package/dist/llm/clients/adapterErrorUtils.test.d.ts +1 -0
- package/dist/llm/clients/adapterErrorUtils.test.js +123 -0
- package/dist/llm/config.test.d.ts +1 -0
- package/dist/llm/config.test.js +159 -0
- package/dist/providers/fromEnvironment.test.d.ts +1 -0
- package/dist/providers/fromEnvironment.test.js +46 -0
- package/dist/types/presets.d.ts +19 -0
- package/dist/types/presets.js +2 -0
- package/dist/utils/prompt.test.d.ts +1 -0
- package/dist/utils/prompt.test.js +115 -0
- package/package.json +9 -4
- package/src/config/presets.json +222 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ A lightweight, portable Node.js/TypeScript library providing a unified interface
|
|
|
10
10
|
- 🎯 **TypeScript First** - Full type safety and IntelliSense support
|
|
11
11
|
- ⚡ **Lightweight** - Minimal dependencies, focused functionality
|
|
12
12
|
- 🛡️ **Provider Normalization** - Consistent responses across different AI APIs
|
|
13
|
+
- 🎨 **Configurable Model Presets** - Built-in presets with full customization options
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -135,6 +136,80 @@ const providers = await llmService.getProviders();
|
|
|
135
136
|
|
|
136
137
|
// Get models for a specific provider
|
|
137
138
|
const models = await llmService.getModels('anthropic');
|
|
139
|
+
|
|
140
|
+
// Get configured model presets
|
|
141
|
+
const presets = llmService.getPresets();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Model Presets
|
|
145
|
+
|
|
146
|
+
genai-lite includes a built-in set of model presets for common use cases. You can use these defaults, extend them with your own, or replace them entirely.
|
|
147
|
+
|
|
148
|
+
#### Using Default Presets
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const llmService = new LLMService(fromEnvironment);
|
|
152
|
+
|
|
153
|
+
// Get all default presets
|
|
154
|
+
const presets = llmService.getPresets();
|
|
155
|
+
// Returns presets like:
|
|
156
|
+
// - anthropic-claude-3-5-sonnet-20241022-default
|
|
157
|
+
// - openai-gpt-4.1-default
|
|
158
|
+
// - google-gemini-2.5-pro
|
|
159
|
+
// ... and more
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Extending Default Presets
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { LLMService, fromEnvironment, ModelPreset } from 'genai-lite';
|
|
166
|
+
|
|
167
|
+
const customPresets: ModelPreset[] = [
|
|
168
|
+
{
|
|
169
|
+
id: 'my-creative-preset',
|
|
170
|
+
displayName: 'Creative Writing Assistant',
|
|
171
|
+
providerId: 'openai',
|
|
172
|
+
modelId: 'gpt-4.1',
|
|
173
|
+
settings: {
|
|
174
|
+
temperature: 0.9,
|
|
175
|
+
maxTokens: 2000,
|
|
176
|
+
topP: 0.95
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
const llmService = new LLMService(fromEnvironment, {
|
|
182
|
+
presets: customPresets,
|
|
183
|
+
presetMode: 'extend' // Default behavior - adds to existing presets
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Replacing Default Presets
|
|
188
|
+
|
|
189
|
+
For applications that need full control over available presets:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const applicationPresets: ModelPreset[] = [
|
|
193
|
+
{
|
|
194
|
+
id: 'app-gpt4-default',
|
|
195
|
+
displayName: 'GPT-4 Standard',
|
|
196
|
+
providerId: 'openai',
|
|
197
|
+
modelId: 'gpt-4.1',
|
|
198
|
+
settings: { temperature: 0.7 }
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'app-claude-creative',
|
|
202
|
+
displayName: 'Claude Creative',
|
|
203
|
+
providerId: 'anthropic',
|
|
204
|
+
modelId: 'claude-3-5-sonnet-20241022',
|
|
205
|
+
settings: { temperature: 0.8, maxTokens: 4000 }
|
|
206
|
+
}
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
const llmService = new LLMService(fromEnvironment, {
|
|
210
|
+
presets: applicationPresets,
|
|
211
|
+
presetMode: 'replace' // Use ONLY these presets, ignore defaults
|
|
212
|
+
});
|
|
138
213
|
```
|
|
139
214
|
|
|
140
215
|
### Error Handling
|
|
@@ -211,7 +286,10 @@ import type {
|
|
|
211
286
|
LLMResponse,
|
|
212
287
|
LLMFailureResponse,
|
|
213
288
|
LLMSettings,
|
|
214
|
-
ApiKeyProvider
|
|
289
|
+
ApiKeyProvider,
|
|
290
|
+
ModelPreset,
|
|
291
|
+
LLMServiceOptions,
|
|
292
|
+
PresetMode
|
|
215
293
|
} from 'genai-lite';
|
|
216
294
|
```
|
|
217
295
|
|
|
@@ -327,6 +405,26 @@ npm run build
|
|
|
327
405
|
npm test
|
|
328
406
|
```
|
|
329
407
|
|
|
408
|
+
### End-to-End Testing
|
|
409
|
+
|
|
410
|
+
The project includes an end-to-end test suite that makes real API calls to providers. These tests are separate from the main unit test suite and are not run in CI by default.
|
|
411
|
+
|
|
412
|
+
To run these tests locally, you must first provide API keys as environment variables with the `E2E_` prefix:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
export E2E_OPENAI_API_KEY="sk-..."
|
|
416
|
+
export E2E_ANTHROPIC_API_KEY="sk-ant-..."
|
|
417
|
+
export E2E_GEMINI_API_KEY="AIza..."
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Then, run the E2E test script:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
npm run test:e2e
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
The tests will automatically skip any provider for which an API key is not found.
|
|
427
|
+
|
|
330
428
|
## License
|
|
331
429
|
|
|
332
430
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "anthropic-claude-sonnet-4-20250514-default",
|
|
4
|
+
"displayName": "Anthropic - Claude Sonnet 4",
|
|
5
|
+
"description": "Default preset for Claude Sonnet 4.",
|
|
6
|
+
"providerId": "anthropic",
|
|
7
|
+
"modelId": "claude-sonnet-4-20250514",
|
|
8
|
+
"settings": {
|
|
9
|
+
"temperature": 0.3
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "anthropic-claude-opus-4-20250514-default",
|
|
14
|
+
"displayName": "Anthropic - Claude Opus 4",
|
|
15
|
+
"description": "Default preset for Claude Opus 4.",
|
|
16
|
+
"providerId": "anthropic",
|
|
17
|
+
"modelId": "claude-opus-4-20250514",
|
|
18
|
+
"settings": {
|
|
19
|
+
"temperature": 0.3
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "anthropic-claude-3-7-sonnet-20250219-default",
|
|
24
|
+
"displayName": "Anthropic - Claude 3.7 Sonnet",
|
|
25
|
+
"description": "Default preset for Claude 3.7 Sonnet.",
|
|
26
|
+
"providerId": "anthropic",
|
|
27
|
+
"modelId": "claude-3-7-sonnet-20250219",
|
|
28
|
+
"settings": {
|
|
29
|
+
"temperature": 0.3
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "anthropic-claude-3-5-sonnet-20241022-default",
|
|
34
|
+
"displayName": "Anthropic - Claude 3.5 Sonnet",
|
|
35
|
+
"description": "Default preset for Claude 3.5 Sonnet.",
|
|
36
|
+
"providerId": "anthropic",
|
|
37
|
+
"modelId": "claude-3-5-sonnet-20241022",
|
|
38
|
+
"settings": {
|
|
39
|
+
"temperature": 0.3
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "anthropic-claude-3-5-haiku-20241022-default",
|
|
44
|
+
"displayName": "Anthropic - Claude 3.5 Haiku",
|
|
45
|
+
"description": "Default preset for Claude 3.5 Haiku.",
|
|
46
|
+
"providerId": "anthropic",
|
|
47
|
+
"modelId": "claude-3-5-haiku-20241022",
|
|
48
|
+
"settings": {
|
|
49
|
+
"temperature": 0.3
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "google-gemini-2.5-pro",
|
|
54
|
+
"displayName": "Google - Gemini 2.5 Pro",
|
|
55
|
+
"description": "Default preset for Gemini 2.5 Pro.",
|
|
56
|
+
"providerId": "gemini",
|
|
57
|
+
"modelId": "gemini-2.5-pro",
|
|
58
|
+
"settings": {
|
|
59
|
+
"temperature": 0.3,
|
|
60
|
+
"geminiSafetySettings": [
|
|
61
|
+
{ "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
|
|
62
|
+
{
|
|
63
|
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
64
|
+
"threshold": "BLOCK_NONE"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
68
|
+
"threshold": "BLOCK_NONE"
|
|
69
|
+
},
|
|
70
|
+
{ "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE" }
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "google-gemini-2.5-flash",
|
|
76
|
+
"displayName": "Google - Gemini 2.5 Flash",
|
|
77
|
+
"description": "Default preset for Gemini 2.5 Flash.",
|
|
78
|
+
"providerId": "gemini",
|
|
79
|
+
"modelId": "gemini-2.5-flash",
|
|
80
|
+
"settings": {
|
|
81
|
+
"temperature": 0.3,
|
|
82
|
+
"geminiSafetySettings": [
|
|
83
|
+
{ "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
|
|
84
|
+
{
|
|
85
|
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
86
|
+
"threshold": "BLOCK_NONE"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
90
|
+
"threshold": "BLOCK_NONE"
|
|
91
|
+
},
|
|
92
|
+
{ "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE" }
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "google-gemini-2.5-flash-lite-preview",
|
|
98
|
+
"displayName": "Google - Gemini 2.5 Flash-Lite Preview",
|
|
99
|
+
"description": "Default preset for Gemini 2.5 Flash-Lite.",
|
|
100
|
+
"providerId": "gemini",
|
|
101
|
+
"modelId": "gemini-2.5-flash-lite-preview-06-17",
|
|
102
|
+
"settings": {
|
|
103
|
+
"temperature": 0.3,
|
|
104
|
+
"geminiSafetySettings": [
|
|
105
|
+
{ "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
|
|
106
|
+
{
|
|
107
|
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
108
|
+
"threshold": "BLOCK_NONE"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
112
|
+
"threshold": "BLOCK_NONE"
|
|
113
|
+
},
|
|
114
|
+
{ "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE" }
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"id": "google-gemini-2.0-flash-default",
|
|
120
|
+
"displayName": "Google - Gemini 2.0 Flash",
|
|
121
|
+
"description": "Default preset for Gemini 2.0 Flash.",
|
|
122
|
+
"providerId": "gemini",
|
|
123
|
+
"modelId": "gemini-2.0-flash",
|
|
124
|
+
"settings": {
|
|
125
|
+
"temperature": 0.3,
|
|
126
|
+
"geminiSafetySettings": [
|
|
127
|
+
{ "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
|
|
128
|
+
{
|
|
129
|
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
130
|
+
"threshold": "BLOCK_NONE"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
134
|
+
"threshold": "BLOCK_NONE"
|
|
135
|
+
},
|
|
136
|
+
{ "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE" }
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"id": "google-gemini-2.0-flash-lite-default",
|
|
142
|
+
"displayName": "Google - Gemini 2.0 Flash Lite",
|
|
143
|
+
"description": "Default preset for Gemini 2.0 Flash Lite.",
|
|
144
|
+
"providerId": "gemini",
|
|
145
|
+
"modelId": "gemini-2.0-flash-lite",
|
|
146
|
+
"settings": {
|
|
147
|
+
"temperature": 0.3,
|
|
148
|
+
"geminiSafetySettings": [
|
|
149
|
+
{ "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
|
|
150
|
+
{
|
|
151
|
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
152
|
+
"threshold": "BLOCK_NONE"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
156
|
+
"threshold": "BLOCK_NONE"
|
|
157
|
+
},
|
|
158
|
+
{ "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE" }
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "openai-o4-mini-default",
|
|
164
|
+
"displayName": "OpenAI - o4-mini",
|
|
165
|
+
"description": "Default preset for o4-mini.",
|
|
166
|
+
"providerId": "openai",
|
|
167
|
+
"modelId": "o4-mini",
|
|
168
|
+
"settings": {
|
|
169
|
+
"temperature": 1.0
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "openai-gpt-4.1-default",
|
|
174
|
+
"displayName": "OpenAI - GPT-4.1",
|
|
175
|
+
"description": "Default preset for GPT-4.1.",
|
|
176
|
+
"providerId": "openai",
|
|
177
|
+
"modelId": "gpt-4.1",
|
|
178
|
+
"settings": {
|
|
179
|
+
"temperature": 0.3
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "openai-gpt-4.1-mini-default",
|
|
184
|
+
"displayName": "OpenAI - GPT-4.1 Mini",
|
|
185
|
+
"description": "Default preset for GPT-4.1 Mini.",
|
|
186
|
+
"providerId": "openai",
|
|
187
|
+
"modelId": "gpt-4.1-mini",
|
|
188
|
+
"settings": {
|
|
189
|
+
"temperature": 0.3
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"id": "openai-gpt-4.1-nano-default",
|
|
194
|
+
"displayName": "OpenAI - GPT-4.1 Nano",
|
|
195
|
+
"description": "Default preset for GPT-4.1 Nano.",
|
|
196
|
+
"providerId": "openai",
|
|
197
|
+
"modelId": "gpt-4.1-nano",
|
|
198
|
+
"settings": {
|
|
199
|
+
"temperature": 0.3
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"id": "mistral-codestral-2501-default",
|
|
204
|
+
"displayName": "Mistral AI - Codestral",
|
|
205
|
+
"description": "Default preset for Codestral.",
|
|
206
|
+
"providerId": "mistral",
|
|
207
|
+
"modelId": "codestral-2501",
|
|
208
|
+
"settings": {
|
|
209
|
+
"temperature": 0.3
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"id": "mistral-devstral-small-2505-default",
|
|
214
|
+
"displayName": "Mistral AI - Devstral Small",
|
|
215
|
+
"description": "Default preset for Devstral Small.",
|
|
216
|
+
"providerId": "mistral",
|
|
217
|
+
"modelId": "devstral-small-2505",
|
|
218
|
+
"settings": {
|
|
219
|
+
"temperature": 0.3
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
]
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type { ApiKeyProvider } from "./types";
|
|
2
2
|
export { LLMService } from "./llm/LLMService";
|
|
3
|
+
export type { LLMServiceOptions, PresetMode } from "./llm/LLMService";
|
|
4
|
+
export type { ModelPreset } from "./types/presets";
|
|
3
5
|
export * from "./llm/types";
|
|
4
6
|
export * from "./llm/clients/types";
|
|
5
7
|
export { fromEnvironment } from "./providers/fromEnvironment";
|
package/dist/llm/LLMService.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import type { ApiKeyProvider } from '../types';
|
|
2
2
|
import type { LLMChatRequest, LLMResponse, LLMFailureResponse, ProviderInfo, ModelInfo, ApiProviderId } from "./types";
|
|
3
3
|
import type { ILLMClientAdapter } from "./clients/types";
|
|
4
|
+
import type { ModelPreset } from "../types/presets";
|
|
5
|
+
/**
|
|
6
|
+
* Defines how custom presets interact with the default presets.
|
|
7
|
+
* 'replace': Use only the custom presets provided. The default set is ignored.
|
|
8
|
+
* 'extend': Use the default presets, and add/override them with the custom presets. This is the default behavior.
|
|
9
|
+
*/
|
|
10
|
+
export type PresetMode = 'replace' | 'extend';
|
|
11
|
+
/**
|
|
12
|
+
* Options for configuring the LLMService
|
|
13
|
+
*/
|
|
14
|
+
export interface LLMServiceOptions {
|
|
15
|
+
/** An array of custom presets to integrate. */
|
|
16
|
+
presets?: ModelPreset[];
|
|
17
|
+
/** The strategy for integrating custom presets. Defaults to 'extend'. */
|
|
18
|
+
presetMode?: PresetMode;
|
|
19
|
+
}
|
|
4
20
|
/**
|
|
5
21
|
* Main process service for LLM operations
|
|
6
22
|
*
|
|
@@ -10,12 +26,14 @@ import type { ILLMClientAdapter } from "./clients/types";
|
|
|
10
26
|
* - Validates requests and applies default settings
|
|
11
27
|
* - Routes requests to appropriate provider adapters
|
|
12
28
|
* - Handles errors and provides standardized responses
|
|
29
|
+
* - Provides configurable model presets for common use cases
|
|
13
30
|
*/
|
|
14
31
|
export declare class LLMService {
|
|
15
32
|
private getApiKey;
|
|
16
33
|
private clientAdapters;
|
|
17
34
|
private mockClientAdapter;
|
|
18
|
-
|
|
35
|
+
private presets;
|
|
36
|
+
constructor(getApiKey: ApiKeyProvider, options?: LLMServiceOptions);
|
|
19
37
|
/**
|
|
20
38
|
* Gets list of supported LLM providers
|
|
21
39
|
*
|
|
@@ -83,4 +101,10 @@ export declare class LLMService {
|
|
|
83
101
|
availableProviders: string[];
|
|
84
102
|
unavailableProviders: string[];
|
|
85
103
|
};
|
|
104
|
+
/**
|
|
105
|
+
* Gets all configured model presets
|
|
106
|
+
*
|
|
107
|
+
* @returns Array of model presets
|
|
108
|
+
*/
|
|
109
|
+
getPresets(): ModelPreset[];
|
|
86
110
|
}
|
package/dist/llm/LLMService.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// AI Summary: Main process service for LLM operations, integrating with ApiKeyProvider for secure key access.
|
|
3
3
|
// Orchestrates LLM requests through provider-specific client adapters with proper error handling.
|
|
4
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
+
};
|
|
4
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
8
|
exports.LLMService = void 0;
|
|
6
9
|
const MockClientAdapter_1 = require("./clients/MockClientAdapter");
|
|
7
10
|
const config_1 = require("./config");
|
|
11
|
+
const presets_json_1 = __importDefault(require("../config/presets.json"));
|
|
8
12
|
/**
|
|
9
13
|
* Main process service for LLM operations
|
|
10
14
|
*
|
|
@@ -14,12 +18,33 @@ const config_1 = require("./config");
|
|
|
14
18
|
* - Validates requests and applies default settings
|
|
15
19
|
* - Routes requests to appropriate provider adapters
|
|
16
20
|
* - Handles errors and provides standardized responses
|
|
21
|
+
* - Provides configurable model presets for common use cases
|
|
17
22
|
*/
|
|
18
23
|
class LLMService {
|
|
19
|
-
constructor(getApiKey) {
|
|
24
|
+
constructor(getApiKey, options = {}) {
|
|
20
25
|
this.getApiKey = getApiKey;
|
|
21
26
|
this.clientAdapters = new Map();
|
|
22
27
|
this.mockClientAdapter = new MockClientAdapter_1.MockClientAdapter();
|
|
28
|
+
// Initialize presets based on mode
|
|
29
|
+
const finalPresets = new Map();
|
|
30
|
+
const customPresets = options.presets || [];
|
|
31
|
+
const mode = options.presetMode || 'extend';
|
|
32
|
+
if (mode === 'replace') {
|
|
33
|
+
// Replace Mode: Only use custom presets.
|
|
34
|
+
for (const preset of customPresets) {
|
|
35
|
+
finalPresets.set(preset.id, preset);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// Extend Mode: Load defaults first, then add/override.
|
|
40
|
+
for (const preset of presets_json_1.default) {
|
|
41
|
+
finalPresets.set(preset.id, preset);
|
|
42
|
+
}
|
|
43
|
+
for (const preset of customPresets) {
|
|
44
|
+
finalPresets.set(preset.id, preset);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
this.presets = Array.from(finalPresets.values());
|
|
23
48
|
// Dynamically register client adapters based on configuration
|
|
24
49
|
let registeredCount = 0;
|
|
25
50
|
const successfullyRegisteredProviders = [];
|
|
@@ -406,5 +431,13 @@ class LLMService {
|
|
|
406
431
|
unavailableProviders,
|
|
407
432
|
};
|
|
408
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Gets all configured model presets
|
|
436
|
+
*
|
|
437
|
+
* @returns Array of model presets
|
|
438
|
+
*/
|
|
439
|
+
getPresets() {
|
|
440
|
+
return [...this.presets]; // Return a copy to prevent external modification
|
|
441
|
+
}
|
|
409
442
|
}
|
|
410
443
|
exports.LLMService = LLMService;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const LLMService_1 = require("./LLMService");
|
|
7
|
+
const presets_json_1 = __importDefault(require("../config/presets.json"));
|
|
8
|
+
describe('LLMService Presets', () => {
|
|
9
|
+
let mockApiKeyProvider;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
mockApiKeyProvider = jest.fn().mockResolvedValue('test-api-key');
|
|
13
|
+
});
|
|
14
|
+
describe('Default behavior', () => {
|
|
15
|
+
it('should load default presets when no options provided', async () => {
|
|
16
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider);
|
|
17
|
+
const presets = service.getPresets();
|
|
18
|
+
expect(presets).toHaveLength(presets_json_1.default.length);
|
|
19
|
+
expect(presets).toEqual(expect.arrayContaining(presets_json_1.default.map(preset => expect.objectContaining({
|
|
20
|
+
id: preset.id,
|
|
21
|
+
displayName: preset.displayName,
|
|
22
|
+
providerId: preset.providerId,
|
|
23
|
+
modelId: preset.modelId
|
|
24
|
+
}))));
|
|
25
|
+
});
|
|
26
|
+
it('should return a copy of presets to prevent external modification', async () => {
|
|
27
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider);
|
|
28
|
+
const presets1 = service.getPresets();
|
|
29
|
+
const presets2 = service.getPresets();
|
|
30
|
+
expect(presets1).not.toBe(presets2); // Different array instances
|
|
31
|
+
expect(presets1).toEqual(presets2); // Same content
|
|
32
|
+
// Modifying returned array should not affect service
|
|
33
|
+
presets1.push({
|
|
34
|
+
id: 'test-preset',
|
|
35
|
+
displayName: 'Test',
|
|
36
|
+
providerId: 'openai',
|
|
37
|
+
modelId: 'gpt-4',
|
|
38
|
+
settings: {}
|
|
39
|
+
});
|
|
40
|
+
const presets3 = service.getPresets();
|
|
41
|
+
expect(presets3).toHaveLength(presets_json_1.default.length);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('Extend mode', () => {
|
|
45
|
+
it('should add new presets to defaults in extend mode', async () => {
|
|
46
|
+
const customPresets = [
|
|
47
|
+
{
|
|
48
|
+
id: 'custom-preset-1',
|
|
49
|
+
displayName: 'Custom Preset 1',
|
|
50
|
+
providerId: 'openai',
|
|
51
|
+
modelId: 'gpt-4',
|
|
52
|
+
settings: { temperature: 0.5 }
|
|
53
|
+
}
|
|
54
|
+
];
|
|
55
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
56
|
+
presets: customPresets,
|
|
57
|
+
presetMode: 'extend'
|
|
58
|
+
});
|
|
59
|
+
const presets = service.getPresets();
|
|
60
|
+
expect(presets).toHaveLength(presets_json_1.default.length + 1);
|
|
61
|
+
expect(presets).toContainEqual(expect.objectContaining({
|
|
62
|
+
id: 'custom-preset-1',
|
|
63
|
+
displayName: 'Custom Preset 1'
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
it('should override default presets with same ID in extend mode', async () => {
|
|
67
|
+
const existingPresetId = presets_json_1.default[0].id;
|
|
68
|
+
const customPresets = [
|
|
69
|
+
{
|
|
70
|
+
id: existingPresetId,
|
|
71
|
+
displayName: 'Overridden Preset',
|
|
72
|
+
providerId: 'anthropic',
|
|
73
|
+
modelId: 'claude-3-5-sonnet-20241022',
|
|
74
|
+
settings: { temperature: 0.8 }
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
78
|
+
presets: customPresets,
|
|
79
|
+
presetMode: 'extend'
|
|
80
|
+
});
|
|
81
|
+
const presets = service.getPresets();
|
|
82
|
+
const overriddenPreset = presets.find(p => p.id === existingPresetId);
|
|
83
|
+
expect(presets).toHaveLength(presets_json_1.default.length);
|
|
84
|
+
expect(overriddenPreset).toBeDefined();
|
|
85
|
+
expect(overriddenPreset?.displayName).toBe('Overridden Preset');
|
|
86
|
+
expect(overriddenPreset?.providerId).toBe('anthropic');
|
|
87
|
+
});
|
|
88
|
+
it('should use extend mode by default when mode not specified', async () => {
|
|
89
|
+
const customPresets = [
|
|
90
|
+
{
|
|
91
|
+
id: 'custom-preset-default',
|
|
92
|
+
displayName: 'Custom Default',
|
|
93
|
+
providerId: 'gemini',
|
|
94
|
+
modelId: 'gemini-2.0-flash',
|
|
95
|
+
settings: { temperature: 0.3 }
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
99
|
+
presets: customPresets
|
|
100
|
+
// presetMode not specified, should default to 'extend'
|
|
101
|
+
});
|
|
102
|
+
const presets = service.getPresets();
|
|
103
|
+
expect(presets).toHaveLength(presets_json_1.default.length + 1);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('Replace mode', () => {
|
|
107
|
+
it('should use only custom presets in replace mode', async () => {
|
|
108
|
+
const customPresets = [
|
|
109
|
+
{
|
|
110
|
+
id: 'replace-preset-1',
|
|
111
|
+
displayName: 'Replace Preset 1',
|
|
112
|
+
providerId: 'openai',
|
|
113
|
+
modelId: 'gpt-4',
|
|
114
|
+
settings: { temperature: 0.5 }
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'replace-preset-2',
|
|
118
|
+
displayName: 'Replace Preset 2',
|
|
119
|
+
providerId: 'anthropic',
|
|
120
|
+
modelId: 'claude-3-5-sonnet-20241022',
|
|
121
|
+
settings: { temperature: 0.3 }
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
125
|
+
presets: customPresets,
|
|
126
|
+
presetMode: 'replace'
|
|
127
|
+
});
|
|
128
|
+
const presets = service.getPresets();
|
|
129
|
+
expect(presets).toHaveLength(2);
|
|
130
|
+
expect(presets).toEqual(expect.arrayContaining([
|
|
131
|
+
expect.objectContaining({ id: 'replace-preset-1' }),
|
|
132
|
+
expect.objectContaining({ id: 'replace-preset-2' })
|
|
133
|
+
]));
|
|
134
|
+
// Should not contain any default presets
|
|
135
|
+
const defaultPresetIds = presets_json_1.default.map(p => p.id);
|
|
136
|
+
const actualPresetIds = presets.map(p => p.id);
|
|
137
|
+
expect(actualPresetIds).not.toContain(expect.arrayContaining(defaultPresetIds));
|
|
138
|
+
});
|
|
139
|
+
it('should return empty array when replace mode with no custom presets', async () => {
|
|
140
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
141
|
+
presets: [],
|
|
142
|
+
presetMode: 'replace'
|
|
143
|
+
});
|
|
144
|
+
const presets = service.getPresets();
|
|
145
|
+
expect(presets).toHaveLength(0);
|
|
146
|
+
});
|
|
147
|
+
it('should handle undefined presets array in replace mode', async () => {
|
|
148
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
149
|
+
presetMode: 'replace'
|
|
150
|
+
// presets not provided
|
|
151
|
+
});
|
|
152
|
+
const presets = service.getPresets();
|
|
153
|
+
expect(presets).toHaveLength(0);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('Edge cases', () => {
|
|
157
|
+
it('should handle duplicate IDs within custom presets', async () => {
|
|
158
|
+
const customPresets = [
|
|
159
|
+
{
|
|
160
|
+
id: 'duplicate-id',
|
|
161
|
+
displayName: 'First Preset',
|
|
162
|
+
providerId: 'openai',
|
|
163
|
+
modelId: 'gpt-4',
|
|
164
|
+
settings: { temperature: 0.5 }
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'duplicate-id',
|
|
168
|
+
displayName: 'Second Preset',
|
|
169
|
+
providerId: 'anthropic',
|
|
170
|
+
modelId: 'claude-3-5-sonnet-20241022',
|
|
171
|
+
settings: { temperature: 0.3 }
|
|
172
|
+
}
|
|
173
|
+
];
|
|
174
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
175
|
+
presets: customPresets,
|
|
176
|
+
presetMode: 'replace'
|
|
177
|
+
});
|
|
178
|
+
const presets = service.getPresets();
|
|
179
|
+
const duplicatePresets = presets.filter(p => p.id === 'duplicate-id');
|
|
180
|
+
// Last one should win
|
|
181
|
+
expect(duplicatePresets).toHaveLength(1);
|
|
182
|
+
expect(duplicatePresets[0].displayName).toBe('Second Preset');
|
|
183
|
+
});
|
|
184
|
+
it('should handle presets with complex settings including gemini safety settings', async () => {
|
|
185
|
+
const customPresets = [
|
|
186
|
+
{
|
|
187
|
+
id: 'gemini-complex',
|
|
188
|
+
displayName: 'Gemini Complex',
|
|
189
|
+
providerId: 'gemini',
|
|
190
|
+
modelId: 'gemini-2.0-flash',
|
|
191
|
+
settings: {
|
|
192
|
+
temperature: 0.5,
|
|
193
|
+
maxTokens: 2000,
|
|
194
|
+
geminiSafetySettings: [
|
|
195
|
+
{ category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
|
|
196
|
+
{ category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' }
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
const service = new LLMService_1.LLMService(mockApiKeyProvider, {
|
|
202
|
+
presets: customPresets,
|
|
203
|
+
presetMode: 'replace'
|
|
204
|
+
});
|
|
205
|
+
const presets = service.getPresets();
|
|
206
|
+
expect(presets).toHaveLength(1);
|
|
207
|
+
expect(presets[0].settings.geminiSafetySettings).toHaveLength(2);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|