genai-lite 0.4.0 → 0.4.2

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 (59) hide show
  1. package/README.md +47 -37
  2. package/dist/llm/LLMService.d.ts +29 -2
  3. package/dist/llm/LLMService.js +80 -36
  4. package/dist/llm/config.js +4 -4
  5. package/dist/llm/services/SettingsManager.js +17 -11
  6. package/dist/llm/types.d.ts +81 -22
  7. package/dist/prompting/parser.d.ts +2 -2
  8. package/dist/prompting/parser.js +2 -2
  9. package/package.json +1 -1
  10. package/dist/llm/LLMService.createMessages.test.d.ts +0 -4
  11. package/dist/llm/LLMService.createMessages.test.js +0 -364
  12. package/dist/llm/LLMService.original.d.ts +0 -147
  13. package/dist/llm/LLMService.original.js +0 -656
  14. package/dist/llm/LLMService.prepareMessage.test.d.ts +0 -1
  15. package/dist/llm/LLMService.prepareMessage.test.js +0 -303
  16. package/dist/llm/LLMService.presets.test.d.ts +0 -1
  17. package/dist/llm/LLMService.presets.test.js +0 -210
  18. package/dist/llm/LLMService.sendMessage.preset.test.d.ts +0 -1
  19. package/dist/llm/LLMService.sendMessage.preset.test.js +0 -153
  20. package/dist/llm/LLMService.test.d.ts +0 -1
  21. package/dist/llm/LLMService.test.js +0 -639
  22. package/dist/llm/clients/AnthropicClientAdapter.test.d.ts +0 -1
  23. package/dist/llm/clients/AnthropicClientAdapter.test.js +0 -273
  24. package/dist/llm/clients/GeminiClientAdapter.test.d.ts +0 -1
  25. package/dist/llm/clients/GeminiClientAdapter.test.js +0 -405
  26. package/dist/llm/clients/LlamaCppClientAdapter.test.d.ts +0 -1
  27. package/dist/llm/clients/LlamaCppClientAdapter.test.js +0 -447
  28. package/dist/llm/clients/LlamaCppServerClient.test.d.ts +0 -1
  29. package/dist/llm/clients/LlamaCppServerClient.test.js +0 -294
  30. package/dist/llm/clients/MockClientAdapter.test.d.ts +0 -1
  31. package/dist/llm/clients/MockClientAdapter.test.js +0 -250
  32. package/dist/llm/clients/OpenAIClientAdapter.test.d.ts +0 -1
  33. package/dist/llm/clients/OpenAIClientAdapter.test.js +0 -258
  34. package/dist/llm/clients/adapterErrorUtils.test.d.ts +0 -1
  35. package/dist/llm/clients/adapterErrorUtils.test.js +0 -123
  36. package/dist/llm/config.test.d.ts +0 -1
  37. package/dist/llm/config.test.js +0 -176
  38. package/dist/llm/services/AdapterRegistry.test.d.ts +0 -1
  39. package/dist/llm/services/AdapterRegistry.test.js +0 -239
  40. package/dist/llm/services/ModelResolver.test.d.ts +0 -1
  41. package/dist/llm/services/ModelResolver.test.js +0 -179
  42. package/dist/llm/services/PresetManager.test.d.ts +0 -1
  43. package/dist/llm/services/PresetManager.test.js +0 -210
  44. package/dist/llm/services/RequestValidator.test.d.ts +0 -1
  45. package/dist/llm/services/RequestValidator.test.js +0 -159
  46. package/dist/llm/services/SettingsManager.test.d.ts +0 -1
  47. package/dist/llm/services/SettingsManager.test.js +0 -266
  48. package/dist/prompting/builder.d.ts +0 -38
  49. package/dist/prompting/builder.js +0 -63
  50. package/dist/prompting/builder.test.d.ts +0 -4
  51. package/dist/prompting/builder.test.js +0 -109
  52. package/dist/prompting/content.test.d.ts +0 -4
  53. package/dist/prompting/content.test.js +0 -212
  54. package/dist/prompting/parser.test.d.ts +0 -4
  55. package/dist/prompting/parser.test.js +0 -464
  56. package/dist/prompting/template.test.d.ts +0 -1
  57. package/dist/prompting/template.test.js +0 -250
  58. package/dist/providers/fromEnvironment.test.d.ts +0 -1
  59. package/dist/providers/fromEnvironment.test.js +0 -59
@@ -1,239 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- // Mock the client adapters first
4
- jest.mock('../clients/MockClientAdapter');
5
- jest.mock('../clients/OpenAIClientAdapter', () => ({
6
- OpenAIClientAdapter: jest.fn().mockImplementation(() => ({
7
- sendMessage: jest.fn(),
8
- getAdapterInfo: () => ({ providerId: 'openai', name: 'OpenAI Adapter' })
9
- }))
10
- }));
11
- jest.mock('../clients/AnthropicClientAdapter', () => ({
12
- AnthropicClientAdapter: jest.fn().mockImplementation(() => ({
13
- sendMessage: jest.fn(),
14
- getAdapterInfo: () => ({ providerId: 'anthropic', name: 'Anthropic Adapter' })
15
- }))
16
- }));
17
- jest.mock('../clients/GeminiClientAdapter', () => ({
18
- GeminiClientAdapter: jest.fn().mockImplementation(() => ({
19
- sendMessage: jest.fn(),
20
- getAdapterInfo: () => ({ providerId: 'gemini', name: 'Gemini Adapter' })
21
- }))
22
- }));
23
- // Mock the config imports - must be before imports that use it
24
- jest.mock('../config', () => {
25
- const { OpenAIClientAdapter } = require('../clients/OpenAIClientAdapter');
26
- const { AnthropicClientAdapter } = require('../clients/AnthropicClientAdapter');
27
- const { GeminiClientAdapter } = require('../clients/GeminiClientAdapter');
28
- return {
29
- SUPPORTED_PROVIDERS: [
30
- { id: 'openai', name: 'OpenAI' },
31
- { id: 'anthropic', name: 'Anthropic' },
32
- { id: 'gemini', name: 'Google Gemini' },
33
- { id: 'mistral', name: 'Mistral' }
34
- ],
35
- ADAPTER_CONSTRUCTORS: {
36
- openai: OpenAIClientAdapter,
37
- anthropic: AnthropicClientAdapter,
38
- gemini: GeminiClientAdapter,
39
- // mistral intentionally missing to test fallback
40
- },
41
- ADAPTER_CONFIGS: {
42
- openai: {},
43
- anthropic: {},
44
- gemini: {},
45
- mistral: {}
46
- }
47
- };
48
- });
49
- // Now import the modules
50
- const AdapterRegistry_1 = require("./AdapterRegistry");
51
- const MockClientAdapter_1 = require("../clients/MockClientAdapter");
52
- const OpenAIClientAdapter_1 = require("../clients/OpenAIClientAdapter");
53
- describe('AdapterRegistry', () => {
54
- let registry;
55
- let consoleLogSpy;
56
- let consoleWarnSpy;
57
- let consoleErrorSpy;
58
- beforeEach(() => {
59
- jest.clearAllMocks();
60
- consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
61
- consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
62
- consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
63
- });
64
- afterEach(() => {
65
- consoleLogSpy.mockRestore();
66
- consoleWarnSpy.mockRestore();
67
- consoleErrorSpy.mockRestore();
68
- });
69
- describe('initialization', () => {
70
- it('should initialize with adapters for providers with constructors', () => {
71
- registry = new AdapterRegistry_1.AdapterRegistry();
72
- // Should have logged successful initialization
73
- expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Initialized with 3 dynamically registered adapter(s)'));
74
- expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('openai, anthropic, gemini'));
75
- // Should warn about missing constructor for mistral
76
- expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("No adapter constructor found for supported provider 'mistral'"));
77
- });
78
- it('should handle adapter initialization errors gracefully', () => {
79
- // Make OpenAIClientAdapter throw an error on construction
80
- OpenAIClientAdapter_1.OpenAIClientAdapter.mockImplementationOnce(() => {
81
- throw new Error('Initialization failed');
82
- });
83
- registry = new AdapterRegistry_1.AdapterRegistry();
84
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Failed to instantiate adapter for provider 'openai'"), expect.any(Error));
85
- });
86
- it('should log when no adapters are registered', () => {
87
- // Clear all module cache to ensure clean isolation
88
- jest.resetModules();
89
- // Mock the config module with no constructors before requiring AdapterRegistry
90
- jest.doMock('../config', () => ({
91
- SUPPORTED_PROVIDERS: [
92
- { id: 'openai', name: 'OpenAI' },
93
- { id: 'anthropic', name: 'Anthropic' },
94
- { id: 'gemini', name: 'Google Gemini' },
95
- { id: 'mistral', name: 'Mistral' }
96
- ],
97
- ADAPTER_CONSTRUCTORS: {}, // No constructors
98
- ADAPTER_CONFIGS: {
99
- openai: {},
100
- anthropic: {},
101
- gemini: {},
102
- mistral: {}
103
- }
104
- }));
105
- // Mock the client adapters to prevent import errors
106
- jest.doMock('../clients/MockClientAdapter', () => ({
107
- MockClientAdapter: jest.fn()
108
- }));
109
- jest.doMock('../clients/OpenAIClientAdapter', () => ({
110
- OpenAIClientAdapter: jest.fn()
111
- }));
112
- jest.doMock('../clients/AnthropicClientAdapter', () => ({
113
- AnthropicClientAdapter: jest.fn()
114
- }));
115
- jest.doMock('../clients/GeminiClientAdapter', () => ({
116
- GeminiClientAdapter: jest.fn()
117
- }));
118
- // Clear console spy calls before the test
119
- consoleLogSpy.mockClear();
120
- // Now require the AdapterRegistry with mocked dependencies
121
- const { AdapterRegistry: IsolatedAdapterRegistry } = require('./AdapterRegistry');
122
- const isolatedRegistry = new IsolatedAdapterRegistry();
123
- expect(consoleLogSpy).toHaveBeenCalledWith('LLMService: No real adapters were dynamically registered. All providers will use the mock adapter.');
124
- // Restore modules after test
125
- jest.resetModules();
126
- });
127
- });
128
- describe('getAdapter', () => {
129
- beforeEach(() => {
130
- registry = new AdapterRegistry_1.AdapterRegistry();
131
- });
132
- it('should return registered adapter for supported provider', () => {
133
- const adapter = registry.getAdapter('openai');
134
- expect(adapter).toBeDefined();
135
- expect(consoleLogSpy).toHaveBeenCalledWith('Using registered adapter for provider: openai');
136
- });
137
- it('should return mock adapter for provider without adapter', () => {
138
- const adapter = registry.getAdapter('mistral');
139
- expect(adapter).toBeInstanceOf(MockClientAdapter_1.MockClientAdapter);
140
- expect(consoleLogSpy).toHaveBeenCalledWith('No real adapter found for mistral, using mock adapter');
141
- });
142
- it('should return mock adapter for unknown provider', () => {
143
- const adapter = registry.getAdapter('unknown-provider');
144
- expect(adapter).toBeInstanceOf(MockClientAdapter_1.MockClientAdapter);
145
- expect(consoleLogSpy).toHaveBeenCalledWith('No real adapter found for unknown-provider, using mock adapter');
146
- });
147
- });
148
- describe('registerAdapter', () => {
149
- beforeEach(() => {
150
- registry = new AdapterRegistry_1.AdapterRegistry();
151
- });
152
- it('should register new adapter', () => {
153
- const mockAdapter = {
154
- sendMessage: jest.fn(),
155
- getAdapterInfo: () => ({ providerId: 'custom-provider', name: 'Custom Adapter' })
156
- };
157
- registry.registerAdapter('custom-provider', mockAdapter);
158
- expect(consoleLogSpy).toHaveBeenCalledWith('Registered client adapter for provider: custom-provider');
159
- // Should be able to retrieve it
160
- const retrievedAdapter = registry.getAdapter('custom-provider');
161
- expect(retrievedAdapter).toBe(mockAdapter);
162
- });
163
- it('should override existing adapter', () => {
164
- const newAdapter = {
165
- sendMessage: jest.fn(),
166
- getAdapterInfo: () => ({ providerId: 'openai', name: 'New OpenAI Adapter' })
167
- };
168
- registry.registerAdapter('openai', newAdapter);
169
- const retrievedAdapter = registry.getAdapter('openai');
170
- expect(retrievedAdapter).toBe(newAdapter);
171
- });
172
- });
173
- describe('getRegisteredAdapters', () => {
174
- beforeEach(() => {
175
- registry = new AdapterRegistry_1.AdapterRegistry();
176
- });
177
- it('should return info for all registered adapters', () => {
178
- const adapterInfo = registry.getRegisteredAdapters();
179
- expect(adapterInfo.size).toBe(3); // openai, anthropic, gemini
180
- expect(adapterInfo.get('openai')).toEqual({
181
- providerId: 'openai',
182
- hasAdapter: true,
183
- adapterInfo: { providerId: 'openai', name: 'OpenAI Adapter' }
184
- });
185
- expect(adapterInfo.get('anthropic')).toEqual({
186
- providerId: 'anthropic',
187
- hasAdapter: true,
188
- adapterInfo: { providerId: 'anthropic', name: 'Anthropic Adapter' }
189
- });
190
- expect(adapterInfo.get('gemini')).toEqual({
191
- providerId: 'gemini',
192
- hasAdapter: true,
193
- adapterInfo: { providerId: 'gemini', name: 'Gemini Adapter' }
194
- });
195
- // mistral should not be in the map
196
- expect(adapterInfo.has('mistral')).toBe(false);
197
- });
198
- it('should handle adapters without getAdapterInfo method', () => {
199
- const adapterWithoutInfo = {
200
- sendMessage: jest.fn(),
201
- // No getAdapterInfo method
202
- };
203
- registry.registerAdapter('custom', adapterWithoutInfo);
204
- const adapterInfo = registry.getRegisteredAdapters();
205
- expect(adapterInfo.get('custom')).toEqual({
206
- providerId: 'custom',
207
- hasAdapter: true,
208
- adapterInfo: { name: 'Unknown Adapter' }
209
- });
210
- });
211
- });
212
- describe('getProviderSummary', () => {
213
- beforeEach(() => {
214
- registry = new AdapterRegistry_1.AdapterRegistry();
215
- });
216
- it('should return correct summary of provider availability', () => {
217
- const summary = registry.getProviderSummary();
218
- expect(summary).toEqual({
219
- totalProviders: 4,
220
- providersWithAdapters: 3,
221
- availableProviders: ['openai', 'anthropic', 'gemini'],
222
- unavailableProviders: ['mistral']
223
- });
224
- });
225
- it('should update summary after registering new adapter', () => {
226
- const mockAdapter = {
227
- sendMessage: jest.fn(),
228
- };
229
- registry.registerAdapter('mistral', mockAdapter);
230
- const summary = registry.getProviderSummary();
231
- expect(summary).toEqual({
232
- totalProviders: 4,
233
- providersWithAdapters: 4,
234
- availableProviders: ['openai', 'anthropic', 'gemini', 'mistral'],
235
- unavailableProviders: []
236
- });
237
- });
238
- });
239
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,179 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const ModelResolver_1 = require("./ModelResolver");
4
- describe('ModelResolver', () => {
5
- let resolver;
6
- let mockPresetManager;
7
- beforeEach(() => {
8
- mockPresetManager = {
9
- getPresets: jest.fn(),
10
- resolvePreset: jest.fn(),
11
- };
12
- resolver = new ModelResolver_1.ModelResolver(mockPresetManager);
13
- });
14
- describe('preset resolution', () => {
15
- it('should resolve model from valid preset', () => {
16
- const mockPreset = {
17
- id: 'test-preset',
18
- displayName: 'Test Preset',
19
- providerId: 'openai',
20
- modelId: 'gpt-4.1',
21
- settings: { temperature: 0.7 }
22
- };
23
- mockPresetManager.resolvePreset.mockReturnValue(mockPreset);
24
- const result = resolver.resolve({ presetId: 'test-preset' });
25
- expect(mockPresetManager.resolvePreset).toHaveBeenCalledWith('test-preset');
26
- expect(result.error).toBeUndefined();
27
- expect(result.providerId).toBe('openai');
28
- expect(result.modelId).toBe('gpt-4.1');
29
- expect(result.modelInfo).toBeDefined();
30
- expect(result.settings).toEqual({ temperature: 0.7 });
31
- });
32
- it('should return error for non-existent preset', () => {
33
- mockPresetManager.resolvePreset.mockReturnValue(null);
34
- const result = resolver.resolve({ presetId: 'non-existent' });
35
- expect(result.error).toBeDefined();
36
- expect(result.error?.error.code).toBe('PRESET_NOT_FOUND');
37
- expect(result.error?.error.message).toContain('Preset not found: non-existent');
38
- });
39
- it('should return error for preset with invalid model', () => {
40
- const mockPreset = {
41
- id: 'invalid-model-preset',
42
- displayName: 'Invalid Model Preset',
43
- providerId: 'openai',
44
- modelId: 'invalid-model',
45
- settings: {}
46
- };
47
- mockPresetManager.resolvePreset.mockReturnValue(mockPreset);
48
- const result = resolver.resolve({ presetId: 'invalid-model-preset' });
49
- expect(result.error).toBeDefined();
50
- expect(result.error?.error.code).toBe('MODEL_NOT_FOUND');
51
- expect(result.error?.error.message).toContain('Model not found for preset');
52
- });
53
- it('should merge preset settings with user settings', () => {
54
- const mockPreset = {
55
- id: 'test-preset',
56
- displayName: 'Test Preset',
57
- providerId: 'openai',
58
- modelId: 'gpt-4.1',
59
- settings: {
60
- temperature: 0.7,
61
- maxTokens: 1000
62
- }
63
- };
64
- mockPresetManager.resolvePreset.mockReturnValue(mockPreset);
65
- const result = resolver.resolve({
66
- presetId: 'test-preset',
67
- settings: {
68
- temperature: 0.9, // Override
69
- topP: 0.95 // New setting
70
- }
71
- });
72
- expect(result.settings).toEqual({
73
- temperature: 0.9, // User override
74
- maxTokens: 1000, // From preset
75
- topP: 0.95 // User addition
76
- });
77
- });
78
- });
79
- describe('direct model resolution', () => {
80
- it('should resolve model from valid provider and model IDs', () => {
81
- const result = resolver.resolve({
82
- providerId: 'openai',
83
- modelId: 'gpt-4.1'
84
- });
85
- expect(result.error).toBeUndefined();
86
- expect(result.providerId).toBe('openai');
87
- expect(result.modelId).toBe('gpt-4.1');
88
- expect(result.modelInfo).toBeDefined();
89
- expect(result.modelInfo?.id).toBe('gpt-4.1');
90
- });
91
- it('should return error when neither preset nor provider/model provided', () => {
92
- const result = resolver.resolve({});
93
- expect(result.error).toBeDefined();
94
- expect(result.error?.error.code).toBe('INVALID_MODEL_SELECTION');
95
- expect(result.error?.error.message).toContain('Either presetId or both providerId and modelId must be provided');
96
- });
97
- it('should return error when only providerId provided', () => {
98
- const result = resolver.resolve({ providerId: 'openai' });
99
- expect(result.error).toBeDefined();
100
- expect(result.error?.error.code).toBe('INVALID_MODEL_SELECTION');
101
- });
102
- it('should return error when only modelId provided', () => {
103
- const result = resolver.resolve({ modelId: 'gpt-4.1' });
104
- expect(result.error).toBeDefined();
105
- expect(result.error?.error.code).toBe('INVALID_MODEL_SELECTION');
106
- });
107
- it('should return error for unsupported provider', () => {
108
- const result = resolver.resolve({
109
- providerId: 'unsupported-provider',
110
- modelId: 'some-model'
111
- });
112
- expect(result.error).toBeDefined();
113
- expect(result.error?.error.code).toBe('UNSUPPORTED_PROVIDER');
114
- expect(result.error?.error.message).toContain('Unsupported provider');
115
- expect(result.error?.error.message).toContain('Supported providers:');
116
- });
117
- it('should create fallback model info for unknown models (with warning)', () => {
118
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
119
- const result = resolver.resolve({
120
- providerId: 'openai',
121
- modelId: 'unsupported-model'
122
- });
123
- // Should succeed with fallback, not error
124
- expect(result.error).toBeUndefined();
125
- expect(result.modelInfo).toBeDefined();
126
- expect(result.modelInfo?.id).toBe('unsupported-model');
127
- expect(result.modelInfo?.providerId).toBe('openai');
128
- // Should warn about unknown model
129
- expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Unknown model "unsupported-model"'));
130
- consoleWarnSpy.mockRestore();
131
- });
132
- it('should silently create fallback for llamacpp unknown models (no warning)', () => {
133
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
134
- const result = resolver.resolve({
135
- providerId: 'llamacpp',
136
- modelId: 'my-custom-gguf-model'
137
- });
138
- // Should succeed with fallback, not error
139
- expect(result.error).toBeUndefined();
140
- expect(result.modelInfo).toBeDefined();
141
- expect(result.modelInfo?.id).toBe('my-custom-gguf-model');
142
- expect(result.modelInfo?.providerId).toBe('llamacpp');
143
- // Should NOT warn (llamacpp allows unknown models silently)
144
- expect(consoleWarnSpy).not.toHaveBeenCalled();
145
- consoleWarnSpy.mockRestore();
146
- });
147
- it('should pass through user settings for direct resolution', () => {
148
- const settings = {
149
- temperature: 0.8,
150
- maxTokens: 2000
151
- };
152
- const result = resolver.resolve({
153
- providerId: 'openai',
154
- modelId: 'gpt-4.1',
155
- settings
156
- });
157
- expect(result.settings).toBe(settings); // Should be the same reference
158
- });
159
- });
160
- describe('priority handling', () => {
161
- it('should prioritize presetId over providerId/modelId when both provided', () => {
162
- const mockPreset = {
163
- id: 'test-preset',
164
- displayName: 'Test Preset',
165
- providerId: 'anthropic',
166
- modelId: 'claude-3-5-sonnet-20241022',
167
- settings: {}
168
- };
169
- mockPresetManager.resolvePreset.mockReturnValue(mockPreset);
170
- const result = resolver.resolve({
171
- presetId: 'test-preset',
172
- providerId: 'openai', // These should be ignored
173
- modelId: 'gpt-4.1' // These should be ignored
174
- });
175
- expect(result.providerId).toBe('anthropic');
176
- expect(result.modelId).toBe('claude-3-5-sonnet-20241022');
177
- });
178
- });
179
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,210 +0,0 @@
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 PresetManager_1 = require("./PresetManager");
7
- const presets_json_1 = __importDefault(require("../../config/presets.json"));
8
- describe('PresetManager', () => {
9
- describe('Default behavior', () => {
10
- it('should load default presets when no options provided', () => {
11
- const manager = new PresetManager_1.PresetManager();
12
- const presets = manager.getPresets();
13
- expect(presets).toHaveLength(presets_json_1.default.length);
14
- expect(presets).toEqual(expect.arrayContaining(presets_json_1.default.map(preset => expect.objectContaining({
15
- id: preset.id,
16
- displayName: preset.displayName,
17
- providerId: preset.providerId,
18
- modelId: preset.modelId
19
- }))));
20
- });
21
- it('should return a copy of presets to prevent external modification', () => {
22
- const manager = new PresetManager_1.PresetManager();
23
- const presets1 = manager.getPresets();
24
- const presets2 = manager.getPresets();
25
- expect(presets1).not.toBe(presets2); // Different array instances
26
- expect(presets1).toEqual(presets2); // Same content
27
- // Modifying returned array should not affect service
28
- presets1.push({
29
- id: 'test-preset',
30
- displayName: 'Test',
31
- providerId: 'openai',
32
- modelId: 'gpt-4',
33
- settings: {}
34
- });
35
- const presets3 = manager.getPresets();
36
- expect(presets3).toHaveLength(presets_json_1.default.length);
37
- });
38
- });
39
- describe('Extend mode', () => {
40
- it('should add new presets to defaults in extend mode', () => {
41
- const customPresets = [
42
- {
43
- id: 'custom-preset-1',
44
- displayName: 'Custom Preset 1',
45
- providerId: 'openai',
46
- modelId: 'gpt-4',
47
- settings: { temperature: 0.5 }
48
- }
49
- ];
50
- const manager = new PresetManager_1.PresetManager(customPresets, 'extend');
51
- const presets = manager.getPresets();
52
- expect(presets).toHaveLength(presets_json_1.default.length + 1);
53
- expect(presets).toContainEqual(expect.objectContaining({
54
- id: 'custom-preset-1',
55
- displayName: 'Custom Preset 1'
56
- }));
57
- });
58
- it('should override default presets with same ID in extend mode', () => {
59
- const existingPresetId = presets_json_1.default[0].id;
60
- const customPresets = [
61
- {
62
- id: existingPresetId,
63
- displayName: 'Overridden Preset',
64
- providerId: 'anthropic',
65
- modelId: 'claude-3-5-sonnet-20241022',
66
- settings: { temperature: 0.8 }
67
- }
68
- ];
69
- const manager = new PresetManager_1.PresetManager(customPresets, 'extend');
70
- const presets = manager.getPresets();
71
- const overriddenPreset = presets.find(p => p.id === existingPresetId);
72
- expect(presets).toHaveLength(presets_json_1.default.length);
73
- expect(overriddenPreset).toBeDefined();
74
- expect(overriddenPreset?.displayName).toBe('Overridden Preset');
75
- expect(overriddenPreset?.providerId).toBe('anthropic');
76
- });
77
- it('should use extend mode by default when mode not specified', () => {
78
- const customPresets = [
79
- {
80
- id: 'custom-preset-default',
81
- displayName: 'Custom Default',
82
- providerId: 'gemini',
83
- modelId: 'gemini-2.0-flash',
84
- settings: { temperature: 0.3 }
85
- }
86
- ];
87
- const manager = new PresetManager_1.PresetManager(customPresets);
88
- const presets = manager.getPresets();
89
- expect(presets).toHaveLength(presets_json_1.default.length + 1);
90
- });
91
- });
92
- describe('Replace mode', () => {
93
- it('should use only custom presets in replace mode', () => {
94
- const customPresets = [
95
- {
96
- id: 'replace-preset-1',
97
- displayName: 'Replace Preset 1',
98
- providerId: 'openai',
99
- modelId: 'gpt-4',
100
- settings: { temperature: 0.5 }
101
- },
102
- {
103
- id: 'replace-preset-2',
104
- displayName: 'Replace Preset 2',
105
- providerId: 'anthropic',
106
- modelId: 'claude-3-5-sonnet-20241022',
107
- settings: { temperature: 0.3 }
108
- }
109
- ];
110
- const manager = new PresetManager_1.PresetManager(customPresets, 'replace');
111
- const presets = manager.getPresets();
112
- expect(presets).toHaveLength(2);
113
- expect(presets).toEqual(expect.arrayContaining([
114
- expect.objectContaining({ id: 'replace-preset-1' }),
115
- expect.objectContaining({ id: 'replace-preset-2' })
116
- ]));
117
- // Should not contain any default presets
118
- const defaultPresetIds = presets_json_1.default.map(p => p.id);
119
- const actualPresetIds = presets.map(p => p.id);
120
- expect(actualPresetIds).not.toContain(expect.arrayContaining(defaultPresetIds));
121
- });
122
- it('should return empty array when replace mode with no custom presets', () => {
123
- const manager = new PresetManager_1.PresetManager([], 'replace');
124
- const presets = manager.getPresets();
125
- expect(presets).toHaveLength(0);
126
- });
127
- it('should handle undefined presets array in replace mode', () => {
128
- const manager = new PresetManager_1.PresetManager(undefined, 'replace');
129
- const presets = manager.getPresets();
130
- expect(presets).toHaveLength(0);
131
- });
132
- });
133
- describe('Edge cases', () => {
134
- it('should handle duplicate IDs within custom presets', () => {
135
- const customPresets = [
136
- {
137
- id: 'duplicate-id',
138
- displayName: 'First Preset',
139
- providerId: 'openai',
140
- modelId: 'gpt-4',
141
- settings: { temperature: 0.5 }
142
- },
143
- {
144
- id: 'duplicate-id',
145
- displayName: 'Second Preset',
146
- providerId: 'anthropic',
147
- modelId: 'claude-3-5-sonnet-20241022',
148
- settings: { temperature: 0.3 }
149
- }
150
- ];
151
- const manager = new PresetManager_1.PresetManager(customPresets, 'replace');
152
- const presets = manager.getPresets();
153
- const duplicatePresets = presets.filter(p => p.id === 'duplicate-id');
154
- // Last one should win
155
- expect(duplicatePresets).toHaveLength(1);
156
- expect(duplicatePresets[0].displayName).toBe('Second Preset');
157
- });
158
- it('should handle presets with complex settings including gemini safety settings', () => {
159
- const customPresets = [
160
- {
161
- id: 'gemini-complex',
162
- displayName: 'Gemini Complex',
163
- providerId: 'gemini',
164
- modelId: 'gemini-2.0-flash',
165
- settings: {
166
- temperature: 0.5,
167
- maxTokens: 2000,
168
- geminiSafetySettings: [
169
- { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
170
- { category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' }
171
- ]
172
- }
173
- }
174
- ];
175
- const manager = new PresetManager_1.PresetManager(customPresets, 'replace');
176
- const presets = manager.getPresets();
177
- expect(presets).toHaveLength(1);
178
- expect(presets[0].settings.geminiSafetySettings).toHaveLength(2);
179
- });
180
- });
181
- describe('resolvePreset', () => {
182
- it('should find preset by ID', () => {
183
- const customPresets = [
184
- {
185
- id: 'test-preset-1',
186
- displayName: 'Test Preset 1',
187
- providerId: 'openai',
188
- modelId: 'gpt-4',
189
- settings: {}
190
- }
191
- ];
192
- const manager = new PresetManager_1.PresetManager(customPresets, 'replace');
193
- const preset = manager.resolvePreset('test-preset-1');
194
- expect(preset).toBeDefined();
195
- expect(preset?.displayName).toBe('Test Preset 1');
196
- });
197
- it('should return null for non-existent preset', () => {
198
- const manager = new PresetManager_1.PresetManager();
199
- const preset = manager.resolvePreset('non-existent-preset');
200
- expect(preset).toBeNull();
201
- });
202
- it('should find default preset in extend mode', () => {
203
- const manager = new PresetManager_1.PresetManager();
204
- const firstDefaultPresetId = presets_json_1.default[0].id;
205
- const preset = manager.resolvePreset(firstDefaultPresetId);
206
- expect(preset).toBeDefined();
207
- expect(preset?.id).toBe(firstDefaultPresetId);
208
- });
209
- });
210
- });
@@ -1 +0,0 @@
1
- export {};